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

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