Autopsy  4.19.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
DirectoryTreeTopComponent.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2012-2020 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.Cursor;
22 import java.awt.EventQueue;
23 import java.beans.PropertyChangeEvent;
24 import java.beans.PropertyChangeListener;
25 import java.beans.PropertyVetoException;
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.EnumSet;
31 import java.util.HashSet;
32 import java.util.LinkedList;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Objects;
36 import java.util.Optional;
37 import java.util.Set;
38 import java.util.concurrent.ExecutionException;
39 import java.util.logging.Level;
40 import java.util.prefs.PreferenceChangeEvent;
41 import java.util.prefs.PreferenceChangeListener;
42 import java.util.stream.Collectors;
43 import java.util.stream.Stream;
44 import javax.swing.Action;
45 import javax.swing.SwingUtilities;
46 import javax.swing.SwingWorker;
47 import javax.swing.event.PopupMenuEvent;
48 import javax.swing.event.PopupMenuListener;
49 import javax.swing.tree.TreeSelectionModel;
50 import org.apache.commons.lang3.StringUtils;
51 import org.openide.explorer.ExplorerManager;
52 import org.openide.explorer.ExplorerUtils;
53 import org.openide.explorer.view.BeanTreeView;
54 import org.openide.nodes.AbstractNode;
55 import org.openide.nodes.Children;
56 import org.openide.nodes.Node;
57 import org.openide.nodes.NodeNotFoundException;
58 import org.openide.nodes.NodeOp;
59 import org.openide.util.NbBundle;
60 import org.openide.util.NbBundle.Messages;
61 import org.openide.windows.TopComponent;
62 import org.openide.windows.WindowManager;
93 import org.sleuthkit.datamodel.Account;
94 import org.sleuthkit.datamodel.BlackboardArtifact;
95 import org.sleuthkit.datamodel.BlackboardArtifact.Category;
96 import org.sleuthkit.datamodel.BlackboardAttribute;
97 import org.sleuthkit.datamodel.Content;
98 import org.sleuthkit.datamodel.DataSource;
99 import org.sleuthkit.datamodel.Host;
100 import org.sleuthkit.datamodel.OsAccount;
101 import org.sleuthkit.datamodel.Person;
102 import org.sleuthkit.datamodel.TskCoreException;
103 
107 // Registered as a service provider for DataExplorer in layer.xml
108 @Messages({
109  "DirectoryTreeTopComponent.resultsView.title=Listing"
110 })
111 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
112 public final class DirectoryTreeTopComponent extends TopComponent implements DataExplorer, ExplorerManager.Provider {
113 
114  private final transient ExplorerManager em = new ExplorerManager();
116  private final DataResultTopComponent dataResult = new DataResultTopComponent(Bundle.DirectoryTreeTopComponent_resultsView_title());
117  private final ViewPreferencesPanel viewPreferencesPanel = new ViewPreferencesPanel(true);
118  private final LinkedList<String[]> backList;
119  private final LinkedList<String[]> forwardList;
120  private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS
121  private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName());
123  private Children autopsyTreeChildren;
125  private boolean showRejectedResults;
126  private static final long DEFAULT_DATASOURCE_GROUPING_THRESHOLD = 5; // Threshold for prompting the user about grouping by data source
127  private static final String GROUPING_THRESHOLD_NAME = "GroupDataSourceThreshold";
128  private static final String SETTINGS_FILE = "CasePreferences.properties"; //NON-NLS
129 
130  // nodes to be opened if present at top level
131  private static final Set<String> NODES_TO_EXPAND = Stream.of(AnalysisResults.getName(), DataArtifacts.getName(), ViewsNode.NAME)
132  .collect(Collectors.toSet());
133 
138  initComponents();
139 
140  // only allow one item to be selected at a time
141  getTree().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
142 
143  // remove the close button
144  putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE);
145  setName(NbBundle.getMessage(DirectoryTreeTopComponent.class, "CTL_DirectoryTreeTopComponent"));
146  setToolTipText(NbBundle.getMessage(DirectoryTreeTopComponent.class, "HINT_DirectoryTreeTopComponent"));
147 
148  subscribeToChangeEvents();
149  associateLookup(ExplorerUtils.createLookup(em, getActionMap()));
150 
151  // set the back & forward list and also disable the back & forward button
152  this.backList = new LinkedList<>();
153  this.forwardList = new LinkedList<>();
154  backButton.setEnabled(false);
155  forwardButton.setEnabled(false);
156 
157  viewPreferencesPopupMenu.add(viewPreferencesPanel);
158  viewPreferencesPopupMenu.setSize(viewPreferencesPanel.getPreferredSize().width + 6, viewPreferencesPanel.getPreferredSize().height + 6);
159  viewPreferencesPopupMenu.addPopupMenuListener(new PopupMenuListener() {
160  @Override
161  public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
162  openViewPreferencesButton.setSelected(true);
163  }
164 
165  @Override
166  public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
167  openViewPreferencesButton.setSelected(false);
168  }
169 
170  @Override
171  public void popupMenuCanceled(PopupMenuEvent e) {
172  openViewPreferencesButton.setSelected(false);
173  }
174  });
175  }
176 
183  private void preExpandNodes(Children rootChildren) {
184  BeanTreeView tree = getTree();
185 
186  // using getNodes(true) to fetch children so that async nodes are loaded
187  Node[] rootChildrenNodes = rootChildren.getNodes(true);
188  if (rootChildrenNodes == null || rootChildrenNodes.length < 1) {
189  return;
190  }
191 
192  // expand all nodes parents of and including hosts if group by host/person
193  if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
194  Stream.of(rootChildrenNodes)
195  .flatMap((n) -> getHostNodesAndParents(n).stream())
196  .filter((n) -> n != null)
197  .forEach(tree::expandNode);
198  } else {
199  Stream.of(rootChildrenNodes)
200  .filter(n -> n != null && NODES_TO_EXPAND.contains(n.getName()))
201  .forEach(tree::expandNode);
202  }
203  }
204 
213  private List<Node> getHostNodesAndParents(Node node) {
214  if (node == null) {
215  return Collections.emptyList();
216  } else if (node.getLookup().lookup(Person.class) != null
217  || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
218  Children children = node.getChildren();
219  Node[] childNodes = children == null ? null : children.getNodes();
220  if (childNodes != null) {
221  return Stream.of(childNodes)
222  .flatMap((n) -> Stream.concat(Stream.of(n), getHostNodesAndParents(n).stream()))
223  .collect(Collectors.toList());
224  }
225  } else if (node.getLookup().lookup(Host.class) != null) {
226  return Arrays.asList(node);
227  }
228  return Collections.emptyList();
229  }
230 
234  private void subscribeToChangeEvents() {
235  UserPreferences.addChangeListener(new PreferenceChangeListener() {
236  @Override
237  public void preferenceChange(PreferenceChangeEvent evt) {
238  switch (evt.getKey()) {
247  refreshContentTreeSafe();
248  break;
250  refreshTagsTree();
251  break;
254  // TODO: Need a way to refresh the Views subtree alone.
255  refreshContentTreeSafe();
256  break;
257  }
258  }
259  });
260 
262  this.em.addPropertyChangeListener(this);
263  }
264 
266  this.dataResult.requestActive();
267  }
268 
269  public void openDirectoryListing() {
270  this.dataResult.open();
271  }
272 
274  return this.dataResult;
275  }
276 
282  public boolean getShowRejectedResults() {
283  return showRejectedResults;
284  }
285 
292  public void setShowRejectedResults(boolean showRejectedResults) {
293  this.showRejectedResults = showRejectedResults;
294  if (accounts != null) {
295  accounts.setShowRejected(showRejectedResults);
296  }
297  }
298 
304  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
305  private void initComponents() {
306 
307  viewPreferencesPopupMenu = new javax.swing.JPopupMenu();
308  treeView = new ExpansionBeanTreeView();
309  backButton = new javax.swing.JButton();
310  forwardButton = new javax.swing.JButton();
311  openViewPreferencesButton = new javax.swing.JButton();
312 
313  treeView.setBorder(null);
314 
315  backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back.png"))); // NOI18N
316  org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.backButton.text")); // NOI18N
317  backButton.setBorderPainted(false);
318  backButton.setContentAreaFilled(false);
319  backButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_disabled.png"))); // NOI18N
320  backButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
321  backButton.setMaximumSize(new java.awt.Dimension(55, 100));
322  backButton.setMinimumSize(new java.awt.Dimension(5, 5));
323  backButton.setPreferredSize(new java.awt.Dimension(24, 24));
324  backButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_hover.png"))); // NOI18N
325  backButton.addActionListener(new java.awt.event.ActionListener() {
326  public void actionPerformed(java.awt.event.ActionEvent evt) {
327  backButtonActionPerformed(evt);
328  }
329  });
330 
331  forwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward.png"))); // NOI18N
332  org.openide.awt.Mnemonics.setLocalizedText(forwardButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.forwardButton.text")); // NOI18N
333  forwardButton.setBorderPainted(false);
334  forwardButton.setContentAreaFilled(false);
335  forwardButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_disabled.png"))); // NOI18N
336  forwardButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
337  forwardButton.setMaximumSize(new java.awt.Dimension(55, 100));
338  forwardButton.setMinimumSize(new java.awt.Dimension(5, 5));
339  forwardButton.setPreferredSize(new java.awt.Dimension(24, 24));
340  forwardButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_hover.png"))); // NOI18N
341  forwardButton.addActionListener(new java.awt.event.ActionListener() {
342  public void actionPerformed(java.awt.event.ActionEvent evt) {
343  forwardButtonActionPerformed(evt);
344  }
345  });
346 
347  openViewPreferencesButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/view-preferences-23.png"))); // NOI18N
348  org.openide.awt.Mnemonics.setLocalizedText(openViewPreferencesButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.openViewPreferencesButton.text")); // NOI18N
349  openViewPreferencesButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
350  openViewPreferencesButton.setBorderPainted(false);
351  openViewPreferencesButton.setContentAreaFilled(false);
352  openViewPreferencesButton.setMaximumSize(new java.awt.Dimension(24, 24));
353  openViewPreferencesButton.setMinimumSize(new java.awt.Dimension(24, 24));
354  openViewPreferencesButton.setPreferredSize(new java.awt.Dimension(24, 24));
355  openViewPreferencesButton.addActionListener(new java.awt.event.ActionListener() {
356  public void actionPerformed(java.awt.event.ActionEvent evt) {
357  openViewPreferencesButtonActionPerformed(evt);
358  }
359  });
360 
361  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
362  this.setLayout(layout);
363  layout.setHorizontalGroup(
364  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
365  .addComponent(treeView)
366  .addGroup(layout.createSequentialGroup()
367  .addContainerGap()
368  .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
369  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
370  .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
371  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 140, Short.MAX_VALUE)
372  .addComponent(openViewPreferencesButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
373  .addContainerGap())
374  );
375  layout.setVerticalGroup(
376  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
377  .addGroup(layout.createSequentialGroup()
378  .addGap(0, 0, 0)
379  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
380  .addComponent(openViewPreferencesButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
381  .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
382  .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
383  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
384  .addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 919, Short.MAX_VALUE))
385  );
386  }// </editor-fold>//GEN-END:initComponents
387 
388  private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
389  // change the cursor to "waiting cursor" for this operation
390  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
391 
392  // the end is the current place,
393  String[] currentNodePath = backList.pollLast();
394  forwardList.addLast(currentNodePath);
395  forwardButton.setEnabled(true);
396 
397  /*
398  * We peek instead of poll because we use its existence in the list
399  * later on so that we do not reset the forward list after the selection
400  * occurs.
401  */
402  String[] newCurrentNodePath = backList.peekLast();
403 
404  // enable / disable the back and forward button
405  if (backList.size() > 1) {
406  backButton.setEnabled(true);
407  } else {
408  backButton.setEnabled(false);
409  }
410 
411  // update the selection on directory tree
412  setSelectedNode(newCurrentNodePath, null);
413 
414  this.setCursor(null);
415  }//GEN-LAST:event_backButtonActionPerformed
416 
417  private void forwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed
418  // change the cursor to "waiting cursor" for this operation
419  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
420 
421  String[] newCurrentNodePath = forwardList.pollLast();
422  if (!forwardList.isEmpty()) {
423  forwardButton.setEnabled(true);
424  } else {
425  forwardButton.setEnabled(false);
426  }
427 
428  backList.addLast(newCurrentNodePath);
429  backButton.setEnabled(true);
430 
431  // update the selection on directory tree
432  setSelectedNode(newCurrentNodePath, null);
433 
434  this.setCursor(null);
435  }//GEN-LAST:event_forwardButtonActionPerformed
436 
437  private void openViewPreferencesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openViewPreferencesButtonActionPerformed
438  viewPreferencesPanel.load();
439  viewPreferencesPopupMenu.show(openViewPreferencesButton, 0, openViewPreferencesButton.getHeight() - 1);
440  }//GEN-LAST:event_openViewPreferencesButtonActionPerformed
441 
442  // Variables declaration - do not modify//GEN-BEGIN:variables
443  private javax.swing.JButton backButton;
444  private javax.swing.JButton forwardButton;
445  private javax.swing.JButton openViewPreferencesButton;
446  private javax.swing.JScrollPane treeView;
447  private javax.swing.JPopupMenu viewPreferencesPopupMenu;
448  // End of variables declaration//GEN-END:variables
449 
458  public static synchronized DirectoryTreeTopComponent getDefault() {
459  if (instance == null) {
460  instance = new DirectoryTreeTopComponent();
461  }
462  return instance;
463  }
464 
471  public static synchronized DirectoryTreeTopComponent findInstance() {
472  WindowManager winManager = WindowManager.getDefault();
473  TopComponent win = winManager.findTopComponent(PREFERRED_ID);
474  if (win == null) {
475  LOGGER.warning(
476  "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system."); //NON-NLS
477  return getDefault();
478  }
479  if (win instanceof DirectoryTreeTopComponent) {
480  return (DirectoryTreeTopComponent) win;
481  }
482  LOGGER.warning(
483  "There seem to be multiple components with the '" + PREFERRED_ID //NON-NLS
484  + "' ID. That is a potential source of errors and unexpected behavior."); //NON-NLS
485  return getDefault();
486  }
487 
494  @Override
495  public int getPersistenceType() {
496  return TopComponent.PERSISTENCE_NEVER;
497  }
498 
505  private void promptForDataSourceGrouping(int dataSourceCount) {
507  GroupDataSourcesDialog dialog = new GroupDataSourcesDialog(dataSourceCount);
508  dialog.display();
509  if (dialog.groupByDataSourceSelected()) {
511  refreshContentTreeSafe();
512  } else {
514  }
515  }
516  }
517 
525  @NbBundle.Messages({"# {0} - dataSourceCount",
526  "DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contains {0} data sources. Would you like to group by data source for faster loading?",
527  "DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source?"})
528  @Override
529  public void componentOpened() {
530  // change the cursor to "waiting cursor" for this operation
531  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
532  Case openCase = null;
533  try {
534  openCase = Case.getCurrentCaseThrows();
535  } catch (NoCurrentCaseException ex) {
536  // No open case.
537  }
538  final Case currentCase = openCase;
539  // close the top component if there's no image in this case
540  if (!caseHasData(currentCase)) {
541  getTree().setRootVisible(false); // hide the root
542  } else {
543  // If the case contains a lot of data sources, and they aren't already grouping
544  // by data source, give the user the option to do so before loading the tree.
546  Long settingsThreshold = null;
547  if (ModuleSettings.settingExists(ModuleSettings.MAIN_SETTINGS, GROUPING_THRESHOLD_NAME)) {
548  try {
549  settingsThreshold = Long.parseLong(ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, GROUPING_THRESHOLD_NAME));
550  } catch (NumberFormatException ex) {
551  LOGGER.log(Level.SEVERE, "Group data sources threshold is not a number", ex);
552  }
553  } else {
554  ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, GROUPING_THRESHOLD_NAME, String.valueOf(DEFAULT_DATASOURCE_GROUPING_THRESHOLD));
555  }
556  final long threshold = settingsThreshold == null ? DEFAULT_DATASOURCE_GROUPING_THRESHOLD : settingsThreshold;
557 
558  new SwingWorker<Integer, Void>() {
559  @Override
560  protected Integer doInBackground() throws Exception {
561  int dataSourceCount = 0;
562  try {
563  dataSourceCount = currentCase.getDataSources().size();
564  } catch (TskCoreException ex) {
565  LOGGER.log(Level.SEVERE, "Error loading data sources", ex);
566  }
567  return dataSourceCount;
568  }
569 
570  @Override
571  protected void done() {
572  int dataSourceCount = 0;
573  try {
574  dataSourceCount = get();
575  } catch (ExecutionException | InterruptedException ex) {
576  LOGGER.log(Level.SEVERE, "Error loading data sources and getting count on background thread", ex);
577  }
579  && dataSourceCount > threshold) {
580  promptForDataSourceGrouping(dataSourceCount);
581  }
582  }
583  }.execute();
584  }
585 
586  // if there's at least one image, load the image and open the top componen
587  autopsyTreeChildFactory = new AutopsyTreeChildFactory();
588  autopsyTreeChildren = Children.create(autopsyTreeChildFactory, true);
589  Node root = new AbstractNode(autopsyTreeChildren) {
590  //JIRA-2807: What is the point of these overrides?
595  @Override
596  public Action[] getActions(boolean popup) {
597  return new Action[]{};
598  }
599 
600  // Overide the AbstractNode use of DefaultHandle to return
601  // a handle which can be serialized without a parent
602  @Override
603  public Node.Handle getHandle() {
604  return new Node.Handle() {
605  @Override
606  public Node getNode() throws IOException {
607  return em.getRootContext();
608  }
609  };
610  }
611  };
612 
613  root = new DirectoryTreeFilterNode(root, true);
614 
615  em.setRootContext(root);
616  em.getRootContext().setName(currentCase.getName());
617  em.getRootContext().setDisplayName(currentCase.getName());
618  getTree().setRootVisible(false); // hide the root
619 
620  // Reset the forward and back lists because we're resetting the root context
621  resetHistory();
622  new SwingWorker<Node[], Void>() {
623  @Override
624  protected Node[] doInBackground() throws Exception {
625  Children rootChildren = em.getRootContext().getChildren();
626  preExpandNodes(rootChildren);
627  /*
628  * JIRA-2806: What is this supposed to do? Right now it
629  * selects the data sources node, but the comment seems to
630  * indicate it is supposed to select the first datasource.
631  */
632  // select the first image node, if there is one
633  // (this has to happen after dataResult is opened, because the event
634  // of changing the selected node fires a handler that tries to make
635  // dataResult active)
636  if (rootChildren.getNodesCount() > 0) {
637  return new Node[]{rootChildren.getNodeAt(0)};
638  }
639  return new Node[]{};
640  }
641 
642  @Override
643  protected void done() {
644  super.done();
645 
646  // if the dataResult is not opened
647  if (!dataResult.isOpened()) {
648  dataResult.open(); // open the data result top component as well when the directory tree is opened
649  }
650  /*
651  * JIRA-2806: What is this supposed to do?
652  */
653  // select the first image node, if there is one
654  // (this has to happen after dataResult is opened, because the event
655  // of changing the selected node fires a handler that tries to make
656  // dataResult active)
657  try {
658  Node[] selections = get();
659  if (selections != null && selections.length > 0) {
660  em.setSelectedNodes(selections);
661  }
662  } catch (PropertyVetoException ex) {
663  LOGGER.log(Level.SEVERE, "Error setting default selected node.", ex); //NON-NLS
664  } catch (InterruptedException | ExecutionException ex) {
665  LOGGER.log(Level.SEVERE, "Error expanding tree to initial state.", ex); //NON-NLS
666  } finally {
667  setCursor(null);
668  }
669  }
670  }.execute();
671  }
672  }
673 
680  @Override
681  public void componentClosed() {
682  //@@@ push the selection node to null?
683  autopsyTreeChildren = null;
684  }
685 
686  void writeProperties(java.util.Properties p) {
687  // better to version settings since initial version as advocated at
688  // http://wiki.apidesign.org/wiki/PropertyFiles
689  p.setProperty("version", "1.0");
690  // TODO store your settings
691  }
692 
693  Object readProperties(java.util.Properties p) {
694  if (instance == null) {
695  instance = this;
696  }
697  instance.readPropertiesImpl(p);
698  return instance;
699  }
700 
701  private void readPropertiesImpl(java.util.Properties p) {
702  String version = p.getProperty("version");
703  // TODO read your settings according to their version
704  }
705 
711  @Override
712  protected String preferredID() {
713  return PREFERRED_ID;
714  }
715 
716  @Override
717  public boolean canClose() {
718  /*
719  * Only allow the main tree view in the left side of the main window to
720  * be closed if there is no opne case or the open case has no data
721  * sources.
722  */
723  try {
724  Case openCase = Case.getCurrentCaseThrows();
725  return caseHasData(openCase) == false;
726  } catch (NoCurrentCaseException ex) {
727  return true;
728  }
729  }
730 
736  @Override
737  public ExplorerManager getExplorerManager() {
738  return this.em;
739  }
740 
746  @Override
747  public Action[] getActions() {
748  return new Action[]{};
749  }
750 
756  public Node getSelectedNode() {
757  Node result = null;
758 
759  Node[] selectedNodes = this.getExplorerManager().getSelectedNodes();
760  if (selectedNodes.length > 0) {
761  result = selectedNodes[0];
762  }
763  return result;
764  }
765 
772  @Override
773  public void propertyChange(PropertyChangeEvent event) {
775  String changed = event.getPropertyName();
776  if (changed.equals(Case.Events.CURRENT_CASE.toString())) { // changed current case
777  // When a case is closed, the old value of this property is the
778  // closed Case object and the new value is null. When a case is
779  // opened, the old value is null and the new value is the new Case
780  // object.
781  // @@@ This needs to be revisited. Perhaps case closed and case
782  // opened events instead of property change events would be a better
783  // solution. Either way, more probably needs to be done to clean up
784  // data model objects when a case is closed.
785  if (event.getOldValue() != null && event.getNewValue() == null) {
786  // The current case has been closed. Reset the ExplorerManager.
787  SwingUtilities.invokeLater(() -> {
788  Node emptyNode = new AbstractNode(Children.LEAF);
789  em.setRootContext(emptyNode);
790  });
791  } else if (event.getNewValue() != null) {
792  // A new case has been opened. Reset the ExplorerManager.
793  Case newCase = (Case) event.getNewValue();
794  final String newCaseName = newCase.getName();
795  SwingUtilities.invokeLater(() -> {
796  em.getRootContext().setName(newCaseName);
797  em.getRootContext().setDisplayName(newCaseName);
798 
799  // Reset the forward and back
800  // buttons. Note that a call to CoreComponentControl.openCoreWindows()
801  // by the new Case object will lead to a componentOpened() call
802  // that will repopulate the tree.
803  // @@@ The repopulation of the tree in this fashion also merits
804  // reconsideration.
805  resetHistory();
806  });
807  }
808  } // if the image is added to the case
809  else if (changed.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
816  try {
818  /*
819  * In case the Case 'updateGUIForCaseOpened()' method hasn't
820  * already done so, open the tree and all other core
821  * windows.
822  *
823  * TODO: (JIRA-4053) DirectoryTreeTopComponent should not be
824  * responsible for opening core windows. Consider moving
825  * this elsewhere.
826  */
827  SwingUtilities.invokeLater(() -> {
828  if (!DirectoryTreeTopComponent.this.isOpened()) {
830  }
831  });
832  } catch (NoCurrentCaseException notUsed) {
836  }
837  } // change in node selection
838  else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
839  respondSelection((Node[]) event.getOldValue(), (Node[]) event.getNewValue());
840  }
841  }
842  }
843 
852  @NbBundle.Messages("DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.")
853  void respondSelection(final Node[] oldNodes, final Node[] newNodes) {
854  if (!Case.isCaseOpen()) {
855  return;
856  }
857 
858  // Some lock that prevents certain Node operations is set during the
859  // ExplorerManager selection-change, so we must handle changes after the
860  // selection-change event is processed.
861  //TODO find a different way to refresh data result viewer, scheduling this
862  //to EDT breaks loading of nodes in the background
863  EventQueue.invokeLater(() -> {
864  // change the cursor to "waiting cursor" for this operation
865  DirectoryTreeTopComponent.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
866  try {
867  Node treeNode = DirectoryTreeTopComponent.this.getSelectedNode();
868  if (treeNode != null) {
869  Node originNode = ((DirectoryTreeFilterNode) treeNode).getOriginal();
870  //set node, wrap in filter node first to filter out children
871  Node drfn = new DataResultFilterNode(originNode, DirectoryTreeTopComponent.this.em);
872  // Create a TableFilterNode with knowledge of the node's type to allow for column order settings
873  if (FileTypesByMimeType.isEmptyMimeTypeNode(originNode)) {
874  //Special case for when File Type Identification has not yet been run and
875  //there are no mime types to populate Files by Mime Type Tree
876  EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTreeTopComponent_emptyMimeNode_text());
877  dataResult.setNode(new TableFilterNode(emptyNode, true, "This Node Is Empty")); //NON-NLS
878  } else if (originNode instanceof DisplayableItemNode) {
879  dataResult.setNode(new TableFilterNode(drfn, true, ((DisplayableItemNode) originNode).getItemType()));
880  } else {
881  dataResult.setNode(new TableFilterNode(drfn, true));
882  }
883  String displayName = "";
884  Content content = originNode.getLookup().lookup(Content.class);
885  if (content != null) {
886  try {
887  displayName = content.getUniquePath();
888  } catch (TskCoreException ex) {
889  LOGGER.log(Level.SEVERE, "Exception while calling Content.getUniquePath() for node: {0}", originNode); //NON-NLS
890  }
891  } else if (originNode.getLookup().lookup(String.class) != null) {
892  displayName = originNode.getLookup().lookup(String.class);
893  }
894  dataResult.setPath(displayName);
895  }
896  // set the directory listing to be active
897  if (oldNodes != null && newNodes != null
898  && (oldNodes.length == newNodes.length)) {
899  boolean sameNodes = true;
900  for (int i = 0; i < oldNodes.length; i++) {
901  sameNodes = sameNodes && oldNodes[i].getName().equals(newNodes[i].getName());
902  }
903  if (!sameNodes) {
904  dataResult.requestActive();
905  }
906  }
907  } finally {
908  setCursor(null);
909  }
910  });
911 
912  // update the back and forward list
913  updateHistory(em.getSelectedNodes());
914  }
915 
916  private void updateHistory(Node[] selectedNodes) {
917  if (selectedNodes.length == 0) {
918  return;
919  }
920 
921  Node selectedNode = selectedNodes[0];
922  String selectedNodeName = selectedNode.getName();
923 
924  /*
925  * get the previous entry to make sure we don't duplicate it. Motivation
926  * for this is also that if we used the back button, then we already
927  * added the 'current' node to 'back' and we will detect that and not
928  * reset the forward list.
929  */
930  String[] currentLast = backList.peekLast();
931  String lastNodeName = null;
932  if (currentLast != null && currentLast.length > 0) {
933  lastNodeName = currentLast[currentLast.length - 1];
934  }
935 
936  if (currentLast == null || !selectedNodeName.equals(lastNodeName)) {
937  //add to the list if the last if not the same as current
938  final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
939  backList.addLast(selectedPath); // add the node to the "backList"
940  if (backList.size() > 1) {
941  backButton.setEnabled(true);
942  } else {
943  backButton.setEnabled(false);
944  }
945 
946  forwardList.clear(); // clear the "forwardList"
947  forwardButton.setEnabled(false); // disable the forward Button
948  }
949  }
950 
955  private void resetHistory() {
956  // clear the back and forward list
957  backList.clear();
958  forwardList.clear();
959  backButton.setEnabled(false);
960  forwardButton.setEnabled(false);
961  }
962 
968  BeanTreeView getTree() {
969  return (BeanTreeView) this.treeView;
970  }
971 
975  public void refreshContentTreeSafe() {
976  SwingUtilities.invokeLater(this::rebuildTree);
977  }
978 
982  private void refreshTagsTree() {
983  SwingUtilities.invokeLater(() -> {
984  // Ensure the component children have been created first.
985  if (autopsyTreeChildren == null) {
986  return;
987  }
988 
989  if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
990  for (Node dataSource : autopsyTreeChildren.getNodes()) {
991  Node tagsNode = dataSource.getChildren().findChild(Tags.getTagsDisplayName());
992  if (tagsNode != null) {
993  //Reports is at the same level as the data sources so we want to ignore it
994  ((Tags.RootNode) tagsNode).refresh();
995  }
996  }
997  } else {
998  Node tagsNode = autopsyTreeChildren.findChild(Tags.getTagsDisplayName());
999  if (tagsNode != null) {
1000  ((Tags.RootNode) tagsNode).refresh();
1001  }
1002  }
1003  });
1004  }
1005 
1011  private void rebuildTree() {
1012  Case currentCase = null;
1013  try {
1014  currentCase = Case.getCurrentCaseThrows();
1015  } catch (NoCurrentCaseException ex) {
1016  // No open case.
1017  }
1018  //Will return if no open case or case has no data.
1019  if (!caseHasData(currentCase)) {
1020  return;
1021  }
1022 
1023  // refresh all children of the root.
1024  autopsyTreeChildFactory.refreshChildren();
1025 
1026  // Select the first node and reset the selection history
1027  // This should happen on the EDT once the tree has been rebuilt.
1028  // hence the SwingWorker that does this in the done() method
1029  new SwingWorker<Void, Void>() {
1030 
1031  @Override
1032  protected Void doInBackground() throws Exception {
1033  return null;
1034  }
1035 
1036  @Override
1037  protected void done() {
1038  super.done();
1039  try {
1040  get();
1041  resetHistory();
1042  preExpandNodes(em.getRootContext().getChildren());
1043  } catch (InterruptedException | ExecutionException ex) {
1044  LOGGER.log(Level.SEVERE, "Error selecting tree node.", ex); //NON-NLS
1045  } //NON-NLS
1046  }
1047  }.execute();
1048  }
1049 
1057  private static boolean caseHasData(Case currentCase) {
1058  // if no open case or has no data then there is no tree to rebuild
1059  boolean hasData;
1060  if (null == currentCase) {
1061  hasData = false;
1062  } else {
1063  hasData = currentCase.hasData();
1064  }
1065  return hasData;
1066  }
1067 
1075  private void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName) {
1076  if (previouslySelectedNodePath == null) {
1077  return;
1078  }
1079  SwingUtilities.invokeLater(new Runnable() {
1080  @Override
1081  public void run() {
1082  if (previouslySelectedNodePath.length > 0 && (rootNodeName == null || previouslySelectedNodePath[0].equals(rootNodeName))) {
1083  Node selectedNode = null;
1084  ArrayList<String> selectedNodePath = new ArrayList<>(Arrays.asList(previouslySelectedNodePath));
1085  while (null == selectedNode && !selectedNodePath.isEmpty()) {
1086  try {
1087  selectedNode = NodeOp.findPath(em.getRootContext(), selectedNodePath.toArray(new String[selectedNodePath.size()]));
1088  } catch (NodeNotFoundException ex) {
1089  // The selected node may have been deleted (e.g., a deleted tag), so truncate the path and try again.
1090  if (selectedNodePath.size() > 1) {
1091  selectedNodePath.remove(selectedNodePath.size() - 1);
1092  } else {
1093  StringBuilder nodePath = new StringBuilder();
1094  for (int i = 0; i < previouslySelectedNodePath.length; ++i) {
1095  nodePath.append(previouslySelectedNodePath[i]).append("/");
1096  }
1097  LOGGER.log(Level.WARNING, "Failed to find any nodes to select on path " + nodePath.toString(), ex); //NON-NLS
1098  break;
1099  }
1100  }
1101  }
1102 
1103  if (null != selectedNode) {
1104  if (rootNodeName != null) {
1105  //called from tree auto refresh context
1106  //remove last from backlist, because auto select will result in duplication
1107  backList.pollLast();
1108  }
1109  try {
1110  em.setExploredContextAndSelection(selectedNode, new Node[]{selectedNode});
1111  } catch (PropertyVetoException ex) {
1112  LOGGER.log(Level.WARNING, "Property veto from ExplorerManager setting selection to " + selectedNode.getName(), ex); //NON-NLS
1113  }
1114  }
1115  }
1116  }
1117  });
1118  }
1119 
1120  @Override
1121  public TopComponent getTopComponent() {
1122  return this;
1123  }
1124 
1125  @Override
1126  public boolean hasMenuOpenAction() {
1127  return false;
1128  }
1129 
1140  private Optional<Node> getCategoryNodeChild(Children children, Category category) {
1141  switch (category) {
1142  case DATA_ARTIFACT:
1143  return Optional.ofNullable(children.findChild(DataArtifacts.getName()));
1144  case ANALYSIS_RESULT:
1145  return Optional.ofNullable(children.findChild(AnalysisResults.getName()));
1146  default:
1147  LOGGER.log(Level.WARNING, "Unbale to find category of type: " + category.name());
1148  return Optional.empty();
1149  }
1150  }
1151 
1163  private Optional<Node> searchForCategoryNode(Node node, long dataSourceId, Category category) {
1164  if (node == null) {
1165  // if no node, no result
1166  return Optional.empty();
1167  } else if (node.getLookup().lookup(Host.class) != null
1168  || node.getLookup().lookup(Person.class) != null
1169  || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
1170  // if host or person node, recurse until we find correct data source node.
1171  Children children = node.getChildren();
1172 
1173  Stream<Node> childNodeStream = children == null ? Stream.empty() : Stream.of(children.getNodes());
1174  return childNodeStream
1175  .map(childNode -> searchForCategoryNode(childNode, dataSourceId, category))
1176  .filter(Optional::isPresent)
1177  .map(Optional::get)
1178  .findFirst();
1179  } else {
1180  DataSource dataSource = node.getLookup().lookup(DataSource.class);
1181  // if data source node and the one we want, find the right category node.
1182  if (dataSource != null && dataSource.getId() == dataSourceId) {
1183  Children dsChildren = node.getChildren();
1184  if (dsChildren != null) {
1185  return getCategoryNodeChild(dsChildren, category);
1186  }
1187  }
1188 
1189  return Optional.empty();
1190  }
1191  }
1192 
1202  private Optional<Node> getCategoryNode(Category category, BlackboardArtifact art) {
1203  Children rootChildren = em.getRootContext().getChildren();
1204  Optional<Node> categoryNode = getCategoryNodeChild(rootChildren, category);
1205  if (categoryNode.isPresent()) {
1206  return categoryNode;
1207  }
1208 
1209  long dataSourceId;
1210  try {
1211  dataSourceId = art.getDataSource().getId();
1212  } catch (TskCoreException ex) {
1213  LOGGER.log(Level.WARNING, "There was an error fetching the data source id for artifact.", ex);
1214  return null;
1215  }
1216 
1217  Node[] rootNodes = rootChildren.getNodes();
1218  Stream<Node> rootNodesStream = rootNodes == null ? Stream.empty() : Stream.of(rootNodes);
1219  return rootNodesStream
1220  .map((rootNode) -> searchForCategoryNode(rootNode, dataSourceId, category))
1221  .filter(Optional::isPresent)
1222  .map(Optional::get)
1223  .findFirst();
1224  }
1225 
1236  private Optional<Node> getOsAccountListNode(Node node, OsAccount osAccount, Set<Host> hosts) {
1237  if (node == null) {
1238  return Optional.empty();
1239  }
1240 
1241  Host nodeHost = node.getLookup().lookup(Host.class);
1242  if ((nodeHost != null && hosts != null && hosts.contains(nodeHost))
1243  || node.getLookup().lookup(DataSource.class) != null
1244  || node.getLookup().lookup(Person.class) != null
1245  || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
1246 
1247  return Stream.of(node.getChildren().getNodes(true))
1248  .map(childNode -> getOsAccountListNode(childNode, osAccount, hosts))
1249  .filter(Optional::isPresent)
1250  .map(Optional::get)
1251  .findFirst();
1252 
1253  }
1254 
1255  if (OsAccounts.getListName().equals(node.getName())) {
1256  return Optional.of(node);
1257  }
1258 
1259  return Optional.empty();
1260  }
1261 
1267  public void viewOsAccount(OsAccount osAccount) {
1268  Set<Host> hosts = null;
1269 
1271  try {
1272  hosts = new HashSet<>(Case.getCurrentCase().getSleuthkitCase().getOsAccountManager().getHosts(osAccount));
1273  } catch (TskCoreException ex) {
1274  LOGGER.log(Level.WARNING, "Unable to get valid hosts for osAccount: " + osAccount, ex);
1275  return;
1276  }
1277  }
1278 
1279  final Set<Host> finalHosts = hosts;
1280 
1281  Optional<Node> osAccountListNodeOpt = Stream.of(em.getRootContext().getChildren().getNodes(true))
1282  .map(nd -> getOsAccountListNode(nd, osAccount, finalHosts))
1283  .filter(Optional::isPresent)
1284  .map(Optional::get)
1285  .findFirst();
1286 
1287  if (!osAccountListNodeOpt.isPresent()) {
1288  return;
1289  }
1290 
1291  Node osAccountListNode = osAccountListNodeOpt.get();
1292 
1293  DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) osAccountListNode).getOriginal();
1294  undecoratedParentNode.setChildNodeSelectionInfo((osAcctNd) -> {
1295  OsAccount osAcctOfNd = osAcctNd.getLookup().lookup(OsAccount.class);
1296  return osAcctOfNd != null && osAcctOfNd.getId() == osAccount.getId();
1297  });
1298  getTree().expandNode(osAccountListNode);
1299  try {
1300  em.setExploredContextAndSelection(osAccountListNode, new Node[]{osAccountListNode});
1301  } catch (PropertyVetoException ex) {
1302  LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
1303  }
1304  }
1305 
1313  private Optional<BlackboardArtifact.Type> getType(long artifactTypeId) {
1314  try {
1315  return Case.getCurrentCaseThrows().getSleuthkitCase().getArtifactTypesInUse().stream()
1316  .filter(type -> type.getTypeID() == artifactTypeId)
1317  .findFirst();
1318  } catch (NoCurrentCaseException | TskCoreException ex) {
1319  LOGGER.log(Level.WARNING, "Error occurred while looking up blackboard artifact type for: " + artifactTypeId, ex);
1320  return Optional.empty();
1321  }
1322  }
1323 
1335  public void viewArtifact(final BlackboardArtifact art) {
1336  int typeID = art.getArtifactTypeID();
1337  String typeName = art.getArtifactTypeName();
1338 
1339  Optional<BlackboardArtifact.Type> typeOpt = getType(typeID);
1340  Optional<Children> categoryChildrenOpt = typeOpt
1341  .flatMap(type -> getCategoryNode(type.getCategory(), art))
1342  .flatMap(categoryNode -> Optional.ofNullable(categoryNode.getChildren()));
1343 
1344  if (!categoryChildrenOpt.isPresent()) {
1345  LOGGER.log(Level.WARNING, String.format("Category node children for artifact of typeID: %d and artifactID: %d not found.",
1346  typeID, art.getArtifactID()));
1347  return;
1348  }
1349 
1350  Children typesChildren = categoryChildrenOpt.get();
1351 
1352  Node treeNode = null;
1353  if (typeID == BlackboardArtifact.Type.TSK_HASHSET_HIT.getTypeID()) {
1354  treeNode = getHashsetNode(typesChildren, art);
1355  } else if (typeID == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
1356  treeNode = getKeywordHitNode(typesChildren, art);
1357  } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID()) {
1358  treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, art);
1359  } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
1360  treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, art);
1361  } else if (typeID == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) {
1362  treeNode = getEmailNode(typesChildren, art);
1363  } else if (typeID == BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()) {
1364  treeNode = getAccountNode(typesChildren, art);
1365  } else {
1366  treeNode = typesChildren.findChild(typeName);
1367  }
1368 
1369  if (treeNode == null) {
1370  return;
1371  }
1372 
1373  DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) treeNode).getOriginal();
1374  undecoratedParentNode.setChildNodeSelectionInfo(new ArtifactNodeSelectionInfo(art));
1375  getTree().expandNode(treeNode);
1376  if (this.getSelectedNode().equals(treeNode)) {
1377  this.setDirectoryListingActive();
1378  this.respondSelection(em.getSelectedNodes(), new Node[]{treeNode});
1379  } else {
1380  try {
1381  em.setExploredContextAndSelection(treeNode, new Node[]{treeNode});
1382  } catch (PropertyVetoException ex) {
1383  LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
1384  }
1385  }
1386  // Another thread is needed because we have to wait for dataResult to populate
1387  }
1388 
1400  private Node getHashsetNode(Children typesChildren, final BlackboardArtifact art) {
1401  Node hashsetRootNode = typesChildren.findChild(art.getArtifactTypeName());
1402  Children hashsetRootChilds = hashsetRootNode.getChildren();
1403  try {
1404  String setName = null;
1405  List<BlackboardAttribute> attributes = art.getAttributes();
1406  for (BlackboardAttribute att : attributes) {
1407  int typeId = att.getAttributeType().getTypeID();
1408  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
1409  setName = att.getValueString();
1410  }
1411  }
1412  return hashsetRootChilds.findChild(setName);
1413  } catch (TskCoreException ex) {
1414  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1415  return null;
1416  }
1417  }
1418 
1430  private Node getKeywordHitNode(Children typesChildren, BlackboardArtifact art) {
1431  Node keywordRootNode = typesChildren.findChild(art.getArtifactTypeName());
1432  Children keywordRootChilds = keywordRootNode.getChildren();
1433  try {
1434  String listName = null;
1435  String keywordName = null;
1436  String regex = null;
1437  List<BlackboardAttribute> attributes = art.getAttributes();
1438  for (BlackboardAttribute att : attributes) {
1439  int typeId = att.getAttributeType().getTypeID();
1440  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
1441  listName = att.getValueString();
1442  } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
1443  keywordName = att.getValueString();
1444  } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()) {
1445  regex = att.getValueString();
1446  }
1447  }
1448  if (listName == null) {
1449  if (regex == null) { //using same labels used for creation
1450  listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.simpleLiteralSearch.text");
1451  } else {
1452  listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text");
1453  }
1454  }
1455  Node listNode = keywordRootChilds.findChild(listName);
1456  if (listNode == null) {
1457  return null;
1458  }
1459  Children listChildren = listNode.getChildren();
1460  if (listChildren == null) {
1461  return null;
1462  }
1463  if (regex != null) { //For support of regex nodes such as URLs, IPs, Phone Numbers, and Email Addrs as they are down another level
1464  Node regexNode = listChildren.findChild(listName);
1465  regexNode = (regexNode == null) ? listChildren.findChild(listName + "_" + regex) : regexNode;
1466  if (regexNode == null) {
1467  return null;
1468  }
1469  listChildren = regexNode.getChildren();
1470  if (listChildren == null) {
1471  return null;
1472  }
1473  }
1474 
1475  return listChildren.findChild(keywordName);
1476  } catch (TskCoreException ex) {
1477  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1478  return null;
1479  }
1480  }
1481 
1495  private Node getInterestingItemNode(Children typesChildren, BlackboardArtifact.Type artifactType, BlackboardArtifact art) {
1496  Node interestingItemsRootNode = typesChildren.findChild(artifactType.getDisplayName());
1497  Children setNodeChildren = (interestingItemsRootNode == null) ? null : interestingItemsRootNode.getChildren();
1498 
1499  // set node children for type could not be found, so return null.
1500  if (setNodeChildren == null) {
1501  return null;
1502  }
1503 
1504  String setName = null;
1505  try {
1506  setName = art.getAttributes().stream()
1507  .filter(attr -> attr.getAttributeType().getTypeID() == BlackboardAttribute.Type.TSK_SET_NAME.getTypeID())
1508  .map(attr -> attr.getValueString())
1509  .findFirst()
1510  .orElse(null);
1511 
1512  } catch (TskCoreException ex) {
1513  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1514  return null;
1515  }
1516 
1517  // if no set name, no set node will be identified.
1518  if (setName == null) {
1519  return null;
1520  }
1521 
1522  // make sure data is fully loaded
1523  final String finalSetName = setName;
1524  return Stream.of(setNodeChildren.getNodes(true))
1525  .filter(setNode -> finalSetName.equals(setNode.getLookup().lookup(String.class)))
1526  .findFirst()
1527  .orElse(null);
1528  }
1529 
1538  private Node getEmailNode(Children typesChildren, BlackboardArtifact art) {
1539  Node emailMsgRootNode = typesChildren.findChild(art.getArtifactTypeName());
1540  Children emailMsgRootChilds = emailMsgRootNode.getChildren();
1541  Map<String, String> parsedPath = null;
1542  try {
1543  List<BlackboardAttribute> attributes = art.getAttributes();
1544  for (BlackboardAttribute att : attributes) {
1545  int typeId = att.getAttributeType().getTypeID();
1546  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
1547  parsedPath = EmailExtracted.parsePath(att.getValueString());
1548  break;
1549  }
1550  }
1551  if (parsedPath == null) {
1552  return null;
1553  }
1554  Node defaultNode = emailMsgRootChilds.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text")));
1555  Children defaultChildren = defaultNode.getChildren();
1556  return defaultChildren.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text")));
1557  } catch (TskCoreException ex) {
1558  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1559  return null;
1560  }
1561  }
1562 
1572  private Node getAccountNode(Children typesChildren, BlackboardArtifact art) {
1573  Node accountRootNode = typesChildren.findChild(art.getDisplayName());
1574  Children accountRootChilds = accountRootNode.getChildren();
1575  List<BlackboardAttribute> attributes;
1576  String accountType = null;
1577  String ccNumberName = null;
1578  try {
1579  attributes = art.getAttributes();
1580  for (BlackboardAttribute att : attributes) {
1581  int typeId = att.getAttributeType().getTypeID();
1582  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()) {
1583  accountType = att.getValueString();
1584  }
1585  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID()) {
1586  ccNumberName = att.getValueString();
1587  }
1588  }
1589  if (accountType == null) {
1590  return null;
1591  }
1592 
1593  if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) {
1594  return getCreditCardAccountNode(accountRootChilds, ccNumberName);
1595  } else { //default account type
1596  return accountRootChilds.findChild(accountType);
1597  }
1598  } catch (TskCoreException ex) {
1599  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1600  return null;
1601  }
1602  }
1603 
1614  private Node getCreditCardAccountNode(Children accountRootChildren, String ccNumberName) {
1615  Node accountNode = accountRootChildren.findChild(Account.Type.CREDIT_CARD.getDisplayName());
1616  if (accountNode == null) {
1617  return null;
1618  }
1619  Children accountChildren = accountNode.getChildren();
1620  if (accountChildren == null) {
1621  return null;
1622  }
1623  Node binNode = accountChildren.findChild(NbBundle.getMessage(Accounts.class, "Accounts.ByBINNode.name"));
1624  if (binNode == null) {
1625  return null;
1626  }
1627  Children binChildren = binNode.getChildren();
1628  if (ccNumberName == null) {
1629  return null;
1630  }
1631  //right padded with 0s to 8 digits when single number
1632  //when a range of numbers, the first 6 digits are rightpadded with 0s to 8 digits then a dash then 3 digits, the 6,7,8, digits of the end number right padded with 9s
1633  String binName = StringUtils.rightPad(ccNumberName, 8, "0");
1634  binName = binName.substring(0, 8);
1635  int bin;
1636  try {
1637  bin = Integer.parseInt(binName);
1638  } catch (NumberFormatException ex) {
1639  LOGGER.log(Level.WARNING, "Unable to parseInt a BIN for node selection from string binName=" + binName, ex); //NON-NLS
1640  return null;
1641  }
1643  if (binInfo != null) {
1644  int startBin = ((BINRange) binInfo).getBINstart();
1645  int endBin = ((BINRange) binInfo).getBINend();
1646  if (startBin != endBin) {
1647  binName = Integer.toString(startBin) + "-" + Integer.toString(endBin).substring(5); //if there is a range re-construct the name it appears as
1648  }
1649  }
1650  if (binName == null) {
1651  return null;
1652  }
1653  return binChildren.findChild(binName);
1654  }
1655 
1656  public void viewArtifactContent(BlackboardArtifact art) {
1657  new ViewContextAction(
1658  NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.action.viewArtContent.text"),
1659  new BlackboardArtifactNode(art)).actionPerformed(null);
1660  }
1661 
1662  public void addOnFinishedListener(PropertyChangeListener l) {
1663  DirectoryTreeTopComponent.this.addPropertyChangeListener(l);
1664  }
1665 
1666 }
static synchronized String getConfigSetting(String moduleName, String settingName)
List< Content > getDataSources()
Definition: Case.java:1703
static final Map< String, String > parsePath(String path)
Node getAccountNode(Children typesChildren, BlackboardArtifact art)
Node getCreditCardAccountNode(Children accountRootChildren, String ccNumberName)
void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName)
Optional< Node > getCategoryNodeChild(Children children, Category category)
static String getTagsDisplayName()
Definition: Tags.java:81
static synchronized BankIdentificationNumber getBINInfo(int bin)
Optional< BlackboardArtifact.Type > getType(long artifactTypeId)
Optional< Node > searchForCategoryNode(Node node, long dataSourceId, Category category)
Node getEmailNode(Children typesChildren, BlackboardArtifact art)
static synchronized boolean settingExists(String moduleName, String settingName)
Optional< Node > getOsAccountListNode(Node node, OsAccount osAccount, Set< Host > hosts)
static void setGroupItemsInTreeByDataSource(boolean value)
static synchronized void setConfigSetting(String moduleName, String settingName, String settingVal)
Optional< Node > getCategoryNode(Category category, BlackboardArtifact art)
Node getHashsetNode(Children typesChildren, final BlackboardArtifact art)
Node getKeywordHitNode(Children typesChildren, BlackboardArtifact art)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:711
static void addChangeListener(PreferenceChangeListener listener)
void setChildNodeSelectionInfo(NodeSelectionInfo selectedChildNodeInfo)
static synchronized DirectoryTreeTopComponent findInstance()
Node getInterestingItemNode(Children typesChildren, BlackboardArtifact.Type artifactType, BlackboardArtifact art)

Copyright © 2012-2021 Basis Technology. Generated on: Thu Sep 30 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.