Autopsy  4.19.1
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  InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_ARTIFACT_HIT, dsObjId).new RootNode();
255  return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_ARTIFACT_HIT);
256  } else if (TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) {
257  InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_FILE_HIT, dsObjId).new RootNode();
258  return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_FILE_HIT);
259  } else if (TSK_HASHSET_HIT.getTypeID() == typeId) {
260  HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode();
261  return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT);
262 
263  } else {
264  return new TypeNodeKey(type, dsObjId);
265  }
266  }
267 
268  // maps the artifact type to its child node
269  private final Map<BlackboardArtifact.Type, TypeNodeKey> typeNodeMap = new HashMap<>();
270  private final long filteringDSObjId;
271 
277  private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
278  private final Category category;
279 
280  private final PropertyChangeListener weakPcl;
281 
290  TypeFactory(Category category, long filteringDSObjId) {
291  super();
292  this.filteringDSObjId = filteringDSObjId;
293  this.category = category;
294 
295  PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
296  String eventType = evt.getPropertyName();
297  if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
298  // case was closed. Remove listeners so that we don't get called with a stale case handle
299  if (evt.getNewValue() == null) {
300  removeNotify();
301  }
302  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
303  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
310  try {
311  Case.getCurrentCaseThrows();
312  refresh(false);
313  } catch (NoCurrentCaseException notUsed) {
317  }
318  }
319  };
320 
321  weakPcl = WeakListeners.propertyChange(pcl, null);
322  }
323 
324  @Override
325  protected void addNotify() {
326  super.addNotify();
327  refreshThrottler.registerForIngestModuleEvents();
328  IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
329  Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
330  }
331 
332  @Override
333  protected void finalize() throws Throwable {
334  super.finalize();
335  refreshThrottler.unregisterEventListener();
336  IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
337  Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
338  typeNodeMap.clear();
339  }
340 
341  @Override
342  protected boolean createKeys(List<TypeNodeKey> list) {
343  try {
344  // Get all types in use
345  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
346  List<BlackboardArtifact.Type> types = (this.filteringDSObjId > 0)
347  ? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId)
348  : skCase.getArtifactTypesInUse();
349 
350  List<TypeNodeKey> allKeysSorted = types.stream()
351  // filter types by category and ensure they are not in the list of ignored types
352  .filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp))
353  .map(tp -> {
354  // if typeNodeMap already contains key, update the relevant node and return the node
355  if (typeNodeMap.containsKey(tp)) {
356  TypeNodeKey typeKey = typeNodeMap.get(tp);
357  typeKey.getNode().updateDisplayName();
358  return typeKey;
359  } else {
360  // if key is not in map, create the type key and add to map
361  TypeNodeKey newTypeKey = getTypeKey(tp, skCase, filteringDSObjId);
362  for (BlackboardArtifact.Type recordType : newTypeKey.getApplicableTypes()) {
363  typeNodeMap.put(recordType, newTypeKey);
364  }
365  return newTypeKey;
366  }
367  })
368  // ensure record is returned
369  .filter(record -> record != null)
370  // there are potentially multiple types that apply to the same node (i.e. Interesting Files / Artifacts)
371  // ensure the keys are distinct
372  .distinct()
373  // sort by display name
374  .sorted((a, b) -> {
375  String aSafe = (a.getNode() == null || a.getNode().getDisplayName() == null) ? "" : a.getNode().getDisplayName();
376  String bSafe = (b.getNode() == null || b.getNode().getDisplayName() == null) ? "" : b.getNode().getDisplayName();
377  return aSafe.compareToIgnoreCase(bSafe);
378  })
379  .collect(Collectors.toList());
380 
381  list.addAll(allKeysSorted);
382 
383  } catch (NoCurrentCaseException ex) {
384  logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
385  } catch (TskCoreException ex) {
386  logger.log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
387  }
388  return true;
389  }
390 
391  @Override
392  protected Node createNodeForKey(TypeNodeKey key) {
393  return key.getNode();
394  }
395 
396  @Override
397  public void refresh() {
398  refresh(false);
399  }
400 
401  @Override
402  public boolean isRefreshRequired(PropertyChangeEvent evt) {
403  String eventType = evt.getPropertyName();
404  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
410  try {
411  Case.getCurrentCaseThrows();
417  final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
418  if (null != event && category.equals(event.getBlackboardArtifactType().getCategory())
419  && !(IGNORED_TYPES.contains(event.getBlackboardArtifactType()))) {
420  return true;
421  }
422  } catch (NoCurrentCaseException notUsed) {
426  }
427  }
428  return false;
429  }
430  }
431 
436  public static abstract class UpdatableCountTypeNode extends DisplayableItemNode {
437 
438  private static final Logger logger = Logger.getLogger(UpdatableCountTypeNode.class.getName());
439 
440  private final Set<BlackboardArtifact.Type> types;
441  private final long filteringDSObjId;
442  private long childCount = 0;
443  private final String baseName;
444 
457  public UpdatableCountTypeNode(Children children, Lookup lookup, String baseName,
458  long filteringDSObjId, BlackboardArtifact.Type... types) {
459 
460  super(children, lookup);
461  this.types = Stream.of(types).collect(Collectors.toSet());
462  this.filteringDSObjId = filteringDSObjId;
463  this.baseName = baseName;
464  updateDisplayName();
465  }
466 
472  protected long getChildCount() {
473  return this.childCount;
474  }
475 
485  protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
486  int count = 0;
487  for (BlackboardArtifact.Type type : this.types) {
488  if (filteringDSObjId > 0) {
489  count += skCase.getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId);
490  } else {
491  count += skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
492  }
493  }
494  return count;
495  }
496 
501  void updateDisplayName() {
502  try {
503  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
504  this.childCount = fetchChildCount(skCase);
505  } catch (NoCurrentCaseException ex) {
506  logger.log(Level.WARNING, "Error fetching data when case closed.", ex);
507  } catch (TskCoreException ex) {
508  logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
509  }
510  super.setDisplayName(this.baseName + " \u200E(\u200E" + this.childCount + ")\u200E");
511  }
512  }
513 
520  static class TypeNode extends UpdatableCountTypeNode {
521 
522  private final BlackboardArtifact.Type type;
523 
532  TypeNode(BlackboardArtifact.Type type, long filteringDSObjId) {
533  super(Children.create(new ArtifactFactory(type, filteringDSObjId), true),
534  Lookups.singleton(type.getDisplayName()),
535  type.getDisplayName(),
536  filteringDSObjId,
537  type);
538 
539  super.setName(type.getTypeName());
540  this.type = type;
541  String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
542  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
543  }
544 
545  @Override
546  protected Sheet createSheet() {
547  Sheet sheet = super.createSheet();
548  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
549  if (sheetSet == null) {
550  sheetSet = Sheet.createPropertiesSet();
551  sheet.put(sheetSet);
552  }
553 
554  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
555  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
556  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
557  type.getDisplayName()));
558 
559  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
560  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
561  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
562  getChildCount()));
563 
564  return sheet;
565  }
566 
567  @Override
568  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
569  return visitor.visit(this);
570  }
571 
572  @Override
573  public boolean isLeafTypeNode() {
574  return true;
575  }
576 
577  @Override
578  public String getItemType() {
579  return getClass().getName() + type.getDisplayName();
580  }
581  }
582 
586  private static class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
587 
588  private static final Logger logger = Logger.getLogger(ArtifactFactory.class.getName());
589  private final BlackboardArtifact.Type type;
590 
596  private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
597  private final long filteringDSObjId;
598 
607  ArtifactFactory(BlackboardArtifact.Type type, long filteringDSObjId) {
608  super(type.getTypeName());
609  this.type = type;
610  this.filteringDSObjId = filteringDSObjId;
611  }
612 
613  private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
614  String eventType = evt.getPropertyName();
615  if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
616  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
623  try {
625  refresh(false);
626  } catch (NoCurrentCaseException notUsed) {
630  }
631  }
632  };
633 
634  private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
635 
636  @Override
637  protected void onAdd() {
638  refreshThrottler.registerForIngestModuleEvents();
640  }
641 
642  @Override
643  protected void onRemove() {
644  if (refreshThrottler != null) {
645  refreshThrottler.unregisterEventListener();
646  }
648  }
649 
650  @Override
651  protected Node createNodeForKey(BlackboardArtifact key) {
652  return new BlackboardArtifactNode(key);
653  }
654 
655  @Override
656  protected List<BlackboardArtifact> makeKeys() {
657  try {
658  List<? extends BlackboardArtifact> arts;
659  Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
660  switch (this.type.getCategory()) {
661 
662  case ANALYSIS_RESULT:
663  arts = (filteringDSObjId > 0)
664  ? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId)
665  : blackboard.getAnalysisResultsByType(type.getTypeID());
666  break;
667  case DATA_ARTIFACT:
668  default:
669  arts = (filteringDSObjId > 0)
670  ? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId)
671  : blackboard.getDataArtifacts(type.getTypeID());
672  break;
673  }
674 
675  for (BlackboardArtifact art : arts) {
676  //Cache attributes while we are off the EDT.
677  //See JIRA-5969
678  art.getAttributes();
679  }
680 
681  @SuppressWarnings("unchecked")
682  List<BlackboardArtifact> toRet = (List<BlackboardArtifact>) (List<?>) arts;
683  return toRet;
684  } catch (NoCurrentCaseException ex) {
685  logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
686  } catch (TskCoreException ex) {
687  logger.log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
688  }
689  return Collections.emptyList();
690  }
691 
692  @Override
693  public void refresh() {
694  refresh(false);
695  }
696 
697  @Override
698  public boolean isRefreshRequired(PropertyChangeEvent evt) {
699  String eventType = evt.getPropertyName();
700  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
701 
708  try {
716  final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
717  if (null != event && event.getBlackboardArtifactType().equals(type)) {
718  return true;
719  }
720 
721  } catch (NoCurrentCaseException notUsed) {
725  }
726  }
727  return false;
728  }
729  }
730 }
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:698
UpdatableCountTypeNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type...types)
Definition: Artifacts.java:457
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

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