Autopsy  4.19.3
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_INTERESTING_ITEM;
64 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_TL_EVENT;
65 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ASSOCIATED_OBJECT;
66 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
67 
71 public class Artifacts {
72 
75 
79  static class BaseArtifactNode extends DisplayableItemNode {
80 
89  BaseArtifactNode(Children children, String icon, String name, String displayName) {
90  super(children, Lookups.singleton(name));
91  super.setName(name);
92  super.setDisplayName(displayName);
93  this.setIconBaseWithExtension(icon); //NON-NLS
94  }
95 
96  @Override
97  public boolean isLeafTypeNode() {
98  return false;
99  }
100 
101  @Override
102  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
103  return visitor.visit(this);
104  }
105 
106  @Override
107  protected Sheet createSheet() {
108  Sheet sheet = super.createSheet();
109  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
110  if (sheetSet == null) {
111  sheetSet = Sheet.createPropertiesSet();
112  sheet.put(sheetSet);
113  }
114 
115  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"),
116  NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"),
117  NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"),
118  super.getDisplayName()));
119  return sheet;
120  }
121 
122  @Override
123  public String getItemType() {
124  return getClass().getName();
125  }
126  }
127 
131  private static class TypeNodeKey {
132 
134  private final Set<BlackboardArtifact.Type> applicableTypes;
135 
144  TypeNodeKey(BlackboardArtifact.Type type, long dsObjId) {
145  this(new TypeNode(type, dsObjId), type);
146  }
147 
155  TypeNodeKey(UpdatableCountTypeNode typeNode, BlackboardArtifact.Type... types) {
156  this.node = typeNode;
157  this.applicableTypes = Stream.of(types)
158  .filter(t -> t != null)
159  .collect(Collectors.toSet());
160  }
161 
167  UpdatableCountTypeNode getNode() {
168  return node;
169  }
170 
176  Set<BlackboardArtifact.Type> getApplicableTypes() {
177  return applicableTypes;
178  }
179 
180  @Override
181  public int hashCode() {
182  int hash = 3;
183  hash = 61 * hash + Objects.hashCode(this.applicableTypes);
184  return hash;
185  }
186 
187  @Override
188  public boolean equals(Object obj) {
189  if (this == obj) {
190  return true;
191  }
192  if (obj == null) {
193  return false;
194  }
195  if (getClass() != obj.getClass()) {
196  return false;
197  }
198  final TypeNodeKey other = (TypeNodeKey) obj;
199  if (!Objects.equals(this.applicableTypes, other.applicableTypes)) {
200  return false;
201  }
202  return true;
203  }
204 
205  }
206 
211  static class TypeFactory extends ChildFactory.Detachable<TypeNodeKey> implements RefreshThrottler.Refresher {
212 
213  private static final Logger logger = Logger.getLogger(TypeNode.class.getName());
214 
218  @SuppressWarnings("deprecation")
219  private static final Set<BlackboardArtifact.Type> IGNORED_TYPES = Sets.newHashSet(
220  // these are shown in other parts of the UI (and different node types)
221  TSK_DATA_SOURCE_USAGE,
222  TSK_GEN_INFO,
223  new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE),
224  TSK_TL_EVENT,
225  //This is not meant to be shown in the UI at all. It is more of a meta artifact.
226  TSK_ASSOCIATED_OBJECT
227  );
228 
243  @SuppressWarnings("deprecation")
244  private static TypeNodeKey getTypeKey(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) {
245  int typeId = type.getTypeID();
246  if (TSK_EMAIL_MSG.getTypeID() == typeId) {
247  EmailExtracted.RootNode emailNode = new EmailExtracted(skCase, dsObjId).new RootNode();
248  return new TypeNodeKey(emailNode, TSK_EMAIL_MSG);
249 
250  } else if (TSK_ACCOUNT.getTypeID() == typeId) {
251  Accounts.AccountsRootNode accountsNode = new Accounts(dsObjId).new AccountsRootNode();
252  return new TypeNodeKey(accountsNode, TSK_ACCOUNT);
253 
254  } else if (TSK_KEYWORD_HIT.getTypeID() == typeId) {
255  KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode();
256  return new TypeNodeKey(keywordsNode, TSK_KEYWORD_HIT);
257 
258  } else if (TSK_INTERESTING_ITEM.getTypeID() == typeId) {
259  InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_ITEM, dsObjId).new RootNode();
260  return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_ITEM);
261  } else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId) {
262  InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_ARTIFACT_HIT, dsObjId).new RootNode();
263  return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_ARTIFACT_HIT);
264  } else if (TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) {
265  InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_FILE_HIT, dsObjId).new RootNode();
266  return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_FILE_HIT);
267  } else if (TSK_HASHSET_HIT.getTypeID() == typeId) {
268  HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode();
269  return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT);
270 
271  } else {
272  return new TypeNodeKey(type, dsObjId);
273  }
274  }
275 
276  // maps the artifact type to its child node
277  private final Map<BlackboardArtifact.Type, TypeNodeKey> typeNodeMap = new HashMap<>();
278  private final long filteringDSObjId;
279 
285  private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
286  private final Category category;
287 
288  private final PropertyChangeListener weakPcl;
289 
298  TypeFactory(Category category, long filteringDSObjId) {
299  super();
300  this.filteringDSObjId = filteringDSObjId;
301  this.category = category;
302 
303  PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
304  String eventType = evt.getPropertyName();
305  if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
306  // case was closed. Remove listeners so that we don't get called with a stale case handle
307  if (evt.getNewValue() == null) {
308  removeNotify();
309  }
310  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
311  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
318  try {
319  Case.getCurrentCaseThrows();
320  refresh(false);
321  } catch (NoCurrentCaseException notUsed) {
325  }
326  }
327  };
328 
329  weakPcl = WeakListeners.propertyChange(pcl, null);
330  }
331 
332  @Override
333  protected void addNotify() {
334  super.addNotify();
335  refreshThrottler.registerForIngestModuleEvents();
336  IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
337  Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
338  }
339 
340  @Override
341  protected void finalize() throws Throwable {
342  super.finalize();
343  refreshThrottler.unregisterEventListener();
344  IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
345  Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
346  typeNodeMap.clear();
347  }
348 
349  @Override
350  protected boolean createKeys(List<TypeNodeKey> list) {
351  try {
352  // Get all types in use
353  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
354  List<BlackboardArtifact.Type> types = (this.filteringDSObjId > 0)
355  ? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId)
356  : skCase.getArtifactTypesInUse();
357 
358  List<TypeNodeKey> allKeysSorted = types.stream()
359  // filter types by category and ensure they are not in the list of ignored types
360  .filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp))
361  .map(tp -> {
362  // if typeNodeMap already contains key, update the relevant node and return the node
363  if (typeNodeMap.containsKey(tp)) {
364  TypeNodeKey typeKey = typeNodeMap.get(tp);
365  typeKey.getNode().updateDisplayName();
366  return typeKey;
367  } else {
368  // if key is not in map, create the type key and add to map
369  TypeNodeKey newTypeKey = getTypeKey(tp, skCase, filteringDSObjId);
370  for (BlackboardArtifact.Type recordType : newTypeKey.getApplicableTypes()) {
371  typeNodeMap.put(recordType, newTypeKey);
372  }
373  return newTypeKey;
374  }
375  })
376  // ensure record is returned
377  .filter(record -> record != null)
378  // there are potentially multiple types that apply to the same node (i.e. Interesting Files / Artifacts)
379  // ensure the keys are distinct
380  .distinct()
381  // sort by display name
382  .sorted((a, b) -> {
383  String aSafe = (a.getNode() == null || a.getNode().getDisplayName() == null) ? "" : a.getNode().getDisplayName();
384  String bSafe = (b.getNode() == null || b.getNode().getDisplayName() == null) ? "" : b.getNode().getDisplayName();
385  return aSafe.compareToIgnoreCase(bSafe);
386  })
387  .collect(Collectors.toList());
388 
389  list.addAll(allKeysSorted);
390 
391  } catch (NoCurrentCaseException ex) {
392  logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
393  } catch (TskCoreException ex) {
394  logger.log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
395  }
396  return true;
397  }
398 
399  @Override
400  protected Node createNodeForKey(TypeNodeKey key) {
401  return key.getNode();
402  }
403 
404  @Override
405  public void refresh() {
406  refresh(false);
407  }
408 
409  @Override
410  public boolean isRefreshRequired(PropertyChangeEvent evt) {
411  String eventType = evt.getPropertyName();
412  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
418  try {
419  Case.getCurrentCaseThrows();
425  final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
426  if (null != event && category.equals(event.getBlackboardArtifactType().getCategory())
427  && !(IGNORED_TYPES.contains(event.getBlackboardArtifactType()))) {
428  return true;
429  }
430  } catch (NoCurrentCaseException notUsed) {
434  }
435  }
436  return false;
437  }
438  }
439 
444  public static abstract class UpdatableCountTypeNode extends DisplayableItemNode {
445 
446  private static final Logger logger = Logger.getLogger(UpdatableCountTypeNode.class.getName());
447 
448  private final Set<BlackboardArtifact.Type> types;
449  private final long filteringDSObjId;
450  private long childCount = 0;
451  private final String baseName;
452 
465  public UpdatableCountTypeNode(Children children, Lookup lookup, String baseName,
466  long filteringDSObjId, BlackboardArtifact.Type... types) {
467 
468  super(children, lookup);
469  this.types = Stream.of(types).collect(Collectors.toSet());
470  this.filteringDSObjId = filteringDSObjId;
471  this.baseName = baseName;
472  updateDisplayName();
473  }
474 
480  protected long getChildCount() {
481  return this.childCount;
482  }
483 
493  protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
494  int count = 0;
495  for (BlackboardArtifact.Type type : this.types) {
496  if (filteringDSObjId > 0) {
497  count += skCase.getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId);
498  } else {
499  count += skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
500  }
501  }
502  return count;
503  }
504 
509  void updateDisplayName() {
510  try {
511  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
512  this.childCount = fetchChildCount(skCase);
513  } catch (NoCurrentCaseException ex) {
514  logger.log(Level.WARNING, "Error fetching data when case closed.", ex);
515  } catch (TskCoreException ex) {
516  logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
517  }
518  super.setDisplayName(this.baseName + " \u200E(\u200E" + this.childCount + ")\u200E");
519  }
520  }
521 
528  static class TypeNode extends UpdatableCountTypeNode {
529 
530  private final BlackboardArtifact.Type type;
531 
540  TypeNode(BlackboardArtifact.Type type, long filteringDSObjId) {
541  super(Children.create(new ArtifactFactory(type, filteringDSObjId), true),
542  Lookups.singleton(type.getDisplayName()),
543  type.getDisplayName(),
544  filteringDSObjId,
545  type);
546 
547  super.setName(type.getTypeName());
548  this.type = type;
549  String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
550  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
551  }
552 
553  @Override
554  protected Sheet createSheet() {
555  Sheet sheet = super.createSheet();
556  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
557  if (sheetSet == null) {
558  sheetSet = Sheet.createPropertiesSet();
559  sheet.put(sheetSet);
560  }
561 
562  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
563  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
564  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
565  type.getDisplayName()));
566 
567  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
568  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
569  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
570  getChildCount()));
571 
572  return sheet;
573  }
574 
575  @Override
576  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
577  return visitor.visit(this);
578  }
579 
580  @Override
581  public boolean isLeafTypeNode() {
582  return true;
583  }
584 
585  @Override
586  public String getItemType() {
587  return getClass().getName() + type.getDisplayName();
588  }
589  }
590 
594  private static class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
595 
596  private static final Logger logger = Logger.getLogger(ArtifactFactory.class.getName());
597  private final BlackboardArtifact.Type type;
598 
604  private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
605  private final long filteringDSObjId;
606 
615  ArtifactFactory(BlackboardArtifact.Type type, long filteringDSObjId) {
616  super(type.getTypeName());
617  this.type = type;
618  this.filteringDSObjId = filteringDSObjId;
619  }
620 
621  private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
622  String eventType = evt.getPropertyName();
623  if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
624  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
631  try {
633  refresh(false);
634  } catch (NoCurrentCaseException notUsed) {
638  }
639  }
640  };
641 
642  private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
643 
644  @Override
645  protected void onAdd() {
646  refreshThrottler.registerForIngestModuleEvents();
648  }
649 
650  @Override
651  protected void onRemove() {
652  if (refreshThrottler != null) {
653  refreshThrottler.unregisterEventListener();
654  }
656  }
657 
658  @Override
659  protected Node createNodeForKey(BlackboardArtifact key) {
660  return new BlackboardArtifactNode(key);
661  }
662 
663  @Override
664  protected List<BlackboardArtifact> makeKeys() {
665  try {
666  List<? extends BlackboardArtifact> arts;
667  Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
668  switch (this.type.getCategory()) {
669 
670  case ANALYSIS_RESULT:
671  arts = (filteringDSObjId > 0)
672  ? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId)
673  : blackboard.getAnalysisResultsByType(type.getTypeID());
674  break;
675  case DATA_ARTIFACT:
676  default:
677  arts = (filteringDSObjId > 0)
678  ? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId)
679  : blackboard.getDataArtifacts(type.getTypeID());
680  break;
681  }
682 
683  for (BlackboardArtifact art : arts) {
684  //Cache attributes while we are off the EDT.
685  //See JIRA-5969
686  art.getAttributes();
687  }
688 
689  @SuppressWarnings("unchecked")
690  List<BlackboardArtifact> toRet = (List<BlackboardArtifact>) (List<?>) arts;
691  return toRet;
692  } catch (NoCurrentCaseException ex) {
693  logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
694  } catch (TskCoreException ex) {
695  logger.log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
696  }
697  return Collections.emptyList();
698  }
699 
700  @Override
701  public void refresh() {
702  refresh(false);
703  }
704 
705  @Override
706  public boolean isRefreshRequired(PropertyChangeEvent evt) {
707  String eventType = evt.getPropertyName();
708  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
709 
716  try {
724  final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
725  if (null != event && event.getBlackboardArtifactType().equals(type)) {
726  return true;
727  }
728 
729  } catch (NoCurrentCaseException notUsed) {
733  }
734  }
735  return false;
736  }
737  }
738 }
static synchronized IngestManager getInstance()
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
Definition: Artifacts.java:74
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
final Set< BlackboardArtifact.Type > applicableTypes
Definition: Artifacts.java:134
boolean isRefreshRequired(PropertyChangeEvent evt)
Definition: Artifacts.java:706
UpdatableCountTypeNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type...types)
Definition: Artifacts.java:465
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2022 Basis Technology. Generated on: Tue Oct 4 2022
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.