Autopsy 4.22.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 2012-2020 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.autopsy.directorytree;
20
21import java.awt.Cursor;
22import java.awt.EventQueue;
23import java.beans.PropertyChangeEvent;
24import java.beans.PropertyChangeListener;
25import java.beans.PropertyVetoException;
26import java.io.IOException;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.Collections;
30import java.util.EnumSet;
31import java.util.HashSet;
32import java.util.LinkedList;
33import java.util.List;
34import java.util.Map;
35import java.util.Objects;
36import java.util.Optional;
37import java.util.Set;
38import java.util.concurrent.ExecutionException;
39import java.util.logging.Level;
40import java.util.prefs.PreferenceChangeEvent;
41import java.util.prefs.PreferenceChangeListener;
42import java.util.stream.Collectors;
43import java.util.stream.Stream;
44import javax.swing.Action;
45import javax.swing.SwingUtilities;
46import javax.swing.SwingWorker;
47import javax.swing.event.PopupMenuEvent;
48import javax.swing.event.PopupMenuListener;
49import javax.swing.tree.TreeSelectionModel;
50import org.apache.commons.lang3.StringUtils;
51import org.openide.explorer.ExplorerManager;
52import org.openide.explorer.ExplorerUtils;
53import org.openide.explorer.view.BeanTreeView;
54import org.openide.nodes.AbstractNode;
55import org.openide.nodes.Children;
56import org.openide.nodes.Node;
57import org.openide.nodes.NodeNotFoundException;
58import org.openide.nodes.NodeOp;
59import org.openide.util.NbBundle;
60import org.openide.util.NbBundle.Messages;
61import org.openide.windows.TopComponent;
62import org.openide.windows.WindowManager;
63import org.sleuthkit.autopsy.casemodule.Case;
64import org.sleuthkit.autopsy.casemodule.CasePreferences;
65import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
66import org.sleuthkit.autopsy.core.RuntimeProperties;
67import org.sleuthkit.autopsy.core.UserPreferences;
68import org.sleuthkit.autopsy.corecomponentinterfaces.CoreComponentControl;
69import org.sleuthkit.autopsy.corecomponentinterfaces.DataExplorer;
70import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
71import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
72import org.sleuthkit.autopsy.corecomponents.ViewPreferencesPanel;
73import org.sleuthkit.autopsy.coreutils.Logger;
74import org.sleuthkit.autopsy.coreutils.ModuleSettings;
75import org.sleuthkit.autopsy.datamodel.AnalysisResults;
76import org.sleuthkit.autopsy.datamodel.ArtifactNodeSelectionInfo;
77import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
78import org.sleuthkit.autopsy.datamodel.CreditCards;
79import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
80import org.sleuthkit.autopsy.datamodel.EmailExtracted;
81import org.sleuthkit.autopsy.datamodel.EmptyNode;
82import org.sleuthkit.autopsy.datamodel.FileTypesByMimeType;
83import org.sleuthkit.autopsy.datamodel.InterestingHits;
84import org.sleuthkit.autopsy.datamodel.KeywordHits;
85import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory;
86import org.sleuthkit.autopsy.datamodel.DataArtifacts;
87import org.sleuthkit.autopsy.datamodel.OsAccounts;
88import org.sleuthkit.autopsy.datamodel.PersonNode;
89import org.sleuthkit.autopsy.datamodel.Tags;
90import org.sleuthkit.autopsy.datamodel.ViewsNode;
91import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
92import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
93import org.sleuthkit.datamodel.Account;
94import org.sleuthkit.datamodel.BlackboardArtifact;
95import org.sleuthkit.datamodel.BlackboardArtifact.Category;
96import org.sleuthkit.datamodel.BlackboardAttribute;
97import org.sleuthkit.datamodel.Content;
98import org.sleuthkit.datamodel.DataSource;
99import org.sleuthkit.datamodel.Host;
100import org.sleuthkit.datamodel.OsAccount;
101import org.sleuthkit.datamodel.Person;
102import org.sleuthkit.datamodel.TskCoreException;
103
107// Registered as a service provider for DataExplorer in layer.xml
108@Messages({
109 "DirectoryTreeTopComponent.resultsView.title=Listing"
110})
111@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
112public final class DirectoryTreeTopComponent extends TopComponent implements DataExplorer, ExplorerManager.Provider {
113
114 private final transient ExplorerManager em = new ExplorerManager();
116 private final DataResultTopComponent dataResult = new DataResultTopComponent(Bundle.DirectoryTreeTopComponent_resultsView_title());
118 private final LinkedList<String[]> backList;
119 private final LinkedList<String[]> forwardList;
120 private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS
121 private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName());
123 private Children autopsyTreeChildren;
125 private boolean showRejectedResults;
126 private static final long DEFAULT_DATASOURCE_GROUPING_THRESHOLD = 5; // Threshold for prompting the user about grouping by data source
127 private static final String GROUPING_THRESHOLD_NAME = "GroupDataSourceThreshold";
128 private static final String SETTINGS_FILE = "CasePreferences.properties"; //NON-NLS
129
130 // nodes to be opened if present at top level
131 private static final Set<String> NODES_TO_EXPAND = Stream.of(AnalysisResults.getName(), DataArtifacts.getName(), ViewsNode.NAME)
132 .collect(Collectors.toSet());
133
139
140 // only allow one item to be selected at a time
141 getTree().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
142
143 // remove the close button
144 putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE);
145 setName(NbBundle.getMessage(DirectoryTreeTopComponent.class, "CTL_DirectoryTreeTopComponent"));
146 setToolTipText(NbBundle.getMessage(DirectoryTreeTopComponent.class, "HINT_DirectoryTreeTopComponent"));
147
149 associateLookup(ExplorerUtils.createLookup(em, getActionMap()));
150
151 // set the back & forward list and also disable the back & forward button
152 this.backList = new LinkedList<>();
153 this.forwardList = new LinkedList<>();
154 backButton.setEnabled(false);
155 forwardButton.setEnabled(false);
156
158 viewPreferencesPopupMenu.setSize(viewPreferencesPanel.getPreferredSize().width + 6, viewPreferencesPanel.getPreferredSize().height + 6);
159 viewPreferencesPopupMenu.addPopupMenuListener(new PopupMenuListener() {
160 @Override
161 public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
162 openViewPreferencesButton.setSelected(true);
163 }
164
165 @Override
166 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
167 openViewPreferencesButton.setSelected(false);
168 }
169
170 @Override
171 public void popupMenuCanceled(PopupMenuEvent e) {
172 openViewPreferencesButton.setSelected(false);
173 }
174 });
175 }
176
183 private void preExpandNodes(Children rootChildren) {
184 BeanTreeView tree = getTree();
185
186 // using getNodes(true) to fetch children so that async nodes are loaded
187 Node[] rootChildrenNodes = rootChildren.getNodes(true);
188 if (rootChildrenNodes == null || rootChildrenNodes.length < 1) {
189 return;
190 }
191
192 // expand all nodes parents of and including hosts if group by host/person
193 if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
194 Stream.of(rootChildrenNodes)
195 .flatMap((n) -> getHostNodesAndParents(n).stream())
196 .filter((n) -> n != null)
197 .forEach(tree::expandNode);
198 } else {
199 Stream.of(rootChildrenNodes)
200 .filter(n -> n != null && NODES_TO_EXPAND.contains(n.getName()))
201 .forEach(tree::expandNode);
202 }
203 }
204
213 private List<Node> getHostNodesAndParents(Node node) {
214 if (node == null) {
215 return Collections.emptyList();
216 } else if (node.getLookup().lookup(Person.class) != null
217 || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
218 Children children = node.getChildren();
219 Node[] childNodes = children == null ? null : children.getNodes();
220 if (childNodes != null) {
221 return Stream.of(childNodes)
222 .flatMap((n) -> Stream.concat(Stream.of(n), getHostNodesAndParents(n).stream()))
223 .collect(Collectors.toList());
224 }
225 } else if (node.getLookup().lookup(Host.class) != null) {
226 return Arrays.asList(node);
227 }
228 return Collections.emptyList();
229 }
230
234 private void subscribeToChangeEvents() {
235 UserPreferences.addChangeListener(new PreferenceChangeListener() {
236 @Override
237 public void preferenceChange(PreferenceChangeEvent evt) {
238 switch (evt.getKey()) {
248 break;
251 break;
254 // TODO: Need a way to refresh the Views subtree alone.
256 break;
257 }
258 }
259 });
260
262 this.em.addPropertyChangeListener(this);
263 }
264
266 this.dataResult.requestActive();
267 }
268
269 public void openDirectoryListing() {
270 this.dataResult.open();
271 }
272
274 return this.dataResult;
275 }
276
282 public boolean getShowRejectedResults() {
283 return showRejectedResults;
284 }
285
293 this.showRejectedResults = showRejectedResults;
294 if (accounts != null) {
295 accounts.setShowRejected(showRejectedResults);
296 }
297 }
298
304 // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
305 private void initComponents() {
306
307 viewPreferencesPopupMenu = new javax.swing.JPopupMenu();
308 treeView = new ExpansionBeanTreeView();
309 backButton = new javax.swing.JButton();
310 forwardButton = new javax.swing.JButton();
311 openViewPreferencesButton = new javax.swing.JButton();
312
313 treeView.setBorder(null);
314
315 backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back.png"))); // NOI18N
316 org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.backButton.text")); // NOI18N
317 backButton.setBorderPainted(false);
318 backButton.setContentAreaFilled(false);
319 backButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_disabled.png"))); // NOI18N
320 backButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
321 backButton.setMaximumSize(new java.awt.Dimension(55, 100));
322 backButton.setMinimumSize(new java.awt.Dimension(5, 5));
323 backButton.setPreferredSize(new java.awt.Dimension(24, 24));
324 backButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_hover.png"))); // NOI18N
325 backButton.addActionListener(new java.awt.event.ActionListener() {
326 public void actionPerformed(java.awt.event.ActionEvent evt) {
327 backButtonActionPerformed(evt);
328 }
329 });
330
331 forwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward.png"))); // NOI18N
332 org.openide.awt.Mnemonics.setLocalizedText(forwardButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.forwardButton.text")); // NOI18N
333 forwardButton.setBorderPainted(false);
334 forwardButton.setContentAreaFilled(false);
335 forwardButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_disabled.png"))); // NOI18N
336 forwardButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
337 forwardButton.setMaximumSize(new java.awt.Dimension(55, 100));
338 forwardButton.setMinimumSize(new java.awt.Dimension(5, 5));
339 forwardButton.setPreferredSize(new java.awt.Dimension(24, 24));
340 forwardButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_hover.png"))); // NOI18N
341 forwardButton.addActionListener(new java.awt.event.ActionListener() {
342 public void actionPerformed(java.awt.event.ActionEvent evt) {
343 forwardButtonActionPerformed(evt);
344 }
345 });
346
347 openViewPreferencesButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/view-preferences-23.png"))); // NOI18N
348 org.openide.awt.Mnemonics.setLocalizedText(openViewPreferencesButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.openViewPreferencesButton.text")); // NOI18N
349 openViewPreferencesButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
350 openViewPreferencesButton.setBorderPainted(false);
351 openViewPreferencesButton.setContentAreaFilled(false);
352 openViewPreferencesButton.setMaximumSize(new java.awt.Dimension(24, 24));
353 openViewPreferencesButton.setMinimumSize(new java.awt.Dimension(24, 24));
354 openViewPreferencesButton.setPreferredSize(new java.awt.Dimension(24, 24));
355 openViewPreferencesButton.addActionListener(new java.awt.event.ActionListener() {
356 public void actionPerformed(java.awt.event.ActionEvent evt) {
357 openViewPreferencesButtonActionPerformed(evt);
358 }
359 });
360
361 javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
362 this.setLayout(layout);
363 layout.setHorizontalGroup(
364 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
365 .addComponent(treeView)
366 .addGroup(layout.createSequentialGroup()
367 .addContainerGap()
368 .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
369 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
370 .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
371 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 140, Short.MAX_VALUE)
372 .addComponent(openViewPreferencesButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
373 .addContainerGap())
374 );
375 layout.setVerticalGroup(
376 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
377 .addGroup(layout.createSequentialGroup()
378 .addGap(0, 0, 0)
379 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
380 .addComponent(openViewPreferencesButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
381 .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
382 .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
383 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
384 .addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 919, Short.MAX_VALUE))
385 );
386 }// </editor-fold>//GEN-END:initComponents
387
388 private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
389 // change the cursor to "waiting cursor" for this operation
390 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
391
392 // the end is the current place,
393 String[] currentNodePath = backList.pollLast();
394 forwardList.addLast(currentNodePath);
395 forwardButton.setEnabled(true);
396
397 /*
398 * We peek instead of poll because we use its existence in the list
399 * later on so that we do not reset the forward list after the selection
400 * occurs.
401 */
402 String[] newCurrentNodePath = backList.peekLast();
403
404 // enable / disable the back and forward button
405 if (backList.size() > 1) {
406 backButton.setEnabled(true);
407 } else {
408 backButton.setEnabled(false);
409 }
410
411 // update the selection on directory tree
412 setSelectedNode(newCurrentNodePath, null);
413
414 this.setCursor(null);
415 }//GEN-LAST:event_backButtonActionPerformed
416
417 private void forwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed
418 // change the cursor to "waiting cursor" for this operation
419 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
420
421 String[] newCurrentNodePath = forwardList.pollLast();
422 if (!forwardList.isEmpty()) {
423 forwardButton.setEnabled(true);
424 } else {
425 forwardButton.setEnabled(false);
426 }
427
428 backList.addLast(newCurrentNodePath);
429 backButton.setEnabled(true);
430
431 // update the selection on directory tree
432 setSelectedNode(newCurrentNodePath, null);
433
434 this.setCursor(null);
435 }//GEN-LAST:event_forwardButtonActionPerformed
436
437 private void openViewPreferencesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openViewPreferencesButtonActionPerformed
440 }//GEN-LAST:event_openViewPreferencesButtonActionPerformed
441
442 // Variables declaration - do not modify//GEN-BEGIN:variables
443 private javax.swing.JButton backButton;
444 private javax.swing.JButton forwardButton;
445 private javax.swing.JButton openViewPreferencesButton;
446 private javax.swing.JScrollPane treeView;
447 private javax.swing.JPopupMenu viewPreferencesPopupMenu;
448 // End of variables declaration//GEN-END:variables
449
458 public static synchronized DirectoryTreeTopComponent getDefault() {
459 if (instance == null) {
461 }
462 return instance;
463 }
464
471 public static synchronized DirectoryTreeTopComponent findInstance() {
472 WindowManager winManager = WindowManager.getDefault();
473 TopComponent win = winManager.findTopComponent(PREFERRED_ID);
474 if (win == null) {
475 LOGGER.warning(
476 "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system."); //NON-NLS
477 return getDefault();
478 }
479 if (win instanceof DirectoryTreeTopComponent) {
480 return (DirectoryTreeTopComponent) win;
481 }
482 LOGGER.warning(
483 "There seem to be multiple components with the '" + PREFERRED_ID //NON-NLS
484 + "' ID. That is a potential source of errors and unexpected behavior."); //NON-NLS
485 return getDefault();
486 }
487
494 @Override
495 public int getPersistenceType() {
496 return TopComponent.PERSISTENCE_NEVER;
497 }
498
505 private void promptForDataSourceGrouping(int dataSourceCount) {
507 GroupDataSourcesDialog dialog = new GroupDataSourcesDialog(dataSourceCount);
508 dialog.display();
509 if (dialog.groupByDataSourceSelected()) {
512 } else {
514 }
515 }
516 }
517
525 @NbBundle.Messages({"# {0} - dataSourceCount",
526 "DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contains {0} data sources. Would you like to group by data source for faster loading?",
527 "DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source?"})
528 @Override
529 public void componentOpened() {
530 // change the cursor to "waiting cursor" for this operation
531 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
532 Case openCase = null;
533 try {
534 openCase = Case.getCurrentCaseThrows();
535 } catch (NoCurrentCaseException ex) {
536 // No open case.
537 }
538 final Case currentCase = openCase;
539 // close the top component if there's no image in this case
540 if (!caseHasData(currentCase)) {
541 getTree().setRootVisible(false); // hide the root
542 } else {
543 // If the case contains a lot of data sources, and they aren't already grouping
544 // by data source, give the user the option to do so before loading the tree.
546 Long settingsThreshold = null;
548 try {
550 } catch (NumberFormatException ex) {
551 LOGGER.log(Level.SEVERE, "Group data sources threshold is not a number", ex);
552 }
553 } else {
555 }
556 final long threshold = settingsThreshold == null ? DEFAULT_DATASOURCE_GROUPING_THRESHOLD : settingsThreshold;
557
558 new SwingWorker<Integer, Void>() {
559 @Override
560 protected Integer doInBackground() throws Exception {
561 int dataSourceCount = 0;
562 try {
563 dataSourceCount = currentCase.getDataSources().size();
564 } catch (TskCoreException ex) {
565 LOGGER.log(Level.SEVERE, "Error loading data sources", ex);
566 }
567 return dataSourceCount;
568 }
569
570 @Override
571 protected void done() {
572 int dataSourceCount = 0;
573 try {
574 dataSourceCount = get();
575 } catch (ExecutionException | InterruptedException ex) {
576 LOGGER.log(Level.SEVERE, "Error loading data sources and getting count on background thread", ex);
577 }
579 && dataSourceCount > threshold) {
580 promptForDataSourceGrouping(dataSourceCount);
581 }
582 }
583 }.execute();
584 }
585
586 // if there's at least one image, load the image and open the top componen
588 autopsyTreeChildren = Children.create(autopsyTreeChildFactory, true);
589 Node root = new AbstractNode(autopsyTreeChildren) {
590 //JIRA-2807: What is the point of these overrides?
595 @Override
596 public Action[] getActions(boolean popup) {
597 return new Action[]{};
598 }
599
600 // Overide the AbstractNode use of DefaultHandle to return
601 // a handle which can be serialized without a parent
602 @Override
603 public Node.Handle getHandle() {
604 return new Node.Handle() {
605 @Override
606 public Node getNode() throws IOException {
607 return em.getRootContext();
608 }
609 };
610 }
611 };
612
613 root = new DirectoryTreeFilterNode(root, true);
614
615 em.setRootContext(root);
616 em.getRootContext().setName(currentCase.getName());
617 em.getRootContext().setDisplayName(currentCase.getName());
618 getTree().setRootVisible(false); // hide the root
619
620 // Reset the forward and back lists because we're resetting the root context
621 resetHistory();
622 new SwingWorker<Node[], Void>() {
623 @Override
624 protected Node[] doInBackground() throws Exception {
625 Children rootChildren = em.getRootContext().getChildren();
626 preExpandNodes(rootChildren);
627 /*
628 * JIRA-2806: What is this supposed to do? Right now it
629 * selects the data sources node, but the comment seems to
630 * indicate it is supposed to select the first datasource.
631 */
632 // select the first image node, if there is one
633 // (this has to happen after dataResult is opened, because the event
634 // of changing the selected node fires a handler that tries to make
635 // dataResult active)
636 if (rootChildren.getNodesCount() > 0) {
637 return new Node[]{rootChildren.getNodeAt(0)};
638 }
639 return new Node[]{};
640 }
641
642 @Override
643 protected void done() {
644 super.done();
645
646 // if the dataResult is not opened
647 if (!dataResult.isOpened()) {
648 dataResult.open(); // open the data result top component as well when the directory tree is opened
649 }
650 /*
651 * JIRA-2806: What is this supposed to do?
652 */
653 // select the first image node, if there is one
654 // (this has to happen after dataResult is opened, because the event
655 // of changing the selected node fires a handler that tries to make
656 // dataResult active)
657 try {
658 Node[] selections = get();
659 if (selections != null && selections.length > 0) {
660 em.setSelectedNodes(selections);
661 }
662 } catch (PropertyVetoException ex) {
663 LOGGER.log(Level.SEVERE, "Error setting default selected node.", ex); //NON-NLS
664 } catch (InterruptedException | ExecutionException ex) {
665 LOGGER.log(Level.SEVERE, "Error expanding tree to initial state.", ex); //NON-NLS
666 } finally {
667 setCursor(null);
668 }
669 }
670 }.execute();
671 }
672 }
673
680 @Override
681 public void componentClosed() {
682 //@@@ push the selection node to null?
683 autopsyTreeChildren = null;
684 }
685
686 void writeProperties(java.util.Properties p) {
687 // better to version settings since initial version as advocated at
688 // http://wiki.apidesign.org/wiki/PropertyFiles
689 p.setProperty("version", "1.0");
690 // TODO store your settings
691 }
692
693 Object readProperties(java.util.Properties p) {
694 if (instance == null) {
695 instance = this;
696 }
697 instance.readPropertiesImpl(p);
698 return instance;
699 }
700
701 private void readPropertiesImpl(java.util.Properties p) {
702 String version = p.getProperty("version");
703 // TODO read your settings according to their version
704 }
705
711 @Override
712 protected String preferredID() {
713 return PREFERRED_ID;
714 }
715
716 @Override
717 public boolean canClose() {
718 /*
719 * Only allow the main tree view in the left side of the main window to
720 * be closed if there is no opne case or the open case has no data
721 * sources.
722 */
723 try {
724 Case openCase = Case.getCurrentCaseThrows();
725 return caseHasData(openCase) == false;
726 } catch (NoCurrentCaseException ex) {
727 return true;
728 }
729 }
730
736 @Override
737 public ExplorerManager getExplorerManager() {
738 return this.em;
739 }
740
746 @Override
747 public Action[] getActions() {
748 return new Action[]{};
749 }
750
756 public Node getSelectedNode() {
757 Node result = null;
758
759 Node[] selectedNodes = this.getExplorerManager().getSelectedNodes();
760 if (selectedNodes.length > 0) {
761 result = selectedNodes[0];
762 }
763 return result;
764 }
765
772 @Override
773 public void propertyChange(PropertyChangeEvent event) {
775 String changed = event.getPropertyName();
776 if (changed.equals(Case.Events.CURRENT_CASE.toString())) { // changed current case
777 // When a case is closed, the old value of this property is the
778 // closed Case object and the new value is null. When a case is
779 // opened, the old value is null and the new value is the new Case
780 // object.
781 // @@@ This needs to be revisited. Perhaps case closed and case
782 // opened events instead of property change events would be a better
783 // solution. Either way, more probably needs to be done to clean up
784 // data model objects when a case is closed.
785 if (event.getOldValue() != null && event.getNewValue() == null) {
786 // The current case has been closed. Reset the ExplorerManager.
787 SwingUtilities.invokeLater(() -> {
788 Node emptyNode = new AbstractNode(Children.LEAF);
789 em.setRootContext(emptyNode);
790 });
791 } else if (event.getNewValue() != null) {
792 // A new case has been opened. Reset the ExplorerManager.
793 Case newCase = (Case) event.getNewValue();
794 final String newCaseName = newCase.getName();
795 SwingUtilities.invokeLater(() -> {
796 em.getRootContext().setName(newCaseName);
797 em.getRootContext().setDisplayName(newCaseName);
798
799 // Reset the forward and back
800 // buttons. Note that a call to CoreComponentControl.openCoreWindows()
801 // by the new Case object will lead to a componentOpened() call
802 // that will repopulate the tree.
803 // @@@ The repopulation of the tree in this fashion also merits
804 // reconsideration.
805 resetHistory();
806 });
807 }
808 } // if the image is added to the case
809 else if (changed.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
816 try {
818 /*
819 * In case the Case 'updateGUIForCaseOpened()' method hasn't
820 * already done so, open the tree and all other core
821 * windows.
822 *
823 * TODO: (JIRA-4053) DirectoryTreeTopComponent should not be
824 * responsible for opening core windows. Consider moving
825 * this elsewhere.
826 */
827 SwingUtilities.invokeLater(() -> {
828 if (!DirectoryTreeTopComponent.this.isOpened()) {
830 }
831 });
832 } catch (NoCurrentCaseException notUsed) {
836 }
837 } // change in node selection
838 else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
839 respondSelection((Node[]) event.getOldValue(), (Node[]) event.getNewValue());
840 }
841 }
842 }
843
852 @NbBundle.Messages("DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.")
853 void respondSelection(final Node[] oldNodes, final Node[] newNodes) {
854 if (!Case.isCaseOpen()) {
855 return;
856 }
857
858 // Some lock that prevents certain Node operations is set during the
859 // ExplorerManager selection-change, so we must handle changes after the
860 // selection-change event is processed.
861 //TODO find a different way to refresh data result viewer, scheduling this
862 //to EDT breaks loading of nodes in the background
863 EventQueue.invokeLater(() -> {
864 // change the cursor to "waiting cursor" for this operation
865 DirectoryTreeTopComponent.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
866 try {
867 Node treeNode = DirectoryTreeTopComponent.this.getSelectedNode();
868 if (treeNode != null) {
869 Node originNode = ((DirectoryTreeFilterNode) treeNode).getOriginal();
870 //set node, wrap in filter node first to filter out children
871 Node drfn = new DataResultFilterNode(originNode, DirectoryTreeTopComponent.this.em);
872 // Create a TableFilterNode with knowledge of the node's type to allow for column order settings
873 if (FileTypesByMimeType.isEmptyMimeTypeNode(originNode)) {
874 //Special case for when File Type Identification has not yet been run and
875 //there are no mime types to populate Files by Mime Type Tree
876 EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTreeTopComponent_emptyMimeNode_text());
877 dataResult.setNode(new TableFilterNode(emptyNode, true, "This Node Is Empty")); //NON-NLS
878 } else if (originNode instanceof DisplayableItemNode) {
879 dataResult.setNode(new TableFilterNode(drfn, true, ((DisplayableItemNode) originNode).getItemType()));
880 } else {
881 dataResult.setNode(new TableFilterNode(drfn, true));
882 }
883 String displayName = "";
884 Content content = originNode.getLookup().lookup(Content.class);
885 if (content != null) {
886 try {
887 displayName = content.getUniquePath();
888 } catch (TskCoreException ex) {
889 LOGGER.log(Level.SEVERE, "Exception while calling Content.getUniquePath() for node: {0}", originNode); //NON-NLS
890 }
891 } else if (originNode.getLookup().lookup(String.class) != null) {
892 displayName = originNode.getLookup().lookup(String.class);
893 }
894 dataResult.setPath(displayName);
895 }
896 // set the directory listing to be active
897 if (oldNodes != null && newNodes != null
898 && (oldNodes.length == newNodes.length)) {
899 boolean sameNodes = true;
900 for (int i = 0; i < oldNodes.length; i++) {
901 sameNodes = sameNodes && oldNodes[i].getName().equals(newNodes[i].getName());
902 }
903 if (!sameNodes) {
904 dataResult.requestActive();
905 }
906 }
907 } finally {
908 setCursor(null);
909 }
910 });
911
912 // update the back and forward list
913 updateHistory(em.getSelectedNodes());
914 }
915
916 private void updateHistory(Node[] selectedNodes) {
917 if (selectedNodes.length == 0) {
918 return;
919 }
920
921 Node selectedNode = selectedNodes[0];
922 String selectedNodeName = selectedNode.getName();
923
924 /*
925 * get the previous entry to make sure we don't duplicate it. Motivation
926 * for this is also that if we used the back button, then we already
927 * added the 'current' node to 'back' and we will detect that and not
928 * reset the forward list.
929 */
930 String[] currentLast = backList.peekLast();
931 String lastNodeName = null;
932 if (currentLast != null && currentLast.length > 0) {
933 lastNodeName = currentLast[currentLast.length - 1];
934 }
935
936 if (currentLast == null || !selectedNodeName.equals(lastNodeName)) {
937 //add to the list if the last if not the same as current
938 final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
939 backList.addLast(selectedPath); // add the node to the "backList"
940 if (backList.size() > 1) {
941 backButton.setEnabled(true);
942 } else {
943 backButton.setEnabled(false);
944 }
945
946 forwardList.clear(); // clear the "forwardList"
947 forwardButton.setEnabled(false); // disable the forward Button
948 }
949 }
950
955 private void resetHistory() {
956 // clear the back and forward list
957 backList.clear();
958 forwardList.clear();
959 backButton.setEnabled(false);
960 forwardButton.setEnabled(false);
961 }
962
968 BeanTreeView getTree() {
969 return (BeanTreeView) this.treeView;
970 }
971
976 SwingUtilities.invokeLater(this::rebuildTree);
977 }
978
982 private void refreshTagsTree() {
983 SwingUtilities.invokeLater(() -> {
984 // Ensure the component children have been created first.
985 if (autopsyTreeChildren == null) {
986 return;
987 }
988
989 if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
990 for (Node dataSource : autopsyTreeChildren.getNodes()) {
991 Node tagsNode = dataSource.getChildren().findChild(Tags.getTagsDisplayName());
992 if (tagsNode != null) {
993 //Reports is at the same level as the data sources so we want to ignore it
994 ((Tags.RootNode) tagsNode).refresh();
995 }
996 }
997 } else {
998 Node tagsNode = autopsyTreeChildren.findChild(Tags.getTagsDisplayName());
999 if (tagsNode != null) {
1000 ((Tags.RootNode) tagsNode).refresh();
1001 }
1002 }
1003 });
1004 }
1005
1011 private void rebuildTree() {
1012 Case currentCase = null;
1013 try {
1014 currentCase = Case.getCurrentCaseThrows();
1015 } catch (NoCurrentCaseException ex) {
1016 // No open case.
1017 }
1018 //Will return if no open case or case has no data.
1019 if (!caseHasData(currentCase)) {
1020 return;
1021 }
1022
1023 // refresh all children of the root.
1024 autopsyTreeChildFactory.refreshChildren();
1025
1026 // Select the first node and reset the selection history
1027 // This should happen on the EDT once the tree has been rebuilt.
1028 // hence the SwingWorker that does this in the done() method
1029 new SwingWorker<Void, Void>() {
1030
1031 @Override
1032 protected Void doInBackground() throws Exception {
1033 return null;
1034 }
1035
1036 @Override
1037 protected void done() {
1038 super.done();
1039 try {
1040 get();
1041 resetHistory();
1042 preExpandNodes(em.getRootContext().getChildren());
1043 } catch (InterruptedException | ExecutionException ex) {
1044 LOGGER.log(Level.SEVERE, "Error selecting tree node.", ex); //NON-NLS
1045 } //NON-NLS
1046 }
1047 }.execute();
1048 }
1049
1057 private static boolean caseHasData(Case currentCase) {
1058 // if no open case or has no data then there is no tree to rebuild
1059 boolean hasData;
1060 if (null == currentCase) {
1061 hasData = false;
1062 } else {
1063 hasData = currentCase.hasData();
1064 }
1065 return hasData;
1066 }
1067
1075 private void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName) {
1076 if (previouslySelectedNodePath == null) {
1077 return;
1078 }
1079 SwingUtilities.invokeLater(new Runnable() {
1080 @Override
1081 public void run() {
1082 if (previouslySelectedNodePath.length > 0 && (rootNodeName == null || previouslySelectedNodePath[0].equals(rootNodeName))) {
1083 Node selectedNode = null;
1084 ArrayList<String> selectedNodePath = new ArrayList<>(Arrays.asList(previouslySelectedNodePath));
1085 while (null == selectedNode && !selectedNodePath.isEmpty()) {
1086 try {
1087 selectedNode = NodeOp.findPath(em.getRootContext(), selectedNodePath.toArray(new String[selectedNodePath.size()]));
1088 } catch (NodeNotFoundException ex) {
1089 // The selected node may have been deleted (e.g., a deleted tag), so truncate the path and try again.
1090 if (selectedNodePath.size() > 1) {
1091 selectedNodePath.remove(selectedNodePath.size() - 1);
1092 } else {
1093 StringBuilder nodePath = new StringBuilder();
1094 for (int i = 0; i < previouslySelectedNodePath.length; ++i) {
1095 nodePath.append(previouslySelectedNodePath[i]).append("/");
1096 }
1097 LOGGER.log(Level.WARNING, "Failed to find any nodes to select on path " + nodePath.toString(), ex); //NON-NLS
1098 break;
1099 }
1100 }
1101 }
1102
1103 if (null != selectedNode) {
1104 if (rootNodeName != null) {
1105 //called from tree auto refresh context
1106 //remove last from backlist, because auto select will result in duplication
1107 backList.pollLast();
1108 }
1109 try {
1110 em.setExploredContextAndSelection(selectedNode, new Node[]{selectedNode});
1111 } catch (PropertyVetoException ex) {
1112 LOGGER.log(Level.WARNING, "Property veto from ExplorerManager setting selection to " + selectedNode.getName(), ex); //NON-NLS
1113 }
1114 }
1115 }
1116 }
1117 });
1118 }
1119
1120 @Override
1121 public TopComponent getTopComponent() {
1122 return this;
1123 }
1124
1125 @Override
1126 public boolean hasMenuOpenAction() {
1127 return false;
1128 }
1129
1140 private Optional<Node> getCategoryNodeChild(Children children, Category category) {
1141 switch (category) {
1142 case DATA_ARTIFACT:
1143 return Optional.ofNullable(children.findChild(DataArtifacts.getName()));
1144 case ANALYSIS_RESULT:
1145 return Optional.ofNullable(children.findChild(AnalysisResults.getName()));
1146 default:
1147 LOGGER.log(Level.WARNING, "Unbale to find category of type: " + category.name());
1148 return Optional.empty();
1149 }
1150 }
1151
1163 private Optional<Node> searchForCategoryNode(Node node, long dataSourceId, Category category) {
1164 if (node == null) {
1165 // if no node, no result
1166 return Optional.empty();
1167 } else if (node.getLookup().lookup(Host.class) != null
1168 || node.getLookup().lookup(Person.class) != null
1169 || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
1170 // if host or person node, recurse until we find correct data source node.
1171 Children children = node.getChildren();
1172
1173 Stream<Node> childNodeStream = children == null ? Stream.empty() : Stream.of(children.getNodes());
1174 return childNodeStream
1175 .map(childNode -> searchForCategoryNode(childNode, dataSourceId, category))
1176 .filter(Optional::isPresent)
1177 .map(Optional::get)
1178 .findFirst();
1179 } else {
1180 DataSource dataSource = node.getLookup().lookup(DataSource.class);
1181 // if data source node and the one we want, find the right category node.
1182 if (dataSource != null && dataSource.getId() == dataSourceId) {
1183 Children dsChildren = node.getChildren();
1184 if (dsChildren != null) {
1185 return getCategoryNodeChild(dsChildren, category);
1186 }
1187 }
1188
1189 return Optional.empty();
1190 }
1191 }
1192
1202 private Optional<Node> getCategoryNode(Category category, BlackboardArtifact art) {
1203 Children rootChildren = em.getRootContext().getChildren();
1204 Optional<Node> categoryNode = getCategoryNodeChild(rootChildren, category);
1205 if (categoryNode.isPresent()) {
1206 return categoryNode;
1207 }
1208
1209 long dataSourceId;
1210 try {
1211 dataSourceId = art.getDataSource().getId();
1212 } catch (TskCoreException ex) {
1213 LOGGER.log(Level.WARNING, "There was an error fetching the data source id for artifact.", ex);
1214 return null;
1215 }
1216
1217 Node[] rootNodes = rootChildren.getNodes();
1218 Stream<Node> rootNodesStream = rootNodes == null ? Stream.empty() : Stream.of(rootNodes);
1219 return rootNodesStream
1220 .map((rootNode) -> searchForCategoryNode(rootNode, dataSourceId, category))
1221 .filter(Optional::isPresent)
1222 .map(Optional::get)
1223 .findFirst();
1224 }
1225
1236 private Optional<Node> getOsAccountListNode(Node node, OsAccount osAccount, Set<Host> hosts) {
1237 if (node == null) {
1238 return Optional.empty();
1239 }
1240
1241 Host nodeHost = node.getLookup().lookup(Host.class);
1242 if ((nodeHost != null && hosts != null && hosts.contains(nodeHost))
1243 || node.getLookup().lookup(DataSource.class) != null
1244 || node.getLookup().lookup(Person.class) != null
1245 || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
1246
1247 return Stream.of(node.getChildren().getNodes(true))
1248 .map(childNode -> getOsAccountListNode(childNode, osAccount, hosts))
1249 .filter(Optional::isPresent)
1250 .map(Optional::get)
1251 .findFirst();
1252
1253 }
1254
1255 if (OsAccounts.getListName().equals(node.getName())) {
1256 return Optional.of(node);
1257 }
1258
1259 return Optional.empty();
1260 }
1261
1267 public void viewOsAccount(OsAccount osAccount) {
1268 Set<Host> hosts = null;
1269
1271 try {
1272 hosts = new HashSet<>(Case.getCurrentCase().getSleuthkitCase().getOsAccountManager().getHosts(osAccount));
1273 } catch (TskCoreException ex) {
1274 LOGGER.log(Level.WARNING, "Unable to get valid hosts for osAccount: " + osAccount, ex);
1275 return;
1276 }
1277 }
1278
1279 final Set<Host> finalHosts = hosts;
1280
1281 Optional<Node> osAccountListNodeOpt = Stream.of(em.getRootContext().getChildren().getNodes(true))
1282 .map(nd -> getOsAccountListNode(nd, osAccount, finalHosts))
1283 .filter(Optional::isPresent)
1284 .map(Optional::get)
1285 .findFirst();
1286
1287 if (!osAccountListNodeOpt.isPresent()) {
1288 return;
1289 }
1290
1291 Node osAccountListNode = osAccountListNodeOpt.get();
1292
1293 DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) osAccountListNode).getOriginal();
1294 undecoratedParentNode.setChildNodeSelectionInfo((osAcctNd) -> {
1295 OsAccount osAcctOfNd = osAcctNd.getLookup().lookup(OsAccount.class);
1296 return osAcctOfNd != null && osAcctOfNd.getId() == osAccount.getId();
1297 });
1298 getTree().expandNode(osAccountListNode);
1299 try {
1300 em.setExploredContextAndSelection(osAccountListNode, new Node[]{osAccountListNode});
1301 } catch (PropertyVetoException ex) {
1302 LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
1303 }
1304 }
1305
1313 private Optional<BlackboardArtifact.Type> getType(long artifactTypeId) {
1314 try {
1315 return Case.getCurrentCaseThrows().getSleuthkitCase().getArtifactTypesInUse().stream()
1316 .filter(type -> type.getTypeID() == artifactTypeId)
1317 .findFirst();
1318 } catch (NoCurrentCaseException | TskCoreException ex) {
1319 LOGGER.log(Level.WARNING, "Error occurred while looking up blackboard artifact type for: " + artifactTypeId, ex);
1320 return Optional.empty();
1321 }
1322 }
1323
1338 @SuppressWarnings("deprecation")
1339 public void viewArtifact(final BlackboardArtifact art) {
1340 int typeID = art.getArtifactTypeID();
1341 String typeName = art.getArtifactTypeName();
1342
1343 Optional<BlackboardArtifact.Type> typeOpt = getType(typeID);
1344 Optional<Children> categoryChildrenOpt = typeOpt
1345 .flatMap(type -> getCategoryNode(type.getCategory(), art))
1346 .flatMap(categoryNode -> Optional.ofNullable(categoryNode.getChildren()));
1347
1348 if (!categoryChildrenOpt.isPresent()) {
1349 LOGGER.log(Level.WARNING, String.format("Category node children for artifact of typeID: %d and artifactID: %d not found.",
1350 typeID, art.getArtifactID()));
1351 return;
1352 }
1353
1354 Children typesChildren = categoryChildrenOpt.get();
1355
1356 Node treeNode = null;
1357 if (typeID == BlackboardArtifact.Type.TSK_HASHSET_HIT.getTypeID()) {
1358 treeNode = getHashsetNode(typesChildren, art);
1359 } else if (typeID == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
1360 treeNode = getKeywordHitNode(typesChildren, art);
1361 } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID()) {
1362 treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, art);
1363 } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
1364 treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, art);
1365 } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_ITEM.getTypeID()) {
1366 treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_ITEM, art);
1367 } else if (typeID == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) {
1368 treeNode = getEmailNode(typesChildren, art);
1369 } else if (typeID == BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()) {
1370 treeNode = getAccountNode(typesChildren, art);
1371 } else {
1372 treeNode = typesChildren.findChild(typeName);
1373 }
1374
1375 if (treeNode == null) {
1376 return;
1377 }
1378
1379 DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) treeNode).getOriginal();
1380 undecoratedParentNode.setChildNodeSelectionInfo(new ArtifactNodeSelectionInfo(art));
1381 getTree().expandNode(treeNode);
1382 if (this.getSelectedNode().equals(treeNode)) {
1384 this.respondSelection(em.getSelectedNodes(), new Node[]{treeNode});
1385 } else {
1386 try {
1387 em.setExploredContextAndSelection(treeNode, new Node[]{treeNode});
1388 } catch (PropertyVetoException ex) {
1389 LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
1390 }
1391 }
1392 // Another thread is needed because we have to wait for dataResult to populate
1393 }
1394
1406 private Node getHashsetNode(Children typesChildren, final BlackboardArtifact art) {
1407 Node hashsetRootNode = typesChildren.findChild(art.getArtifactTypeName());
1408 Children hashsetRootChilds = hashsetRootNode.getChildren();
1409 try {
1410 String setName = null;
1411 List<BlackboardAttribute> attributes = art.getAttributes();
1412 for (BlackboardAttribute att : attributes) {
1413 int typeId = att.getAttributeType().getTypeID();
1414 if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
1415 setName = att.getValueString();
1416 }
1417 }
1418 return hashsetRootChilds.findChild(setName);
1419 } catch (TskCoreException ex) {
1420 LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1421 return null;
1422 }
1423 }
1424
1436 private Node getKeywordHitNode(Children typesChildren, BlackboardArtifact art) {
1437 Node keywordRootNode = typesChildren.findChild(art.getArtifactTypeName());
1438 Children keywordRootChilds = keywordRootNode.getChildren();
1439 try {
1440 String listName = null;
1441 String keywordName = null;
1442 String regex = null;
1443 List<BlackboardAttribute> attributes = art.getAttributes();
1444 for (BlackboardAttribute att : attributes) {
1445 int typeId = att.getAttributeType().getTypeID();
1446 if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
1447 listName = att.getValueString();
1448 } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
1449 keywordName = att.getValueString();
1450 } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()) {
1451 regex = att.getValueString();
1452 }
1453 }
1454 if (listName == null) {
1455 if (regex == null) { //using same labels used for creation
1456 listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.simpleLiteralSearch.text");
1457 } else {
1458 listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text");
1459 }
1460 }
1461 Node listNode = keywordRootChilds.findChild(listName);
1462 if (listNode == null) {
1463 return null;
1464 }
1465 Children listChildren = listNode.getChildren();
1466 if (listChildren == null) {
1467 return null;
1468 }
1469 if (regex != null) { //For support of regex nodes such as URLs, IPs, Phone Numbers, and Email Addrs as they are down another level
1470 Node regexNode = listChildren.findChild(listName);
1471 regexNode = (regexNode == null) ? listChildren.findChild(listName + "_" + regex) : regexNode;
1472 if (regexNode == null) {
1473 return null;
1474 }
1475 listChildren = regexNode.getChildren();
1476 if (listChildren == null) {
1477 return null;
1478 }
1479 }
1480
1481 return listChildren.findChild(keywordName);
1482 } catch (TskCoreException ex) {
1483 LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1484 return null;
1485 }
1486 }
1487
1501 private Node getInterestingItemNode(Children typesChildren, BlackboardArtifact.Type artifactType, BlackboardArtifact art) {
1502 Node interestingItemsRootNode = typesChildren.findChild(artifactType.getDisplayName());
1503 Children setNodeChildren = (interestingItemsRootNode == null) ? null : interestingItemsRootNode.getChildren();
1504
1505 // set node children for type could not be found, so return null.
1506 if (setNodeChildren == null) {
1507 return null;
1508 }
1509
1510 String setName = null;
1511 try {
1512 setName = art.getAttributes().stream()
1513 .filter(attr -> attr.getAttributeType().getTypeID() == BlackboardAttribute.Type.TSK_SET_NAME.getTypeID())
1514 .map(attr -> attr.getValueString())
1515 .findFirst()
1516 .orElse(null);
1517
1518 } catch (TskCoreException ex) {
1519 LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1520 return null;
1521 }
1522
1523 // if no set name, no set node will be identified.
1524 if (setName == null) {
1525 return null;
1526 }
1527
1528 // make sure data is fully loaded
1529 final String finalSetName = setName;
1530 return Stream.of(setNodeChildren.getNodes(true))
1531 .filter(setNode -> finalSetName.equals(setNode.getLookup().lookup(String.class)))
1532 .findFirst()
1533 .orElse(null);
1534 }
1535
1544 private Node getEmailNode(Children typesChildren, BlackboardArtifact art) {
1545 Node emailMsgRootNode = typesChildren.findChild(art.getArtifactTypeName());
1546 Children emailMsgRootChilds = emailMsgRootNode.getChildren();
1547 Map<String, String> parsedPath = null;
1548 try {
1549 List<BlackboardAttribute> attributes = art.getAttributes();
1550 for (BlackboardAttribute att : attributes) {
1551 int typeId = att.getAttributeType().getTypeID();
1552 if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
1553 parsedPath = EmailExtracted.parsePath(att.getValueString());
1554 break;
1555 }
1556 }
1557 if (parsedPath == null) {
1558 return null;
1559 }
1560 Node defaultNode = emailMsgRootChilds.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text")));
1561 Children defaultChildren = defaultNode.getChildren();
1562 return defaultChildren.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text")));
1563 } catch (TskCoreException ex) {
1564 LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1565 return null;
1566 }
1567 }
1568
1578 private Node getAccountNode(Children typesChildren, BlackboardArtifact art) {
1579 Node accountRootNode = typesChildren.findChild(art.getDisplayName());
1580 Children accountRootChilds = accountRootNode.getChildren();
1581 List<BlackboardAttribute> attributes;
1582 String accountType = null;
1583 String ccNumberName = null;
1584 try {
1585 attributes = art.getAttributes();
1586 for (BlackboardAttribute att : attributes) {
1587 int typeId = att.getAttributeType().getTypeID();
1588 if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()) {
1589 accountType = att.getValueString();
1590 }
1591 if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID()) {
1592 ccNumberName = att.getValueString();
1593 }
1594 }
1595 if (accountType == null) {
1596 return null;
1597 }
1598
1599 if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) {
1600 return getCreditCardAccountNode(accountRootChilds, ccNumberName);
1601 } else { //default account type
1602 return accountRootChilds.findChild(accountType);
1603 }
1604 } catch (TskCoreException ex) {
1605 LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
1606 return null;
1607 }
1608 }
1609
1620 private Node getCreditCardAccountNode(Children accountRootChildren, String ccNumberName) {
1621 Node accountNode = accountRootChildren.findChild(Account.Type.CREDIT_CARD.getDisplayName());
1622 if (accountNode == null) {
1623 return null;
1624 }
1625 Children accountChildren = accountNode.getChildren();
1626 if (accountChildren == null) {
1627 return null;
1628 }
1629 Node binNode = accountChildren.findChild(NbBundle.getMessage(Accounts.class, "Accounts.ByBINNode.name"));
1630 if (binNode == null) {
1631 return null;
1632 }
1633 Children binChildren = binNode.getChildren();
1634 if (ccNumberName == null) {
1635 return null;
1636 }
1637 //right padded with 0s to 8 digits when single number
1638 //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
1639 String binName = StringUtils.rightPad(ccNumberName, 8, "0");
1640 binName = binName.substring(0, 8);
1641 int bin;
1642 try {
1643 bin = Integer.parseInt(binName);
1644 } catch (NumberFormatException ex) {
1645 LOGGER.log(Level.WARNING, "Unable to parseInt a BIN for node selection from string binName=" + binName, ex); //NON-NLS
1646 return null;
1647 }
1649 if (binInfo != null) {
1650 int startBin = ((BINRange) binInfo).getBINstart();
1651 int endBin = ((BINRange) binInfo).getBINend();
1652 if (startBin != endBin) {
1653 binName = Integer.toString(startBin) + "-" + Integer.toString(endBin).substring(5); //if there is a range re-construct the name it appears as
1654 }
1655 }
1656 if (binName == null) {
1657 return null;
1658 }
1659 return binChildren.findChild(binName);
1660 }
1661
1662 public void viewArtifactContent(BlackboardArtifact art) {
1664 NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.action.viewArtContent.text"),
1666 }
1667
1668 public void addOnFinishedListener(PropertyChangeListener l) {
1669 DirectoryTreeTopComponent.this.addPropertyChangeListener(l);
1670 }
1671
1672}
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:712
static void setGroupItemsInTreeByDataSource(boolean value)
static void addChangeListener(PreferenceChangeListener listener)
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static synchronized void setConfigSetting(String moduleName, String settingName, String settingVal)
static synchronized String getConfigSetting(String moduleName, String settingName)
static synchronized boolean settingExists(String moduleName, String settingName)
static synchronized BankIdentificationNumber getBINInfo(int bin)
void setChildNodeSelectionInfo(NodeSelectionInfo selectedChildNodeInfo)
static final Map< String, String > parsePath(String path)
static String getTagsDisplayName()
Definition Tags.java:81
void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName)
Optional< Node > getCategoryNodeChild(Children children, Category category)
Node getHashsetNode(Children typesChildren, final BlackboardArtifact art)
Optional< BlackboardArtifact.Type > getType(long artifactTypeId)
Optional< Node > getCategoryNode(Category category, BlackboardArtifact art)
Node getInterestingItemNode(Children typesChildren, BlackboardArtifact.Type artifactType, BlackboardArtifact art)
Node getCreditCardAccountNode(Children accountRootChildren, String ccNumberName)
Node getAccountNode(Children typesChildren, BlackboardArtifact art)
Node getEmailNode(Children typesChildren, BlackboardArtifact art)
Optional< Node > searchForCategoryNode(Node node, long dataSourceId, Category category)
Node getKeywordHitNode(Children typesChildren, BlackboardArtifact art)
Optional< Node > getOsAccountListNode(Node node, OsAccount osAccount, Set< Host > hosts)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.