diff --git a/DataModel/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java b/DataModel/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java index 89166b0828..9750d9489c 100644 --- a/DataModel/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java +++ b/DataModel/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java @@ -24,11 +24,14 @@ import java.io.IOException; import java.io.InputStream; import java.util.LinkedList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentVisitor; import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.File; import org.sleuthkit.datamodel.FileSystem; +import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.Volume; @@ -39,6 +42,8 @@ import org.sleuthkit.datamodel.VolumeSystem; */ public final class ContentUtils { + private final static Logger logger = Logger.getLogger(ContentUtils.class.getName()); + // don't instantiate private ContentUtils() { throw new AssertionError(); @@ -201,4 +206,118 @@ public final class ContentUtils { out.close(); } } + + /** + * Helper to ignore the '.' and '..' directories + */ + public static boolean isDotDirectory(Directory dir) { + String name = dir.getName(); + return name.equals(".") || name.equals(".."); + } + + + /** + * Extracts file/folder as given destination file, recursing into folders. + * Assumes there will be no collisions with existing directories/files, and + * that the directory to contain the destination file already exists. + */ + public static class ExtractFscContentVisitor extends ContentVisitor.Default { + + java.io.File dest; + + /** + * Make new extractor for a specific destination + * @param dest The file/folder visited will be extracted as this file + */ + public ExtractFscContentVisitor(java.io.File dest) { + this.dest = dest; + } + + /** + * Convenience method to make a new instance for given destination + * and extract given content + */ + public static void extract(Content cntnt, java.io.File dest) { + cntnt.accept(new ExtractFscContentVisitor(dest)); + } + + public Void visit(File f) { + try { + ContentUtils.writeToFile(f, dest); + } catch (IOException ex) { + logger.log(Level.SEVERE, + "Trouble extracting file to " + dest.getAbsolutePath(), + ex); + } + return null; + } + + @Override + public Void visit(Directory dir) { + + // don't extract . and .. directories + if (isDotDirectory(dir)) { + return null; + } + + dest.mkdir(); + + // member visitor to generate destination files for children + DestFileContentVisitor destFileCV = new DestFileContentVisitor(); + + try { + // recurse on children + for (Content child : dir.getChildren()) { + java.io.File childFile = child.accept(destFileCV); + ExtractFscContentVisitor childVisitor = + new ExtractFscContentVisitor(childFile); + child.accept(childVisitor); + } + } catch (TskException ex) { + logger.log(Level.SEVERE, + "Trouble fetching children to extract.", ex); + } + + return null; + } + + @Override + protected Void defaultVisit(Content cntnt) { + throw new UnsupportedOperationException("Can't extract a " + + cntnt.getClass().getSimpleName()); + } + + /** + * Helper visitor to get the destination file for a child Content object + */ + private class DestFileContentVisitor extends + ContentVisitor.Default { + + /** + * Get destination file by adding File/Directory name to the path + * of parent + */ + private java.io.File getFsContentDest(FsContent fsc) { + String path = dest.getAbsolutePath() + java.io.File.separator + + fsc.getName(); + return new java.io.File(path); + } + + @Override + public java.io.File visit(File f) { + return getFsContentDest(f); + } + + @Override + public java.io.File visit(Directory dir) { + return getFsContentDest(dir); + } + + @Override + protected java.io.File defaultVisit(Content cntnt) { + throw new UnsupportedOperationException("Can't get destination file for a " + + cntnt.getClass().getSimpleName()); + } + } + } } diff --git a/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 6d56da242a..588df9b53a 100644 --- a/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -70,40 +70,10 @@ public class DataResultFilterNode extends FilterNode{ List actions = new ArrayList(); - actions.add(new NewWindowViewAction("View in New Window", getOriginal())); - // TODO: ContentNode fix - restore right-click actions - // TODO: ContentVisitor instead of instanceof - Content nodeContent = this.getOriginal().getLookup().lookup(Content.class); actions.addAll(nodeContent.accept(getActionsCV)); - - -// // right click action(s) for image node -// if (originalNode instanceof ImageNode) { -// actions.add(new NewWindowViewAction("View in New Window", (ImageNode) originalNode)); -// actions.addAll(ShowDetailActionVisitor.getActions(nodeContent)); -// } // right click action(s) for volume node -// else if (originalNode instanceof VolumeNode) { -// actions.add(new NewWindowViewAction("View in New Window", (VolumeNode) originalNode)); -// //new ShowDetailActionVisitor("Volume Details", this.currentNode.getName(), (VolumeNode) this.currentNode), -// actions.addAll(ShowDetailActionVisitor.getActions(nodeContent)); -// actions.add(new ChangeViewAction("View", 0, this.getOriginal())); -// } // right click action(s) for directory node -// else if (originalNode instanceof DirectoryNode) { -// actions.add(new NewWindowViewAction("View in New Window", (DirectoryNode) originalNode)); -// actions.add(new ChangeViewAction("View", 0, originalNode)); -// // TODO: ContentNode fix - reimplement ExtractAction -// //actions.add(new ExtractAction("Extract Directory", (DirectoryNode) this.currentNode)); -// } // right click action(s) for the file node -// else if (originalNode instanceof FileNode) { -// actions.add(new ExternalViewerAction("Open File in External Viewer", (FileNode) originalNode)); -// actions.add(new NewWindowViewAction("View in New Window", (FileNode) originalNode)); -// // TODO: ContentNode fix - reimplement ExtractAction -// //actions.add(new ExtractAction("Extract", (FileNode) this.currentNode)); -// actions.add(new ChangeViewAction("View", 0, originalNode)); -// } return actions.toArray(new Action[actions.size()]); } @@ -127,8 +97,7 @@ public class DataResultFilterNode extends FilterNode{ public List visit(Directory dir) { List actions = new ArrayList(); actions.add(new ChangeViewAction("View", 0, getOriginal())); - // TODO: ContentNode fix - reimplement ExtractAction - //actions.add(new ExtractAction("Extract Directory", (DirectoryNode) this.currentNode)); + actions.add(new ExtractAction("Extract Directory", getOriginal())); return actions; } @@ -136,8 +105,7 @@ public class DataResultFilterNode extends FilterNode{ public List visit(File f) { List actions = new ArrayList(); actions.add(new ExternalViewerAction("Open File in External Viewer", getOriginal())); - // TODO: ContentNode fix - reimplement ExtractAction - //actions.add(new ExtractAction("Extract", (FileNode) this.currentNode)); + actions.add(new ExtractAction("Extract", getOriginal())); return actions; } diff --git a/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java b/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java index 546fe65d65..3ea8aa522f 100644 --- a/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java +++ b/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java @@ -1,225 +1,111 @@ -// TODO: ContentNode fix - reimplement ExtractAction +/* + * Autopsy Forensic Browser + * + * Copyright 2011 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.directorytree; +import java.awt.event.ActionEvent; +import javax.swing.JFileChooser; +import java.io.File; +import java.awt.Component; +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import org.openide.nodes.Node; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor; +import org.sleuthkit.autopsy.logging.Log; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentVisitor; +import org.sleuthkit.datamodel.Directory; +import org.sleuthkit.datamodel.FsContent; -///* -// * Autopsy Forensic Browser -// * -// * Copyright 2011 Basis Technology Corp. -// * Contact: carrier sleuthkit org -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -//package org.sleuthkit.autopsy.directorytree; -// -//import org.sleuthkit.autopsy.datamodel.FileNode; -//import java.io.*; -//import java.awt.event.ActionEvent; -//import javax.swing.JFileChooser; -//import java.io.File; -//import java.awt.Component; -//import java.util.logging.Level; -//import java.util.logging.Logger; -//import javax.swing.AbstractAction; -//import javax.swing.JPanel; -//import javax.swing.filechooser.FileFilter; -//import org.openide.nodes.Node; -//import org.sleuthkit.autopsy.casemodule.GeneralFilter; -//import org.sleuthkit.autopsy.datamodel.DirectoryNode; -//import org.sleuthkit.autopsy.logging.Log; -//import org.sleuthkit.datamodel.Content; -//import org.sleuthkit.datamodel.TskException; -// -///** -// * This is an action class to extract and save the bytes given as a file. -// * -// * @author jantonius -// */ -//public final class ExtractAction extends AbstractAction { -// -// private JFileChooser fc = new JFileChooser(); -// private byte[] source; -// private Content content; -// private String fileName; -// private String extension; -// // for error handling -// private JPanel caller; -// private String className = this.getClass().toString(); -// -// /** the constructor */ -// public ExtractAction(String title, Node contentNode) { -// super(title); -// -// String fullFileName = contentNode.getDisplayName(); -// -// -// if (fullFileName.equals(".")) { -// // . folders are not associated with their children in the database, -// // so get original -// Node parentNode = contentNode.getParentNode(); -// contentNode = parentNode; -// fullFileName = parentNode.getDisplayName(); -// } -// -// -// this.content = contentNode.getLookup().lookup(Content.class); -// -// long size = content.getSize(); -// -// -// -// /** -// * Checks first if the the selected it file or directory. If it's a file, -// * check if the file size is bigger than 0. If it's a directory, check -// * if it's not referring to the parent directory. Disables the menu otherwise. -// */ -// if ((contentNode instanceof FileNode && size > 0) || (contentNode instanceof DirectoryNode && !fullFileName.equals(".."))) { -// if (contentNode instanceof FileNode && fullFileName.contains(".")) { -// String tempFileName = fullFileName.substring(0, fullFileName.indexOf(".")); -// String tempExtension = fullFileName.substring(fullFileName.indexOf(".")); -// this.fileName = tempFileName; -// this.extension = tempExtension; -// } else { -// this.fileName = fullFileName; -// this.extension = ""; -// } -// } else { -// this.fileName = fullFileName; -// this.extension = ""; -// this.setEnabled(false); // can't extract zero-sized file or ".." directory -// } -// -// } -// -// /** -// * Converts and saves the bytes into the file. -// * -// * @param e the action event -// */ -// @Override -// public void actionPerformed(ActionEvent e) { -// Log.noteAction(this.getClass()); -// -// // set the filter for File -// if (content instanceof org.sleuthkit.datamodel.File && !extension.equals("")) { -// //FileFilter filter = new ExtensionFileFilter(extension.substring(1).toUpperCase() + " File (*" + extension + ")", new String[]{extension.substring(1)}); -// String[] fileExt = {extension}; -// FileFilter filter = new GeneralFilter(fileExt, extension.substring(1).toUpperCase() + " File (*" + extension + ")", false); -// fc.setFileFilter(filter); -// } -// -// fc.setSelectedFile(new File(this.fileName)); -// -// int returnValue = fc.showSaveDialog((Component) e.getSource()); -// if (returnValue == JFileChooser.APPROVE_OPTION) { -// String path = fc.getSelectedFile().getPath() + extension; -// -// -// // TODO: convert this to a Content Visitor -// try { -// // file extraction -// if (content instanceof org.sleuthkit.datamodel.File) { -// extractFile(path, (org.sleuthkit.datamodel.File) content); -// } -// -// // directory extraction -// if (content instanceof org.sleuthkit.datamodel.File) { -// extractDirectory(path, (org.sleuthkit.datamodel.Directory) content); -// } -// } catch (Exception ex) { -// Logger.getLogger(this.className).log(Level.WARNING, "Error: Couldn't extract file/directory.", ex); -// } -// -// } -// -// } -// -// /** -// * Extracts the content of the given fileNode into the given path. -// * -// * @param givenPath the path to extract the file -// * @param file the file node that contain the file -// */ -// private void extractFile(String givenPath, org.sleuthkit.datamodel.File file) throws Exception { -// try { -// if (file.getSize() > 0) { -// try { -// this.source = file.read(0, file.getSize()); -// } catch (TskException ex) { -// throw new Exception("Error: can't read the content of the file.", ex); -// } -// } else { -// this.source = new byte[0]; -// } -// -// String path = givenPath; -// -// File outputFile = new File(path); -// if (outputFile.exists()) { -// outputFile.delete(); -// } -// outputFile.createNewFile(); -// // convert char to byte -// byte[] dataSource = new byte[source.length]; -// for (int i = 0; i < source.length; i++) { -// dataSource[i] = (byte) source[i]; -// } -// FileOutputStream fos = new FileOutputStream(outputFile); -// //fos.write(dataSource); -// fos.write(dataSource); -// fos.close(); -// } catch (IOException ex) { -// throw new Exception("Error while trying to extract the file.", ex); -// } -// } -// -// /** -// * Extracts the content of the given directoryNode into the given path. -// * -// * @param givenPath the path to extract the directory -// * @param dirNode the directory node that contain the directory -// */ -// private void extractDirectory(String givenPath, DirectoryNode dirNode) throws Exception { -// String path = givenPath; -// File dir = new File(path); -// if (!dir.exists()) { -// dir.mkdir(); -// } -// -// int totalChildren = dirNode.getChildren().getNodesCount(); -// for (int i = 0; i < totalChildren; i++) { -// Node childNode = dirNode.getChildren().getNodeAt(i); -// -// if (childNode instanceof FileNode) { -// FileNode fileNode = (FileNode) childNode; -// String tempPath = path + File.separator + ((Node)fileNode).getDisplayName(); -// try { -// extractFile(tempPath, fileNode); -// } catch (Exception ex) { -// throw ex; -// } -// } -// -// if (childNode instanceof DirectoryNode) { -// DirectoryNode dirNode2 = (DirectoryNode) childNode; -// String dirNode2Name = ((Node)dirNode2).getDisplayName(); -// -// if (!dirNode2Name.trim().equals(".") && !dirNode2Name.trim().equals("..")) { -// String tempPath = path + File.separator + dirNode2Name; -// extractDirectory(tempPath, dirNode2); -// } -// } -// } -// -// -// } -//} +/** + * Exports files and folders + */ +public final class ExtractAction extends AbstractAction { + + private static final InitializeContentVisitor initializeCV = new InitializeContentVisitor(); + private FsContent fsContent; + + public ExtractAction(String title, Node contentNode) { + super(title); + Content tempContent = contentNode.getLookup().lookup(Content.class); + + this.fsContent = tempContent.accept(initializeCV); + this.setEnabled(fsContent != null); + } + + /** + * Returns the FsContent if it is supported, otherwise null + */ + private static class InitializeContentVisitor extends ContentVisitor.Default { + + @Override + public FsContent visit(org.sleuthkit.datamodel.File f) { + return f; + } + + @Override + public FsContent visit(Directory dir) { + return ContentUtils.isDotDirectory(dir) ? null : dir; + } + + @Override + protected FsContent defaultVisit(Content cntnt) { + return null; + } + } + + /** + * Asks user to choose destination, then extracts file/directory to + * destination (recursing on directories) + * @param e the action event + */ + @Override + public void actionPerformed(ActionEvent e) { + Log.noteAction(this.getClass()); + + JFileChooser fc = new JFileChooser(); + fc.setSelectedFile(new File(this.fsContent.getName())); + int returnValue = fc.showSaveDialog((Component) e.getSource()); + + if (returnValue == JFileChooser.APPROVE_OPTION) { + File destination = fc.getSelectedFile(); + + // check that it's okay to overwrite existing file + if (destination.exists()) { + int choice = JOptionPane.showConfirmDialog( + (Component) e.getSource(), + "Destination file already exists, it will be overwritten.", + "Destination already exists!", + JOptionPane.OK_CANCEL_OPTION); + + if (choice != JOptionPane.OK_OPTION) { + return; + } + + if (!destination.delete()) { + JOptionPane.showMessageDialog( + (Component) e.getSource(), + "Couldn't delete existing file."); + } + } + + ExtractFscContentVisitor.extract(fsContent, destination); + } + } +}