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