Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ViewContextAction.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.directorytree;
20 
21 import java.awt.EventQueue;
22 import java.awt.event.ActionEvent;
23 import java.beans.PropertyVetoException;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Objects;
29 import java.util.logging.Level;
30 import java.util.stream.Collectors;
31 import java.util.stream.Stream;
33 import javax.swing.AbstractAction;
34 import org.apache.commons.lang3.StringUtils;
35 import org.openide.nodes.AbstractNode;
36 import org.openide.explorer.ExplorerManager;
37 import org.openide.explorer.view.TreeView;
38 import org.openide.nodes.Children;
39 import org.openide.nodes.Node;
40 import org.openide.util.NbBundle.Messages;
55 import org.sleuthkit.datamodel.AbstractFile;
56 import org.sleuthkit.datamodel.BlackboardArtifact;
57 import org.sleuthkit.datamodel.Content;
58 import org.sleuthkit.datamodel.ContentVisitor;
59 import org.sleuthkit.datamodel.DataSource;
60 import org.sleuthkit.datamodel.FileSystem;
61 import org.sleuthkit.datamodel.Host;
62 import org.sleuthkit.datamodel.Person;
63 import org.sleuthkit.datamodel.SleuthkitCase;
64 import org.sleuthkit.datamodel.TskCoreException;
65 import org.sleuthkit.datamodel.TskData;
66 import org.sleuthkit.datamodel.TskDataException;
67 import org.sleuthkit.datamodel.UnsupportedContent;
68 import org.sleuthkit.datamodel.VolumeSystem;
69 
76 public class ViewContextAction extends AbstractAction {
77 
78  private static final long serialVersionUID = 1L;
79  private static final Logger logger = Logger.getLogger(ViewContextAction.class.getName());
80  private final Content content;
81 
91  public ViewContextAction(String displayName, BlackboardArtifactNode artifactNode) {
92  super(displayName);
93  this.content = artifactNode.getLookup().lookup(Content.class);
94  if (this.content != null && this.content instanceof AbstractFile) {
95  AbstractFile file = (AbstractFile) content;
96  //disable the action if the content is a file and the file is hidden
97  if ((TskData.FileKnown.KNOWN == file.getKnown() && UserPreferences.hideKnownFilesInDataSourcesTree())
98  || (TskData.TSK_DB_FILES_TYPE_ENUM.SLACK == file.getType() && UserPreferences.hideSlackFilesInDataSourcesTree())) {
99  this.setEnabled(false);
100  }
101  }
102  }
103 
114  public ViewContextAction(String displayName, AbstractFsContentNode<? extends AbstractFile> fileSystemContentNode) {
115  super(displayName);
116  this.content = fileSystemContentNode.getLookup().lookup(Content.class);
117  }
118 
129  public ViewContextAction(String displayName, AbstractAbstractFileNode<? extends AbstractFile> abstractAbstractFileNode) {
130  super(displayName);
131  this.content = abstractAbstractFileNode.getLookup().lookup(Content.class);
132  }
133 
143  public ViewContextAction(String displayName, Content content) {
144  super(displayName);
145  this.content = content;
146  }
147 
162  @Override
163  @Messages({
164  "ViewContextAction.errorMessage.cannotFindDirectory=Failed to locate directory.",
165  "ViewContextAction.errorMessage.cannotSelectDirectory=Failed to select directory in tree.",
166  "ViewContextAction.errorMessage.cannotFindNode=Failed to locate data source node in tree.",
167  "ViewContextAction.errorMessage.unsupportedParent=Unable to navigate to content not supported in this release."
168  })
169  public void actionPerformed(ActionEvent event) {
170  EventQueue.invokeLater(() -> {
171 
172  Content parentContent = getParentContent(this.content);
173 
174  if ((parentContent != null) && (parentContent instanceof UnsupportedContent)) {
175  MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_unsupportedParent());
176  logger.log(Level.WARNING, String.format("Could not navigate to unsupported content with id: %d", parentContent.getId())); //NON-NLS
177  return;
178  }
179 
180  // Get the "Data Sources" node from the tree view.
182  ExplorerManager treeViewExplorerMgr = treeViewTopComponent.getExplorerManager();
183 
184  Node parentTreeViewNode = null;
185  if (parentContent != null) {
186  if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
187  parentTreeViewNode = getParentNodeGroupedByPersonHost(treeViewExplorerMgr, parentContent);
188  } else {
189  parentTreeViewNode = getParentNodeGroupedByDataSource(treeViewExplorerMgr, parentContent);
190  }
191  }
192 
193  // if no node is found, report error and do nothing
194  if (parentTreeViewNode == null) {
195  MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_cannotFindNode());
196  logger.log(Level.SEVERE, "Failed to locate data source node in tree."); //NON-NLS
197  return;
198  }
199 
200  setNodeSelection(this.content, parentTreeViewNode, treeViewTopComponent, treeViewExplorerMgr);
201  });
202  }
203 
217  private Content getParentContent(Content content) {
218 
219  try {
220  return (content instanceof DataSource)
221  ? content
222  : content.getParent();
223  } catch (TskCoreException ex) {
224  MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_cannotFindDirectory());
225  logger.log(Level.SEVERE, String.format("Could not get parent of Content object: %s", content), ex); //NON-NLS
226  return null;
227  }
228  }
229 
241  private Node getParentNodeGroupedByDataSource(ExplorerManager treeViewExplorerMgr, Content parentContent) {
242  // Classic view
243  // Start the search at the DataSourcesNode
244  Children rootChildren = treeViewExplorerMgr.getRootContext().getChildren();
245  Node rootDsNode = rootChildren == null ? null : rootChildren.findChild(DataSourcesNode.getNameIdentifier());
246  if (rootDsNode != null) {
247  for (Node dataSourceLevelNode : getDataSourceLevelNodes(rootDsNode)) {
248  DataSource dataSource = dataSourceLevelNode.getLookup().lookup(DataSource.class);
249  if (dataSource != null) {
250  // the tree view needs to be searched to find the parent treeview node.
251  Node potentialParentTreeViewNode = findParentNodeInTree(parentContent, dataSourceLevelNode);
252  if (potentialParentTreeViewNode != null) {
253  return potentialParentTreeViewNode;
254  }
255  }
256  }
257  }
258 
259  return null;
260  }
261 
273  private Node getParentNodeGroupedByPersonHost(ExplorerManager treeViewExplorerMgr, Content parentContent) {
274  // 'Group by Data Source' view
275 
276  SleuthkitCase skCase;
277  String dsname;
278  try {
279  // get the objid/name of the datasource of the selected content.
281  long contentDSObjid = parentContent.getDataSource().getId();
282  DataSource datasource = skCase.getDataSource(contentDSObjid);
283  dsname = datasource.getName();
284  Children rootChildren = treeViewExplorerMgr.getRootContext().getChildren();
285 
286  // the tree view needs to be searched to find the parent treeview node.
287  /* NOTE: we can't do a lookup by data source name here, becase if there
288  are multiple data sources with the same name, then "getChildren().findChild(dsname)"
289  simply returns the first one that it finds. Instead we have to loop over all
290  data sources with that name, and make sure we find the correct one.
291  */
292  List<Node> dataSourceLevelNodes = Stream.of(rootChildren.getNodes(true))
293  .flatMap(rootNode -> getDataSourceLevelNodes(rootNode).stream())
294  .collect(Collectors.toList());
295 
296  for (Node treeNode : dataSourceLevelNodes) {
297  // in the root, look for a data source node with the name of interest
298  if (!(treeNode.getName().equals(dsname))) {
299  continue;
300  }
301 
302  // for this data source, get the "Data Sources" child node
303  Node datasourceGroupingNode = treeNode.getChildren().findChild(DataSourceFilesNode.getNameIdentifier());
304 
305  // check whether this is the data source we are looking for
306  Node parentTreeViewNode = findParentNodeInTree(parentContent, datasourceGroupingNode);
307  if (parentTreeViewNode != null) {
308  // found the data source node
309  return parentTreeViewNode;
310  }
311  }
312  } catch (NoCurrentCaseException | TskDataException | TskCoreException ex) {
313  MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_cannotFindNode());
314  logger.log(Level.SEVERE, "Failed to locate data source node in tree.", ex); //NON-NLS
315  }
316 
317  return null;
318  }
319 
327  private void setNodeSelection(Content content, Node parentTreeViewNode, DirectoryTreeTopComponent treeViewTopComponent, ExplorerManager treeViewExplorerMgr) {
328  /*
329  * Set the child selection info of the parent tree node, then select
330  * the parent node in the tree view. The results view will retrieve
331  * this selection info and use it to complete this action when the
332  * tree view top component responds to the selection of the parent
333  * node by pushing it into the results view top component.
334  */
335  DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) parentTreeViewNode).getOriginal();
336  undecoratedParentNode.setChildNodeSelectionInfo(new ContentNodeSelectionInfo(content));
337  if (content instanceof BlackboardArtifact) {
338  BlackboardArtifact artifact = ((BlackboardArtifact) content);
339  long associatedId = artifact.getObjectID();
340  try {
341  Content associatedFileContent = artifact.getSleuthkitCase().getContentById(associatedId);
342  undecoratedParentNode.setChildNodeSelectionInfo(new ContentNodeSelectionInfo(associatedFileContent));
343  } catch (TskCoreException ex) {
344  logger.log(Level.SEVERE, "Could not find associated content from artifact with id %d", artifact.getId());
345  }
346  }
347 
348  TreeView treeView = treeViewTopComponent.getTree();
349  treeView.expandNode(parentTreeViewNode);
350  if (treeViewTopComponent.getSelectedNode().equals(parentTreeViewNode)) {
351  //In the case where our tree view already has the destination directory selected
352  //due to an optimization in the ExplorerManager.setExploredContextAndSelection method
353  //the property change we listen for to call DirectoryTreeTopComponent.respondSelection
354  //will not be sent so we call it manually ourselves after making
355  //the directory listing the active tab.
356  treeViewTopComponent.setDirectoryListingActive();
357  treeViewTopComponent.respondSelection(treeViewExplorerMgr.getSelectedNodes(), new Node[]{parentTreeViewNode});
358  } else {
359  try {
360  treeViewExplorerMgr.setExploredContextAndSelection(parentTreeViewNode, new Node[]{parentTreeViewNode});
361  } catch (PropertyVetoException ex) {
362  MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_cannotSelectDirectory());
363  logger.log(Level.SEVERE, "Failed to select the parent node in the tree view", ex); //NON-NLS
364  }
365  }
366  }
367 
376  private List<Node> getDataSourceLevelNodes(Node node) {
377  if (node == null) {
378  return Collections.emptyList();
379  } else if (node.getLookup().lookup(Host.class) != null
380  || node.getLookup().lookup(Person.class) != null
381  || DataSourcesNode.getNameIdentifier().equals(node.getLookup().lookup(String.class))
382  || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
383  Children children = node.getChildren();
384  Node[] childNodes = children == null ? null : children.getNodes(true);
385  if (childNodes == null) {
386  return Collections.emptyList();
387  }
388 
389  return Stream.of(node.getChildren().getNodes(true))
390  .flatMap(parent -> getDataSourceLevelNodes(parent).stream())
391  .collect(Collectors.toList());
392  } else {
393  return Arrays.asList(node);
394  }
395  }
396 
406  private Node findParentNodeInTree(Content parentContent, Node node) {
407  /*
408  * Get an ordered list of the ancestors of the specified
409  * content, starting with its data source.
410  *
411  */
412  AncestorVisitor ancestorVisitor = new AncestorVisitor();
413  List<Content> contentBranch = parentContent.accept(ancestorVisitor);
414  Collections.reverse(contentBranch);
415 
427  Node dummyRootNode = new DirectoryTreeFilterNode(new AbstractNode(new RootContentChildren(contentBranch)), true);
428  Children ancestorChildren = dummyRootNode.getChildren();
429 
430  // if content is the data source provided, return that.
431  if (ancestorChildren.getNodesCount() == 1 && StringUtils.equals(ancestorChildren.getNodeAt(0).getName(), node.getName())) {
432  return node;
433  }
434 
435  /*
436  * Search the tree for the parent node. Note that this algorithm
437  * simply discards "extra" ancestor nodes not shown in the tree,
438  * such as the root directory of the file system for file system
439  * content.
440  */
441  Children treeNodeChildren = node.getChildren();
442  Node parentTreeViewNode = null;
443  for (int i = 0; i < ancestorChildren.getNodesCount(); i++) {
444  Node ancestorNode = ancestorChildren.getNodeAt(i);
445  Node[] treeNodeChilds = treeNodeChildren.getNodes(true);
446  for (int j = 0; j < treeNodeChilds.length; j++) {
447  Node treeNode = treeNodeChilds[j];
448  if (ancestorNode.getName().equals(treeNode.getName())) {
449  parentTreeViewNode = treeNode;
450  treeNodeChildren = treeNode.getChildren();
451  break;
452  }
453  }
454  }
455  return parentTreeViewNode;
456  }
457 
463  private static class AncestorVisitor extends ContentVisitor.Default<List<Content>> {
464 
465  List<Content> lineage = new ArrayList<>();
466 
467  @Override
468  protected List<Content> defaultVisit(Content content) {
469  lineage.add(content);
470  Content parent = null;
471  try {
472  parent = content.getParent();
473  } catch (TskCoreException ex) {
474  logger.log(Level.SEVERE, String.format("Could not get parent of Content object: %s", content), ex); //NON-NLS
475  }
476  return parent == null ? lineage : parent.accept(this);
477  }
478 
479  @Override
480  public List<Content> visit(VolumeSystem volumeSystem) {
481  /*
482  * Volume systems are not shown in the tree view. This is not
483  * strictly necesssary given the algorithm above, but it is a simple
484  * optimization.
485  */
486  return skipToParent(volumeSystem);
487  }
488 
489  @Override
490  public List<Content> visit(FileSystem fileSystem) {
491  /*
492  * File systems are not shown in the tree view. This is not strictly
493  * necesssary given the algorithm above, but it is a simple
494  * optimization.
495  */
496  return skipToParent(fileSystem);
497  }
498 
499  private List<Content> skipToParent(Content content) {
500  Content parent = null;
501  try {
502  parent = content.getParent();
503  } catch (TskCoreException ex) {
504  logger.log(Level.SEVERE, String.format("Could not get parent of Content object: %s", content), ex); //NON-NLS
505  }
506  return parent == null ? lineage : parent.accept(this);
507  }
508  }
509 
510 }
Node getParentNodeGroupedByPersonHost(ExplorerManager treeViewExplorerMgr, Content parentContent)
ViewContextAction(String displayName, Content content)
ViewContextAction(String displayName, BlackboardArtifactNode artifactNode)
ViewContextAction(String displayName, AbstractFsContentNode<?extends AbstractFile > fileSystemContentNode)
Node findParentNodeInTree(Content parentContent, Node node)
ViewContextAction(String displayName, AbstractAbstractFileNode<?extends AbstractFile > abstractAbstractFileNode)
void setNodeSelection(Content content, Node parentTreeViewNode, DirectoryTreeTopComponent treeViewTopComponent, ExplorerManager treeViewExplorerMgr)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void setChildNodeSelectionInfo(NodeSelectionInfo selectedChildNodeInfo)
Node getParentNodeGroupedByDataSource(ExplorerManager treeViewExplorerMgr, Content parentContent)
static synchronized DirectoryTreeTopComponent findInstance()

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.