Autopsy  4.6.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 2011-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.directorytree;
20 
21 import java.awt.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.EnumSet;
30 import java.util.LinkedList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.concurrent.ExecutionException;
34 import java.util.logging.Level;
35 import java.util.prefs.PreferenceChangeEvent;
36 import java.util.prefs.PreferenceChangeListener;
37 import javax.swing.Action;
38 import javax.swing.SwingUtilities;
39 import javax.swing.SwingWorker;
40 import javax.swing.tree.TreeSelectionModel;
41 import org.apache.commons.lang3.StringUtils;
42 import org.openide.explorer.ExplorerManager;
43 import org.openide.explorer.ExplorerUtils;
44 import org.openide.explorer.view.BeanTreeView;
45 import org.openide.explorer.view.TreeView;
46 import org.openide.nodes.AbstractNode;
47 import org.openide.nodes.Children;
48 import org.openide.nodes.Node;
49 import org.openide.nodes.NodeNotFoundException;
50 import org.openide.nodes.NodeOp;
51 import org.openide.util.NbBundle;
52 import org.openide.util.NbBundle.Messages;
53 import org.openide.windows.TopComponent;
54 import org.openide.windows.WindowManager;
86 import org.sleuthkit.datamodel.Account;
87 import org.sleuthkit.datamodel.BlackboardArtifact;
88 import org.sleuthkit.datamodel.BlackboardAttribute;
89 import org.sleuthkit.datamodel.Content;
90 import org.sleuthkit.datamodel.SleuthkitCase;
91 import org.sleuthkit.datamodel.TskCoreException;
92 
96 // Registered as a service provider for DataExplorer in layer.xml
97 @Messages({
98  "DirectoryTreeTopComponent.resultsView.title=Listing"
99 })
100 public final class DirectoryTreeTopComponent extends TopComponent implements DataExplorer, ExplorerManager.Provider {
101 
102  private final transient ExplorerManager em = new ExplorerManager();
104  private final DataResultTopComponent dataResult = new DataResultTopComponent(Bundle.DirectoryTreeTopComponent_resultsView_title());
105  private final LinkedList<String[]> backList;
106  private final LinkedList<String[]> forwardList;
107  private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS
108  private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName());
110 
115  initComponents();
116 
117  // only allow one item to be selected at a time
118  getTree().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
119  // remove the close button
120  putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE);
121  setName(NbBundle.getMessage(DirectoryTreeTopComponent.class, "CTL_DirectoryTreeTopComponent"));
122  setToolTipText(NbBundle.getMessage(DirectoryTreeTopComponent.class, "HINT_DirectoryTreeTopComponent"));
123 
124  subscribeToChangeEvents();
125  associateLookup(ExplorerUtils.createLookup(em, getActionMap()));
126 
127  // set the back & forward list and also disable the back & forward button
128  this.backList = new LinkedList<>();
129  this.forwardList = new LinkedList<>();
130  backButton.setEnabled(false);
131  forwardButton.setEnabled(false);
132  }
133 
137  private void subscribeToChangeEvents() {
138  UserPreferences.addChangeListener(new PreferenceChangeListener() {
139  @Override
140  public void preferenceChange(PreferenceChangeEvent evt) {
141  switch (evt.getKey()) {
144  refreshContentTreeSafe();
145  break;
148  // TODO: Need a way to refresh the Views subtree
149  break;
150  }
151  }
152  });
153 
155  this.em.addPropertyChangeListener(this);
158  }
159 
161  this.dataResult.requestActive();
162  }
163 
164  public void openDirectoryListing() {
165  this.dataResult.open();
166  }
167 
169  return this.dataResult;
170  }
171 
177  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
178  private void initComponents() {
179 
180  treeView = new BeanTreeView();
181  backButton = new javax.swing.JButton();
182  forwardButton = new javax.swing.JButton();
183  showRejectedCheckBox = new javax.swing.JCheckBox();
184 
185  treeView.setBorder(null);
186 
187  backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back.png"))); // NOI18N
188  org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.backButton.text")); // NOI18N
189  backButton.setBorderPainted(false);
190  backButton.setContentAreaFilled(false);
191  backButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_disabled.png"))); // NOI18N
192  backButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
193  backButton.setMaximumSize(new java.awt.Dimension(55, 100));
194  backButton.setMinimumSize(new java.awt.Dimension(5, 5));
195  backButton.setPreferredSize(new java.awt.Dimension(23, 23));
196  backButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_hover.png"))); // NOI18N
197  backButton.addActionListener(new java.awt.event.ActionListener() {
198  public void actionPerformed(java.awt.event.ActionEvent evt) {
199  backButtonActionPerformed(evt);
200  }
201  });
202 
203  forwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward.png"))); // NOI18N
204  org.openide.awt.Mnemonics.setLocalizedText(forwardButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.forwardButton.text")); // NOI18N
205  forwardButton.setBorderPainted(false);
206  forwardButton.setContentAreaFilled(false);
207  forwardButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_disabled.png"))); // NOI18N
208  forwardButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
209  forwardButton.setMaximumSize(new java.awt.Dimension(55, 100));
210  forwardButton.setMinimumSize(new java.awt.Dimension(5, 5));
211  forwardButton.setPreferredSize(new java.awt.Dimension(23, 23));
212  forwardButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_hover.png"))); // NOI18N
213  forwardButton.addActionListener(new java.awt.event.ActionListener() {
214  public void actionPerformed(java.awt.event.ActionEvent evt) {
215  forwardButtonActionPerformed(evt);
216  }
217  });
218 
219  org.openide.awt.Mnemonics.setLocalizedText(showRejectedCheckBox, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.showRejectedCheckBox.text")); // NOI18N
220 
221  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
222  this.setLayout(layout);
223  layout.setHorizontalGroup(
224  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
225  .addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 262, Short.MAX_VALUE)
226  .addGroup(layout.createSequentialGroup()
227  .addGap(5, 5, 5)
228  .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
229  .addGap(0, 0, 0)
230  .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
231  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 46, Short.MAX_VALUE)
232  .addComponent(showRejectedCheckBox)
233  .addContainerGap())
234  );
235  layout.setVerticalGroup(
236  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
237  .addGroup(layout.createSequentialGroup()
238  .addGap(5, 5, 5)
239  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
240  .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
241  .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
242  .addComponent(showRejectedCheckBox))
243  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
244  .addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 854, Short.MAX_VALUE)
245  .addGap(0, 0, 0))
246  );
247  }// </editor-fold>//GEN-END:initComponents
248 
249  private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
250  // change the cursor to "waiting cursor" for this operation
251  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
252 
253  // the end is the current place,
254  String[] currentNodePath = backList.pollLast();
255  forwardList.addLast(currentNodePath);
256  forwardButton.setEnabled(true);
257 
258  /*
259  * We peek instead of poll because we use its existence in the list
260  * later on so that we do not reset the forward list after the selection
261  * occurs.
262  */
263  String[] newCurrentNodePath = backList.peekLast();
264 
265  // enable / disable the back and forward button
266  if (backList.size() > 1) {
267  backButton.setEnabled(true);
268  } else {
269  backButton.setEnabled(false);
270  }
271 
272  // update the selection on directory tree
273  setSelectedNode(newCurrentNodePath, null);
274 
275  this.setCursor(null);
276  }//GEN-LAST:event_backButtonActionPerformed
277 
278  private void forwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed
279  // change the cursor to "waiting cursor" for this operation
280  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
281 
282  String[] newCurrentNodePath = forwardList.pollLast();
283  if (!forwardList.isEmpty()) {
284  forwardButton.setEnabled(true);
285  } else {
286  forwardButton.setEnabled(false);
287  }
288 
289  backList.addLast(newCurrentNodePath);
290  backButton.setEnabled(true);
291 
292  // update the selection on directory tree
293  setSelectedNode(newCurrentNodePath, null);
294 
295  this.setCursor(null);
296  }//GEN-LAST:event_forwardButtonActionPerformed
297 
298  // Variables declaration - do not modify//GEN-BEGIN:variables
299  private javax.swing.JButton backButton;
300  private javax.swing.JButton forwardButton;
301  private javax.swing.JCheckBox showRejectedCheckBox;
302  private javax.swing.JScrollPane treeView;
303  // End of variables declaration//GEN-END:variables
304 
313  public static synchronized DirectoryTreeTopComponent getDefault() {
314  if (instance == null) {
315  instance = new DirectoryTreeTopComponent();
316  }
317  return instance;
318  }
319 
326  public static synchronized DirectoryTreeTopComponent findInstance() {
327  WindowManager winManager = WindowManager.getDefault();
328  TopComponent win = winManager.findTopComponent(PREFERRED_ID);
329  if (win == null) {
330  LOGGER.warning(
331  "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system."); //NON-NLS
332  return getDefault();
333  }
334  if (win instanceof DirectoryTreeTopComponent) {
335  return (DirectoryTreeTopComponent) win;
336  }
337  LOGGER.warning(
338  "There seem to be multiple components with the '" + PREFERRED_ID //NON-NLS
339  + "' ID. That is a potential source of errors and unexpected behavior."); //NON-NLS
340  return getDefault();
341  }
342 
349  @Override
350  public int getPersistenceType() {
351  return TopComponent.PERSISTENCE_NEVER;
352  }
353 
361  @Override
362  public void componentOpened() {
363  // change the cursor to "waiting cursor" for this operation
364  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
365  Case currentCase = null;
366  try {
367  currentCase = Case.getOpenCase();
368  } catch (NoCurrentCaseException ex) {
369  // No open case.
370  }
371 
372  // close the top component if there's no image in this case
373  if (null == currentCase || currentCase.hasData() == false) {
374  getTree().setRootVisible(false); // hide the root
375  } else {
376  // if there's at least one image, load the image and open the top component
377  final SleuthkitCase tskCase = currentCase.getSleuthkitCase();
378  contentChildren = new RootContentChildren(Arrays.asList(
379  new DataSources(),
380  new Views(tskCase),
381  new Results(tskCase),
382  new Tags(),
383  new Reports()));
384  Node root = new AbstractNode(contentChildren) {
385  //JIRA-2807: What is the point of these overrides?
390  @Override
391  public Action[] getActions(boolean popup) {
392  return new Action[]{};
393  }
394 
395  // Overide the AbstractNode use of DefaultHandle to return
396  // a handle which can be serialized without a parent
397  @Override
398  public Node.Handle getHandle() {
399  return new Node.Handle() {
400  @Override
401  public Node getNode() throws IOException {
402  return em.getRootContext();
403  }
404  };
405  }
406  };
407 
408  root = new DirectoryTreeFilterNode(root, true);
409 
410  em.setRootContext(root);
411  em.getRootContext().setName(currentCase.getName());
412  em.getRootContext().setDisplayName(currentCase.getName());
413  getTree().setRootVisible(false); // hide the root
414 
415  // Reset the forward and back lists because we're resetting the root context
416  resetHistory();
417  new SwingWorker<Node[], Void>() {
418  @Override
419  protected Node[] doInBackground() throws Exception {
420  Children rootChildren = em.getRootContext().getChildren();
421  TreeView tree = getTree();
422 
423  Node results = rootChildren.findChild(ResultsNode.NAME);
424  tree.expandNode(results);
425  Children resultsChildren = results.getChildren();
426  Arrays.stream(resultsChildren.getNodes()).forEach(tree::expandNode);
427 
428  Accounts accounts = resultsChildren.findChild(Accounts.NAME).getLookup().lookup(Accounts.class);
429  showRejectedCheckBox.setAction(accounts.newToggleShowRejectedAction());
430  showRejectedCheckBox.setSelected(false);
431 
432  Node views = rootChildren.findChild(ViewsNode.NAME);
433  Arrays.stream(views.getChildren().getNodes()).forEach(tree::expandNode);
434  tree.collapseNode(views);
435  /*
436  * JIRA-2806: What is this supposed to do? Right now it selects
437  * the data sources node, but the comment seems to indicate
438  * it is supposed to select the first datasource.
439  */
440  // select the first image node, if there is one
441  // (this has to happen after dataResult is opened, because the event
442  // of changing the selected node fires a handler that tries to make
443  // dataResult active)
444  if (rootChildren.getNodesCount() > 0) {
445  return new Node[]{rootChildren.getNodeAt(0)};
446  }
447  return new Node[]{};
448  }
449 
450  @Override
451  protected void done() {
452  super.done();
453 
454  // if the dataResult is not opened
455  if (!dataResult.isOpened()) {
456  dataResult.open(); // open the data result top component as well when the directory tree is opened
457  }
458  /*
459  * JIRA-2806: What is this supposed to do?
460  */
461  // select the first image node, if there is one
462  // (this has to happen after dataResult is opened, because the event
463  // of changing the selected node fires a handler that tries to make
464  // dataResult active)
465  try {
466  em.setSelectedNodes(get());
467  } catch (PropertyVetoException ex) {
468  LOGGER.log(Level.SEVERE, "Error setting default selected node.", ex); //NON-NLS
469  } catch (InterruptedException | ExecutionException ex) {
470  LOGGER.log(Level.SEVERE, "Error expanding tree to initial state.", ex); //NON-NLS
471  } finally {
472  setCursor(null);
473  }
474  }
475  }.execute();
476  }
477  }
478 
485  @Override
486  public void componentClosed() {
487  //@@@ push the selection node to null?
488  contentChildren = null;
489  }
490 
491  void writeProperties(java.util.Properties p) {
492  // better to version settings since initial version as advocated at
493  // http://wiki.apidesign.org/wiki/PropertyFiles
494  p.setProperty("version", "1.0");
495  // TODO store your settings
496  }
497 
498  Object readProperties(java.util.Properties p) {
499  if (instance == null) {
500  instance = this;
501  }
502  instance.readPropertiesImpl(p);
503  return instance;
504  }
505 
506  private void readPropertiesImpl(java.util.Properties p) {
507  String version = p.getProperty("version");
508  // TODO read your settings according to their version
509  }
510 
516  @Override
517  protected String preferredID() {
518  return PREFERRED_ID;
519  }
520 
521  @Override
522  public boolean canClose() {
523  /*
524  * Only allow the main tree view in the left side of the main window to
525  * be closed if there is no opne case or the open case has no data
526  * sources.
527  */
528  try {
529  Case openCase = Case.getOpenCase();
530  return openCase.hasData() == false;
531  } catch (NoCurrentCaseException ex) {
532  return true;
533  }
534  }
535 
541  @Override
542  public ExplorerManager getExplorerManager() {
543  return this.em;
544  }
545 
551  @Override
552  public Action[] getActions() {
553  return new Action[]{};
554  }
555 
561  public Node getSelectedNode() {
562  Node result = null;
563 
564  Node[] selectedNodes = this.getExplorerManager().getSelectedNodes();
565  if (selectedNodes.length > 0) {
566  result = selectedNodes[0];
567  }
568  return result;
569  }
570 
577  @Override
578  public void propertyChange(PropertyChangeEvent evt) {
580  String changed = evt.getPropertyName();
581  if (changed.equals(Case.Events.CURRENT_CASE.toString())) { // changed current case
582  // When a case is closed, the old value of this property is the
583  // closed Case object and the new value is null. When a case is
584  // opened, the old value is null and the new value is the new Case
585  // object.
586  // @@@ This needs to be revisited. Perhaps case closed and case
587  // opened events instead of property change events would be a better
588  // solution. Either way, more probably needs to be done to clean up
589  // data model objects when a case is closed.
590  if (evt.getOldValue() != null && evt.getNewValue() == null) {
591  // The current case has been closed. Reset the ExplorerManager.
592  SwingUtilities.invokeLater(() -> {
593  Node emptyNode = new AbstractNode(Children.LEAF);
594  em.setRootContext(emptyNode);
595  });
596  } else if (evt.getNewValue() != null) {
597  // A new case has been opened. Reset the ExplorerManager.
598  Case newCase = (Case) evt.getNewValue();
599  final String newCaseName = newCase.getName();
600  SwingUtilities.invokeLater(() -> {
601  em.getRootContext().setName(newCaseName);
602  em.getRootContext().setDisplayName(newCaseName);
603 
604  // Reset the forward and back
605  // buttons. Note that a call to CoreComponentControl.openCoreWindows()
606  // by the new Case object will lead to a componentOpened() call
607  // that will repopulate the tree.
608  // @@@ The repopulation of the tree in this fashion also merits
609  // reconsideration.
610  resetHistory();
611  });
612  }
613  } // if the image is added to the case
614  else if (changed.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
621  try {
622  Case currentCase = Case.getOpenCase();
623  // We only need to trigger openCoreWindows() when the
624  // first data source is added.
625  if (currentCase.getDataSources().size() == 1) {
626  SwingUtilities.invokeLater(CoreComponentControl::openCoreWindows);
627  }
628  } catch (NoCurrentCaseException | TskCoreException notUsed) {
632  }
633  } // change in node selection
634  else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
635  respondSelection((Node[]) evt.getOldValue(), (Node[]) evt.getNewValue());
636  } else if (changed.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
637  // nothing to do here.
638  // all nodes should be listening for these events and update accordingly.
639  }
640  }
641  }
642 
651  @NbBundle.Messages("DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.")
652  void respondSelection(final Node[] oldNodes, final Node[] newNodes) {
653  if (!Case.isCaseOpen()) {
654  return;
655  }
656 
657  // Some lock that prevents certain Node operations is set during the
658  // ExplorerManager selection-change, so we must handle changes after the
659  // selection-change event is processed.
660  //TODO find a different way to refresh data result viewer, scheduling this
661  //to EDT breaks loading of nodes in the background
662  EventQueue.invokeLater(() -> {
663  // change the cursor to "waiting cursor" for this operation
664  DirectoryTreeTopComponent.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
665  try {
666  Node treeNode = DirectoryTreeTopComponent.this.getSelectedNode();
667  if (treeNode != null) {
668  Node originNode = ((DirectoryTreeFilterNode) treeNode).getOriginal();
669  //set node, wrap in filter node first to filter out children
670  Node drfn = new DataResultFilterNode(originNode, DirectoryTreeTopComponent.this.em);
671  // Create a TableFilterNode with knowledge of the node's type to allow for column order settings
672  if (FileTypesByMimeType.isEmptyMimeTypeNode(originNode)) {
673  //Special case for when File Type Identification has not yet been run and
674  //there are no mime types to populate Files by Mime Type Tree
675  EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTreeTopComponent_emptyMimeNode_text());
676  dataResult.setNode(new TableFilterNode(emptyNode, true, "This Node Is Empty")); //NON-NLS
677  } else if (originNode instanceof DisplayableItemNode) {
678  dataResult.setNode(new TableFilterNode(drfn, true, ((DisplayableItemNode) originNode).getItemType()));
679  } else {
680  dataResult.setNode(new TableFilterNode(drfn, true));
681  }
682  String displayName = "";
683  Content content = originNode.getLookup().lookup(Content.class);
684  if (content != null) {
685  try {
686  displayName = content.getUniquePath();
687  } catch (TskCoreException ex) {
688  LOGGER.log(Level.SEVERE, "Exception while calling Content.getUniquePath() for node: {0}", originNode); //NON-NLS
689  }
690  } else if (originNode.getLookup().lookup(String.class) != null) {
691  displayName = originNode.getLookup().lookup(String.class);
692  }
693  dataResult.setPath(displayName);
694  }
695  // set the directory listing to be active
696  if (oldNodes != null && newNodes != null
697  && (oldNodes.length == newNodes.length)) {
698  boolean sameNodes = true;
699  for (int i = 0; i < oldNodes.length; i++) {
700  sameNodes = sameNodes && oldNodes[i].getName().equals(newNodes[i].getName());
701  }
702  if (!sameNodes) {
703  dataResult.requestActive();
704  }
705  }
706  } finally {
707  setCursor(null);
708  }
709  });
710 
711  // update the back and forward list
712  updateHistory(em.getSelectedNodes());
713  }
714 
715  private void updateHistory(Node[] selectedNodes) {
716  if (selectedNodes.length == 0) {
717  return;
718  }
719 
720  Node selectedNode = selectedNodes[0];
721  String selectedNodeName = selectedNode.getName();
722 
723  /*
724  * get the previous entry to make sure we don't duplicate it. Motivation
725  * for this is also that if we used the back button, then we already
726  * added the 'current' node to 'back' and we will detect that and not
727  * reset the forward list.
728  */
729  String[] currentLast = backList.peekLast();
730  String lastNodeName = null;
731  if (currentLast != null) {
732  lastNodeName = currentLast[currentLast.length - 1];
733  }
734 
735  if (currentLast == null || !selectedNodeName.equals(lastNodeName)) {
736  //add to the list if the last if not the same as current
737  final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
738  backList.addLast(selectedPath); // add the node to the "backList"
739  if (backList.size() > 1) {
740  backButton.setEnabled(true);
741  } else {
742  backButton.setEnabled(false);
743  }
744 
745  forwardList.clear(); // clear the "forwardList"
746  forwardButton.setEnabled(false); // disable the forward Button
747  }
748  }
749 
754  private void resetHistory() {
755  // clear the back and forward list
756  backList.clear();
757  forwardList.clear();
758  backButton.setEnabled(false);
759  forwardButton.setEnabled(false);
760  }
761 
767  public BeanTreeView getTree() {
768  return (BeanTreeView) this.treeView;
769  }
770 
774  public void refreshContentTreeSafe() {
775  SwingUtilities.invokeLater(this::refreshDataSourceTree);
776  }
777 
781  private void refreshDataSourceTree() {
782  Node selectedNode = getSelectedNode();
783  final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
784  Children rootChildren = em.getRootContext().getChildren();
785  Node dataSourcesFilterNode = rootChildren.findChild(DataSourcesNode.NAME);
786  if (dataSourcesFilterNode == null) {
787  LOGGER.log(Level.SEVERE, "Cannot find data sources filter node, won't refresh the content tree"); //NON-NLS
788  return;
789  }
790  Node dataSourcesNode = ((DirectoryTreeFilterNode) dataSourcesFilterNode).getOriginal();
791  DataSourcesNode.DataSourcesNodeChildren contentRootChildren = (DataSourcesNode.DataSourcesNodeChildren) dataSourcesNode.getChildren();
792  contentRootChildren.refreshContentKeys();
793  setSelectedNode(selectedPath, DataSourcesNode.NAME);
794  }
795 
803  private void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName) {
804  if (previouslySelectedNodePath == null) {
805  return;
806  }
807  SwingUtilities.invokeLater(new Runnable() {
808  @Override
809  public void run() {
810  if (previouslySelectedNodePath.length > 0 && (rootNodeName == null || previouslySelectedNodePath[0].equals(rootNodeName))) {
811  Node selectedNode = null;
812  ArrayList<String> selectedNodePath = new ArrayList<>(Arrays.asList(previouslySelectedNodePath));
813  while (null == selectedNode && !selectedNodePath.isEmpty()) {
814  try {
815  selectedNode = NodeOp.findPath(em.getRootContext(), selectedNodePath.toArray(new String[selectedNodePath.size()]));
816  } catch (NodeNotFoundException ex) {
817  // The selected node may have been deleted (e.g., a deleted tag), so truncate the path and try again.
818  if (selectedNodePath.size() > 1) {
819  selectedNodePath.remove(selectedNodePath.size() - 1);
820  } else {
821  StringBuilder nodePath = new StringBuilder();
822  for (int i = 0; i < previouslySelectedNodePath.length; ++i) {
823  nodePath.append(previouslySelectedNodePath[i]).append("/");
824  }
825  LOGGER.log(Level.WARNING, "Failed to find any nodes to select on path " + nodePath.toString(), ex); //NON-NLS
826  break;
827  }
828  }
829  }
830 
831  if (null != selectedNode) {
832  if (rootNodeName != null) {
833  //called from tree auto refresh context
834  //remove last from backlist, because auto select will result in duplication
835  backList.pollLast();
836  }
837  try {
838  em.setExploredContextAndSelection(selectedNode, new Node[]{selectedNode});
839  } catch (PropertyVetoException ex) {
840  LOGGER.log(Level.WARNING, "Property veto from ExplorerManager setting selection to " + selectedNode.getName(), ex); //NON-NLS
841  }
842  }
843  }
844  }
845  });
846  }
847 
848  @Override
849  public TopComponent getTopComponent() {
850  return this;
851  }
852 
853  @Override
854  public boolean hasMenuOpenAction() {
855  return false;
856  }
857 
858  public void viewArtifact(final BlackboardArtifact art) {
859  int typeID = art.getArtifactTypeID();
860  String typeName = art.getArtifactTypeName();
861  Children rootChilds = em.getRootContext().getChildren();
862  Node treeNode = null;
863  Node resultsNode = rootChilds.findChild(ResultsNode.NAME);
864  Children resultsChilds = resultsNode.getChildren();
865  if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
866  Node hashsetRootNode = resultsChilds.findChild(typeName);
867  Children hashsetRootChilds = hashsetRootNode.getChildren();
868  try {
869  String setName = null;
870  List<BlackboardAttribute> attributes = art.getAttributes();
871  for (BlackboardAttribute att : attributes) {
872  int typeId = att.getAttributeType().getTypeID();
873  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
874  setName = att.getValueString();
875  }
876  }
877  treeNode = hashsetRootChilds.findChild(setName);
878  } catch (TskCoreException ex) {
879  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
880  }
881  } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
882  Node keywordRootNode = resultsChilds.findChild(typeName);
883  Children keywordRootChilds = keywordRootNode.getChildren();
884  try {
885  String listName = null;
886  String keywordName = null;
887  String regex = null;
888  List<BlackboardAttribute> attributes = art.getAttributes();
889  for (BlackboardAttribute att : attributes) {
890  int typeId = att.getAttributeType().getTypeID();
891  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
892  listName = att.getValueString();
893  } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
894  keywordName = att.getValueString();
895  } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()) {
896  regex = att.getValueString();
897  }
898  }
899  if (listName == null) {
900  if (regex == null) { //using same labels used for creation
901  listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.simpleLiteralSearch.text");
902  } else {
903  listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text");
904  }
905  }
906  Node listNode = keywordRootChilds.findChild(listName);
907  if (listNode == null) {
908  return;
909  }
910  Children listChildren = listNode.getChildren();
911  if (listChildren == null) {
912  return;
913  }
914  if (regex != null) { //For support of regex nodes such as URLs, IPs, Phone Numbers, and Email Addrs as they are down another level
915  Node regexNode = listChildren.findChild(regex);
916  if (regexNode == null) {
917  return;
918  }
919  listChildren = regexNode.getChildren();
920  if (listChildren == null) {
921  return;
922  }
923  }
924 
925  treeNode = listChildren.findChild(keywordName);
926 
927  } catch (TskCoreException ex) {
928  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
929  }
930  } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
931  || typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
932  Node interestingItemsRootNode = resultsChilds.findChild(NbBundle
933  .getMessage(InterestingHits.class, "InterestingHits.interestingItems.text"));
934  Children interestingItemsRootChildren = interestingItemsRootNode.getChildren();
935  try {
936  String setName = null;
937  List<BlackboardAttribute> attributes = art.getAttributes();
938  for (BlackboardAttribute att : attributes) {
939  int typeId = att.getAttributeType().getTypeID();
940  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
941  setName = att.getValueString();
942  }
943  }
944  Node setNode = interestingItemsRootChildren.findChild(setName);
945  if (setNode == null) {
946  return;
947  }
948  Children interestingChildren = setNode.getChildren();
949  if (interestingChildren == null) {
950  return;
951  }
952  treeNode = interestingChildren.findChild(art.getDisplayName());
953  } catch (TskCoreException ex) {
954  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
955  }
956  } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
957  Node emailMsgRootNode = resultsChilds.findChild(typeName);
958  Children emailMsgRootChilds = emailMsgRootNode.getChildren();
959  Map<String, String> parsedPath = null;
960  try {
961  List<BlackboardAttribute> attributes = art.getAttributes();
962  for (BlackboardAttribute att : attributes) {
963  int typeId = att.getAttributeType().getTypeID();
964  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
965  parsedPath = EmailExtracted.parsePath(att.getValueString());
966  break;
967  }
968  }
969  if (parsedPath == null) {
970  return;
971  }
972  Node defaultNode = emailMsgRootChilds.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text")));
973  Children defaultChildren = defaultNode.getChildren();
974  treeNode = defaultChildren.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text")));
975  } catch (TskCoreException ex) {
976  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
977  }
978 
979  } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
980  Node accountRootNode = resultsChilds.findChild(art.getDisplayName());
981  Children accountRootChilds = accountRootNode.getChildren();
982  List<BlackboardAttribute> attributes;
983  String accountType = null;
984  String ccNumberName = null;
985  try {
986  attributes = art.getAttributes();
987  for (BlackboardAttribute att : attributes) {
988  int typeId = att.getAttributeType().getTypeID();
989  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()) {
990  accountType = att.getValueString();
991  }
992  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID()) {
993  ccNumberName = att.getValueString();
994  }
995  }
996  if (accountType == null) {
997  return;
998  }
999 
1000  if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) {
1001  Node accountNode = accountRootChilds.findChild(Account.Type.CREDIT_CARD.getDisplayName());
1002  if (accountNode == null) {
1003  return;
1004  }
1005  Children accountChildren = accountNode.getChildren();
1006  if (accountChildren == null) {
1007  return;
1008  }
1009  Node binNode = accountChildren.findChild(NbBundle.getMessage(Accounts.class, "Accounts.ByBINNode.name"));
1010  if (binNode == null) {
1011  return;
1012  }
1013  Children binChildren = binNode.getChildren();
1014  if (ccNumberName == null) {
1015  return;
1016  }
1017  //right padded with 0s to 8 digits when single number
1018  //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
1019  String binName = StringUtils.rightPad(ccNumberName, 8, "0");
1020  binName = binName.substring(0, 8);
1021  int bin;
1022  try {
1023  bin = Integer.parseInt(binName);
1024  } catch (NumberFormatException ex) {
1025  LOGGER.log(Level.WARNING, "Unable to parseInt a BIN for node selection from string binName=" + binName, ex); //NON-NLS
1026  return;
1027  }
1029  if (binInfo != null) {
1030  int startBin = ((BINRange) binInfo).getBINstart();
1031  int endBin = ((BINRange) binInfo).getBINend();
1032  if (startBin != endBin) {
1033  binName = Integer.toString(startBin) + "-" + Integer.toString(endBin).substring(5); //if there is a range re-construct the name it appears as
1034  }
1035  }
1036  if (binName == null) {
1037  return;
1038  }
1039  treeNode = binChildren.findChild(binName);
1040  } else { //default account type
1041  treeNode = accountRootChilds.findChild(accountType);
1042  }
1043  } catch (TskCoreException ex) {
1044  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1045  }
1046  } else {
1047  Node extractedContent = resultsChilds.findChild(ExtractedContent.NAME);
1048  Children extractedChilds = extractedContent.getChildren();
1049  if (extractedChilds == null) {
1050  return;
1051  }
1052  treeNode = extractedChilds.findChild(typeName);
1053  }
1054 
1055  if (treeNode == null) {
1056  return;
1057  }
1058 
1059  DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) treeNode).getOriginal();
1060  undecoratedParentNode.setChildNodeSelectionInfo(new ArtifactNodeSelectionInfo(art));
1061  getTree().expandNode(treeNode);
1062  try {
1063  em.setExploredContextAndSelection(treeNode, new Node[]{treeNode});
1064  } catch (PropertyVetoException ex) {
1065  LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
1066  }
1067  // Another thread is needed because we have to wait for dataResult to populate
1068  }
1069 
1070  public void viewArtifactContent(BlackboardArtifact art) {
1071  new ViewContextAction(
1072  NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.action.viewArtContent.text"),
1073  new BlackboardArtifactNode(art)).actionPerformed(null);
1074  }
1075 
1076  public void addOnFinishedListener(PropertyChangeListener l) {
1077  DirectoryTreeTopComponent.this.addPropertyChangeListener(l);
1078  }
1079 
1080 }
List< Content > getDataSources()
Definition: Case.java:1385
static final Map< String, String > parsePath(String path)
static synchronized IngestManager getInstance()
void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName)
static synchronized BankIdentificationNumber getBINInfo(int bin)
void addIngestJobEventListener(final PropertyChangeListener listener)
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:420
static void addChangeListener(PreferenceChangeListener listener)
void setChildNodeSelectionInfo(NodeSelectionInfo selectedChildNodeInfo)
static synchronized DirectoryTreeTopComponent findInstance()

Copyright © 2012-2016 Basis Technology. Generated on: Mon May 7 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.