diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index ed0ffc23df..8ccc8a7405 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -1,545 +1,545 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2013 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 java.beans.PropertyVetoException; -import java.util.ArrayList; -import java.util.List; -import org.sleuthkit.autopsy.datamodel.VolumeNode; -import org.sleuthkit.autopsy.datamodel.DirectoryNode; -import java.util.logging.Level; -import org.sleuthkit.autopsy.coreutils.Logger; -import javax.swing.AbstractAction; -import javax.swing.Action; -import org.openide.explorer.ExplorerManager; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.FilterNode; -import org.openide.nodes.Node; -import org.openide.nodes.Sheet; -import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType; -import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode; -import org.sleuthkit.autopsy.datamodel.ArtifactTypeNode; -import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; -import org.sleuthkit.autopsy.datamodel.LocalFileNode; -import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsChildren.DeletedContentNode; -import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsNode; -import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; -import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedAccountNode; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedFolderNode; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedRootNode; -import org.sleuthkit.autopsy.datamodel.ExtractedContentNode; -import org.sleuthkit.autopsy.datamodel.FileNode; -import org.sleuthkit.autopsy.datamodel.FileSearchFilterNode; -import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNode; -import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; -import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsRootNode; -import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode; -import org.sleuthkit.autopsy.datamodel.ImageNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode; -import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode; -import org.sleuthkit.autopsy.datamodel.LayoutFileNode; -import org.sleuthkit.autopsy.datamodel.RecentFilesFilterNode; -import org.sleuthkit.autopsy.datamodel.RecentFilesNode; -import org.sleuthkit.autopsy.datamodel.SearchFiltersNode; -import org.sleuthkit.autopsy.datamodel.Tags.TagNodeRoot; -import org.sleuthkit.autopsy.datamodel.Tags.TagsNodeRoot; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.DerivedFile; -import org.sleuthkit.datamodel.Directory; -import org.sleuthkit.datamodel.File; -import org.sleuthkit.datamodel.LayoutFile; -import org.sleuthkit.datamodel.LocalFile; -import org.sleuthkit.datamodel.TskException; -import org.sleuthkit.datamodel.VirtualDirectory; - -/** - * This class wraps nodes as they are passed to the DataResult viewers. It - * defines the actions that the node should have. - */ -public class DataResultFilterNode extends FilterNode { - - private ExplorerManager sourceEm; - private final DisplayableItemNodeVisitor> getActionsDIV; - private final DisplayableItemNodeVisitor getPreferredActionsDIV; - - /** - * - * @param node Root node to be passed to DataResult viewers - * @param em ExplorerManager for component that is creating the node - */ - public DataResultFilterNode(Node node, ExplorerManager em) { - super(node, new DataResultFilterChildren(node, em)); - this.sourceEm = em; - getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor(); - getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor(); - } - - /** - * Right click action for the nodes that we want to pass to the directory - * table and the output view. - * - * @param popup - * @return actions - */ - @Override - public Action[] getActions(boolean popup) { - - List actions = new ArrayList<>(); - - final DisplayableItemNode originalNode = (DisplayableItemNode) this.getOriginal(); - actions.addAll(originalNode.accept(getActionsDIV)); - - //actions.add(new IndexContentFilesAction(nodeContent, "Index")); - - return actions.toArray(new Action[actions.size()]); - } - - /** - * Double click action for the nodes that we want to pass to the directory - * table and the output view. - * - * @return action - */ - @Override - public Action getPreferredAction() { - // double click action(s) for volume node or directory node - - final DisplayableItemNode originalNode; - originalNode = (DisplayableItemNode) this.getOriginal(); - - return originalNode.accept(getPreferredActionsDIV); - } - - @Override - public Node.PropertySet[] getPropertySets() { - Node.PropertySet[] propertySets = super.getPropertySets(); - - for (int i = 0; i < propertySets.length; i++) { - Node.PropertySet ps = propertySets[i]; - - if (ps.getName().equals(Sheet.PROPERTIES)) { - Sheet.Set newPs = new Sheet.Set(); - newPs.setName(ps.getName()); - newPs.setDisplayName(ps.getDisplayName()); - newPs.setShortDescription(ps.getShortDescription()); - - newPs.put(ps.getProperties()); - if (newPs.remove(AbstractFsContentNode.HIDE_PARENT) != null) { - newPs.remove(AbstractFilePropertyType.LOCATION.toString()); - } - propertySets[i] = newPs; - } - } - - return propertySets; - } - - /** - * Uses the default nodes actions per node, adds some custom ones and - * returns them per visited node type - */ - private static class GetPopupActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default> { - - @Override - public List visit(BlackboardArtifactNode ban) { - //set up actions for artifact node based on its Content object - //TODO all actions need to be consolidated in single place! - //they should be set in individual Node subclass and using a utility to get Actions per Content sub-type - - List actions = new ArrayList<>(); - - //merge predefined specific node actions if bban subclasses have their own - for (Action a : ban.getActions(true)) { - actions.add(a); - } - BlackboardArtifact ba = ban.getLookup().lookup(BlackboardArtifact.class); - final int artifactTypeID = ba.getArtifactTypeID(); - - if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() - || artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { - actions.add(new ViewContextAction("View File in Directory", ban)); - } else { - Content c = findLinked(ban); - if (c != null) { - actions.add(new ViewContextAction("View File in Directory", c)); - } - actions.add(new ViewContextAction("View Source File in Directory", ban)); - } - File f = ban.getLookup().lookup(File.class); - LayoutFile lf = null; - AbstractFile locF = null; - Directory d = null; - VirtualDirectory vd = null; - if (f != null) { - final FileNode fn = new FileNode(f); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction("View in New Window", fn)); - actions.add(new ExternalViewerAction("Open in External Viewer", fn)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - actions.add(new HashSearchAction("Search for files with the same MD5 hash", fn)); - - //add file/result tag if itself is not a tag - if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() - && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { - actions.add(null); // creates a menu separator - actions.add(TagAbstractFileAction.getInstance()); - actions.add(TagBlackboardArtifactAction.getInstance()); - } - } - if ((d = ban.getLookup().lookup(Directory.class)) != null) { - DirectoryNode dn = new DirectoryNode(d); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction("View in New Window", dn)); - actions.add(new ExternalViewerAction("Open in External Viewer", dn)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - - //add file/result tag if itself is not a tag - if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() - && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { - actions.add(null); // creates a menu separator - actions.add(TagAbstractFileAction.getInstance()); - actions.add(TagBlackboardArtifactAction.getInstance()); - } - } - if ((vd = ban.getLookup().lookup(VirtualDirectory.class)) != null) { - VirtualDirectoryNode dn = new VirtualDirectoryNode(vd); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction("View in New Window", dn)); - actions.add(new ExternalViewerAction("Open in External Viewer", dn)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - - //add file/result tag if itself is not a tag - if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() - && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { - actions.add(null); // creates a menu separator - actions.add(TagAbstractFileAction.getInstance()); - actions.add(TagBlackboardArtifactAction.getInstance()); - } - } else if ((lf = ban.getLookup().lookup(LayoutFile.class)) != null) { - LayoutFileNode lfn = new LayoutFileNode(lf); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction("View in New Window", lfn)); - actions.add(new ExternalViewerAction("Open in External Viewer", lfn)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - - //add tag if itself is not a tag - if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() - && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { - actions.add(null); // creates a menu separator - actions.add(TagAbstractFileAction.getInstance()); - actions.add(TagBlackboardArtifactAction.getInstance()); - } - } else if ((locF = ban.getLookup().lookup(LocalFile.class)) != null - || (locF = ban.getLookup().lookup(DerivedFile.class)) != null) { - final LocalFileNode locfn = new LocalFileNode(locF); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction("View in New Window", locfn)); - actions.add(new ExternalViewerAction("Open in External Viewer", locfn)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - - //add tag if itself is not a tag - if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() - && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { - actions.add(null); // creates a menu separator - actions.add(TagAbstractFileAction.getInstance()); - actions.add(TagBlackboardArtifactAction.getInstance()); - } - } - - return actions; - } - - @Override - protected List defaultVisit(DisplayableItemNode ditem) { - //preserve the default node's actions - List actions = new ArrayList<>(); - - for (Action action : ditem.getActions(true)) { - actions.add(action); - } - - return actions; - } - - private Content findLinked(BlackboardArtifactNode ba) { - BlackboardArtifact art = ba.getLookup().lookup(BlackboardArtifact.class); - Content c = null; - try { - for (BlackboardAttribute attr : art.getAttributes()) { - if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) { - switch (attr.getValueType()) { - case INTEGER: - int i = attr.getValueInt(); - if (i != -1) { - c = art.getSleuthkitCase().getContentById(i); - } - break; - case LONG: - long l = attr.getValueLong(); - if (l != -1) { - c = art.getSleuthkitCase().getContentById(l); - } - break; - } - } - } - } catch (TskException ex) { - Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error getting linked file", ex); - } - return c; - } - } - - /* - * Action for double-click / preferred action on nodes. - */ - private class GetPreferredActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default { - - @Override - public AbstractAction visit(ImageNode in) { - return openChild(in); - } - - @Override - public AbstractAction visit(VolumeNode vn) { - return openChild(vn); - } - - @Override - public AbstractAction visit(ExtractedContentNode ecn) { - return openChild(ecn); - } - - @Override - public AbstractAction visit(KeywordHitsRootNode khrn) { - return openChild(khrn); - } - - @Override - public AbstractAction visit(HashsetHitsRootNode hhrn) { - return openChild(hhrn); - } - - @Override - public AbstractAction visit(HashsetHitsSetNode hhsn) { - return openChild(hhsn); - } - - @Override - public AbstractAction visit(EmailExtractedRootNode eern) { - return openChild(eern); - } - - @Override - public AbstractAction visit(EmailExtractedAccountNode eean) { - return openChild(eean); - } - - @Override - public AbstractAction visit(EmailExtractedFolderNode eefn) { - return openChild(eefn); - } - - @Override - public AbstractAction visit(RecentFilesNode rfn) { - return openChild(rfn); - } - - @Override - public AbstractAction visit(DeletedContentsNode dcn) { - return openChild(dcn); - } - - @Override - public AbstractAction visit(DeletedContentNode dcn) { - return openChild(dcn); - } - - @Override - public AbstractAction visit(FileSizeRootNode fsrn) { - return openChild(fsrn); - } - - @Override - public AbstractAction visit(FileSizeNode fsn) { - return openChild(fsn); - } - - @Override - public AbstractAction visit(BlackboardArtifactNode ban) { - return new ViewContextAction("View in Directory", ban); - } - - @Override - public AbstractAction visit(ArtifactTypeNode atn) { - return openChild(atn); - } - - @Override - public AbstractAction visit(TagNodeRoot tnr) { - return openChild(tnr); - } - - @Override - public AbstractAction visit(TagsNodeRoot tnr) { - return openChild(tnr); - } - - @Override - public AbstractAction visit(DirectoryNode dn) { - if (dn.getDisplayName().equals(DirectoryNode.DOTDOTDIR)) { - return openParent(dn); - } - else if (dn.getDisplayName().equals(DirectoryNode.DOTDIR) == false) { - return openChild(dn); - } - else { - return null; - } - } - - @Override - public AbstractAction visit(VirtualDirectoryNode ldn) { - return openChild(ldn); - } - - @Override - public AbstractAction visit(FileNode fn) { - if (fn.hasContentChildren()) { - return openChild(fn); - } - else { - return null; - } - } - - @Override - public AbstractAction visit(LocalFileNode dfn) { - if (dfn.hasContentChildren()) { - return openChild(dfn); - } - else { - return null; - } - } - - @Override - public AbstractAction visit(FileSearchFilterNode fsfn) { - return openChild(fsfn); - } - - @Override - public AbstractAction visit(SearchFiltersNode sfn) { - return openChild(sfn); - } - - @Override - public AbstractAction visit(RecentFilesFilterNode rffn) { - return openChild(rffn); - } - - @Override - public AbstractAction visit(KeywordHitsListNode khsn) { - return openChild(khsn); - } - - @Override - public AbstractAction visit(KeywordHitsKeywordNode khmln) { - return openChild(khmln); - } - - @Override - protected AbstractAction defaultVisit(DisplayableItemNode c) { - return null; - } - - /** - * Tell the originating ExplorerManager to display the given node. - * @param node Original (non-filtered) node to open - * @return - */ - private AbstractAction openChild(AbstractNode node) { - // get the parent node from sourceEm because that will get us the filtered version of it. - // node.getParentNode() returns the low-level datamodel node. - final Node[] parentFilterNodes = sourceEm.getSelectedNodes(); - final Node parentFilterNode = parentFilterNodes[0]; - final Node originalNode = node; - - return new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - if (parentFilterNode != null) { - - // Find the filter version of the passed in node. - final int childrenNodesCount = parentFilterNode.getChildren().getNodesCount(); - for (int i = 0; i < childrenNodesCount; i++) { - Node childFilterNode = parentFilterNode.getChildren().getNodeAt(i); - if (childFilterNode != null && childFilterNode.getName().equals(originalNode.getName())) { - try { - sourceEm.setExploredContextAndSelection(childFilterNode, new Node[]{childFilterNode}); - break; - } catch (PropertyVetoException ex) { - // throw an error here - Logger logger = Logger.getLogger(DataResultFilterNode.class.getName()); - logger.log(Level.WARNING, "Error: can't open the selected directory.", ex); - } - } - } - } - } - }; - } - - /** - * Tell the originating ExplorerManager to display the parent of the given node. - * @param node Original (non-filtered) node to open - * @return - */ - private AbstractAction openParent(AbstractNode node) { - // @@@ Why do we ignore node? - Node[] selectedFilterNodes = sourceEm.getSelectedNodes(); - Node selectedFilterNode = selectedFilterNodes[0]; - final Node parentNode = selectedFilterNode.getParentNode(); - - return new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - try { - sourceEm.setSelectedNodes(new Node[]{parentNode}); - } catch (PropertyVetoException ex) { - Logger logger = Logger.getLogger(DataResultFilterNode.class.getName()); - logger.log(Level.WARNING, "Error: can't open the parent directory.", ex); - } - } - }; - } - } +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2013 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 java.beans.PropertyVetoException; +import java.util.ArrayList; +import java.util.List; +import org.sleuthkit.autopsy.datamodel.VolumeNode; +import org.sleuthkit.autopsy.datamodel.DirectoryNode; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import javax.swing.AbstractAction; +import javax.swing.Action; +import org.openide.explorer.ExplorerManager; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.FilterNode; +import org.openide.nodes.Node; +import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType; +import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode; +import org.sleuthkit.autopsy.datamodel.ArtifactTypeNode; +import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; +import org.sleuthkit.autopsy.datamodel.LocalFileNode; +import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsChildren.DeletedContentNode; +import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsNode; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; +import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedAccountNode; +import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedFolderNode; +import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedRootNode; +import org.sleuthkit.autopsy.datamodel.ExtractedContentNode; +import org.sleuthkit.autopsy.datamodel.FileNode; +import org.sleuthkit.autopsy.datamodel.FileTypeNode; +import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNode; +import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; +import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsRootNode; +import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode; +import org.sleuthkit.autopsy.datamodel.ImageNode; +import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; +import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; +import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode; +import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode; +import org.sleuthkit.autopsy.datamodel.LayoutFileNode; +import org.sleuthkit.autopsy.datamodel.RecentFilesFilterNode; +import org.sleuthkit.autopsy.datamodel.RecentFilesNode; +import org.sleuthkit.autopsy.datamodel.FileTypesNode; +import org.sleuthkit.autopsy.datamodel.Tags.TagNodeRoot; +import org.sleuthkit.autopsy.datamodel.Tags.TagsNodeRoot; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.DerivedFile; +import org.sleuthkit.datamodel.Directory; +import org.sleuthkit.datamodel.File; +import org.sleuthkit.datamodel.LayoutFile; +import org.sleuthkit.datamodel.LocalFile; +import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.datamodel.VirtualDirectory; + +/** + * This class wraps nodes as they are passed to the DataResult viewers. It + * defines the actions that the node should have. + */ +public class DataResultFilterNode extends FilterNode { + + private ExplorerManager sourceEm; + private final DisplayableItemNodeVisitor> getActionsDIV; + private final DisplayableItemNodeVisitor getPreferredActionsDIV; + + /** + * + * @param node Root node to be passed to DataResult viewers + * @param em ExplorerManager for component that is creating the node + */ + public DataResultFilterNode(Node node, ExplorerManager em) { + super(node, new DataResultFilterChildren(node, em)); + this.sourceEm = em; + getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor(); + getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor(); + } + + /** + * Right click action for the nodes that we want to pass to the directory + * table and the output view. + * + * @param popup + * @return actions + */ + @Override + public Action[] getActions(boolean popup) { + + List actions = new ArrayList<>(); + + final DisplayableItemNode originalNode = (DisplayableItemNode) this.getOriginal(); + actions.addAll(originalNode.accept(getActionsDIV)); + + //actions.add(new IndexContentFilesAction(nodeContent, "Index")); + + return actions.toArray(new Action[actions.size()]); + } + + /** + * Double click action for the nodes that we want to pass to the directory + * table and the output view. + * + * @return action + */ + @Override + public Action getPreferredAction() { + // double click action(s) for volume node or directory node + + final DisplayableItemNode originalNode; + originalNode = (DisplayableItemNode) this.getOriginal(); + + return originalNode.accept(getPreferredActionsDIV); + } + + @Override + public Node.PropertySet[] getPropertySets() { + Node.PropertySet[] propertySets = super.getPropertySets(); + + for (int i = 0; i < propertySets.length; i++) { + Node.PropertySet ps = propertySets[i]; + + if (ps.getName().equals(Sheet.PROPERTIES)) { + Sheet.Set newPs = new Sheet.Set(); + newPs.setName(ps.getName()); + newPs.setDisplayName(ps.getDisplayName()); + newPs.setShortDescription(ps.getShortDescription()); + + newPs.put(ps.getProperties()); + if (newPs.remove(AbstractFsContentNode.HIDE_PARENT) != null) { + newPs.remove(AbstractFilePropertyType.LOCATION.toString()); + } + propertySets[i] = newPs; + } + } + + return propertySets; + } + + /** + * Uses the default nodes actions per node, adds some custom ones and + * returns them per visited node type + */ + private static class GetPopupActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default> { + + @Override + public List visit(BlackboardArtifactNode ban) { + //set up actions for artifact node based on its Content object + //TODO all actions need to be consolidated in single place! + //they should be set in individual Node subclass and using a utility to get Actions per Content sub-type + + List actions = new ArrayList<>(); + + //merge predefined specific node actions if bban subclasses have their own + for (Action a : ban.getActions(true)) { + actions.add(a); + } + BlackboardArtifact ba = ban.getLookup().lookup(BlackboardArtifact.class); + final int artifactTypeID = ba.getArtifactTypeID(); + + if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() + || artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { + actions.add(new ViewContextAction("View File in Directory", ban)); + } else { + Content c = findLinked(ban); + if (c != null) { + actions.add(new ViewContextAction("View File in Directory", c)); + } + actions.add(new ViewContextAction("View Source File in Directory", ban)); + } + File f = ban.getLookup().lookup(File.class); + LayoutFile lf = null; + AbstractFile locF = null; + Directory d = null; + VirtualDirectory vd = null; + if (f != null) { + final FileNode fn = new FileNode(f); + actions.add(null); // creates a menu separator + actions.add(new NewWindowViewAction("View in New Window", fn)); + actions.add(new ExternalViewerAction("Open in External Viewer", fn)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + actions.add(new HashSearchAction("Search for files with the same MD5 hash", fn)); + + //add file/result tag if itself is not a tag + if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + actions.add(TagBlackboardArtifactAction.getInstance()); + } + } + if ((d = ban.getLookup().lookup(Directory.class)) != null) { + DirectoryNode dn = new DirectoryNode(d); + actions.add(null); // creates a menu separator + actions.add(new NewWindowViewAction("View in New Window", dn)); + actions.add(new ExternalViewerAction("Open in External Viewer", dn)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + + //add file/result tag if itself is not a tag + if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + actions.add(TagBlackboardArtifactAction.getInstance()); + } + } + if ((vd = ban.getLookup().lookup(VirtualDirectory.class)) != null) { + VirtualDirectoryNode dn = new VirtualDirectoryNode(vd); + actions.add(null); // creates a menu separator + actions.add(new NewWindowViewAction("View in New Window", dn)); + actions.add(new ExternalViewerAction("Open in External Viewer", dn)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + + //add file/result tag if itself is not a tag + if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + actions.add(TagBlackboardArtifactAction.getInstance()); + } + } else if ((lf = ban.getLookup().lookup(LayoutFile.class)) != null) { + LayoutFileNode lfn = new LayoutFileNode(lf); + actions.add(null); // creates a menu separator + actions.add(new NewWindowViewAction("View in New Window", lfn)); + actions.add(new ExternalViewerAction("Open in External Viewer", lfn)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + + //add tag if itself is not a tag + if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + actions.add(TagBlackboardArtifactAction.getInstance()); + } + } else if ((locF = ban.getLookup().lookup(LocalFile.class)) != null + || (locF = ban.getLookup().lookup(DerivedFile.class)) != null) { + final LocalFileNode locfn = new LocalFileNode(locF); + actions.add(null); // creates a menu separator + actions.add(new NewWindowViewAction("View in New Window", locfn)); + actions.add(new ExternalViewerAction("Open in External Viewer", locfn)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + + //add tag if itself is not a tag + if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + actions.add(TagBlackboardArtifactAction.getInstance()); + } + } + + return actions; + } + + @Override + protected List defaultVisit(DisplayableItemNode ditem) { + //preserve the default node's actions + List actions = new ArrayList<>(); + + for (Action action : ditem.getActions(true)) { + actions.add(action); + } + + return actions; + } + + private Content findLinked(BlackboardArtifactNode ba) { + BlackboardArtifact art = ba.getLookup().lookup(BlackboardArtifact.class); + Content c = null; + try { + for (BlackboardAttribute attr : art.getAttributes()) { + if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) { + switch (attr.getValueType()) { + case INTEGER: + int i = attr.getValueInt(); + if (i != -1) { + c = art.getSleuthkitCase().getContentById(i); + } + break; + case LONG: + long l = attr.getValueLong(); + if (l != -1) { + c = art.getSleuthkitCase().getContentById(l); + } + break; + } + } + } + } catch (TskException ex) { + Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error getting linked file", ex); + } + return c; + } + } + + /* + * Action for double-click / preferred action on nodes. + */ + private class GetPreferredActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default { + + @Override + public AbstractAction visit(ImageNode in) { + return openChild(in); + } + + @Override + public AbstractAction visit(VolumeNode vn) { + return openChild(vn); + } + + @Override + public AbstractAction visit(ExtractedContentNode ecn) { + return openChild(ecn); + } + + @Override + public AbstractAction visit(KeywordHitsRootNode khrn) { + return openChild(khrn); + } + + @Override + public AbstractAction visit(HashsetHitsRootNode hhrn) { + return openChild(hhrn); + } + + @Override + public AbstractAction visit(HashsetHitsSetNode hhsn) { + return openChild(hhsn); + } + + @Override + public AbstractAction visit(EmailExtractedRootNode eern) { + return openChild(eern); + } + + @Override + public AbstractAction visit(EmailExtractedAccountNode eean) { + return openChild(eean); + } + + @Override + public AbstractAction visit(EmailExtractedFolderNode eefn) { + return openChild(eefn); + } + + @Override + public AbstractAction visit(RecentFilesNode rfn) { + return openChild(rfn); + } + + @Override + public AbstractAction visit(DeletedContentsNode dcn) { + return openChild(dcn); + } + + @Override + public AbstractAction visit(DeletedContentNode dcn) { + return openChild(dcn); + } + + @Override + public AbstractAction visit(FileSizeRootNode fsrn) { + return openChild(fsrn); + } + + @Override + public AbstractAction visit(FileSizeNode fsn) { + return openChild(fsn); + } + + @Override + public AbstractAction visit(BlackboardArtifactNode ban) { + return new ViewContextAction("View in Directory", ban); + } + + @Override + public AbstractAction visit(ArtifactTypeNode atn) { + return openChild(atn); + } + + @Override + public AbstractAction visit(TagNodeRoot tnr) { + return openChild(tnr); + } + + @Override + public AbstractAction visit(TagsNodeRoot tnr) { + return openChild(tnr); + } + + @Override + public AbstractAction visit(DirectoryNode dn) { + if (dn.getDisplayName().equals(DirectoryNode.DOTDOTDIR)) { + return openParent(dn); + } + else if (dn.getDisplayName().equals(DirectoryNode.DOTDIR) == false) { + return openChild(dn); + } + else { + return null; + } + } + + @Override + public AbstractAction visit(VirtualDirectoryNode ldn) { + return openChild(ldn); + } + + @Override + public AbstractAction visit(FileNode fn) { + if (fn.hasContentChildren()) { + return openChild(fn); + } + else { + return null; + } + } + + @Override + public AbstractAction visit(LocalFileNode dfn) { + if (dfn.hasContentChildren()) { + return openChild(dfn); + } + else { + return null; + } + } + + @Override + public AbstractAction visit(FileTypeNode fsfn) { + return openChild(fsfn); + } + + @Override + public AbstractAction visit(FileTypesNode sfn) { + return openChild(sfn); + } + + @Override + public AbstractAction visit(RecentFilesFilterNode rffn) { + return openChild(rffn); + } + + @Override + public AbstractAction visit(KeywordHitsListNode khsn) { + return openChild(khsn); + } + + @Override + public AbstractAction visit(KeywordHitsKeywordNode khmln) { + return openChild(khmln); + } + + @Override + protected AbstractAction defaultVisit(DisplayableItemNode c) { + return null; + } + + /** + * Tell the originating ExplorerManager to display the given node. + * @param node Original (non-filtered) node to open + * @return + */ + private AbstractAction openChild(AbstractNode node) { + // get the parent node from sourceEm because that will get us the filtered version of it. + // node.getParentNode() returns the low-level datamodel node. + final Node[] parentFilterNodes = sourceEm.getSelectedNodes(); + final Node parentFilterNode = parentFilterNodes[0]; + final Node originalNode = node; + + return new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if (parentFilterNode != null) { + + // Find the filter version of the passed in node. + final int childrenNodesCount = parentFilterNode.getChildren().getNodesCount(); + for (int i = 0; i < childrenNodesCount; i++) { + Node childFilterNode = parentFilterNode.getChildren().getNodeAt(i); + if (childFilterNode != null && childFilterNode.getName().equals(originalNode.getName())) { + try { + sourceEm.setExploredContextAndSelection(childFilterNode, new Node[]{childFilterNode}); + break; + } catch (PropertyVetoException ex) { + // throw an error here + Logger logger = Logger.getLogger(DataResultFilterNode.class.getName()); + logger.log(Level.WARNING, "Error: can't open the selected directory.", ex); + } + } + } + } + } + }; + } + + /** + * Tell the originating ExplorerManager to display the parent of the given node. + * @param node Original (non-filtered) node to open + * @return + */ + private AbstractAction openParent(AbstractNode node) { + // @@@ Why do we ignore node? + Node[] selectedFilterNodes = sourceEm.getSelectedNodes(); + Node selectedFilterNode = selectedFilterNodes[0]; + final Node parentNode = selectedFilterNode.getParentNode(); + + return new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + try { + sourceEm.setSelectedNodes(new Node[]{parentNode}); + } catch (PropertyVetoException ex) { + Logger logger = Logger.getLogger(DataResultFilterNode.class.getName()); + logger.log(Level.WARNING, "Error: can't open the parent directory.", ex); + } + } + }; + } + } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 0c7c2ef543..05ae16d893 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -219,10 +219,16 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed // change the cursor to "waiting cursor" for this operation this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - // update the back and forward List + + // the end is the current place, String[] currentNodePath = backList.pollLast(); - String[] newCurrentNodePath = backList.peekLast(); forwardList.addLast(currentNodePath); + forwardButton.setEnabled(true); + + /* We peek instead of poll because we use its existence + * in the list later on so that we do not reset the forward list + * after the selection occurs. */ + String[] newCurrentNodePath = backList.peekLast(); // enable / disable the back and forward button if (backList.size() > 1) { @@ -230,39 +236,31 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } else { backButton.setEnabled(false); } - this.forwardButton.setEnabled(true); - + // update the selection on directory tree setSelectedNode(newCurrentNodePath, null); - this.setCursor(null); - }//GEN-LAST:event_backButtonActionPerformed private void forwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed // change the cursor to "waiting cursor" for this operation this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); -// try { - // update the back and forward List - //int newCurrentIndex = forwardList.size() - 1; - String[] newCurrentNodePath = forwardList.pollLast(); - //forwardList.remove(newCurrentIndex); - backList.addLast(newCurrentNodePath); - // enable / disable the back and forward button + String[] newCurrentNodePath = forwardList.pollLast(); if (!forwardList.isEmpty()) { forwardButton.setEnabled(true); } else { forwardButton.setEnabled(false); } - this.backButton.setEnabled(true); - + + backList.addLast(newCurrentNodePath); + backButton.setEnabled(true); + // update the selection on directory tree setSelectedNode(newCurrentNodePath, null); this.setCursor(null); - }//GEN-LAST:event_forwardButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton backButton; @@ -337,7 +335,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat ((BeanTreeView) this.jScrollPane1).setRootVisible(false); // hide the root } else { // if there's at least one image, load the image and open the top component - List items = new ArrayList(); + List items = new ArrayList<>(); final SleuthkitCase tskCase = currentCase.getSleuthkitCase(); items.add(new DataSources(tskCase)); items.add(new Views(tskCase)); @@ -375,7 +373,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat ((BeanTreeView) this.jScrollPane1).setRootVisible(false); // hide the root // Reset the forward and back lists because we're resetting the root context - resetHistoryListAndButtons(); + resetHistory(); Children childNodes = em.getRootContext().getChildren(); TreeView tree = getTree(); @@ -533,7 +531,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat // case opened if (newValue != null) { - resetHistoryListAndButtons(); + resetHistory(); } } // if the image is added to the case else if (changed.equals(Case.CASE_ADD_DATA_SOURCE)) { @@ -671,31 +669,53 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat }); // update the back and forward list - Node[] selectedNode = em.getSelectedNodes(); - if (selectedNode.length > 0) { - Node selectedContext = selectedNode[0]; - - final String[] selectedPath = NodeOp.createPath(selectedContext, em.getRootContext()); - String[] currentLast = backList.peekLast(); - String lastNodeName = null; - if (currentLast != null) { - lastNodeName = currentLast[currentLast.length - 1]; - } - String selectedNodeName = selectedContext.getName(); - if (currentLast == null || !selectedNodeName.equals(lastNodeName)) { - //add to the list if the last if not the same as current - - backList.addLast(selectedPath); // add the node to the "backList" - if (backList.size() > 1) { - backButton.setEnabled(true); - } else { - backButton.setEnabled(false); - } - - forwardList.clear(); // clear the "forwardList" - forwardButton.setEnabled(false); // disable the forward Button - } + updateHistory(em.getSelectedNodes()); + } + + private void updateHistory(Node[] selectedNodes) { + if (selectedNodes.length == 0) { + return; } + + Node selectedNode = selectedNodes[0]; + String selectedNodeName = selectedNode.getName(); + + /* get the previous entry to make sure we don't duplicate it. + * Motivation for this is also that if we used the back button, + * then we already added the 'current' node to 'back' and we will + * detect that and not reset the forward list. + */ + String[] currentLast = backList.peekLast(); + String lastNodeName = null; + if (currentLast != null) { + lastNodeName = currentLast[currentLast.length - 1]; + } + + if (currentLast == null || !selectedNodeName.equals(lastNodeName)) { + //add to the list if the last if not the same as current + final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext()); + backList.addLast(selectedPath); // add the node to the "backList" + if (backList.size() > 1) { + backButton.setEnabled(true); + } else { + backButton.setEnabled(false); + } + + forwardList.clear(); // clear the "forwardList" + forwardButton.setEnabled(false); // disable the forward Button + } + } + + /** + * Resets the back and forward list, and also disable the back and forward + * buttons. + */ + private void resetHistory() { + // clear the back and forward list + backList.clear(); + forwardList.clear(); + backButton.setEnabled(false); + forwardButton.setEnabled(false); } @Override @@ -708,17 +728,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat pcs.removePropertyChangeListener(listener); } - /** - * Resets the back and forward list, and also disable the back and forward - * buttons. - */ - private void resetHistoryListAndButtons() { - // clear the back and forward list - backList.clear(); - forwardList.clear(); - backButton.setEnabled(false); - forwardButton.setEnabled(false); - } + /** * Gets the tree on this DirectoryTreeTopComponent. @@ -845,19 +855,17 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat if (path.length > 0 && (rootNodeName == null || path[0].equals(rootNodeName))) { try { - final TreeView tree = getTree(); Node newSelection = NodeOp.findPath(em.getRootContext(), path); - //resetHistoryListAndButtons(); + if (newSelection != null) { if (rootNodeName != null) { //called from tree auto refresh context //remove last from backlist, because auto select will result in duplication backList.pollLast(); } - //select - //tree.expandNode(newSelection); em.setExploredContextAndSelection(newSelection, new Node[]{newSelection}); } + // We need to set the selection, which will refresh dataresult and get rid of the oob exception } catch (NodeNotFoundException ex) { logger.log(Level.WARNING, "Node not found", ex);