Autopsy 4.22.1
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 */
19package org.sleuthkit.autopsy.directorytree;
20
21import java.awt.EventQueue;
22import java.awt.event.ActionEvent;
23import java.beans.PropertyVetoException;
24import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.Collections;
27import java.util.List;
28import java.util.Objects;
29import java.util.logging.Level;
30import java.util.stream.Collectors;
31import java.util.stream.Stream;
32import org.sleuthkit.autopsy.coreutils.Logger;
33import javax.swing.AbstractAction;
34import org.apache.commons.lang3.StringUtils;
35import org.openide.nodes.AbstractNode;
36import org.openide.explorer.ExplorerManager;
37import org.openide.explorer.view.TreeView;
38import org.openide.nodes.Children;
39import org.openide.nodes.Node;
40import org.openide.util.NbBundle.Messages;
41import org.sleuthkit.autopsy.casemodule.Case;
42import org.sleuthkit.autopsy.casemodule.CasePreferences;
43import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
44import org.sleuthkit.autopsy.core.UserPreferences;
45import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
46import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
47import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode;
48import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
49import org.sleuthkit.autopsy.datamodel.ContentNodeSelectionInfo;
50import org.sleuthkit.autopsy.datamodel.DataSourcesNode;
51import org.sleuthkit.autopsy.datamodel.DataSourceFilesNode;
52import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
53import org.sleuthkit.autopsy.datamodel.PersonNode;
54import org.sleuthkit.autopsy.datamodel.RootContentChildren;
55import org.sleuthkit.datamodel.AbstractFile;
56import org.sleuthkit.datamodel.BlackboardArtifact;
57import org.sleuthkit.datamodel.Content;
58import org.sleuthkit.datamodel.ContentVisitor;
59import org.sleuthkit.datamodel.DataSource;
60import org.sleuthkit.datamodel.FileSystem;
61import org.sleuthkit.datamodel.Host;
62import org.sleuthkit.datamodel.Person;
63import org.sleuthkit.datamodel.SleuthkitCase;
64import org.sleuthkit.datamodel.TskCoreException;
65import org.sleuthkit.datamodel.TskData;
66import org.sleuthkit.datamodel.TskDataException;
67import org.sleuthkit.datamodel.UnsupportedContent;
68import org.sleuthkit.datamodel.VolumeSystem;
69
76public 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();
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}
synchronized static Logger getLogger(String name)
Definition Logger.java:124
void setChildNodeSelectionInfo(NodeSelectionInfo selectedChildNodeInfo)
Node getParentNodeGroupedByDataSource(ExplorerManager treeViewExplorerMgr, Content parentContent)
ViewContextAction(String displayName, BlackboardArtifactNode artifactNode)
ViewContextAction(String displayName, Content content)
ViewContextAction(String displayName, AbstractAbstractFileNode<? extends AbstractFile > abstractAbstractFileNode)
void setNodeSelection(Content content, Node parentTreeViewNode, DirectoryTreeTopComponent treeViewTopComponent, ExplorerManager treeViewExplorerMgr)
ViewContextAction(String displayName, AbstractFsContentNode<? extends AbstractFile > fileSystemContentNode)
Node getParentNodeGroupedByPersonHost(ExplorerManager treeViewExplorerMgr, Content parentContent)
Node findParentNodeInTree(Content parentContent, Node node)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.