Autopsy  4.19.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
Artifacts.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2021 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.datamodel;
20 
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.util.Collections;
24 import java.util.EnumSet;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Set;
30 import java.util.logging.Level;
31 import java.util.stream.Collectors;
32 import java.util.stream.Stream;
33 import org.openide.nodes.ChildFactory;
34 import org.openide.nodes.Children;
35 import org.openide.nodes.Node;
36 import org.openide.nodes.Sheet;
37 import org.openide.util.Lookup;
38 import org.openide.util.NbBundle;
39 import org.openide.util.WeakListeners;
40 import org.openide.util.lookup.Lookups;
48 import org.sleuthkit.datamodel.BlackboardArtifact;
49 import org.sleuthkit.datamodel.SleuthkitCase;
50 import org.sleuthkit.datamodel.TskCoreException;
52 import org.sleuthkit.datamodel.BlackboardArtifact.Category;
53 import org.python.google.common.collect.Sets;
54 import org.sleuthkit.datamodel.Blackboard;
55 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
56 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_DATA_SOURCE_USAGE;
57 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG;
58 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_HASHSET_HIT;
59 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT;
60 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT;
61 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_GEN_INFO;
62 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
63 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_TL_EVENT;
64 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ASSOCIATED_OBJECT;
65 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
66 
70 public class Artifacts {
71 
74 
78  static class BaseArtifactNode extends DisplayableItemNode {
79 
88  BaseArtifactNode(Children children, String icon, String name, String displayName) {
89  super(children, Lookups.singleton(name));
90  super.setName(name);
91  super.setDisplayName(displayName);
92  this.setIconBaseWithExtension(icon); //NON-NLS
93  }
94 
95  @Override
96  public boolean isLeafTypeNode() {
97  return false;
98  }
99 
100  @Override
101  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
102  return visitor.visit(this);
103  }
104 
105  @Override
106  protected Sheet createSheet() {
107  Sheet sheet = super.createSheet();
108  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
109  if (sheetSet == null) {
110  sheetSet = Sheet.createPropertiesSet();
111  sheet.put(sheetSet);
112  }
113 
114  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"),
115  NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"),
116  NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"),
117  super.getDisplayName()));
118  return sheet;
119  }
120 
121  @Override
122  public String getItemType() {
123  return getClass().getName();
124  }
125  }
126 
130  private static class TypeNodeKey {
131 
133  private final Set<BlackboardArtifact.Type> applicableTypes;
134 
143  TypeNodeKey(BlackboardArtifact.Type type, long dsObjId) {
144  this(new TypeNode(type, dsObjId), type);
145  }
146 
154  TypeNodeKey(UpdatableCountTypeNode typeNode, BlackboardArtifact.Type... types) {
155  this.node = typeNode;
156  this.applicableTypes = Stream.of(types)
157  .filter(t -> t != null)
158  .collect(Collectors.toSet());
159  }
160 
166  UpdatableCountTypeNode getNode() {
167  return node;
168  }
169 
175  Set<BlackboardArtifact.Type> getApplicableTypes() {
176  return applicableTypes;
177  }
178 
179  @Override
180  public int hashCode() {
181  int hash = 3;
182  hash = 61 * hash + Objects.hashCode(this.applicableTypes);
183  return hash;
184  }
185 
186  @Override
187  public boolean equals(Object obj) {
188  if (this == obj) {
189  return true;
190  }
191  if (obj == null) {
192  return false;
193  }
194  if (getClass() != obj.getClass()) {
195  return false;
196  }
197  final TypeNodeKey other = (TypeNodeKey) obj;
198  if (!Objects.equals(this.applicableTypes, other.applicableTypes)) {
199  return false;
200  }
201  return true;
202  }
203 
204  }
205 
210  static class TypeFactory extends ChildFactory.Detachable<TypeNodeKey> implements RefreshThrottler.Refresher {
211 
212  private static final Logger logger = Logger.getLogger(TypeNode.class.getName());
213 
217  @SuppressWarnings("deprecation")
218  private static final Set<BlackboardArtifact.Type> IGNORED_TYPES = Sets.newHashSet(
219  // these are shown in other parts of the UI (and different node types)
220  TSK_DATA_SOURCE_USAGE,
221  TSK_GEN_INFO,
222  new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE),
223  TSK_TL_EVENT,
224  //This is not meant to be shown in the UI at all. It is more of a meta artifact.
225  TSK_ASSOCIATED_OBJECT
226  );
227 
239  private static TypeNodeKey getTypeKey(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) {
240  int typeId = type.getTypeID();
241  if (TSK_EMAIL_MSG.getTypeID() == typeId) {
242  EmailExtracted.RootNode emailNode = new EmailExtracted(skCase, dsObjId).new RootNode();
243  return new TypeNodeKey(emailNode, TSK_EMAIL_MSG);
244 
245  } else if (TSK_ACCOUNT.getTypeID() == typeId) {
246  Accounts.AccountsRootNode accountsNode = new Accounts(skCase, dsObjId).new AccountsRootNode();
247  return new TypeNodeKey(accountsNode, TSK_ACCOUNT);
248 
249  } else if (TSK_KEYWORD_HIT.getTypeID() == typeId) {
250  KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode();
251  return new TypeNodeKey(keywordsNode, TSK_KEYWORD_HIT);
252 
253  } else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId
254  || TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) {
255 
256  InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, dsObjId).new RootNode();
257  return new TypeNodeKey(interestingHitsNode,
258  TSK_INTERESTING_ARTIFACT_HIT,
259  TSK_INTERESTING_FILE_HIT);
260 
261  } else if (TSK_HASHSET_HIT.getTypeID() == typeId) {
262  HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode();
263  return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT);
264 
265  } else {
266  return new TypeNodeKey(type, dsObjId);
267  }
268  }
269 
270  // maps the artifact type to its child node
271  private final Map<BlackboardArtifact.Type, TypeNodeKey> typeNodeMap = new HashMap<>();
272  private final long filteringDSObjId;
273 
279  private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
280  private final Category category;
281 
282  private final PropertyChangeListener weakPcl;
283 
292  TypeFactory(Category category, long filteringDSObjId) {
293  super();
294  this.filteringDSObjId = filteringDSObjId;
295  this.category = category;
296 
297  PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
298  String eventType = evt.getPropertyName();
299  if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
300  // case was closed. Remove listeners so that we don't get called with a stale case handle
301  if (evt.getNewValue() == null) {
302  removeNotify();
303  }
304  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
305  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
312  try {
313  Case.getCurrentCaseThrows();
314  refresh(false);
315  } catch (NoCurrentCaseException notUsed) {
319  }
320  }
321  };
322 
323  weakPcl = WeakListeners.propertyChange(pcl, null);
324  }
325 
326  @Override
327  protected void addNotify() {
328  super.addNotify();
329  refreshThrottler.registerForIngestModuleEvents();
330  IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
331  Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
332  }
333 
334  @Override
335  protected void finalize() throws Throwable {
336  super.finalize();
337  refreshThrottler.unregisterEventListener();
338  IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
339  Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
340  typeNodeMap.clear();
341  }
342 
343  @Override
344  protected boolean createKeys(List<TypeNodeKey> list) {
345  try {
346  // Get all types in use
347  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
348  List<BlackboardArtifact.Type> types = (this.filteringDSObjId > 0)
349  ? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId)
350  : skCase.getArtifactTypesInUse();
351 
352  List<TypeNodeKey> allKeysSorted = types.stream()
353  // filter types by category and ensure they are not in the list of ignored types
354  .filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp))
355  .map(tp -> {
356  // if typeNodeMap already contains key, update the relevant node and return the node
357  if (typeNodeMap.containsKey(tp)) {
358  TypeNodeKey typeKey = typeNodeMap.get(tp);
359  typeKey.getNode().updateDisplayName();
360  return typeKey;
361  } else {
362  // if key is not in map, create the type key and add to map
363  TypeNodeKey newTypeKey = getTypeKey(tp, skCase, filteringDSObjId);
364  for (BlackboardArtifact.Type recordType : newTypeKey.getApplicableTypes()) {
365  typeNodeMap.put(recordType, newTypeKey);
366  }
367  return newTypeKey;
368  }
369  })
370  // ensure record is returned
371  .filter(record -> record != null)
372  // there are potentially multiple types that apply to the same node (i.e. Interesting Files / Artifacts)
373  // ensure the keys are distinct
374  .distinct()
375  // sort by display name
376  .sorted((a, b) -> {
377  String aSafe = (a.getNode() == null || a.getNode().getDisplayName() == null) ? "" : a.getNode().getDisplayName();
378  String bSafe = (b.getNode() == null || b.getNode().getDisplayName() == null) ? "" : b.getNode().getDisplayName();
379  return aSafe.compareToIgnoreCase(bSafe);
380  })
381  .collect(Collectors.toList());
382 
383  list.addAll(allKeysSorted);
384 
385  } catch (NoCurrentCaseException ex) {
386  logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
387  } catch (TskCoreException ex) {
388  logger.log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
389  }
390  return true;
391  }
392 
393  @Override
394  protected Node createNodeForKey(TypeNodeKey key) {
395  return key.getNode();
396  }
397 
398  @Override
399  public void refresh() {
400  refresh(false);
401  }
402 
403  @Override
404  public boolean isRefreshRequired(PropertyChangeEvent evt) {
405  String eventType = evt.getPropertyName();
406  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
412  try {
413  Case.getCurrentCaseThrows();
419  final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
420  if (null != event && category.equals(event.getBlackboardArtifactType().getCategory())
421  && !(IGNORED_TYPES.contains(event.getBlackboardArtifactType()))) {
422  return true;
423  }
424  } catch (NoCurrentCaseException notUsed) {
428  }
429  }
430  return false;
431  }
432  }
433 
438  public static abstract class UpdatableCountTypeNode extends DisplayableItemNode {
439 
440  private static final Logger logger = Logger.getLogger(UpdatableCountTypeNode.class.getName());
441 
442  private final Set<BlackboardArtifact.Type> types;
443  private final long filteringDSObjId;
444  private long childCount = 0;
445  private final String baseName;
446 
459  public UpdatableCountTypeNode(Children children, Lookup lookup, String baseName,
460  long filteringDSObjId, BlackboardArtifact.Type... types) {
461 
462  super(children, lookup);
463  this.types = Stream.of(types).collect(Collectors.toSet());
464  this.filteringDSObjId = filteringDSObjId;
465  this.baseName = baseName;
466  updateDisplayName();
467  }
468 
474  protected long getChildCount() {
475  return this.childCount;
476  }
477 
487  protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
488  int count = 0;
489  for (BlackboardArtifact.Type type : this.types) {
490  if (filteringDSObjId > 0) {
491  count += skCase.getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId);
492  } else {
493  count += skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
494  }
495  }
496  return count;
497  }
498 
503  void updateDisplayName() {
504  try {
505  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
506  this.childCount = fetchChildCount(skCase);
507  } catch (NoCurrentCaseException ex) {
508  logger.log(Level.WARNING, "Error fetching data when case closed.", ex);
509  } catch (TskCoreException ex) {
510  logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
511  }
512  super.setDisplayName(this.baseName + " \u200E(\u200E" + this.childCount + ")\u200E");
513  }
514  }
515 
522  static class TypeNode extends UpdatableCountTypeNode {
523 
524  private final BlackboardArtifact.Type type;
525 
534  TypeNode(BlackboardArtifact.Type type, long filteringDSObjId) {
535  super(Children.create(new ArtifactFactory(type, filteringDSObjId), true),
536  Lookups.singleton(type.getDisplayName()),
537  type.getDisplayName(),
538  filteringDSObjId,
539  type);
540 
541  super.setName(type.getTypeName());
542  this.type = type;
543  String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
544  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
545  }
546 
547  @Override
548  protected Sheet createSheet() {
549  Sheet sheet = super.createSheet();
550  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
551  if (sheetSet == null) {
552  sheetSet = Sheet.createPropertiesSet();
553  sheet.put(sheetSet);
554  }
555 
556  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
557  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
558  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
559  type.getDisplayName()));
560 
561  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
562  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
563  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
564  getChildCount()));
565 
566  return sheet;
567  }
568 
569  @Override
570  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
571  return visitor.visit(this);
572  }
573 
574  @Override
575  public boolean isLeafTypeNode() {
576  return true;
577  }
578 
579  @Override
580  public String getItemType() {
581  return getClass().getName() + type.getDisplayName();
582  }
583  }
584 
588  private static class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
589 
590  private static final Logger logger = Logger.getLogger(ArtifactFactory.class.getName());
591  private final BlackboardArtifact.Type type;
592 
598  private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
599  private final long filteringDSObjId;
600 
609  ArtifactFactory(BlackboardArtifact.Type type, long filteringDSObjId) {
610  super(type.getTypeName());
611  this.type = type;
612  this.filteringDSObjId = filteringDSObjId;
613  }
614 
615  private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
616  String eventType = evt.getPropertyName();
617  if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
618  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
625  try {
627  refresh(false);
628  } catch (NoCurrentCaseException notUsed) {
632  }
633  }
634  };
635 
636  private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
637 
638  @Override
639  protected void onAdd() {
640  refreshThrottler.registerForIngestModuleEvents();
642  }
643 
644  @Override
645  protected void onRemove() {
646  if(refreshThrottler != null) {
647  refreshThrottler.unregisterEventListener();
648  }
650  }
651 
652  @Override
653  protected Node createNodeForKey(BlackboardArtifact key) {
654  return new BlackboardArtifactNode(key);
655  }
656 
657  @Override
658  protected List<BlackboardArtifact> makeKeys() {
659  try {
660  List<? extends BlackboardArtifact> arts;
661  Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
662  switch (this.type.getCategory()) {
663 
664  case ANALYSIS_RESULT:
665  arts = (filteringDSObjId > 0)
666  ? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId)
667  : blackboard.getAnalysisResultsByType(type.getTypeID());
668  break;
669  case DATA_ARTIFACT:
670  default:
671  arts = (filteringDSObjId > 0)
672  ? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId)
673  : blackboard.getDataArtifacts(type.getTypeID());
674  break;
675  }
676 
677  for (BlackboardArtifact art : arts) {
678  //Cache attributes while we are off the EDT.
679  //See JIRA-5969
680  art.getAttributes();
681  }
682 
683  @SuppressWarnings("unchecked")
684  List<BlackboardArtifact> toRet = (List<BlackboardArtifact>)(List<?>)arts;
685  return toRet;
686  } catch (NoCurrentCaseException ex) {
687  logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
688  } catch (TskCoreException ex) {
689  logger.log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
690  }
691  return Collections.emptyList();
692  }
693 
694  @Override
695  public void refresh() {
696  refresh(false);
697  }
698 
699  @Override
700  public boolean isRefreshRequired(PropertyChangeEvent evt) {
701  String eventType = evt.getPropertyName();
702  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
703 
710  try {
718  final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
719  if (null != event && event.getBlackboardArtifactType().equals(type)) {
720  return true;
721  }
722 
723  } catch (NoCurrentCaseException notUsed) {
727  }
728  }
729  return false;
730  }
731  }
732 }
static synchronized IngestManager getInstance()
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
Definition: Artifacts.java:73
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
final Set< BlackboardArtifact.Type > applicableTypes
Definition: Artifacts.java:133
boolean isRefreshRequired(PropertyChangeEvent evt)
Definition: Artifacts.java:700
UpdatableCountTypeNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type...types)
Definition: Artifacts.java:459
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2021 Basis Technology. Generated on: Fri Aug 6 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.