Autopsy  4.19.0
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.LinkedList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.Optional;
36 import java.util.Set;
37 import java.util.concurrent.ExecutionException;
38 import java.util.logging.Level;
39 import java.util.prefs.PreferenceChangeEvent;
40 import java.util.prefs.PreferenceChangeListener;
41 import java.util.stream.Collectors;
42 import java.util.stream.Stream;
43 import javax.swing.Action;
44 import javax.swing.SwingUtilities;
45 import javax.swing.SwingWorker;
46 import javax.swing.event.PopupMenuEvent;
47 import javax.swing.event.PopupMenuListener;
48 import javax.swing.tree.TreeSelectionModel;
49 import org.apache.commons.lang3.StringUtils;
50 import org.openide.explorer.ExplorerManager;
51 import org.openide.explorer.ExplorerUtils;
52 import org.openide.explorer.view.BeanTreeView;
53 import org.openide.nodes.AbstractNode;
54 import org.openide.nodes.Children;
55 import org.openide.nodes.Node;
56 import org.openide.nodes.NodeNotFoundException;
57 import org.openide.nodes.NodeOp;
58 import org.openide.util.NbBundle;
59 import org.openide.util.NbBundle.Messages;
60 import org.openide.windows.TopComponent;
61 import org.openide.windows.WindowManager;
91 import org.sleuthkit.datamodel.Account;
92 import org.sleuthkit.datamodel.BlackboardArtifact;
93 import org.sleuthkit.datamodel.BlackboardArtifact.Category;
94 import org.sleuthkit.datamodel.BlackboardAttribute;
95 import org.sleuthkit.datamodel.Content;
96 import org.sleuthkit.datamodel.DataSource;
97 import org.sleuthkit.datamodel.Host;
98 import org.sleuthkit.datamodel.Person;
99 import org.sleuthkit.datamodel.TskCoreException;
100 
104 // Registered as a service provider for DataExplorer in layer.xml
105 @Messages({
106  "DirectoryTreeTopComponent.resultsView.title=Listing"
107 })
108 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
109 public final class DirectoryTreeTopComponent extends TopComponent implements DataExplorer, ExplorerManager.Provider {
110 
111  private final transient ExplorerManager em = new ExplorerManager();
113  private final DataResultTopComponent dataResult = new DataResultTopComponent(Bundle.DirectoryTreeTopComponent_resultsView_title());
114  private final ViewPreferencesPanel viewPreferencesPanel = new ViewPreferencesPanel(true);
115  private final LinkedList<String[]> backList;
116  private final LinkedList<String[]> forwardList;
117  private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS
118  private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName());
120  private Children autopsyTreeChildren;
122  private boolean showRejectedResults;
123  private static final long DEFAULT_DATASOURCE_GROUPING_THRESHOLD = 5; // Threshold for prompting the user about grouping by data source
124  private static final String GROUPING_THRESHOLD_NAME = "GroupDataSourceThreshold";
125  private static final String SETTINGS_FILE = "CasePreferences.properties"; //NON-NLS
126 
127  // nodes to be opened if present at top level
128  private static final Set<String> NODES_TO_EXPAND = Stream.of(AnalysisResults.getName(), DataArtifacts.getName(), ViewsNode.NAME)
129  .collect(Collectors.toSet());
130 
135  initComponents();
136 
137  // only allow one item to be selected at a time
138  getTree().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
139 
140  // remove the close button
141  putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE);
142  setName(NbBundle.getMessage(DirectoryTreeTopComponent.class, "CTL_DirectoryTreeTopComponent"));
143  setToolTipText(NbBundle.getMessage(DirectoryTreeTopComponent.class, "HINT_DirectoryTreeTopComponent"));
144 
145  subscribeToChangeEvents();
146  associateLookup(ExplorerUtils.createLookup(em, getActionMap()));
147 
148  // set the back & forward list and also disable the back & forward button
149  this.backList = new LinkedList<>();
150  this.forwardList = new LinkedList<>();
151  backButton.setEnabled(false);
152  forwardButton.setEnabled(false);
153 
154  viewPreferencesPopupMenu.add(viewPreferencesPanel);
155  viewPreferencesPopupMenu.setSize(viewPreferencesPanel.getPreferredSize().width + 6, viewPreferencesPanel.getPreferredSize().height + 6);
156  viewPreferencesPopupMenu.addPopupMenuListener(new PopupMenuListener() {
157  @Override
158  public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
159  openViewPreferencesButton.setSelected(true);
160  }
161 
162  @Override
163  public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
164  openViewPreferencesButton.setSelected(false);
165  }
166 
167  @Override
168  public void popupMenuCanceled(PopupMenuEvent e) {
169  openViewPreferencesButton.setSelected(false);
170  }
171  });
172  }
173 
180  private void preExpandNodes(Children rootChildren) {
181  BeanTreeView tree = getTree();
182 
183  // using getNodes(true) to fetch children so that async nodes are loaded
184  Node[] rootChildrenNodes = rootChildren.getNodes(true);
185  if (rootChildrenNodes == null || rootChildrenNodes.length < 1) {
186  return;
187  }
188 
189  // expand all nodes parents of and including hosts if group by host/person
190  if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
191  Stream.of(rootChildrenNodes)
192  .flatMap((n) -> getHostNodesAndParents(n).stream())
193  .filter((n) -> n != null)
194  .forEach(tree::expandNode);
195  } else {
196  Stream.of(rootChildrenNodes)
197  .filter(n -> n != null && NODES_TO_EXPAND.contains(n.getName()))
198  .forEach(tree::expandNode);
199  }
200  }
201 
210  private List<Node> getHostNodesAndParents(Node node) {
211  if (node == null) {
212  return Collections.emptyList();
213  } else if (node.getLookup().lookup(Person.class) != null
214  || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
215  Children children = node.getChildren();
216  Node[] childNodes = children == null ? null : children.getNodes();
217  if (childNodes != null) {
218  return Stream.of(childNodes)
219  .flatMap((n) -> Stream.concat(Stream.of(n), getHostNodesAndParents(n).stream()))
220  .collect(Collectors.toList());
221  }
222  } else if (node.getLookup().lookup(Host.class) != null) {
223  return Arrays.asList(node);
224  }
225  return Collections.emptyList();
226  }
227 
231  private void subscribeToChangeEvents() {
232  UserPreferences.addChangeListener(new PreferenceChangeListener() {
233  @Override
234  public void preferenceChange(PreferenceChangeEvent evt) {
235  switch (evt.getKey()) {
244  refreshContentTreeSafe();
245  break;
247  refreshTagsTree();
248  break;
251  // TODO: Need a way to refresh the Views subtree alone.
252  refreshContentTreeSafe();
253  break;
254  }
255  }
256  });
257 
259  this.em.addPropertyChangeListener(this);
260  }
261 
263  this.dataResult.requestActive();
264  }
265 
266  public void openDirectoryListing() {
267  this.dataResult.open();
268  }
269 
271  return this.dataResult;
272  }
273 
279  public boolean getShowRejectedResults() {
280  return showRejectedResults;
281  }
282 
289  public void setShowRejectedResults(boolean showRejectedResults) {
290  this.showRejectedResults = showRejectedResults;
291  if (accounts != null) {
292  accounts.setShowRejected(showRejectedResults);
293  }
294  }
295 
301  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
302  private void initComponents() {
303 
304  viewPreferencesPopupMenu = new javax.swing.JPopupMenu();
305  treeView = new ExpansionBeanTreeView();
306  backButton = new javax.swing.JButton();
307  forwardButton = new javax.swing.JButton();
308  openViewPreferencesButton = new javax.swing.JButton();
309 
310  treeView.setBorder(null);
311 
312  backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back.png"))); // NOI18N
313  org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.backButton.text")); // NOI18N
314  backButton.setBorderPainted(false);
315  backButton.setContentAreaFilled(false);
316  backButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_disabled.png"))); // NOI18N
317  backButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
318  backButton.setMaximumSize(new java.awt.Dimension(55, 100));
319  backButton.setMinimumSize(new java.awt.Dimension(5, 5));
320  backButton.setPreferredSize(new java.awt.Dimension(24, 24));
321  backButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_hover.png"))); // NOI18N
322  backButton.addActionListener(new java.awt.event.ActionListener() {
323  public void actionPerformed(java.awt.event.ActionEvent evt) {
324  backButtonActionPerformed(evt);
325  }
326  });
327 
328  forwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward.png"))); // NOI18N
329  org.openide.awt.Mnemonics.setLocalizedText(forwardButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.forwardButton.text")); // NOI18N
330  forwardButton.setBorderPainted(false);
331  forwardButton.setContentAreaFilled(false);
332  forwardButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_disabled.png"))); // NOI18N
333  forwardButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
334  forwardButton.setMaximumSize(new java.awt.Dimension(55, 100));
335  forwardButton.setMinimumSize(new java.awt.Dimension(5, 5));
336  forwardButton.setPreferredSize(new java.awt.Dimension(24, 24));
337  forwardButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_hover.png"))); // NOI18N
338  forwardButton.addActionListener(new java.awt.event.ActionListener() {
339  public void actionPerformed(java.awt.event.ActionEvent evt) {
340  forwardButtonActionPerformed(evt);
341  }
342  });
343 
344  openViewPreferencesButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/view-preferences-23.png"))); // NOI18N
345  org.openide.awt.Mnemonics.setLocalizedText(openViewPreferencesButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.openViewPreferencesButton.text")); // NOI18N
346  openViewPreferencesButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
347  openViewPreferencesButton.setBorderPainted(false);
348  openViewPreferencesButton.setContentAreaFilled(false);
349  openViewPreferencesButton.setMaximumSize(new java.awt.Dimension(24, 24));
350  openViewPreferencesButton.setMinimumSize(new java.awt.Dimension(24, 24));
351  openViewPreferencesButton.setPreferredSize(new java.awt.Dimension(24, 24));
352  openViewPreferencesButton.addActionListener(new java.awt.event.ActionListener() {
353  public void actionPerformed(java.awt.event.ActionEvent evt) {
354  openViewPreferencesButtonActionPerformed(evt);
355  }
356  });
357 
358  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
359  this.setLayout(layout);
360  layout.setHorizontalGroup(
361  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
362  .addComponent(treeView)
363  .addGroup(layout.createSequentialGroup()
364  .addContainerGap()
365  .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
366  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
367  .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
368  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 140, Short.MAX_VALUE)
369  .addComponent(openViewPreferencesButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
370  .addContainerGap())
371  );
372  layout.setVerticalGroup(
373  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
374  .addGroup(layout.createSequentialGroup()
375  .addGap(0, 0, 0)
376  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
377  .addComponent(openViewPreferencesButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
378  .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
379  .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
380  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
381  .addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 919, Short.MAX_VALUE))
382  );
383  }// </editor-fold>//GEN-END:initComponents
384 
385  private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
386  // change the cursor to "waiting cursor" for this operation
387  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
388 
389  // the end is the current place,
390  String[] currentNodePath = backList.pollLast();
391  forwardList.addLast(currentNodePath);
392  forwardButton.setEnabled(true);
393 
394  /*
395  * We peek instead of poll because we use its existence in the list
396  * later on so that we do not reset the forward list after the selection
397  * occurs.
398  */
399  String[] newCurrentNodePath = backList.peekLast();
400 
401  // enable / disable the back and forward button
402  if (backList.size() > 1) {
403  backButton.setEnabled(true);
404  } else {
405  backButton.setEnabled(false);
406  }
407 
408  // update the selection on directory tree
409  setSelectedNode(newCurrentNodePath, null);
410 
411  this.setCursor(null);
412  }//GEN-LAST:event_backButtonActionPerformed
413 
414  private void forwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed
415  // change the cursor to "waiting cursor" for this operation
416  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
417 
418  String[] newCurrentNodePath = forwardList.pollLast();
419  if (!forwardList.isEmpty()) {
420  forwardButton.setEnabled(true);
421  } else {
422  forwardButton.setEnabled(false);
423  }
424 
425  backList.addLast(newCurrentNodePath);
426  backButton.setEnabled(true);
427 
428  // update the selection on directory tree
429  setSelectedNode(newCurrentNodePath, null);
430 
431  this.setCursor(null);
432  }//GEN-LAST:event_forwardButtonActionPerformed
433 
434  private void openViewPreferencesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openViewPreferencesButtonActionPerformed
435  viewPreferencesPanel.load();
436  viewPreferencesPopupMenu.show(openViewPreferencesButton, 0, openViewPreferencesButton.getHeight() - 1);
437  }//GEN-LAST:event_openViewPreferencesButtonActionPerformed
438 
439  // Variables declaration - do not modify//GEN-BEGIN:variables
440  private javax.swing.JButton backButton;
441  private javax.swing.JButton forwardButton;
442  private javax.swing.JButton openViewPreferencesButton;
443  private javax.swing.JScrollPane treeView;
444  private javax.swing.JPopupMenu viewPreferencesPopupMenu;
445  // End of variables declaration//GEN-END:variables
446 
455  public static synchronized DirectoryTreeTopComponent getDefault() {
456  if (instance == null) {
457  instance = new DirectoryTreeTopComponent();
458  }
459  return instance;
460  }
461 
468  public static synchronized DirectoryTreeTopComponent findInstance() {
469  WindowManager winManager = WindowManager.getDefault();
470  TopComponent win = winManager.findTopComponent(PREFERRED_ID);
471  if (win == null) {
472  LOGGER.warning(
473  "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system."); //NON-NLS
474  return getDefault();
475  }
476  if (win instanceof DirectoryTreeTopComponent) {
477  return (DirectoryTreeTopComponent) win;
478  }
479  LOGGER.warning(
480  "There seem to be multiple components with the '" + PREFERRED_ID //NON-NLS
481  + "' ID. That is a potential source of errors and unexpected behavior."); //NON-NLS
482  return getDefault();
483  }
484 
491  @Override
492  public int getPersistenceType() {
493  return TopComponent.PERSISTENCE_NEVER;
494  }
495 
502  private void promptForDataSourceGrouping(int dataSourceCount) {
504  GroupDataSourcesDialog dialog = new GroupDataSourcesDialog(dataSourceCount);
505  dialog.display();
506  if (dialog.groupByDataSourceSelected()) {
508  refreshContentTreeSafe();
509  } else {
511  }
512  }
513  }
514 
522  @NbBundle.Messages({"# {0} - dataSourceCount",
523  "DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contains {0} data sources. Would you like to group by data source for faster loading?",
524  "DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source?"})
525  @Override
526  public void componentOpened() {
527  // change the cursor to "waiting cursor" for this operation
528  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
529  Case openCase = null;
530  try {
531  openCase = Case.getCurrentCaseThrows();
532  } catch (NoCurrentCaseException ex) {
533  // No open case.
534  }
535  final Case currentCase = openCase;
536  // close the top component if there's no image in this case
537  if (!caseHasData(currentCase)) {
538  getTree().setRootVisible(false); // hide the root
539  } else {
540  // If the case contains a lot of data sources, and they aren't already grouping
541  // by data source, give the user the option to do so before loading the tree.
543  Long settingsThreshold = null;
544  if (ModuleSettings.settingExists(ModuleSettings.MAIN_SETTINGS, GROUPING_THRESHOLD_NAME)) {
545  try {
546  settingsThreshold = Long.parseLong(ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, GROUPING_THRESHOLD_NAME));
547  } catch (NumberFormatException ex) {
548  LOGGER.log(Level.SEVERE, "Group data sources threshold is not a number", ex);
549  }
550  } else {
551  ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, GROUPING_THRESHOLD_NAME, String.valueOf(DEFAULT_DATASOURCE_GROUPING_THRESHOLD));
552  }
553  final long threshold = settingsThreshold == null ? DEFAULT_DATASOURCE_GROUPING_THRESHOLD : settingsThreshold;
554 
555  new SwingWorker<Integer, Void>() {
556  @Override
557  protected Integer doInBackground() throws Exception {
558  int dataSourceCount = 0;
559  try {
560  dataSourceCount = currentCase.getDataSources().size();
561  } catch (TskCoreException ex) {
562  LOGGER.log(Level.SEVERE, "Error loading data sources", ex);
563  }
564  return dataSourceCount;
565  }
566 
567  @Override
568  protected void done() {
569  int dataSourceCount = 0;
570  try {
571  dataSourceCount = get();
572  } catch (ExecutionException | InterruptedException ex) {
573  LOGGER.log(Level.SEVERE, "Error loading data sources and getting count on background thread", ex);
574  }
576  && dataSourceCount > threshold) {
577  promptForDataSourceGrouping(dataSourceCount);
578  }
579  }
580  }.execute();
581  }
582 
583  // if there's at least one image, load the image and open the top componen
584  autopsyTreeChildFactory = new AutopsyTreeChildFactory();
585  autopsyTreeChildren = Children.create(autopsyTreeChildFactory, true);
586  Node root = new AbstractNode(autopsyTreeChildren) {
587  //JIRA-2807: What is the point of these overrides?
592  @Override
593  public Action[] getActions(boolean popup) {
594  return new Action[]{};
595  }
596 
597  // Overide the AbstractNode use of DefaultHandle to return
598  // a handle which can be serialized without a parent
599  @Override
600  public Node.Handle getHandle() {
601  return new Node.Handle() {
602  @Override
603  public Node getNode() throws IOException {
604  return em.getRootContext();
605  }
606  };
607  }
608  };
609 
610  root = new DirectoryTreeFilterNode(root, true);
611 
612  em.setRootContext(root);
613  em.getRootContext().setName(currentCase.getName());
614  em.getRootContext().setDisplayName(currentCase.getName());
615  getTree().setRootVisible(false); // hide the root
616 
617  // Reset the forward and back lists because we're resetting the root context
618  resetHistory();
619  new SwingWorker<Node[], Void>() {
620  @Override
621  protected Node[] doInBackground() throws Exception {
622  Children rootChildren = em.getRootContext().getChildren();
623  preExpandNodes(rootChildren);
624  /*
625  * JIRA-2806: What is this supposed to do? Right now it
626  * selects the data sources node, but the comment seems to
627  * indicate it is supposed to select the first datasource.
628  */
629  // select the first image node, if there is one
630  // (this has to happen after dataResult is opened, because the event
631  // of changing the selected node fires a handler that tries to make
632  // dataResult active)
633  if (rootChildren.getNodesCount() > 0) {
634  return new Node[]{rootChildren.getNodeAt(0)};
635  }
636  return new Node[]{};
637  }
638 
639  @Override
640  protected void done() {
641  super.done();
642 
643  // if the dataResult is not opened
644  if (!dataResult.isOpened()) {
645  dataResult.open(); // open the data result top component as well when the directory tree is opened
646  }
647  /*
648  * JIRA-2806: What is this supposed to do?
649  */
650  // select the first image node, if there is one
651  // (this has to happen after dataResult is opened, because the event
652  // of changing the selected node fires a handler that tries to make
653  // dataResult active)
654  try {
655  Node[] selections = get();
656  if (selections != null && selections.length > 0) {
657  em.setSelectedNodes(selections);
658  }
659  } catch (PropertyVetoException ex) {
660  LOGGER.log(Level.SEVERE, "Error setting default selected node.", ex); //NON-NLS
661  } catch (InterruptedException | ExecutionException ex) {
662  LOGGER.log(Level.SEVERE, "Error expanding tree to initial state.", ex); //NON-NLS
663  } finally {
664  setCursor(null);
665  }
666  }
667  }.execute();
668  }
669  }
670 
677  @Override
678  public void componentClosed() {
679  //@@@ push the selection node to null?
680  autopsyTreeChildren = null;
681  }
682 
683  void writeProperties(java.util.Properties p) {
684  // better to version settings since initial version as advocated at
685  // http://wiki.apidesign.org/wiki/PropertyFiles
686  p.setProperty("version", "1.0");
687  // TODO store your settings
688  }
689 
690  Object readProperties(java.util.Properties p) {
691  if (instance == null) {
692  instance = this;
693  }
694  instance.readPropertiesImpl(p);
695  return instance;
696  }
697 
698  private void readPropertiesImpl(java.util.Properties p) {
699  String version = p.getProperty("version");
700  // TODO read your settings according to their version
701  }
702 
708  @Override
709  protected String preferredID() {
710  return PREFERRED_ID;
711  }
712 
713  @Override
714  public boolean canClose() {
715  /*
716  * Only allow the main tree view in the left side of the main window to
717  * be closed if there is no opne case or the open case has no data
718  * sources.
719  */
720  try {
721  Case openCase = Case.getCurrentCaseThrows();
722  return caseHasData(openCase) == false;
723  } catch (NoCurrentCaseException ex) {
724  return true;
725  }
726  }
727 
733  @Override
734  public ExplorerManager getExplorerManager() {
735  return this.em;
736  }
737 
743  @Override
744  public Action[] getActions() {
745  return new Action[]{};
746  }
747 
753  public Node getSelectedNode() {
754  Node result = null;
755 
756  Node[] selectedNodes = this.getExplorerManager().getSelectedNodes();
757  if (selectedNodes.length > 0) {
758  result = selectedNodes[0];
759  }
760  return result;
761  }
762 
769  @Override
770  public void propertyChange(PropertyChangeEvent event) {
772  String changed = event.getPropertyName();
773  if (changed.equals(Case.Events.CURRENT_CASE.toString())) { // changed current case
774  // When a case is closed, the old value of this property is the
775  // closed Case object and the new value is null. When a case is
776  // opened, the old value is null and the new value is the new Case
777  // object.
778  // @@@ This needs to be revisited. Perhaps case closed and case
779  // opened events instead of property change events would be a better
780  // solution. Either way, more probably needs to be done to clean up
781  // data model objects when a case is closed.
782  if (event.getOldValue() != null && event.getNewValue() == null) {
783  // The current case has been closed. Reset the ExplorerManager.
784  SwingUtilities.invokeLater(() -> {
785  Node emptyNode = new AbstractNode(Children.LEAF);
786  em.setRootContext(emptyNode);
787  });
788  } else if (event.getNewValue() != null) {
789  // A new case has been opened. Reset the ExplorerManager.
790  Case newCase = (Case) event.getNewValue();
791  final String newCaseName = newCase.getName();
792  SwingUtilities.invokeLater(() -> {
793  em.getRootContext().setName(newCaseName);
794  em.getRootContext().setDisplayName(newCaseName);
795 
796  // Reset the forward and back
797  // buttons. Note that a call to CoreComponentControl.openCoreWindows()
798  // by the new Case object will lead to a componentOpened() call
799  // that will repopulate the tree.
800  // @@@ The repopulation of the tree in this fashion also merits
801  // reconsideration.
802  resetHistory();
803  });
804  }
805  } // if the image is added to the case
806  else if (changed.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
813  try {
815  /*
816  * In case the Case 'updateGUIForCaseOpened()' method hasn't
817  * already done so, open the tree and all other core
818  * windows.
819  *
820  * TODO: (JIRA-4053) DirectoryTreeTopComponent should not be
821  * responsible for opening core windows. Consider moving
822  * this elsewhere.
823  */
824  SwingUtilities.invokeLater(() -> {
825  if (!DirectoryTreeTopComponent.this.isOpened()) {
827  }
828  });
829  } catch (NoCurrentCaseException notUsed) {
833  }
834  } // change in node selection
835  else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
836  respondSelection((Node[]) event.getOldValue(), (Node[]) event.getNewValue());
837  }
838  }
839  }
840 
849  @NbBundle.Messages("DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.")
850  void respondSelection(final Node[] oldNodes, final Node[] newNodes) {
851  if (!Case.isCaseOpen()) {
852  return;
853  }
854 
855  // Some lock that prevents certain Node operations is set during the
856  // ExplorerManager selection-change, so we must handle changes after the
857  // selection-change event is processed.
858  //TODO find a different way to refresh data result viewer, scheduling this
859  //to EDT breaks loading of nodes in the background
860  EventQueue.invokeLater(() -> {
861  // change the cursor to "waiting cursor" for this operation
862  DirectoryTreeTopComponent.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
863  try {
864  Node treeNode = DirectoryTreeTopComponent.this.getSelectedNode();
865  if (treeNode != null) {
866  Node originNode = ((DirectoryTreeFilterNode) treeNode).getOriginal();
867  //set node, wrap in filter node first to filter out children
868  Node drfn = new DataResultFilterNode(originNode, DirectoryTreeTopComponent.this.em);
869  // Create a TableFilterNode with knowledge of the node's type to allow for column order settings
870  if (FileTypesByMimeType.isEmptyMimeTypeNode(originNode)) {
871  //Special case for when File Type Identification has not yet been run and
872  //there are no mime types to populate Files by Mime Type Tree
873  EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTreeTopComponent_emptyMimeNode_text());
874  dataResult.setNode(new TableFilterNode(emptyNode, true, "This Node Is Empty")); //NON-NLS
875  } else if (originNode instanceof DisplayableItemNode) {
876  dataResult.setNode(new TableFilterNode(drfn, true, ((DisplayableItemNode) originNode).getItemType()));
877  } else {
878  dataResult.setNode(new TableFilterNode(drfn, true));
879  }
880  String displayName = "";
881  Content content = originNode.getLookup().lookup(Content.class);
882  if (content != null) {
883  try {
884  displayName = content.getUniquePath();
885  } catch (TskCoreException ex) {
886  LOGGER.log(Level.SEVERE, "Exception while calling Content.getUniquePath() for node: {0}", originNode); //NON-NLS
887  }
888  } else if (originNode.getLookup().lookup(String.class) != null) {
889  displayName = originNode.getLookup().lookup(String.class);
890  }
891  dataResult.setPath(displayName);
892  }
893  // set the directory listing to be active
894  if (oldNodes != null && newNodes != null
895  && (oldNodes.length == newNodes.length)) {
896  boolean sameNodes = true;
897  for (int i = 0; i < oldNodes.length; i++) {
898  sameNodes = sameNodes && oldNodes[i].getName().equals(newNodes[i].getName());
899  }
900  if (!sameNodes) {
901  dataResult.requestActive();
902  }
903  }
904  } finally {
905  setCursor(null);
906  }
907  });
908 
909  // update the back and forward list
910  updateHistory(em.getSelectedNodes());
911  }
912 
913  private void updateHistory(Node[] selectedNodes) {
914  if (selectedNodes.length == 0) {
915  return;
916  }
917 
918  Node selectedNode = selectedNodes[0];
919  String selectedNodeName = selectedNode.getName();
920 
921  /*
922  * get the previous entry to make sure we don't duplicate it. Motivation
923  * for this is also that if we used the back button, then we already
924  * added the 'current' node to 'back' and we will detect that and not
925  * reset the forward list.
926  */
927  String[] currentLast = backList.peekLast();
928  String lastNodeName = null;
929  if (currentLast != null && currentLast.length > 0) {
930  lastNodeName = currentLast[currentLast.length - 1];
931  }
932 
933  if (currentLast == null || !selectedNodeName.equals(lastNodeName)) {
934  //add to the list if the last if not the same as current
935  final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
936  backList.addLast(selectedPath); // add the node to the "backList"
937  if (backList.size() > 1) {
938  backButton.setEnabled(true);
939  } else {
940  backButton.setEnabled(false);
941  }
942 
943  forwardList.clear(); // clear the "forwardList"
944  forwardButton.setEnabled(false); // disable the forward Button
945  }
946  }
947 
952  private void resetHistory() {
953  // clear the back and forward list
954  backList.clear();
955  forwardList.clear();
956  backButton.setEnabled(false);
957  forwardButton.setEnabled(false);
958  }
959 
965  BeanTreeView getTree() {
966  return (BeanTreeView) this.treeView;
967  }
968 
972  public void refreshContentTreeSafe() {
973  SwingUtilities.invokeLater(this::rebuildTree);
974  }
975 
979  private void refreshTagsTree() {
980  SwingUtilities.invokeLater(() -> {
981  // Ensure the component children have been created first.
982  if (autopsyTreeChildren == null) {
983  return;
984  }
985 
986  if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
987  for (Node dataSource : autopsyTreeChildren.getNodes()) {
988  Node tagsNode = dataSource.getChildren().findChild(Tags.getTagsDisplayName());
989  if (tagsNode != null) {
990  //Reports is at the same level as the data sources so we want to ignore it
991  ((Tags.RootNode) tagsNode).refresh();
992  }
993  }
994  } else {
995  Node tagsNode = autopsyTreeChildren.findChild(Tags.getTagsDisplayName());
996  if (tagsNode != null) {
997  ((Tags.RootNode) tagsNode).refresh();
998  }
999  }
1000  });
1001  }
1002 
1008  private void rebuildTree() {
1009  Case currentCase = null;
1010  try {
1011  currentCase = Case.getCurrentCaseThrows();
1012  } catch (NoCurrentCaseException ex) {
1013  // No open case.
1014  }
1015  //Will return if no open case or case has no data.
1016  if (!caseHasData(currentCase)) {
1017  return;
1018  }
1019 
1020  // refresh all children of the root.
1021  autopsyTreeChildFactory.refreshChildren();
1022 
1023  // Select the first node and reset the selection history
1024  // This should happen on the EDT once the tree has been rebuilt.
1025  // hence the SwingWorker that does this in the done() method
1026  new SwingWorker<Void, Void>() {
1027 
1028  @Override
1029  protected Void doInBackground() throws Exception {
1030  return null;
1031  }
1032 
1033  @Override
1034  protected void done() {
1035  super.done();
1036  try {
1037  get();
1038  resetHistory();
1039  preExpandNodes(em.getRootContext().getChildren());
1040  } catch (InterruptedException | ExecutionException ex) {
1041  LOGGER.log(Level.SEVERE, "Error selecting tree node.", ex); //NON-NLS
1042  } //NON-NLS
1043  }
1044  }.execute();
1045  }
1046 
1054  private static boolean caseHasData(Case currentCase) {
1055  // if no open case or has no data then there is no tree to rebuild
1056  boolean hasData;
1057  if (null == currentCase) {
1058  hasData = false;
1059  } else {
1060  hasData = currentCase.hasData();
1061  }
1062  return hasData;
1063  }
1064 
1072  private void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName) {
1073  if (previouslySelectedNodePath == null) {
1074  return;
1075  }
1076  SwingUtilities.invokeLater(new Runnable() {
1077  @Override
1078  public void run() {
1079  if (previouslySelectedNodePath.length > 0 && (rootNodeName == null || previouslySelectedNodePath[0].equals(rootNodeName))) {
1080  Node selectedNode = null;
1081  ArrayList<String> selectedNodePath = new ArrayList<>(Arrays.asList(previouslySelectedNodePath));
1082  while (null == selectedNode && !selectedNodePath.isEmpty()) {
1083  try {
1084  selectedNode = NodeOp.findPath(em.getRootContext(), selectedNodePath.toArray(new String[selectedNodePath.size()]));
1085  } catch (NodeNotFoundException ex) {
1086  // The selected node may have been deleted (e.g., a deleted tag), so truncate the path and try again.
1087  if (selectedNodePath.size() > 1) {
1088  selectedNodePath.remove(selectedNodePath.size() - 1);
1089  } else {
1090  StringBuilder nodePath = new StringBuilder();
1091  for (int i = 0; i < previouslySelectedNodePath.length; ++i) {
1092  nodePath.append(previouslySelectedNodePath[i]).append("/");
1093  }
1094  LOGGER.log(Level.WARNING, "Failed to find any nodes to select on path " + nodePath.toString(), ex); //NON-NLS
1095  break;
1096  }
1097  }
1098  }
1099 
1100  if (null != selectedNode) {
1101  if (rootNodeName != null) {
1102  //called from tree auto refresh context
1103  //remove last from backlist, because auto select will result in duplication
1104  backList.pollLast();
1105  }
1106  try {
1107  em.setExploredContextAndSelection(selectedNode, new Node[]{selectedNode});
1108  } catch (PropertyVetoException ex) {
1109  LOGGER.log(Level.WARNING, "Property veto from ExplorerManager setting selection to " + selectedNode.getName(), ex); //NON-NLS
1110  }
1111  }
1112  }
1113  }
1114  });
1115  }
1116 
1117  @Override
1118  public TopComponent getTopComponent() {
1119  return this;
1120  }
1121 
1122  @Override
1123  public boolean hasMenuOpenAction() {
1124  return false;
1125  }
1126 
1137  private Optional<Node> getCategoryNodeChild(Children children, Category category) {
1138  switch (category) {
1139  case DATA_ARTIFACT:
1140  return Optional.ofNullable(children.findChild(DataArtifacts.getName()));
1141  case ANALYSIS_RESULT:
1142  return Optional.ofNullable(children.findChild(AnalysisResults.getName()));
1143  default:
1144  LOGGER.log(Level.WARNING, "Unbale to find category of type: " + category.name());
1145  return Optional.empty();
1146  }
1147  }
1148 
1160  private Optional<Node> searchForCategoryNode(Node node, long dataSourceId, Category category) {
1161  if (node == null) {
1162  // if no node, no result
1163  return Optional.empty();
1164  } else if (node.getLookup().lookup(Host.class) != null
1165  || node.getLookup().lookup(Person.class) != null
1166  || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
1167  // if host or person node, recurse until we find correct data source node.
1168  Children children = node.getChildren();
1169 
1170  Stream<Node> childNodeStream = children == null ? Stream.empty() : Stream.of(children.getNodes());
1171  return childNodeStream
1172  .map(childNode -> searchForCategoryNode(childNode, dataSourceId, category))
1173  .filter(Optional::isPresent)
1174  .map(Optional::get)
1175  .findFirst();
1176  } else {
1177  DataSource dataSource = node.getLookup().lookup(DataSource.class);
1178  // if data source node and the one we want, find the right category node.
1179  if (dataSource != null && dataSource.getId() == dataSourceId) {
1180  Children dsChildren = node.getChildren();
1181  if (dsChildren != null) {
1182  return getCategoryNodeChild(dsChildren, category);
1183  }
1184  }
1185 
1186  return Optional.empty();
1187  }
1188  }
1189 
1199  private Optional<Node> getCategoryNode(Category category, BlackboardArtifact art) {
1200  Children rootChildren = em.getRootContext().getChildren();
1201  Optional<Node> categoryNode = getCategoryNodeChild(rootChildren, category);
1202  if (categoryNode.isPresent()) {
1203  return categoryNode;
1204  }
1205 
1206  long dataSourceId;
1207  try {
1208  dataSourceId = art.getDataSource().getId();
1209  } catch (TskCoreException ex) {
1210  LOGGER.log(Level.WARNING, "There was an error fetching the data source id for artifact.", ex);
1211  return null;
1212  }
1213 
1214  Node[] rootNodes = rootChildren.getNodes();
1215  Stream<Node> rootNodesStream = rootNodes == null ? Stream.empty() : Stream.of(rootNodes);
1216  return rootNodesStream
1217  .map((rootNode) -> searchForCategoryNode(rootNode, dataSourceId, category))
1218  .filter(Optional::isPresent)
1219  .map(Optional::get)
1220  .findFirst();
1221  }
1222 
1230  private Optional<BlackboardArtifact.Type> getType(long artifactTypeId) {
1231  try {
1232  return Case.getCurrentCaseThrows().getSleuthkitCase().getArtifactTypesInUse().stream()
1233  .filter(type -> type.getTypeID() == artifactTypeId)
1234  .findFirst();
1235  } catch (NoCurrentCaseException | TskCoreException ex) {
1236  LOGGER.log(Level.WARNING, "Error occurred while looking up blackboard artifact type for: " + artifactTypeId, ex);
1237  return Optional.empty();
1238  }
1239  }
1240 
1252  public void viewArtifact(final BlackboardArtifact art) {
1253  int typeID = art.getArtifactTypeID();
1254  String typeName = art.getArtifactTypeName();
1255 
1256  Optional<BlackboardArtifact.Type> typeOpt = getType(typeID);
1257  Optional<Children> categoryChildrenOpt = typeOpt
1258  .flatMap(type -> getCategoryNode(type.getCategory(), art))
1259  .flatMap(categoryNode -> Optional.ofNullable(categoryNode.getChildren()));
1260 
1261  if (!categoryChildrenOpt.isPresent()) {
1262  LOGGER.log(Level.WARNING, String.format("Category node children for artifact of typeID: %d and artifactID: %d not found.",
1263  typeID, art.getArtifactID()));
1264  return;
1265  }
1266 
1267  Children typesChildren = categoryChildrenOpt.get();
1268 
1269  Node treeNode = null;
1270  if (typeID == BlackboardArtifact.Type.TSK_HASHSET_HIT.getTypeID()) {
1271  treeNode = getHashsetNode(typesChildren, art);
1272  } else if (typeID == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
1273  treeNode = getKeywordHitNode(typesChildren, art);
1274  } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID()
1275  || typeID == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
1276  treeNode = getInterestingItemNode(typesChildren, art);
1277  } else if (typeID == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) {
1278  treeNode = getEmailNode(typesChildren, art);
1279  } else if (typeID == BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()) {
1280  treeNode = getAccountNode(typesChildren, art);
1281  } else {
1282  treeNode = typesChildren.findChild(typeName);
1283  }
1284 
1285  if (treeNode == null) {
1286  return;
1287  }
1288 
1289  DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) treeNode).getOriginal();
1290  undecoratedParentNode.setChildNodeSelectionInfo(new ArtifactNodeSelectionInfo(art));
1291  getTree().expandNode(treeNode);
1292  if (this.getSelectedNode().equals(treeNode)) {
1293  this.setDirectoryListingActive();
1294  this.respondSelection(em.getSelectedNodes(), new Node[]{treeNode});
1295  } else {
1296  try {
1297  em.setExploredContextAndSelection(treeNode, new Node[]{treeNode});
1298  } catch (PropertyVetoException ex) {
1299  LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
1300  }
1301  }
1302  // Another thread is needed because we have to wait for dataResult to populate
1303  }
1304 
1316  private Node getHashsetNode(Children typesChildren, final BlackboardArtifact art) {
1317  Node hashsetRootNode = typesChildren.findChild(art.getArtifactTypeName());
1318  Children hashsetRootChilds = hashsetRootNode.getChildren();
1319  try {
1320  String setName = null;
1321  List<BlackboardAttribute> attributes = art.getAttributes();
1322  for (BlackboardAttribute att : attributes) {
1323  int typeId = att.getAttributeType().getTypeID();
1324  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
1325  setName = att.getValueString();
1326  }
1327  }
1328  return hashsetRootChilds.findChild(setName);
1329  } catch (TskCoreException ex) {
1330  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1331  return null;
1332  }
1333  }
1334 
1346  private Node getKeywordHitNode(Children typesChildren, BlackboardArtifact art) {
1347  Node keywordRootNode = typesChildren.findChild(art.getArtifactTypeName());
1348  Children keywordRootChilds = keywordRootNode.getChildren();
1349  try {
1350  String listName = null;
1351  String keywordName = null;
1352  String regex = null;
1353  List<BlackboardAttribute> attributes = art.getAttributes();
1354  for (BlackboardAttribute att : attributes) {
1355  int typeId = att.getAttributeType().getTypeID();
1356  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
1357  listName = att.getValueString();
1358  } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
1359  keywordName = att.getValueString();
1360  } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()) {
1361  regex = att.getValueString();
1362  }
1363  }
1364  if (listName == null) {
1365  if (regex == null) { //using same labels used for creation
1366  listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.simpleLiteralSearch.text");
1367  } else {
1368  listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text");
1369  }
1370  }
1371  Node listNode = keywordRootChilds.findChild(listName);
1372  if (listNode == null) {
1373  return null;
1374  }
1375  Children listChildren = listNode.getChildren();
1376  if (listChildren == null) {
1377  return null;
1378  }
1379  if (regex != null) { //For support of regex nodes such as URLs, IPs, Phone Numbers, and Email Addrs as they are down another level
1380  Node regexNode = listChildren.findChild(listName);
1381  regexNode = (regexNode == null) ? listChildren.findChild(listName + "_" + regex) : regexNode;
1382  if (regexNode == null) {
1383  return null;
1384  }
1385  listChildren = regexNode.getChildren();
1386  if (listChildren == null) {
1387  return null;
1388  }
1389  }
1390 
1391  return listChildren.findChild(keywordName);
1392  } catch (TskCoreException ex) {
1393  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1394  return null;
1395  }
1396  }
1397 
1409  private Node getInterestingItemNode(Children typesChildren, BlackboardArtifact art) {
1410  Node interestingItemsRootNode = typesChildren.findChild(NbBundle
1411  .getMessage(InterestingHits.class, "InterestingHits.interestingItems.text"));
1412  Children interestingItemsRootChildren = interestingItemsRootNode.getChildren();
1413  try {
1414  String setName = null;
1415  List<BlackboardAttribute> attributes = art.getAttributes();
1416  for (BlackboardAttribute att : attributes) {
1417  int typeId = att.getAttributeType().getTypeID();
1418  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
1419  setName = att.getValueString();
1420  }
1421  }
1422  Node setNode = interestingItemsRootChildren.findChild(setName);
1423  if (setNode == null) {
1424  return null;
1425  }
1426 
1427  Children fileArtifactChildren = setNode.getChildren();
1428  Node[] fileArtifactNodes = fileArtifactChildren == null ? null : fileArtifactChildren.getNodes();
1429  if (fileArtifactNodes == null || fileArtifactNodes.length != 2) {
1430  return null;
1431  }
1432 
1433  return (art.getArtifactTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID())
1434  ? fileArtifactNodes[0]
1435  : fileArtifactNodes[1];
1436  } catch (TskCoreException ex) {
1437  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1438  return null;
1439  }
1440  }
1441 
1450  private Node getEmailNode(Children typesChildren, BlackboardArtifact art) {
1451  Node emailMsgRootNode = typesChildren.findChild(art.getArtifactTypeName());
1452  Children emailMsgRootChilds = emailMsgRootNode.getChildren();
1453  Map<String, String> parsedPath = null;
1454  try {
1455  List<BlackboardAttribute> attributes = art.getAttributes();
1456  for (BlackboardAttribute att : attributes) {
1457  int typeId = att.getAttributeType().getTypeID();
1458  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
1459  parsedPath = EmailExtracted.parsePath(att.getValueString());
1460  break;
1461  }
1462  }
1463  if (parsedPath == null) {
1464  return null;
1465  }
1466  Node defaultNode = emailMsgRootChilds.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text")));
1467  Children defaultChildren = defaultNode.getChildren();
1468  return defaultChildren.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text")));
1469  } catch (TskCoreException ex) {
1470  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1471  return null;
1472  }
1473  }
1474 
1484  private Node getAccountNode(Children typesChildren, BlackboardArtifact art) {
1485  Node accountRootNode = typesChildren.findChild(art.getDisplayName());
1486  Children accountRootChilds = accountRootNode.getChildren();
1487  List<BlackboardAttribute> attributes;
1488  String accountType = null;
1489  String ccNumberName = null;
1490  try {
1491  attributes = art.getAttributes();
1492  for (BlackboardAttribute att : attributes) {
1493  int typeId = att.getAttributeType().getTypeID();
1494  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()) {
1495  accountType = att.getValueString();
1496  }
1497  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID()) {
1498  ccNumberName = att.getValueString();
1499  }
1500  }
1501  if (accountType == null) {
1502  return null;
1503  }
1504 
1505  if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) {
1506  return getCreditCardAccountNode(accountRootChilds, ccNumberName);
1507  } else { //default account type
1508  return accountRootChilds.findChild(accountType);
1509  }
1510  } catch (TskCoreException ex) {
1511  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1512  return null;
1513  }
1514  }
1515 
1526  private Node getCreditCardAccountNode(Children accountRootChildren, String ccNumberName) {
1527  Node accountNode = accountRootChildren.findChild(Account.Type.CREDIT_CARD.getDisplayName());
1528  if (accountNode == null) {
1529  return null;
1530  }
1531  Children accountChildren = accountNode.getChildren();
1532  if (accountChildren == null) {
1533  return null;
1534  }
1535  Node binNode = accountChildren.findChild(NbBundle.getMessage(Accounts.class, "Accounts.ByBINNode.name"));
1536  if (binNode == null) {
1537  return null;
1538  }
1539  Children binChildren = binNode.getChildren();
1540  if (ccNumberName == null) {
1541  return null;
1542  }
1543  //right padded with 0s to 8 digits when single number
1544  //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
1545  String binName = StringUtils.rightPad(ccNumberName, 8, "0");
1546  binName = binName.substring(0, 8);
1547  int bin;
1548  try {
1549  bin = Integer.parseInt(binName);
1550  } catch (NumberFormatException ex) {
1551  LOGGER.log(Level.WARNING, "Unable to parseInt a BIN for node selection from string binName=" + binName, ex); //NON-NLS
1552  return null;
1553  }
1555  if (binInfo != null) {
1556  int startBin = ((BINRange) binInfo).getBINstart();
1557  int endBin = ((BINRange) binInfo).getBINend();
1558  if (startBin != endBin) {
1559  binName = Integer.toString(startBin) + "-" + Integer.toString(endBin).substring(5); //if there is a range re-construct the name it appears as
1560  }
1561  }
1562  if (binName == null) {
1563  return null;
1564  }
1565  return binChildren.findChild(binName);
1566  }
1567 
1568  public void viewArtifactContent(BlackboardArtifact art) {
1569  new ViewContextAction(
1570  NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.action.viewArtContent.text"),
1571  new BlackboardArtifactNode(art)).actionPerformed(null);
1572  }
1573 
1574  public void addOnFinishedListener(PropertyChangeListener l) {
1575  DirectoryTreeTopComponent.this.addPropertyChangeListener(l);
1576  }
1577 
1578 }
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)
Node getInterestingItemNode(Children typesChildren, BlackboardArtifact art)
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)
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()

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