Autopsy  4.4.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
DirectoryTreeTopComponent.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 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.HashSet;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.concurrent.ExecutionException;
35 import java.util.logging.Level;
36 import java.util.prefs.PreferenceChangeEvent;
37 import java.util.prefs.PreferenceChangeListener;
38 import javax.swing.Action;
39 import javax.swing.SwingUtilities;
40 import javax.swing.SwingWorker;
41 import javax.swing.tree.TreeSelectionModel;
42 import org.apache.commons.lang3.StringUtils;
43 import org.openide.explorer.ExplorerManager;
44 import org.openide.explorer.ExplorerUtils;
45 import org.openide.explorer.view.BeanTreeView;
46 import org.openide.explorer.view.TreeView;
47 import org.openide.nodes.AbstractNode;
48 import org.openide.nodes.Children;
49 import org.openide.nodes.Node;
50 import org.openide.nodes.NodeNotFoundException;
51 import org.openide.nodes.NodeOp;
52 import org.openide.util.NbBundle;
53 import org.openide.util.NbBundle.Messages;
54 import org.openide.windows.TopComponent;
55 import org.openide.windows.WindowManager;
88 import org.sleuthkit.datamodel.Account;
89 import org.sleuthkit.datamodel.BlackboardArtifact;
90 import org.sleuthkit.datamodel.BlackboardAttribute;
91 import org.sleuthkit.datamodel.Content;
92 import org.sleuthkit.datamodel.SleuthkitCase;
93 import org.sleuthkit.datamodel.TskCoreException;
94 
98 // Registered as a service provider for DataExplorer in layer.xml
99 @Messages({
100  "DirectoryTreeTopComponent.resultsView.title=Listing"
101 })
102 public final class DirectoryTreeTopComponent extends TopComponent implements DataExplorer, ExplorerManager.Provider, BlackboardResultViewer {
103 
104  private final transient ExplorerManager em = new ExplorerManager();
106  private final DataResultTopComponent dataResult = new DataResultTopComponent(true, Bundle.DirectoryTreeTopComponent_resultsView_title());
107  private final LinkedList<String[]> backList;
108  private final LinkedList<String[]> forwardList;
109  private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS
110  private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName());
112 
117  initComponents();
118 
119  // only allow one item to be selected at a time
120  getTree().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
121  // remove the close button
122  putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE);
123  setName(NbBundle.getMessage(DirectoryTreeTopComponent.class, "CTL_DirectoryTreeTopComponent"));
124  setToolTipText(NbBundle.getMessage(DirectoryTreeTopComponent.class, "HINT_DirectoryTreeTopComponent"));
125 
126  subscribeToChangeEvents();
127  associateLookup(ExplorerUtils.createLookup(em, getActionMap()));
128 
129  // set the back & forward list and also disable the back & forward button
130  this.backList = new LinkedList<>();
131  this.forwardList = new LinkedList<>();
132  backButton.setEnabled(false);
133  forwardButton.setEnabled(false);
134  }
135 
139  private void subscribeToChangeEvents() {
140  UserPreferences.addChangeListener(new PreferenceChangeListener() {
141  @Override
142  public void preferenceChange(PreferenceChangeEvent evt) {
143  switch (evt.getKey()) {
146  refreshContentTreeSafe();
147  break;
150  // TODO: Need a way to refresh the Views subtree
151  break;
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.getCurrentCase();
369  } catch (IllegalStateException 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  return !Case.isCaseOpen() || Case.getCurrentCase().hasData() == false;
530  }
531 
537  @Override
538  public ExplorerManager getExplorerManager() {
539  return this.em;
540  }
541 
547  @Override
548  public Action[] getActions() {
549  return new Action[]{};
550  }
551 
557  public Node getSelectedNode() {
558  Node result = null;
559 
560  Node[] selectedNodes = this.getExplorerManager().getSelectedNodes();
561  if (selectedNodes.length > 0) {
562  result = selectedNodes[0];
563  }
564  return result;
565  }
566 
573  @Override
574  public void propertyChange(PropertyChangeEvent evt) {
576  String changed = evt.getPropertyName();
577  if (changed.equals(Case.Events.CURRENT_CASE.toString())) { // changed current case
578  // When a case is closed, the old value of this property is the
579  // closed Case object and the new value is null. When a case is
580  // opened, the old value is null and the new value is the new Case
581  // object.
582  // @@@ This needs to be revisited. Perhaps case closed and case
583  // opened events instead of property change events would be a better
584  // solution. Either way, more probably needs to be done to clean up
585  // data model objects when a case is closed.
586  if (evt.getOldValue() != null && evt.getNewValue() == null) {
587  // The current case has been closed. Reset the ExplorerManager.
588  SwingUtilities.invokeLater(() -> {
589  Node emptyNode = new AbstractNode(Children.LEAF);
590  em.setRootContext(emptyNode);
591  });
592  } else if (evt.getNewValue() != null) {
593  // A new case has been opened. Reset the ExplorerManager.
594  Case newCase = (Case) evt.getNewValue();
595  final String newCaseName = newCase.getName();
596  SwingUtilities.invokeLater(() -> {
597  em.getRootContext().setName(newCaseName);
598  em.getRootContext().setDisplayName(newCaseName);
599 
600  // Reset the forward and back
601  // buttons. Note that a call to CoreComponentControl.openCoreWindows()
602  // by the new Case object will lead to a componentOpened() call
603  // that will repopulate the tree.
604  // @@@ The repopulation of the tree in this fashion also merits
605  // reconsideration.
606  resetHistory();
607  });
608  }
609  } // if the image is added to the case
610  else if (changed.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
617  try {
618  Case currentCase = Case.getCurrentCase();
619  // We only need to trigger openCoreWindows() when the
620  // first data source is added.
621  if (currentCase.getDataSources().size() == 1) {
622  SwingUtilities.invokeLater(CoreComponentControl::openCoreWindows);
623  }
624  } catch (IllegalStateException | TskCoreException notUsed) {
628  }
629  } // change in node selection
630  else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
631  respondSelection((Node[]) evt.getOldValue(), (Node[]) evt.getNewValue());
632  } else if (changed.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
633  // nothing to do here.
634  // all nodes should be listening for these events and update accordingly.
635  }
636  }
637  }
638 
647  @NbBundle.Messages("DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.")
648  private void respondSelection(final Node[] oldNodes, final Node[] newNodes) {
649  if (!Case.isCaseOpen()) {
650  return;
651  }
652 
653  // Some lock that prevents certain Node operations is set during the
654  // ExplorerManager selection-change, so we must handle changes after the
655  // selection-change event is processed.
656  //TODO find a different way to refresh data result viewer, scheduling this
657  //to EDT breaks loading of nodes in the background
658  EventQueue.invokeLater(() -> {
659  // change the cursor to "waiting cursor" for this operation
660  DirectoryTreeTopComponent.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
661  try {
662  Node treeNode = DirectoryTreeTopComponent.this.getSelectedNode();
663  if (treeNode != null) {
664  Node originNode = ((DirectoryTreeFilterNode) treeNode).getOriginal();
665  //set node, wrap in filter node first to filter out children
666  Node drfn = new DataResultFilterNode(originNode, DirectoryTreeTopComponent.this.em);
667  // Create a TableFilterNode with knowledge of the node's type to allow for column order settings
668  if (FileTypesByMimeType.isEmptyMimeTypeNode(originNode)) {
669  //Special case for when File Type Identification has not yet been run and
670  //there are no mime types to populate Files by Mime Type Tree
671  EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTreeTopComponent_emptyMimeNode_text());
672  dataResult.setNode(new TableFilterNode(emptyNode, true, "This Node Is Empty")); //NON-NLS
673  } else if (originNode instanceof DisplayableItemNode) {
674  dataResult.setNode(new TableFilterNode(drfn, true, ((DisplayableItemNode) originNode).getItemType()));
675  } else {
676  dataResult.setNode(new TableFilterNode(drfn, true));
677  }
678  String displayName = "";
679  Content content = originNode.getLookup().lookup(Content.class);
680  if (content != null) {
681  try {
682  displayName = content.getUniquePath();
683  } catch (TskCoreException ex) {
684  LOGGER.log(Level.SEVERE, "Exception while calling Content.getUniquePath() for node: {0}", originNode); //NON-NLS
685  }
686  } else if (originNode.getLookup().lookup(String.class) != null) {
687  displayName = originNode.getLookup().lookup(String.class);
688  }
689  dataResult.setPath(displayName);
690  }
691  // set the directory listing to be active
692  if (oldNodes != null && newNodes != null
693  && (oldNodes.length == newNodes.length)) {
694  boolean sameNodes = true;
695  for (int i = 0; i < oldNodes.length; i++) {
696  sameNodes = sameNodes && oldNodes[i].getName().equals(newNodes[i].getName());
697  }
698  if (!sameNodes) {
699  dataResult.requestActive();
700  }
701  }
702  } finally {
703  setCursor(null);
704  }
705  });
706 
707  // update the back and forward list
708  updateHistory(em.getSelectedNodes());
709  }
710 
711  private void updateHistory(Node[] selectedNodes) {
712  if (selectedNodes.length == 0) {
713  return;
714  }
715 
716  Node selectedNode = selectedNodes[0];
717  String selectedNodeName = selectedNode.getName();
718 
719  /*
720  * get the previous entry to make sure we don't duplicate it. Motivation
721  * for this is also that if we used the back button, then we already
722  * added the 'current' node to 'back' and we will detect that and not
723  * reset the forward list.
724  */
725  String[] currentLast = backList.peekLast();
726  String lastNodeName = null;
727  if (currentLast != null) {
728  lastNodeName = currentLast[currentLast.length - 1];
729  }
730 
731  if (currentLast == null || !selectedNodeName.equals(lastNodeName)) {
732  //add to the list if the last if not the same as current
733  final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
734  backList.addLast(selectedPath); // add the node to the "backList"
735  if (backList.size() > 1) {
736  backButton.setEnabled(true);
737  } else {
738  backButton.setEnabled(false);
739  }
740 
741  forwardList.clear(); // clear the "forwardList"
742  forwardButton.setEnabled(false); // disable the forward Button
743  }
744  }
745 
750  private void resetHistory() {
751  // clear the back and forward list
752  backList.clear();
753  forwardList.clear();
754  backButton.setEnabled(false);
755  forwardButton.setEnabled(false);
756  }
757 
763  public BeanTreeView getTree() {
764  return (BeanTreeView) this.treeView;
765  }
766 
770  public void refreshContentTreeSafe() {
771  SwingUtilities.invokeLater(this::refreshDataSourceTree);
772  }
773 
777  private void refreshDataSourceTree() {
778  Node selectedNode = getSelectedNode();
779  final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
780  Children rootChildren = em.getRootContext().getChildren();
781  Node dataSourcesFilterNode = rootChildren.findChild(DataSourcesNode.NAME);
782  if (dataSourcesFilterNode == null) {
783  LOGGER.log(Level.SEVERE, "Cannot find data sources filter node, won't refresh the content tree"); //NON-NLS
784  return;
785  }
786  Node dataSourcesNode = ((DirectoryTreeFilterNode) dataSourcesFilterNode).getOriginal();
787  DataSourcesNode.DataSourcesNodeChildren contentRootChildren = (DataSourcesNode.DataSourcesNodeChildren) dataSourcesNode.getChildren();
788  contentRootChildren.refreshContentKeys();
789  setSelectedNode(selectedPath, DataSourcesNode.NAME);
790  }
791 
799  private void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName) {
800  if (previouslySelectedNodePath == null) {
801  return;
802  }
803  SwingUtilities.invokeLater(new Runnable() {
804  @Override
805  public void run() {
806  if (previouslySelectedNodePath.length > 0 && (rootNodeName == null || previouslySelectedNodePath[0].equals(rootNodeName))) {
807  Node selectedNode = null;
808  ArrayList<String> selectedNodePath = new ArrayList<>(Arrays.asList(previouslySelectedNodePath));
809  while (null == selectedNode && !selectedNodePath.isEmpty()) {
810  try {
811  selectedNode = NodeOp.findPath(em.getRootContext(), selectedNodePath.toArray(new String[selectedNodePath.size()]));
812  } catch (NodeNotFoundException ex) {
813  // The selected node may have been deleted (e.g., a deleted tag), so truncate the path and try again.
814  if (selectedNodePath.size() > 1) {
815  selectedNodePath.remove(selectedNodePath.size() - 1);
816  } else {
817  StringBuilder nodePath = new StringBuilder();
818  for (int i = 0; i < previouslySelectedNodePath.length; ++i) {
819  nodePath.append(previouslySelectedNodePath[i]).append("/");
820  }
821  LOGGER.log(Level.WARNING, "Failed to find any nodes to select on path " + nodePath.toString(), ex); //NON-NLS
822  break;
823  }
824  }
825  }
826 
827  if (null != selectedNode) {
828  if (rootNodeName != null) {
829  //called from tree auto refresh context
830  //remove last from backlist, because auto select will result in duplication
831  backList.pollLast();
832  }
833  try {
834  em.setExploredContextAndSelection(selectedNode, new Node[]{selectedNode});
835  } catch (PropertyVetoException ex) {
836  LOGGER.log(Level.WARNING, "Property veto from ExplorerManager setting selection to " + selectedNode.getName(), ex); //NON-NLS
837  }
838  }
839  }
840  }
841  });
842  }
843 
844  @Override
845  public TopComponent getTopComponent() {
846  return this;
847  }
848 
849  @Override
850  public boolean hasMenuOpenAction() {
851  return false;
852  }
853 
854  @Override
855  public void viewArtifact(final BlackboardArtifact art) {
856  int typeID = art.getArtifactTypeID();
857  String typeName = art.getArtifactTypeName();
858  Children rootChilds = em.getRootContext().getChildren();
859  Node treeNode = null;
860  Node resultsNode = rootChilds.findChild(ResultsNode.NAME);
861  Children resultsChilds = resultsNode.getChildren();
862  if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
863  Node hashsetRootNode = resultsChilds.findChild(typeName);
864  Children hashsetRootChilds = hashsetRootNode.getChildren();
865  try {
866  String setName = null;
867  List<BlackboardAttribute> attributes = art.getAttributes();
868  for (BlackboardAttribute att : attributes) {
869  int typeId = att.getAttributeType().getTypeID();
870  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
871  setName = att.getValueString();
872  }
873  }
874  treeNode = hashsetRootChilds.findChild(setName);
875  } catch (TskCoreException ex) {
876  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
877  }
878  } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
879  Node keywordRootNode = resultsChilds.findChild(typeName);
880  Children keywordRootChilds = keywordRootNode.getChildren();
881  try {
882  String listName = null;
883  String keywordName = null;
884  String regex = null;
885  List<BlackboardAttribute> attributes = art.getAttributes();
886  for (BlackboardAttribute att : attributes) {
887  int typeId = att.getAttributeType().getTypeID();
888  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
889  listName = att.getValueString();
890  } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
891  keywordName = att.getValueString();
892  } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()) {
893  regex = att.getValueString();
894  }
895  }
896  if (listName == null) {
897  if (regex == null) { //using same labels used for creation
898  listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.simpleLiteralSearch.text");
899  } else {
900  listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text");
901  }
902  }
903  Node listNode = keywordRootChilds.findChild(listName);
904  if (listNode == null) {
905  return;
906  }
907  Children listChildren = listNode.getChildren();
908  if (listChildren == null) {
909  return;
910  }
911  if (regex != null) { //For support of regex nodes such as URLs, IPs, Phone Numbers, and Email Addrs as they are down another level
912  Node regexNode = listChildren.findChild(regex);
913  if (regexNode == null) {
914  return;
915  }
916  listChildren = regexNode.getChildren();
917  if (listChildren == null) {
918  return;
919  }
920  }
921 
922  treeNode = listChildren.findChild(keywordName);
923 
924  } catch (TskCoreException ex) {
925  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
926  }
927  } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
928  || typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
929  Node interestingItemsRootNode = resultsChilds.findChild(NbBundle
930  .getMessage(InterestingHits.class, "InterestingHits.interestingItems.text"));
931  Children interestingItemsRootChildren = interestingItemsRootNode.getChildren();
932  try {
933  String setName = null;
934  List<BlackboardAttribute> attributes = art.getAttributes();
935  for (BlackboardAttribute att : attributes) {
936  int typeId = att.getAttributeType().getTypeID();
937  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
938  setName = att.getValueString();
939  }
940  }
941  Node setNode = interestingItemsRootChildren.findChild(setName);
942  if (setNode == null) {
943  return;
944  }
945  Children interestingChildren = setNode.getChildren();
946  if (interestingChildren == null) {
947  return;
948  }
949  treeNode = interestingChildren.findChild(art.getDisplayName());
950  } catch (TskCoreException ex) {
951  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
952  }
953  } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
954  Node emailMsgRootNode = resultsChilds.findChild(typeName);
955  Children emailMsgRootChilds = emailMsgRootNode.getChildren();
956  Map<String, String> parsedPath = null;
957  try {
958  List<BlackboardAttribute> attributes = art.getAttributes();
959  for (BlackboardAttribute att : attributes) {
960  int typeId = att.getAttributeType().getTypeID();
961  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
962  parsedPath = EmailExtracted.parsePath(att.getValueString());
963  break;
964  }
965  }
966  if (parsedPath == null) {
967  return;
968  }
969  Node defaultNode = emailMsgRootChilds.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text")));
970  Children defaultChildren = defaultNode.getChildren();
971  treeNode = defaultChildren.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text")));
972  } catch (TskCoreException ex) {
973  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
974  }
975 
976  } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
977  Node accountRootNode = resultsChilds.findChild(art.getDisplayName());
978  Children accountRootChilds = accountRootNode.getChildren();
979  List<BlackboardAttribute> attributes;
980  String accountType = null;
981  String ccNumberName = null;
982  try {
983  attributes = art.getAttributes();
984  for (BlackboardAttribute att : attributes) {
985  int typeId = att.getAttributeType().getTypeID();
986  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()) {
987  accountType = att.getValueString();
988  }
989  if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID()) {
990  ccNumberName = att.getValueString();
991  }
992  }
993  if (accountType == null) {
994  return;
995  }
996 
997  if (accountType.equals(Account.Type.CREDIT_CARD.name())) {
998  Node accountNode = accountRootChilds.findChild(Account.Type.CREDIT_CARD.getDisplayName());
999  if (accountNode == null) {
1000  return;
1001  }
1002  Children accountChildren = accountNode.getChildren();
1003  if (accountChildren == null) {
1004  return;
1005  }
1006  Node binNode = accountChildren.findChild(NbBundle.getMessage(Accounts.class, "Accounts.ByBINNode.name"));
1007  if (binNode == null) {
1008  return;
1009  }
1010  Children binChildren = binNode.getChildren();
1011  if (ccNumberName == null) {
1012  return;
1013  }
1014  //right padded with 0s to 8 digits when single number
1015  //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
1016  String binName = StringUtils.rightPad(ccNumberName, 8, "0");
1017  binName = binName.substring(0, 8);
1018  int bin;
1019  try {
1020  bin = Integer.parseInt(binName);
1021  } catch (NumberFormatException ex) {
1022  LOGGER.log(Level.WARNING, "Unable to parseInt a BIN for node selection from string binName=" + binName, ex); //NON-NLS
1023  return;
1024  }
1026  if (binInfo != null) {
1027  int startBin = ((BINRange) binInfo).getBINstart();
1028  int endBin = ((BINRange) binInfo).getBINend();
1029  if (startBin != endBin) {
1030  binName = Integer.toString(startBin) + "-" + Integer.toString(endBin).substring(5); //if there is a range re-construct the name it appears as
1031  }
1032  }
1033  if (binName == null) {
1034  return;
1035  }
1036  treeNode = binChildren.findChild(binName);
1037  } else { //default account type
1038  treeNode = accountRootChilds.findChild(accountType);
1039  }
1040  } catch (TskCoreException ex) {
1041  LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1042  }
1043  } else {
1044  Node extractedContent = resultsChilds.findChild(ExtractedContent.NAME);
1045  Children extractedChilds = extractedContent.getChildren();
1046  if (extractedChilds == null) {
1047  return;
1048  }
1049  treeNode = extractedChilds.findChild(typeName);
1050  }
1051 
1052  if (treeNode == null) {
1053  return;
1054  }
1055 
1056  DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) treeNode).getOriginal();
1057  undecoratedParentNode.setChildNodeSelectionInfo(new ArtifactNodeSelectionInfo(art));
1058  getTree().expandNode(treeNode);
1059  try {
1060  em.setExploredContextAndSelection(treeNode, new Node[]{treeNode});
1061  } catch (PropertyVetoException ex) {
1062  LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
1063  }
1064  // Another thread is needed because we have to wait for dataResult to populate
1065  }
1066 
1067  @Override
1068  public void viewArtifactContent(BlackboardArtifact art) {
1069  new ViewContextAction(
1070  NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.action.viewArtContent.text"),
1071  new BlackboardArtifactNode(art)).actionPerformed(null);
1072  }
1073 
1074  @Override
1075  public void addOnFinishedListener(PropertyChangeListener l) {
1076  DirectoryTreeTopComponent.this.addPropertyChangeListener(l);
1077  }
1078 
1079  void fireViewerComplete() {
1080 
1081  try {
1082  firePropertyChange(BlackboardResultViewer.FINISHED_DISPLAY_EVT, 0, 1);
1083  } catch (Exception e) {
1084  LOGGER.log(Level.SEVERE, "DirectoryTreeTopComponent listener threw exception", e); //NON-NLS
1085  MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.moduleErr"),
1086  NbBundle.getMessage(this.getClass(),
1087  "DirectoryTreeTopComponent.moduleErr.msg"),
1088  MessageNotifyUtil.MessageType.ERROR);
1089  }
1090  }
1091 }
List< Content > getDataSources()
Definition: Case.java:1286
static final Map< String, String > parsePath(String path)
static synchronized IngestManager getInstance()
void respondSelection(final Node[] oldNodes, final Node[] newNodes)
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:161
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:395
static void addChangeListener(PreferenceChangeListener listener)
void setChildNodeSelectionInfo(NodeSelectionInfo selectedChildNodeInfo)
static synchronized DirectoryTreeTopComponent findInstance()

Copyright © 2012-2016 Basis Technology. Generated on: Fri Sep 29 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.