Autopsy  4.19.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
BlackboardArtifactNode.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2012-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 
23 import com.google.common.annotations.Beta;
24 import com.google.common.cache.Cache;
25 import com.google.common.cache.CacheBuilder;
26 import java.beans.PropertyChangeEvent;
27 import java.beans.PropertyChangeListener;
28 import java.lang.ref.WeakReference;
29 import java.text.MessageFormat;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.EnumSet;
34 import java.util.LinkedHashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.MissingResourceException;
38 import java.util.Set;
39 import java.util.concurrent.ExecutionException;
40 import java.util.concurrent.TimeUnit;
41 import java.util.logging.Level;
42 import java.util.stream.Collectors;
43 import java.util.stream.Stream;
44 import javax.swing.Action;
45 import javax.swing.SwingUtilities;
46 import org.apache.commons.lang3.StringUtils;
47 import org.apache.commons.lang3.tuple.Pair;
48 import org.openide.nodes.Node;
49 import org.openide.nodes.Sheet;
50 import org.openide.util.Lookup;
51 import org.openide.util.NbBundle;
52 import org.openide.util.NbBundle.Messages;
53 import org.openide.util.Utilities;
54 import org.openide.util.WeakListeners;
55 import org.openide.util.lookup.Lookups;
73 import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
75 import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
78 import org.sleuthkit.datamodel.AbstractFile;
79 import org.sleuthkit.datamodel.BlackboardArtifact;
80 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
81 import org.sleuthkit.datamodel.BlackboardAttribute;
82 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
83 import org.sleuthkit.datamodel.Content;
84 import org.sleuthkit.datamodel.Tag;
85 import org.sleuthkit.datamodel.TskCoreException;
100 import org.sleuthkit.datamodel.AnalysisResult;
101 import org.sleuthkit.datamodel.BlackboardArtifact.Category;
102 import org.sleuthkit.datamodel.HostAddress;
103 import org.sleuthkit.datamodel.Pool;
104 import org.sleuthkit.datamodel.DataArtifact;
105 import org.sleuthkit.datamodel.DerivedFile;
106 import org.sleuthkit.datamodel.Directory;
107 import org.sleuthkit.datamodel.File;
108 import org.sleuthkit.datamodel.LayoutFile;
109 import org.sleuthkit.datamodel.LocalDirectory;
110 import org.sleuthkit.datamodel.LocalFile;
111 import org.sleuthkit.datamodel.OsAccount;
112 import org.sleuthkit.datamodel.Report;
113 import org.sleuthkit.datamodel.Score;
114 import org.sleuthkit.datamodel.SlackFile;
115 import org.sleuthkit.datamodel.VirtualDirectory;
116 import org.sleuthkit.datamodel.TskData;
117 import org.sleuthkit.datamodel.Volume;
118 import org.sleuthkit.datamodel.VolumeSystem;
119 import org.sleuthkit.datamodel.Image;
120 
125 public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifact> {
126 
127  private static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName());
128 
129  /*
130  * Cache of Content objects used to avoid repeated trips to the case
131  * database to retrieve Content objects that are the source of multiple
132  * artifacts.
133  */
134  private static final Cache<Long, Content> contentCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
135 
136  /*
137  * Case events that indicate an update to the node's property sheet may be
138  * required.
139  */
140  private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
147 
148  /*
149  * Artifact types for which the file metadata of the artifact's source file
150  * should be displayed in the node's property sheet.
151  */
152  private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
153  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
154  };
155 
156  private final BlackboardArtifact artifact;
157  private final BlackboardArtifact.Type artifactType;
158  private Content srcContent;
159  private volatile String translatedSourceName;
160  private final String sourceObjTypeName;
161 
162  /*
163  * A method has been provided to allow the injection of properties into this
164  * node for display in the node's property sheet, independent of the
165  * artifact the node represents.
166  */
167  private List<NodeProperty<? extends Object>> customProperties;
168 
169  private final PropertyChangeListener listener = new PropertyChangeListener() {
170  @Override
171  public void propertyChange(PropertyChangeEvent evt) {
172  String eventType = evt.getPropertyName();
173  if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())) {
175  if (event.getAddedTag().getArtifact().equals(artifact)) {
176  updateSheet();
177  }
178  } else if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) {
180  if (event.getDeletedTagInfo().getArtifactID() == artifact.getArtifactID()) {
181  updateSheet();
182  }
183  } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
185  if (event.getAddedTag().getContent().equals(srcContent)) {
186  updateSheet();
187  }
188  } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
190  if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) {
191  updateSheet();
192  }
193  } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
195  if (event.getContentID() == srcContent.getId()) {
196  updateSheet();
197  }
198  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
199  if (evt.getNewValue() == null) {
200  /*
201  * The case has been closed.
202  */
204  contentCache.invalidateAll();
205  }
206  } else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) {
207  updateSCOColumns((SCOData) evt.getNewValue());
208  } else if (eventType.equals(FileNameTransTask.getPropertyName())) {
209  /*
210  * Replace the value of the Source File property with the
211  * translated name via setDisplayName (see note in createSheet),
212  * and put the untranslated name in the Original Name property
213  * and in the tooltip.
214  */
215  String originalName = evt.getOldValue().toString();
216  translatedSourceName = evt.getNewValue().toString();
217  setDisplayName(translatedSourceName);
218  setShortDescription(originalName);
220  Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
221  Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
222  NO_DESCR,
223  originalName));
224  }
225  }
226  };
227 
228  /*
229  * The node's event listener is wrapped in a weak reference that allows the
230  * node to be garbage collected when the NetBeans infrastructure discards
231  * it. If this is not done, it has been shown that strong references to the
232  * listener held by event publishers prevents garbage collection of this
233  * node.
234  */
235  private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
236 
247  public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
248  super(artifact, createLookup(artifact, false));
249  this.artifact = artifact;
250  this.artifactType = getType(artifact);
251 
252  srcContent = getSourceContentFromLookup(artifact);
253 
254  if (srcContent == null) {
255  throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
256  }
257 
258  try {
259  /*
260  * Calling this getter causes the unique path of the source content
261  * to be cached in the Content object. This is advantageous as long
262  * as this node is constructed in a background thread instead of a
263  * UI thread.
264  */
265  srcContent.getUniquePath();
266  } catch (TskCoreException ex) {
267  logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
268  }
269  sourceObjTypeName = getSourceObjType(srcContent);
271  setName(Long.toString(artifact.getArtifactID()));
272  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
274  }
275 
287  @Beta
288  public BlackboardArtifactNode(BlackboardArtifact artifact, boolean useAssociatedFileInLookup) {
289  super(artifact, createLookup(artifact, useAssociatedFileInLookup));
290  this.artifact = artifact;
291  this.artifactType = getType(artifact);
292 
293  try {
294  srcContent = artifact.getParent();
295  } catch (TskCoreException ex) {
296  logger.log(Level.WARNING, MessageFormat.format("Error getting the parent of the artifact for (artifact objID={0})", artifact.getId()), ex);
297  }
298 
299  if (srcContent != null) {
300  try {
301  /*
302  * Calling this getter causes the unique path of the source
303  * content to be cached in the Content object. This is
304  * advantageous as long as this node is constructed in a
305  * background thread instead of a UI thread.
306  */
307  srcContent.getUniquePath();
308  } catch (TskCoreException ex) {
309  logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
310  }
311  } else {
312  throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
313  }
314  sourceObjTypeName = getSourceObjType(srcContent);
315  setName(Long.toString(artifact.getArtifactID()));
317  String iconPath = IconsUtil.getIconFilePath(artifact.getArtifactTypeID());
318  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
320  }
321 
330  public BlackboardArtifactNode(BlackboardArtifact artifact) {
331  this(artifact, IconsUtil.getIconFilePath(artifact.getArtifactTypeID()));
332  }
333 
341  private static BlackboardArtifact.Type getType(BlackboardArtifact artifact) {
342  try {
343  return artifact.getType();
344  } catch (TskCoreException ex) {
345  logger.log(Level.WARNING, MessageFormat.format("Error getting the artifact type for artifact (artifact objID={0})", artifact.getId()), ex);
346  return null;
347  }
348  }
349 
361  private static Lookup createLookup(BlackboardArtifact artifact, boolean useAssociatedFile) {
362  /*
363  * Get the source content.
364  */
365  Content content = null;
366  try {
367  if (useAssociatedFile) {
368  content = getPathIdFile(artifact);
369  } else {
370  long srcObjectID = artifact.getObjectID();
371  content = contentCache.get(srcObjectID, () -> artifact.getSleuthkitCase().getContentById(srcObjectID));
372  }
373  } catch (ExecutionException ex) {
374  logger.log(Level.SEVERE, MessageFormat.format("Error getting source/associated content (artifact object ID={0})", artifact.getId()), ex); //NON-NLS
375  }
376 
377  /*
378  * Make an Autopsy Data Model wrapper for the artifact.
379  *
380  * NOTE: The creation of an Autopsy Data Model independent of the
381  * NetBeans nodes is a work in progress. At the time this comment is
382  * being written, this object is only being used to indicate the item
383  * represented by this BlackboardArtifactNode.
384  */
385  BlackboardArtifactItem<?> artifactItem;
386  if (artifact instanceof AnalysisResult) {
387  artifactItem = new AnalysisResultItem((AnalysisResult) artifact, content);
388  } else {
389  artifactItem = new DataArtifactItem((DataArtifact) artifact, content);
390  }
391 
392  /*
393  * Create the Lookup.
394  *
395  * NOTE: For now, we are putting both the Autopsy Data Model item and
396  * the Sleuth Kit Data Model item in the Lookup so that code that is not
397  * aware of the new Autopsy Data Model will still function.
398  */
399  if (content == null) {
400  return Lookups.fixed(artifact, artifactItem);
401  } else {
402  return Lookups.fixed(artifact, artifactItem, content);
403  }
404  }
405 
414  private Content getSourceContentFromLookup(BlackboardArtifact artifact) {
415  for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
416  /*
417  * NOTE: createLookup() saves the artifact and its source content
418  * (if one exists). However, createLookup() has to be static because
419  * it is being called by super(), therefore it can't store the
420  * source content in this.srcContent class variable. That's why we
421  * have to have the logic below, which reads the Lookup contents,
422  * and decides that the source content is the entry in Lookup that
423  * is NOT the input artifact.
424  */
425  if ((lookupContent != null) && (lookupContent.getId() != artifact.getId())) {
426  return lookupContent;
427  }
428  }
429  return null;
430  }
431 
445  private static Content getPathIdFile(BlackboardArtifact artifact) throws ExecutionException {
446  try {
447  BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
448  if (attribute != null) {
449  return contentCache.get(attribute.getValueLong(), () -> artifact.getSleuthkitCase().getContentById(attribute.getValueLong()));
450  }
451  } catch (TskCoreException ex) {
452  logger.log(Level.WARNING, MessageFormat.format("Error getting content for path id attrbiute for artifact: ", artifact.getId()), ex); //NON-NLS
453  }
454  return null;
455  }
456 
466  @Override
467  protected void finalize() throws Throwable {
468  super.finalize();
470  }
471 
475  private void unregisterListener() {
477  }
478 
484  public BlackboardArtifact getArtifact() {
485  return this.artifact;
486  }
487 
495  private List<Action> getNonNull(Action... items) {
496  return Stream.of(items)
497  .filter(i -> i != null)
498  .collect(Collectors.toList());
499  }
500 
501  @Override
502  public Action[] getActions(boolean context) {
503  // groupings of actions where each group will be separated by a divider
504  List<List<Action>> actionsLists = new ArrayList<>();
505 
506  // view artifact in timeline
507  actionsLists.add(getNonNull(
508  getTimelineArtifactAction(this.artifact)
509  ));
510 
511  // view associated file (TSK_PATH_ID attr) in directory and timeline
512  actionsLists.add(getAssociatedFileActions(this.artifact, this.artifactType));
513 
514  // view source content in directory and timeline
515  actionsLists.add(getNonNull(
516  getViewSrcContentAction(this.artifact, this.srcContent),
517  getTimelineSrcContentAction(this.srcContent)
518  ));
519 
520  // extract with password from encrypted file
521  actionsLists.add(getNonNull(
522  getExtractWithPasswordAction(this.srcContent)
523  ));
524 
525  // menu options for artifact with report parent
526  if (this.srcContent instanceof Report) {
527  actionsLists.add(DataModelActionsFactory.getActions(this.srcContent, false));
528  }
529 
530  Node parentFileNode = getParentFileNode(srcContent);
531  int selectedFileCount = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).size();
532  int selectedArtifactCount = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactItem.class).size();
533 
534  // view source content if source content is some sort of file
535  actionsLists.add(getSrcContentViewerActions(parentFileNode, selectedFileCount));
536 
537  // extract / export if source content is some sort of file
538  if (parentFileNode != null) {
539  actionsLists.add(Arrays.asList(ExtractAction.getInstance(), ExportCSVAction.getInstance()));
540  }
541 
542  // file and result tagging
543  actionsLists.add(getTagActions(parentFileNode != null, this.artifact, selectedFileCount, selectedArtifactCount));
544 
545  // menu extension items (i.e. add to central repository)
546  actionsLists.add(ContextMenuExtensionPoint.getActions());
547 
548  // netbeans default items (i.e. properties)
549  actionsLists.add(Arrays.asList(super.getActions(context)));
550 
551  return actionsLists.stream()
552  // remove any empty lists
553  .filter((lst) -> lst != null && !lst.isEmpty())
554  // add in null between each list group
555  .flatMap(lst -> Stream.concat(Stream.of((Action) null), lst.stream()))
556  // skip the first null
557  .skip(1)
558  .toArray(sz -> new Action[sz]);
559  }
560 
569  @Messages({
570  "BlackboardArtifactNode_getAssociatedTypeStr_webCache=Cached File",
571  "BlackboardArtifactNode_getAssociatedTypeStr_webDownload=Downloaded File",
572  "BlackboardArtifactNode_getAssociatedTypeStr_associated=Associated File",})
573  private String getAssociatedTypeStr(BlackboardArtifact.Type artifactType) {
574  if (BlackboardArtifact.Type.TSK_WEB_CACHE.equals(artifactType)) {
575  return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_webCache();
576  } else if (BlackboardArtifact.Type.TSK_WEB_DOWNLOAD.equals(artifactType)) {
577  return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_webDownload();
578  } else {
579  return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_associated();
580  }
581  }
582 
591  @Messages({
592  "BlackboardArtifactNode_getViewSrcContentAction_type_File=File",
593  "BlackboardArtifactNode_getViewSrcContentAction_type_DataArtifact=Data Artifact",
594  "BlackboardArtifactNode_getViewSrcContentAction_type_OSAccount=OS Account",
595  "BlackboardArtifactNode_getViewSrcContentAction_type_unknown=Item"
596  })
597  private String getContentTypeStr(Content content) {
598  if (content instanceof AbstractFile) {
599  return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_File();
600  } else if (content instanceof DataArtifact) {
601  return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_DataArtifact();
602  } else if (content instanceof OsAccount) {
603  return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_OSAccount();
604  } else {
605  return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_unknown();
606  }
607  }
608 
619  @Messages({
620  "# {0} - type",
621  "BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileAction=View {0} in Directory",
622  "# {0} - type",
623  "BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileInTimelineAction=View {0} in Timeline..."
624  })
625  private List<Action> getAssociatedFileActions(BlackboardArtifact artifact, BlackboardArtifact.Type artifactType) {
626  try {
627  AbstractFile associatedFile = findLinked(artifact);
628  if (associatedFile != null) {
629  return Arrays.asList(
630  new ViewContextAction(
631  Bundle.BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileAction(
633  associatedFile),
634  new ViewFileInTimelineAction(associatedFile,
635  Bundle.BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileInTimelineAction(
637  );
638  }
639 
640  } catch (TskCoreException ex) {
641  logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS
642  }
643  return Collections.emptyList();
644  }
645 
654  @Messages({
655  "# {0} - contentType",
656  "BlackboardArtifactNode_getSrcContentAction_actionDisplayName=View Source {0} in Directory"
657  })
658  private Action getViewSrcContentAction(BlackboardArtifact artifact, Content content) {
659  if (content instanceof DataArtifact) {
660  return new ViewArtifactAction(
661  (BlackboardArtifact) content,
662  Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
663  getContentTypeStr(content)));
664  } else if (content instanceof OsAccount) {
665  return new ViewOsAccountAction(
666  (OsAccount) content,
667  Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
668  getContentTypeStr(content)));
669  } else if (content instanceof AbstractFile || artifact instanceof DataArtifact) {
670  return new ViewContextAction(
671  Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
672  getContentTypeStr(content)),
673  content);
674  } else {
675  return null;
676  }
677  }
678 
687  private Node getParentFileNode(Content content) {
688  if (content instanceof File) {
689  return new FileNode((AbstractFile) content);
690  } else if (content instanceof Directory) {
691  return new DirectoryNode((Directory) content);
692  } else if (content instanceof VirtualDirectory) {
693  return new VirtualDirectoryNode((VirtualDirectory) content);
694  } else if (content instanceof LocalDirectory) {
695  return new LocalDirectoryNode((LocalDirectory) content);
696  } else if (content instanceof LayoutFile) {
697  return new LayoutFileNode((LayoutFile) content);
698  } else if (content instanceof LocalFile || content instanceof DerivedFile) {
699  return new LocalFileNode((AbstractFile) content);
700  } else if (content instanceof SlackFile) {
701  return new SlackFileNode((AbstractFile) content);
702  } else {
703  return null;
704  }
705  }
706 
714  private Action getExtractWithPasswordAction(Content srcContent) {
715  if ((srcContent instanceof AbstractFile)
717  .contains("." + ((AbstractFile) srcContent).getNameExtension().toLowerCase())) {
718  try {
719  if (srcContent.getArtifacts(BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED.getTypeID()).size() > 0) {
720  return new ExtractArchiveWithPasswordAction((AbstractFile) srcContent);
721  }
722  } catch (TskCoreException ex) {
723  logger.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex);
724  }
725  }
726 
727  return null;
728  }
729 
741  private List<Action> getTagActions(boolean hasSrcFile, BlackboardArtifact artifact, int selectedFileCount, int selectedArtifactCount) {
742  List<Action> actionsList = new ArrayList<>();
743 
744  // don't show AddContentTagAction for data artifacts.
745  if (hasSrcFile && !(artifact instanceof DataArtifact)) {
746  actionsList.add(AddContentTagAction.getInstance());
747  }
748 
749  actionsList.add(AddBlackboardArtifactTagAction.getInstance());
750 
751  // don't show DeleteFileContentTagAction for data artifacts.
752  if (hasSrcFile && (!(artifact instanceof DataArtifact)) && (selectedFileCount == 1)) {
753  actionsList.add(DeleteFileContentTagAction.getInstance());
754  }
755 
756  if (selectedArtifactCount == 1) {
758  }
759 
760  return actionsList;
761  }
762 
771  @Messages({
772  "BlackboardArtifactNode_getSrcContentViewerActions_viewInNewWin=View Item in New Window",
773  "BlackboardArtifactNode_getSrcContentViewerActions_openInExtViewer=Open in External Viewer Ctrl+E"
774  })
775  private List<Action> getSrcContentViewerActions(Node srcFileNode, int selectedFileCount) {
776  List<Action> actionsList = new ArrayList<>();
777  if (srcFileNode != null) {
778  actionsList.add(new NewWindowViewAction(Bundle.BlackboardArtifactNode_getSrcContentViewerActions_viewInNewWin(), srcFileNode));
779  if (selectedFileCount == 1) {
780  actionsList.add(new ExternalViewerAction(Bundle.BlackboardArtifactNode_getSrcContentViewerActions_openInExtViewer(), srcFileNode));
781  } else {
782  actionsList.add(ExternalViewerShortcutAction.getInstance());
783  }
784  }
785  return actionsList;
786  }
787 
796  @NbBundle.Messages({
797  "# {0} - contentType",
798  "BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName=View Source {0} in Timeline... "
799  })
800  private Action getTimelineSrcContentAction(Content srcContent) {
801  if (srcContent instanceof AbstractFile) {
802  return new ViewFileInTimelineAction((AbstractFile) srcContent,
803  Bundle.BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName(
804  getContentTypeStr(srcContent)));
805  } else if (srcContent instanceof DataArtifact) {
806  try {
807  if (ViewArtifactInTimelineAction.hasSupportedTimeStamp((BlackboardArtifact) srcContent)) {
808  return new ViewArtifactInTimelineAction((BlackboardArtifact) srcContent,
809  Bundle.BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName(
810  getContentTypeStr(srcContent)));
811  }
812  } catch (TskCoreException ex) {
813  logger.log(Level.SEVERE, MessageFormat.format("Error getting source data artifact timestamp (artifact objID={0})", srcContent.getId()), ex); //NON-NLS
814  }
815  }
816 
817  return null;
818  }
819 
828  @Messages({
829  "BlackboardArtifactNode_getTimelineArtifactAction_displayName=View Selected Item in Timeline... "
830  })
831  private Action getTimelineArtifactAction(BlackboardArtifact art) {
832  try {
833  // don't show ViewArtifactInTimelineAction for AnalysisResults.
834  if (!(art instanceof AnalysisResult) && ViewArtifactInTimelineAction.hasSupportedTimeStamp(art)) {
835  return new ViewArtifactInTimelineAction(art, Bundle.BlackboardArtifactNode_getTimelineArtifactAction_displayName());
836  }
837  } catch (TskCoreException ex) {
838  logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact timestamp (artifact objID={0})", art.getId()), ex); //NON-NLS
839  }
840 
841  return null;
842  }
843 
850  public String getSourceName() {
851  return srcContent.getName();
852  }
853 
854  @NbBundle.Messages({
855  "BlackboardArtifactNode.createSheet.srcFile.name=Source Name",
856  "BlackboardArtifactNode.createSheet.srcFile.displayName=Source Name",
857  "BlackboardArtifactNode.createSheet.srcFile.origName=Original Name",
858  "BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name",
859  "BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
860  "BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
861  "BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details",
862  "BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details",
863  "BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash",
864  "BlackboardArtifactNode.createSheet.artifactMD5.name=MD5 Hash",
865  "BlackboardArtifactNode.createSheet.fileSize.name=Size",
866  "BlackboardArtifactNode.createSheet.fileSize.displayName=Size",
867  "BlackboardArtifactNode.createSheet.path.displayName=Path",
868  "BlackboardArtifactNode.createSheet.path.name=Path"
869  })
870  @Override
871  protected Sheet createSheet() {
872  /*
873  * Create an empty property sheet.
874  */
875  Sheet sheet = super.createSheet();
876  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
877  if (sheetSet == null) {
878  sheetSet = Sheet.createPropertiesSet();
879  sheet.put(sheetSet);
880  }
881 
882  /*
883  * Add the name of the source content of the artifact represented by
884  * this node to the sheet. The value of this property is the same as
885  * the display name of the node and this a "special" property that
886  * displays the node's icon as well as the display name.
887  */
888  sheetSet.put(new NodeProperty<>(
889  Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
890  Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
891  NO_DESCR,
892  getDisplayName()));
893 
894  GetSCOTask scoTask = null;
895  if (artifact instanceof AnalysisResult
896  && !(artifactType.getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()
897  || artifactType.getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID())) {
898  scoTask = updateSheetForAnalysisResult((AnalysisResult) artifact, sheetSet);
899  } else {
900  /*
901  * Add the name of the source content of the artifact represented by
902  * this node to the sheet. The value of this property is the same as
903  * the display name of the node and this a "special" property that
904  * displays the node's icon as well as the display name.
905  */
906  sheetSet.put(new NodeProperty<>(
907  Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
908  Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
909  NO_DESCR,
910  getDisplayName()));
911  }
912 
914  /*
915  * If machine translation is configured, add the original name of
916  * the of the source content of the artifact represented by this
917  * node to the sheet.
918  */
919  sheetSet.put(new NodeProperty<>(
920  Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
921  Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
922  NO_DESCR,
923  translatedSourceName != null ? srcContent.getName() : ""));
924  if (translatedSourceName == null) {
925  /*
926  * NOTE: The task makes its own weak reference to the listener.
927  */
928  new FileNameTransTask(srcContent.getName(), this, listener).submit();
929  }
930  }
931 
932  if (scoTask == null) {
933  scoTask = addSCOColumns(sheetSet);
934  }
935 
936  /*
937  * If the artifact represented by this node is an interesting artifact
938  * hit, add the type and description of the interesting artifact to the
939  * sheet.
940  */
941  if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
942  try {
943  BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
944  if (attribute != null) {
945  BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
946  sheetSet.put(new NodeProperty<>(
947  NbBundle.getMessage(BlackboardArtifactNode.class,
948  "BlackboardArtifactNode.createSheet.artifactType.name"),
949  NbBundle.getMessage(BlackboardArtifactNode.class,
950  "BlackboardArtifactNode.createSheet.artifactType.displayName"),
951  NO_DESCR,
952  associatedArtifact.getDisplayName()));
953  sheetSet.put(new NodeProperty<>(
954  NbBundle.getMessage(BlackboardArtifactNode.class,
955  "BlackboardArtifactNode.createSheet.artifactDetails.name"),
956  NbBundle.getMessage(BlackboardArtifactNode.class,
957  "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
958  NO_DESCR,
959  associatedArtifact.getShortDescription()));
960  }
961  } catch (TskCoreException | NoCurrentCaseException ex) {
962  logger.log(Level.SEVERE, MessageFormat.format("Error getting associated artifact of TSK_INTERESTING_ARTIFACT_HIT artifact (objID={0}))", artifact.getId()), ex); //NON-NLS
963  }
964  }
965 
966  /*
967  * Add the attributes of the artifact represented by this node to the
968  * sheet.
969  */
970  Map<String, Object> map = new LinkedHashMap<>();
971  fillPropertyMap(map, artifact);
972  for (Map.Entry<String, Object> entry : map.entrySet()) {
973  sheetSet.put(new NodeProperty<>(entry.getKey(),
974  entry.getKey(),
975  NO_DESCR,
976  entry.getValue()));
977  }
978 
979  /*
980  * Add any "custom properties" for the node to the sheet.
981  */
982  if (customProperties != null) {
983  for (NodeProperty<? extends Object> np : customProperties) {
984  sheetSet.put(np);
985  }
986  }
987 
988  /*
989  * If the artifact represented by this node is a file extension mismatch
990  * artifact, add the extension and type of the artifact's source file to
991  * the sheet.
992  */
993  final int artifactTypeId = artifact.getArtifactTypeID();
994  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
995  String ext = ""; //NON-NLS
996  String actualMimeType = ""; //NON-NLS
997  if (srcContent instanceof AbstractFile) {
998  AbstractFile file = (AbstractFile) srcContent;
999  ext = file.getNameExtension();
1000  actualMimeType = file.getMIMEType();
1001  if (actualMimeType == null) {
1002  actualMimeType = ""; //NON-NLS
1003 
1004  }
1005  }
1006  sheetSet.put(new NodeProperty<>(
1007  NbBundle.getMessage(BlackboardArtifactNode.class,
1008  "BlackboardArtifactNode.createSheet.ext.name"),
1009  NbBundle.getMessage(BlackboardArtifactNode.class,
1010  "BlackboardArtifactNode.createSheet.ext.displayName"),
1011  NO_DESCR,
1012  ext));
1013  sheetSet.put(new NodeProperty<>(
1014  NbBundle.getMessage(BlackboardArtifactNode.class,
1015  "BlackboardArtifactNode.createSheet.mimeType.name"),
1016  NbBundle.getMessage(BlackboardArtifactNode.class,
1017  "BlackboardArtifactNode.createSheet.mimeType.displayName"),
1018  NO_DESCR,
1019  actualMimeType));
1020  }
1021 
1022  /*
1023  * If the type of the artifact represented by this node dictates the
1024  * addition of the source content's unique path, add it to the sheet.
1025  */
1026  if (artifactType != null && artifactType.getCategory() == Category.ANALYSIS_RESULT) {
1027  String sourcePath = ""; //NON-NLS
1028  try {
1029  sourcePath = srcContent.getUniquePath();
1030  } catch (TskCoreException ex) {
1031  logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1032 
1033  }
1034 
1035  if (sourcePath.isEmpty() == false) {
1036  sheetSet.put(new NodeProperty<>(
1037  NbBundle.getMessage(BlackboardArtifactNode.class,
1038  "BlackboardArtifactNode.createSheet.filePath.name"),
1039  NbBundle.getMessage(BlackboardArtifactNode.class,
1040  "BlackboardArtifactNode.createSheet.filePath.displayName"),
1041  NO_DESCR,
1042  sourcePath));
1043  }
1044 
1045  /*
1046  * If the type of the artifact represented by this node dictates the
1047  * addition of the source content's file metadata, add it to the
1048  * sheet. Otherwise, add the data source to the sheet.
1049  */
1050  if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
1051  AbstractFile file = srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
1052  sheetSet.put(new NodeProperty<>(
1053  NbBundle.getMessage(BlackboardArtifactNode.class,
1054  "ContentTagNode.createSheet.fileModifiedTime.name"),
1055  NbBundle.getMessage(BlackboardArtifactNode.class,
1056  "ContentTagNode.createSheet.fileModifiedTime.displayName"),
1057  "",
1058  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getMtime())));
1059  sheetSet.put(new NodeProperty<>(
1060  NbBundle.getMessage(BlackboardArtifactNode.class,
1061  "ContentTagNode.createSheet.fileChangedTime.name"),
1062  NbBundle.getMessage(BlackboardArtifactNode.class,
1063  "ContentTagNode.createSheet.fileChangedTime.displayName"),
1064  "",
1065  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCtime())));
1066  sheetSet.put(new NodeProperty<>(
1067  NbBundle.getMessage(BlackboardArtifactNode.class,
1068  "ContentTagNode.createSheet.fileAccessedTime.name"),
1069  NbBundle.getMessage(BlackboardArtifactNode.class,
1070  "ContentTagNode.createSheet.fileAccessedTime.displayName"),
1071  "",
1072  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getAtime())));
1073  sheetSet.put(new NodeProperty<>(
1074  NbBundle.getMessage(BlackboardArtifactNode.class,
1075  "ContentTagNode.createSheet.fileCreatedTime.name"),
1076  NbBundle.getMessage(BlackboardArtifactNode.class,
1077  "ContentTagNode.createSheet.fileCreatedTime.displayName"),
1078  "",
1079  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCrtime())));
1080  sheetSet.put(new NodeProperty<>(
1081  NbBundle.getMessage(BlackboardArtifactNode.class,
1082  "ContentTagNode.createSheet.fileSize.name"),
1083  NbBundle.getMessage(BlackboardArtifactNode.class,
1084  "ContentTagNode.createSheet.fileSize.displayName"),
1085  "",
1086  file == null ? "" : file.getSize()));
1087  sheetSet.put(new NodeProperty<>(
1088  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
1089  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
1090  "",
1091  file == null ? "" : StringUtils.defaultString(file.getMd5Hash())));
1092  }
1093  } else {
1094  String dataSourceStr = "";
1095  try {
1096  Content dataSource = srcContent.getDataSource();
1097  if (dataSource != null) {
1098  dataSourceStr = dataSource.getName();
1099  } else {
1100  dataSourceStr = getRootAncestorName();
1101  }
1102  } catch (TskCoreException ex) {
1103  logger.log(Level.SEVERE, MessageFormat.format("Error getting source data source name (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1104 
1105  }
1106 
1107  if (dataSourceStr.isEmpty() == false) {
1108  sheetSet.put(new NodeProperty<>(
1109  NbBundle.getMessage(BlackboardArtifactNode.class,
1110  "BlackboardArtifactNode.createSheet.dataSrc.name"),
1111  NbBundle.getMessage(BlackboardArtifactNode.class,
1112  "BlackboardArtifactNode.createSheet.dataSrc.displayName"),
1113  NO_DESCR,
1114  dataSourceStr));
1115  }
1116  }
1117 
1118  /*
1119  * If the artifact represented by this node is an EXIF artifact, add the
1120  * source file size and path to the sheet.
1121  */
1122  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
1123  long size = 0;
1124  String path = ""; //NON-NLS
1125  if (srcContent instanceof AbstractFile) {
1126  AbstractFile af = (AbstractFile) srcContent;
1127  size = af.getSize();
1128  try {
1129  path = af.getUniquePath();
1130  } catch (TskCoreException ex) {
1131  path = af.getParentPath();
1132 
1133  }
1134  }
1135  sheetSet.put(new NodeProperty<>(
1136  NbBundle.getMessage(BlackboardArtifactNode.class,
1137  "BlackboardArtifactNode.createSheet.fileSize.name"),
1138  NbBundle.getMessage(BlackboardArtifactNode.class,
1139  "BlackboardArtifactNode.createSheet.fileSize.displayName"),
1140  NO_DESCR,
1141  size));
1142  sheetSet
1143  .put(new NodeProperty<>(
1144  NbBundle.getMessage(BlackboardArtifactNode.class,
1145  "BlackboardArtifactNode.createSheet.path.name"),
1146  NbBundle.getMessage(BlackboardArtifactNode.class,
1147  "BlackboardArtifactNode.createSheet.path.displayName"),
1148  NO_DESCR,
1149  path));
1150  }
1151 
1152  backgroundTasksPool.submit(scoTask);
1153 
1154  return sheet;
1155  }
1156 
1163  @Override
1164  protected final List<Tag> getAllTagsFromDatabase() {
1165  List<Tag> tags = new ArrayList<>();
1166  try {
1169  } catch (TskCoreException | NoCurrentCaseException ex) {
1170  logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and its source content (artifact objID={0})", artifact.getId()), ex);
1171  }
1172  return tags;
1173  }
1174 
1191  @Override
1192  protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, List<CorrelationAttributeInstance> attributes) {
1193 
1194  /*
1195  * Has a tag with a comment been applied to the artifact or its source
1196  * content?
1197  */
1199  for (Tag tag : tags) {
1200  if (!StringUtils.isBlank(tag.getComment())) {
1201  status = HasCommentStatus.TAG_COMMENT;
1202  break;
1203  }
1204  }
1205 
1206  /*
1207  * Does the given correlation attribute instance have a comment in the
1208  * central repository?
1209  */
1210  if (attributes != null && !attributes.isEmpty()) {
1211  for (CorrelationAttributeInstance attribute : attributes) {
1212  if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
1215  } else {
1217  }
1218  break;
1219  }
1220  }
1221  }
1222 
1223  return status;
1224  }
1225 
1226  @Override
1227  protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute, String defaultDescription) {
1228  Long count = -1L;
1229  String description = defaultDescription;
1230  try {
1231  if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
1233  description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, attribute.getCorrelationType().getDisplayName());
1234  } else if (attribute != null) {
1235  description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description();
1236  }
1237  } catch (CentralRepoException ex) {
1238  logger.log(Level.SEVERE, MessageFormat.format("Error querying central repository for other occurences count (artifact objID={0}, corrAttrType={1}, corrAttrValue={2})", artifact.getId(), attribute.getCorrelationType(), attribute.getCorrelationValue()), ex);
1240  logger.log(Level.SEVERE, MessageFormat.format("Error normalizing correlation attribute for central repository query (artifact objID={0}, corrAttrType={2}, corrAttrValue={3})", artifact.getId(), attribute.getCorrelationType(), attribute.getCorrelationValue()), ex);
1241  }
1242  return Pair.of(count, description);
1243  }
1244 
1248  private void updateSheet() {
1249  this.setSheet(createSheet());
1250  }
1251 
1258  private String getRootAncestorName() {
1259  String parentName = srcContent.getName();
1260  Content parent = srcContent;
1261  try {
1262  while ((parent = parent.getParent()) != null) {
1263  parentName = parent.getName();
1264  }
1265  } catch (TskCoreException ex) {
1266  logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1267  return "";
1268  }
1269  return parentName;
1270  }
1271 
1278  public void addNodeProperty(NodeProperty<?> property) {
1279  if (customProperties == null) {
1280  customProperties = new ArrayList<>();
1281  }
1282  customProperties.add(property);
1283  }
1284 
1293  @SuppressWarnings("deprecation")
1294  private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifact) {
1295  try {
1296  for (BlackboardAttribute attribute : artifact.getAttributes()) {
1297  final int attributeTypeID = attribute.getAttributeType().getTypeID();
1298  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
1299  || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
1300  || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
1301  || attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
1302  || attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
1303  || attribute.getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
1304  /*
1305  * Do nothing.
1306  */
1307  } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
1308  addEmailMsgProperty(map, attribute);
1309  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
1310  map.put(attribute.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(attribute.getValueLong()));
1311  } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
1312  && attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
1313  /*
1314  * The truncation of text attributes appears to have been
1315  * motivated by the statement that "RegRipper output would
1316  * often cause the UI to get a black line accross it and
1317  * hang if you hovered over large output or selected it.
1318  * This reduces the amount of data in the table. Could
1319  * consider doing this for all fields in the UI."
1320  */
1321  String value = attribute.getDisplayString();
1322  if (value.length() > 512) {
1323  value = value.substring(0, 512);
1324  }
1325  map.put(attribute.getAttributeType().getDisplayName(), value);
1326  } else {
1327  switch (attribute.getAttributeType().getValueType()) {
1328  case INTEGER:
1329  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt());
1330  break;
1331  case DOUBLE:
1332  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble());
1333  break;
1334  case LONG:
1335  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong());
1336  break;
1337  default:
1338  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
1339 
1340  }
1341 
1342  }
1343  }
1344  } catch (TskCoreException ex) {
1345  logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact attributes (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1346  }
1347  }
1348 
1358  private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute) {
1359  final int attributeTypeID = attribute.getAttributeType().getTypeID();
1360  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
1361  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
1362  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
1363  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
1364  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
1365  || attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()) {
1366  /*
1367  * Do nothing.
1368  */
1369  } else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
1370  String value = attribute.getDisplayString();
1371  if (value.length() > 160) {
1372  value = value.substring(0, 160) + "...";
1373  }
1374  map.put(attribute.getAttributeType().getDisplayName(), value);
1375  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
1376  map.put(attribute.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(attribute.getValueLong()));
1377  } else {
1378  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
1379  }
1380  }
1381 
1382  @Override
1383  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1384  return visitor.visit(this);
1385  }
1386 
1387  @Override
1388  public boolean isLeafTypeNode() {
1389  return true;
1390  }
1391 
1392  @Override
1393  public String getItemType() {
1394  return getClass().getName();
1395  }
1396 
1397  @Override
1398  public <T> T accept(ContentNodeVisitor<T> visitor) {
1399  return visitor.visit(this);
1400  }
1401 
1402  @Messages({
1403  "BlackboardArtifactNode_analysisSheet_sourceType_name=Source Type",
1404  "BlackboardArtifactNode_analysisSheet_soureName_name=Source Name",
1405  "BlackboardArtifactNode_analysisSheet_score_name=Score",
1406  "BlackboardArtifactNode_analysisSheet_conclusion_name=Conclusion",
1407  "BlackboardArtifactNode_analysisSheet_configuration_name=Configuration",
1408  "BlackboardArtifactNode_analysisSheet_justifaction_name=Justification"
1409  })
1410 
1417  private GetSCOTask updateSheetForAnalysisResult(AnalysisResult result, Sheet.Set sheetSet) {
1418  sheetSet.put(new NodeProperty<>(
1419  Bundle.BlackboardArtifactNode_analysisSheet_soureName_name(),
1420  Bundle.BlackboardArtifactNode_analysisSheet_soureName_name(),
1421  NO_DESCR,
1422  getDisplayName()));
1423 
1424  GetSCOTask task = addSCOColumns(sheetSet);
1425 
1426  sheetSet.put(new NodeProperty<>(
1427  Bundle.BlackboardArtifactNode_analysisSheet_sourceType_name(),
1428  Bundle.BlackboardArtifactNode_analysisSheet_sourceType_name(),
1429  NO_DESCR,
1431 
1432  sheetSet.put(new NodeProperty<>(
1433  Bundle.BlackboardArtifactNode_analysisSheet_score_name(),
1434  Bundle.BlackboardArtifactNode_analysisSheet_score_name(),
1435  NO_DESCR,
1436  result.getScore().getSignificance().getDisplayName()));
1437 
1438  sheetSet.put(new NodeProperty<>(
1439  Bundle.BlackboardArtifactNode_analysisSheet_conclusion_name(),
1440  Bundle.BlackboardArtifactNode_analysisSheet_conclusion_name(),
1441  NO_DESCR,
1442  result.getConclusion()));
1443 
1444  sheetSet.put(new NodeProperty<>(
1445  Bundle.BlackboardArtifactNode_analysisSheet_configuration_name(),
1446  Bundle.BlackboardArtifactNode_analysisSheet_configuration_name(),
1447  NO_DESCR,
1448  result.getConfiguration()));
1449 
1450  sheetSet.put(new NodeProperty<>(
1451  Bundle.BlackboardArtifactNode_analysisSheet_justifaction_name(),
1452  Bundle.BlackboardArtifactNode_analysisSheet_justifaction_name(),
1453  NO_DESCR,
1454  result.getJustification()));
1455 
1456  return task;
1457  }
1458 
1459  private GetSCOTask addSCOColumns(Sheet.Set sheetSet) {
1461  /*
1462  * Add S(core), C(omments), and O(ther occurences) columns to the
1463  * sheet and start a background task to compute the value of these
1464  * properties for the artifact represented by this node. The task
1465  * will fire a PropertyChangeEvent when the computation is completed
1466  * and this node's PropertyChangeListener will update the sheet.
1467  */
1468  sheetSet.put(new NodeProperty<>(
1469  Bundle.BlackboardArtifactNode_createSheet_score_name(),
1470  Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
1471  VALUE_LOADING,
1472  ""));
1473  sheetSet.put(new NodeProperty<>(
1474  Bundle.BlackboardArtifactNode_createSheet_comment_name(),
1475  Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
1476  VALUE_LOADING,
1477  ""));
1478  if (CentralRepository.isEnabled()) {
1479  sheetSet.put(new NodeProperty<>(
1480  Bundle.BlackboardArtifactNode_createSheet_count_name(),
1481  Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
1482  VALUE_LOADING,
1483  ""));
1484  }
1485  return new GetSCOTask(new WeakReference<>(this), weakListener);
1486  }
1487  return null;
1488  }
1489 
1500  private String getSourceObjType(Content source) {
1501  if (source instanceof BlackboardArtifact) {
1502  BlackboardArtifact srcArtifact = (BlackboardArtifact) source;
1503  try {
1504  return srcArtifact.getType().getDisplayName();
1505  } catch (TskCoreException ex) {
1506  logger.log(Level.SEVERE, "Failed to get custom artifact type id=" + source.getId(), ex);
1507  }
1508  } else if (srcContent instanceof Volume) {
1509  return TskData.ObjectType.VOL.toString();
1510  } else if (srcContent instanceof AbstractFile) {
1511  return TskData.ObjectType.ABSTRACTFILE.toString();
1512  } else if (srcContent instanceof Image) {
1513  return TskData.ObjectType.IMG.toString();
1514  } else if (srcContent instanceof VolumeSystem) {
1515  return TskData.ObjectType.VS.toString();
1516  } else if (srcContent instanceof OsAccount) {
1517  return TskData.ObjectType.OS_ACCOUNT.toString();
1518  } else if (srcContent instanceof HostAddress) {
1519  return TskData.ObjectType.HOST_ADDRESS.toString();
1520  } else if (srcContent instanceof Pool) {
1521  return TskData.ObjectType.POOL.toString();
1522  }
1523  return "";
1524  }
1525 
1532  private void updateSCOColumns(final SCOData scoData) {
1533  // Make sure this happens in the EDT
1534  SwingUtilities.invokeLater(new Runnable() {
1535  @Override
1536  public void run() {
1537  if (scoData.getScoreAndDescription() != null) {
1539  Bundle.BlackboardArtifactNode_createSheet_score_name(),
1540  Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
1541  scoData.getScoreAndDescription().getRight(),
1542  scoData.getScoreAndDescription().getLeft()));
1543  }
1544  if (scoData.getComment() != null) {
1546  Bundle.BlackboardArtifactNode_createSheet_comment_name(),
1547  Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
1548  NO_DESCR, scoData.getComment()));
1549  }
1550  if (scoData.getCountAndDescription() != null) {
1552  Bundle.BlackboardArtifactNode_createSheet_count_name(),
1553  Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
1554  scoData.getCountAndDescription().getRight(),
1555  scoData.getCountAndDescription().getLeft()));
1556  }
1557  }
1558  });
1559  }
1560 
1565  if(srcContent instanceof BlackboardArtifact) {
1566  try {
1567  setDisplayName(((BlackboardArtifact)srcContent).getShortDescription());
1568  } catch (TskCoreException ex) {
1569  // Log the error, but set the display name to
1570  // Content.getName so there is something visible to the user.
1571  logger.log(Level.WARNING, "Failed to get short description for artifact id = " + srcContent.getId(), ex);
1572  setDisplayName(srcContent.getName());
1573  }
1574  } else if(srcContent instanceof OsAccount) {
1575  setDisplayName(((OsAccount)srcContent).getAddr().orElse(srcContent.getName()));
1576  } else {
1577  setDisplayName(srcContent.getName());
1578  }
1579 
1580  setShortDescription(getDisplayName());
1581  }
1582 
1595  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
1596  "BlackboardArtifactNode.createSheet.score.displayName=S",
1597  "BlackboardArtifactNode.createSheet.notableFile.description=Associated file recognized as notable.",
1598  "BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.",
1599  "BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
1600  "BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
1601  "BlackboardArtifactNode.createSheet.noScore.description=No score"})
1602  @Deprecated
1603  protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) {
1604  Pair<Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
1605  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
1606  }
1607 
1617  @NbBundle.Messages({
1618  "BlackboardArtifactNode.createSheet.tags.displayName=Tags"}
1619  )
1620  @Deprecated
1621  protected void addTagProperty(Sheet.Set sheetSet) throws MissingResourceException {
1622  List<Tag> tags = new ArrayList<>();
1623  try {
1626  } catch (TskCoreException | NoCurrentCaseException ex) {
1627  logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and source content (artifact objID={0})", artifact.getId()), ex);
1628  }
1629  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1630  }
1631 
1643  @Deprecated
1644  protected final void addTagProperty(Sheet.Set sheetSet, List<Tag> tags) {
1645  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1646  }
1647 
1660  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
1661  "BlackboardArtifactNode.createSheet.count.displayName=O",
1662  "BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found",
1663  "BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property",
1664  "# {0} - occurrenceCount",
1665  "# {1} - attributeType",
1666  "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}"})
1667  @Deprecated
1668  protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
1669  Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute, Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description());
1670  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
1671  }
1672 
1687  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
1688  "BlackboardArtifactNode.createSheet.comment.displayName=C"})
1689  @Deprecated
1690  protected final void addCommentProperty(Sheet.Set sheetSet, List<Tag> tags, CorrelationAttributeInstance attribute) {
1691  List<CorrelationAttributeInstance> attributes = new ArrayList<>();
1692  attributes.add(attribute);
1693  HasCommentStatus status = getCommentProperty(tags, attributes);
1694  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, status));
1695  }
1696 }
static BlackboardArtifact.Type getType(BlackboardArtifact artifact)
final void addTagProperty(Sheet.Set sheetSet, List< Tag > tags)
void fillPropertyMap(Map< String, Object > map, BlackboardArtifact artifact)
List< Action > getAssociatedFileActions(BlackboardArtifact artifact, BlackboardArtifact.Type artifactType)
List< Action > getTagActions(boolean hasSrcFile, BlackboardArtifact artifact, int selectedFileCount, int selectedArtifactCount)
static List< Action > getActions(File file, boolean isArtifactSource)
Action getViewSrcContentAction(BlackboardArtifact artifact, Content content)
static synchronized AddBlackboardArtifactTagAction getInstance()
static String getFormattedTime(long epochTime)
static synchronized DeleteFileBlackboardArtifactTagAction getInstance()
BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath)
List< ContentTag > getContentTagsByContent(Content content)
static synchronized ExportCSVAction getInstance()
static synchronized ExtractAction getInstance()
static synchronized DeleteFileContentTagAction getInstance()
final void addScorePropertyAndDescription(Sheet.Set sheetSet, List< Tag > tags)
GetSCOTask updateSheetForAnalysisResult(AnalysisResult result, Sheet.Set sheetSet)
BlackboardArtifactNode(BlackboardArtifact artifact, boolean useAssociatedFileInLookup)
static Content getPathIdFile(BlackboardArtifact artifact)
static Lookup createLookup(BlackboardArtifact artifact, boolean useAssociatedFile)
void addEmailMsgProperty(Map< String, Object > map, BlackboardAttribute attribute)
final void addCommentProperty(Sheet.Set sheetSet, List< Tag > tags, CorrelationAttributeInstance attribute)
Pair< Long, String > getCountPropertyAndDescription(CorrelationAttributeInstance attribute, String defaultDescription)
DataResultViewerTable.HasCommentStatus getCommentProperty(List< Tag > tags, List< CorrelationAttributeInstance > attributes)
final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute)
Long getCountCasesWithOtherInstances(CorrelationAttributeInstance instance)
List< Action > getSrcContentViewerActions(Node srcFileNode, int selectedFileCount)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
Content getSourceContentFromLookup(BlackboardArtifact artifact)
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:711
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:756
String getAssociatedTypeStr(BlackboardArtifact.Type artifactType)
static synchronized AddContentTagAction getInstance()
List< BlackboardArtifactTag > getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact)

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.