Autopsy  4.19.3
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;
74 import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
76 import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
79 import org.sleuthkit.datamodel.AbstractFile;
80 import org.sleuthkit.datamodel.BlackboardArtifact;
81 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
82 import org.sleuthkit.datamodel.BlackboardAttribute;
83 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
84 import org.sleuthkit.datamodel.Content;
85 import org.sleuthkit.datamodel.Tag;
86 import org.sleuthkit.datamodel.TskCoreException;
101 import org.sleuthkit.datamodel.AnalysisResult;
102 import org.sleuthkit.datamodel.BlackboardArtifact.Category;
103 import org.sleuthkit.datamodel.HostAddress;
104 import org.sleuthkit.datamodel.Pool;
105 import org.sleuthkit.datamodel.DataArtifact;
106 import org.sleuthkit.datamodel.DerivedFile;
107 import org.sleuthkit.datamodel.Directory;
108 import org.sleuthkit.datamodel.File;
109 import org.sleuthkit.datamodel.LayoutFile;
110 import org.sleuthkit.datamodel.LocalDirectory;
111 import org.sleuthkit.datamodel.LocalFile;
112 import org.sleuthkit.datamodel.OsAccount;
113 import org.sleuthkit.datamodel.Report;
114 import org.sleuthkit.datamodel.Score;
115 import org.sleuthkit.datamodel.SlackFile;
116 import org.sleuthkit.datamodel.VirtualDirectory;
117 import org.sleuthkit.datamodel.TskData;
118 import org.sleuthkit.datamodel.Volume;
119 import org.sleuthkit.datamodel.VolumeSystem;
120 import org.sleuthkit.datamodel.Image;
121 
126 public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifact> {
127 
128  private static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName());
129 
130  /*
131  * Cache of Content objects used to avoid repeated trips to the case
132  * database to retrieve Content objects that are the source of multiple
133  * artifacts.
134  */
135  private static final Cache<Long, Content> contentCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
136 
137  /*
138  * Case events that indicate an update to the node's property sheet may be
139  * required.
140  */
141  private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
148 
149  /*
150  * Artifact types for which the file metadata of the artifact's source file
151  * should be displayed in the node's property sheet.
152  *
153  * @SuppressWarnings("deprecation") - we need to support already existing
154  * interesting file and artifact hits.
155  */
156  @SuppressWarnings("deprecation")
157  private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
158  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
159  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ITEM.getTypeID()
160  };
161 
162  private final BlackboardArtifact artifact;
163  private final BlackboardArtifact.Type artifactType;
164  private Content srcContent;
165  private volatile String translatedSourceName;
166  private final String sourceObjTypeName;
167 
168  /*
169  * A method has been provided to allow the injection of properties into this
170  * node for display in the node's property sheet, independent of the
171  * artifact the node represents.
172  */
173  private List<NodeProperty<? extends Object>> customProperties;
174 
175  private final PropertyChangeListener listener = new PropertyChangeListener() {
176  @Override
177  public void propertyChange(PropertyChangeEvent evt) {
178  String eventType = evt.getPropertyName();
179  if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())) {
181  if (event.getAddedTag().getArtifact().equals(artifact)) {
182  updateSheet();
183  }
184  } else if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) {
186  if (event.getDeletedTagInfo().getArtifactID() == artifact.getArtifactID()) {
187  updateSheet();
188  }
189  } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
191  if (event.getAddedTag().getContent().equals(srcContent)) {
192  updateSheet();
193  }
194  } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
196  if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) {
197  updateSheet();
198  }
199  } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
201  if (event.getContentID() == srcContent.getId()) {
202  updateSheet();
203  }
204  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
205  if (evt.getNewValue() == null) {
206  /*
207  * The case has been closed.
208  */
210  contentCache.invalidateAll();
211  }
212  } else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) {
213  updateSCOColumns((SCOData) evt.getNewValue());
214  } else if (eventType.equals(FileNameTransTask.getPropertyName())) {
215  /*
216  * Replace the value of the Source File property with the
217  * translated name via setDisplayName (see note in createSheet),
218  * and put the untranslated name in the Original Name property
219  * and in the tooltip.
220  */
221  String originalName = evt.getOldValue().toString();
222  translatedSourceName = evt.getNewValue().toString();
223  setDisplayName(translatedSourceName);
224  setShortDescription(originalName);
226  Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
227  Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
228  NO_DESCR,
229  originalName));
230  }
231  }
232  };
233 
234  /*
235  * The node's event listener is wrapped in a weak reference that allows the
236  * node to be garbage collected when the NetBeans infrastructure discards
237  * it. If this is not done, it has been shown that strong references to the
238  * listener held by event publishers prevents garbage collection of this
239  * node.
240  */
241  private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
242 
253  public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
254  super(artifact, createLookup(artifact, false));
255  this.artifact = artifact;
256  this.artifactType = getType(artifact);
257 
258  srcContent = getSourceContentFromLookup(artifact);
259 
260  if (srcContent == null) {
261  throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
262  }
263 
264  try {
265  /*
266  * Calling this getter causes the unique path of the source content
267  * to be cached in the Content object. This is advantageous as long
268  * as this node is constructed in a background thread instead of a
269  * UI thread.
270  */
271  srcContent.getUniquePath();
272  } catch (TskCoreException ex) {
273  logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
274  }
275  sourceObjTypeName = getSourceObjType(srcContent);
277  setName(Long.toString(artifact.getArtifactID()));
278  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
280  }
281 
293  @Beta
294  public BlackboardArtifactNode(BlackboardArtifact artifact, boolean useAssociatedFileInLookup) {
295  super(artifact, createLookup(artifact, useAssociatedFileInLookup));
296  this.artifact = artifact;
297  this.artifactType = getType(artifact);
298 
299  try {
300  srcContent = artifact.getParent();
301  } catch (TskCoreException ex) {
302  logger.log(Level.WARNING, MessageFormat.format("Error getting the parent of the artifact for (artifact objID={0})", artifact.getId()), ex);
303  }
304 
305  if (srcContent != null) {
306  try {
307  /*
308  * Calling this getter causes the unique path of the source
309  * content to be cached in the Content object. This is
310  * advantageous as long as this node is constructed in a
311  * background thread instead of a UI thread.
312  */
313  srcContent.getUniquePath();
314  } catch (TskCoreException ex) {
315  logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
316  }
317  } else {
318  throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
319  }
320  sourceObjTypeName = getSourceObjType(srcContent);
321  setName(Long.toString(artifact.getArtifactID()));
323  String iconPath = IconsUtil.getIconFilePath(artifact.getArtifactTypeID());
324  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
326  }
327 
336  public BlackboardArtifactNode(BlackboardArtifact artifact) {
337  this(artifact, IconsUtil.getIconFilePath(artifact.getArtifactTypeID()));
338  }
339 
347  private static BlackboardArtifact.Type getType(BlackboardArtifact artifact) {
348  try {
349  return artifact.getType();
350  } catch (TskCoreException ex) {
351  logger.log(Level.WARNING, MessageFormat.format("Error getting the artifact type for artifact (artifact objID={0})", artifact.getId()), ex);
352  return null;
353  }
354  }
355 
367  private static Lookup createLookup(BlackboardArtifact artifact, boolean useAssociatedFile) {
368  /*
369  * Get the source content.
370  */
371  Content content = null;
372  try {
373  if (useAssociatedFile) {
374  content = getPathIdFile(artifact);
375  } else {
376  long srcObjectID = artifact.getObjectID();
377  content = contentCache.get(srcObjectID, () -> artifact.getSleuthkitCase().getContentById(srcObjectID));
378  }
379  } catch (ExecutionException ex) {
380  logger.log(Level.SEVERE, MessageFormat.format("Error getting source/associated content (artifact object ID={0})", artifact.getId()), ex); //NON-NLS
381  }
382 
383  /*
384  * Make an Autopsy Data Model wrapper for the artifact.
385  *
386  * NOTE: The creation of an Autopsy Data Model independent of the
387  * NetBeans nodes is a work in progress. At the time this comment is
388  * being written, this object is only being used to indicate the item
389  * represented by this BlackboardArtifactNode.
390  */
391  BlackboardArtifactItem<?> artifactItem;
392  if (artifact instanceof AnalysisResult) {
393  artifactItem = new AnalysisResultItem((AnalysisResult) artifact, content);
394  } else {
395  artifactItem = new DataArtifactItem((DataArtifact) artifact, content);
396  }
397 
398  /*
399  * Create the Lookup.
400  *
401  * NOTE: For now, we are putting both the Autopsy Data Model item and
402  * the Sleuth Kit Data Model item in the Lookup so that code that is not
403  * aware of the new Autopsy Data Model will still function.
404  */
405  if (content == null) {
406  return Lookups.fixed(artifact, artifactItem);
407  } else {
408  return Lookups.fixed(artifact, artifactItem, content);
409  }
410  }
411 
420  private Content getSourceContentFromLookup(BlackboardArtifact artifact) {
421  for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
422  /*
423  * NOTE: createLookup() saves the artifact and its source content
424  * (if one exists). However, createLookup() has to be static because
425  * it is being called by super(), therefore it can't store the
426  * source content in this.srcContent class variable. That's why we
427  * have to have the logic below, which reads the Lookup contents,
428  * and decides that the source content is the entry in Lookup that
429  * is NOT the input artifact.
430  */
431  if ((lookupContent != null) && (lookupContent.getId() != artifact.getId())) {
432  return lookupContent;
433  }
434  }
435  return null;
436  }
437 
451  private static Content getPathIdFile(BlackboardArtifact artifact) throws ExecutionException {
452  try {
453  BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
454  if (attribute != null) {
455  return contentCache.get(attribute.getValueLong(), () -> artifact.getSleuthkitCase().getContentById(attribute.getValueLong()));
456  }
457  } catch (TskCoreException ex) {
458  logger.log(Level.WARNING, MessageFormat.format("Error getting content for path id attrbiute for artifact: ", artifact.getId()), ex); //NON-NLS
459  }
460  return null;
461  }
462 
472  @Override
473  protected void finalize() throws Throwable {
474  super.finalize();
476  }
477 
481  private void unregisterListener() {
483  }
484 
490  public BlackboardArtifact getArtifact() {
491  return this.artifact;
492  }
493 
501  private List<Action> getNonNull(Action... items) {
502  return Stream.of(items)
503  .filter(i -> i != null)
504  .collect(Collectors.toList());
505  }
506 
507  @Override
508  public Action[] getActions(boolean context) {
509  // groupings of actions where each group will be separated by a divider
510  List<List<Action>> actionsLists = new ArrayList<>();
511 
512  // view artifact in timeline
513  actionsLists.add(getNonNull(
514  getTimelineArtifactAction(this.artifact)
515  ));
516 
517  // view associated file (TSK_PATH_ID attr) in directory and timeline
518  actionsLists.add(getAssociatedFileActions(this.artifact, this.artifactType));
519 
520  // view source content in directory and timeline
521  actionsLists.add(getNonNull(
522  getViewSrcContentAction(this.artifact, this.srcContent),
523  getTimelineSrcContentAction(this.srcContent)
524  ));
525 
526  // extract with password from encrypted file
527  actionsLists.add(getNonNull(
528  getExtractWithPasswordAction(this.srcContent)
529  ));
530 
531  // menu options for artifact with report parent
532  if (this.srcContent instanceof Report) {
533  actionsLists.add(DataModelActionsFactory.getActions(this.srcContent, false));
534  }
535 
536  Node parentFileNode = getParentFileNode(srcContent);
537  int selectedFileCount = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).size();
538  int selectedArtifactCount = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactItem.class).size();
539 
540  // view source content if source content is some sort of file
541  actionsLists.add(getSrcContentViewerActions(parentFileNode, selectedFileCount));
542 
543  // extract / export if source content is some sort of file
544  if (parentFileNode != null) {
545  actionsLists.add(Arrays.asList(ExtractAction.getInstance(), ExportCSVAction.getInstance()));
546  }
547 
548  // file and result tagging
549  actionsLists.add(getTagActions(parentFileNode != null, this.artifact, selectedFileCount, selectedArtifactCount));
550 
551  // menu extension items (i.e. add to central repository)
552  actionsLists.add(ContextMenuExtensionPoint.getActions());
553 
554  // netbeans default items (i.e. properties)
555  actionsLists.add(Arrays.asList(super.getActions(context)));
556 
557  return actionsLists.stream()
558  // remove any empty lists
559  .filter((lst) -> lst != null && !lst.isEmpty())
560  // add in null between each list group
561  .flatMap(lst -> Stream.concat(Stream.of((Action) null), lst.stream()))
562  // skip the first null
563  .skip(1)
564  .toArray(sz -> new Action[sz]);
565  }
566 
575  @Messages({
576  "BlackboardArtifactNode_getAssociatedTypeStr_webCache=Cached File",
577  "BlackboardArtifactNode_getAssociatedTypeStr_webDownload=Downloaded File",
578  "BlackboardArtifactNode_getAssociatedTypeStr_associated=Associated File",})
579  private String getAssociatedTypeStr(BlackboardArtifact.Type artifactType) {
580  if (BlackboardArtifact.Type.TSK_WEB_CACHE.equals(artifactType)) {
581  return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_webCache();
582  } else if (BlackboardArtifact.Type.TSK_WEB_DOWNLOAD.equals(artifactType)) {
583  return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_webDownload();
584  } else {
585  return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_associated();
586  }
587  }
588 
597  @Messages({
598  "BlackboardArtifactNode_getViewSrcContentAction_type_File=File",
599  "BlackboardArtifactNode_getViewSrcContentAction_type_DataArtifact=Data Artifact",
600  "BlackboardArtifactNode_getViewSrcContentAction_type_OSAccount=OS Account",
601  "BlackboardArtifactNode_getViewSrcContentAction_type_unknown=Item"
602  })
603  private String getContentTypeStr(Content content) {
604  if (content instanceof AbstractFile) {
605  return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_File();
606  } else if (content instanceof DataArtifact) {
607  return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_DataArtifact();
608  } else if (content instanceof OsAccount) {
609  return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_OSAccount();
610  } else {
611  return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_unknown();
612  }
613  }
614 
625  @Messages({
626  "# {0} - type",
627  "BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileAction=View {0} in Directory",
628  "# {0} - type",
629  "BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileInTimelineAction=View {0} in Timeline..."
630  })
631  private List<Action> getAssociatedFileActions(BlackboardArtifact artifact, BlackboardArtifact.Type artifactType) {
632  try {
633  AbstractFile associatedFile = findLinked(artifact);
634  if (associatedFile != null) {
635  return Arrays.asList(
636  new ViewContextAction(
637  Bundle.BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileAction(
639  associatedFile),
640  new ViewFileInTimelineAction(associatedFile,
641  Bundle.BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileInTimelineAction(
643  );
644  }
645 
646  } catch (TskCoreException ex) {
647  logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS
648  }
649  return Collections.emptyList();
650  }
651 
660  @Messages({
661  "# {0} - contentType",
662  "BlackboardArtifactNode_getSrcContentAction_actionDisplayName=View Source {0} in Directory"
663  })
664  private Action getViewSrcContentAction(BlackboardArtifact artifact, Content content) {
665  if (content instanceof DataArtifact) {
666  return new ViewArtifactAction(
667  (BlackboardArtifact) content,
668  Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
669  getContentTypeStr(content)));
670  } else if (content instanceof OsAccount) {
671  return new ViewOsAccountAction(
672  (OsAccount) content,
673  Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
674  getContentTypeStr(content)));
675  } else if (content instanceof AbstractFile || artifact instanceof DataArtifact) {
676  return new ViewContextAction(
677  Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
678  getContentTypeStr(content)),
679  content);
680  } else {
681  return null;
682  }
683  }
684 
693  private Node getParentFileNode(Content content) {
694  if (content instanceof File) {
695  return new FileNode((AbstractFile) content);
696  } else if (content instanceof Directory) {
697  return new DirectoryNode((Directory) content);
698  } else if (content instanceof VirtualDirectory) {
699  return new VirtualDirectoryNode((VirtualDirectory) content);
700  } else if (content instanceof LocalDirectory) {
701  return new LocalDirectoryNode((LocalDirectory) content);
702  } else if (content instanceof LayoutFile) {
703  return new LayoutFileNode((LayoutFile) content);
704  } else if (content instanceof LocalFile || content instanceof DerivedFile) {
705  return new LocalFileNode((AbstractFile) content);
706  } else if (content instanceof SlackFile) {
707  return new SlackFileNode((AbstractFile) content);
708  } else {
709  return null;
710  }
711  }
712 
720  private Action getExtractWithPasswordAction(Content srcContent) {
721  if ((srcContent instanceof AbstractFile)
723  .contains("." + ((AbstractFile) srcContent).getNameExtension().toLowerCase())) {
724  try {
725  if (srcContent.getArtifacts(BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED.getTypeID()).size() > 0) {
726  return new ExtractArchiveWithPasswordAction((AbstractFile) srcContent);
727  }
728  } catch (TskCoreException ex) {
729  logger.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex);
730  }
731  }
732 
733  return null;
734  }
735 
747  private List<Action> getTagActions(boolean hasSrcFile, BlackboardArtifact artifact, int selectedFileCount, int selectedArtifactCount) {
748  List<Action> actionsList = new ArrayList<>();
749 
750  // don't show AddContentTagAction for data artifacts.
751  if (hasSrcFile && !(artifact instanceof DataArtifact)) {
752  actionsList.add(AddContentTagAction.getInstance());
753  }
754 
755  actionsList.add(AddBlackboardArtifactTagAction.getInstance());
756 
757  // don't show DeleteFileContentTagAction for data artifacts.
758  if (hasSrcFile && (!(artifact instanceof DataArtifact)) && (selectedFileCount == 1)) {
759  actionsList.add(DeleteFileContentTagAction.getInstance());
760  }
761 
762  if (selectedArtifactCount == 1) {
764  }
765 
766  return actionsList;
767  }
768 
777  @Messages({
778  "BlackboardArtifactNode_getSrcContentViewerActions_viewInNewWin=View Item in New Window",
779  "BlackboardArtifactNode_getSrcContentViewerActions_openInExtViewer=Open in External Viewer Ctrl+E"
780  })
781  private List<Action> getSrcContentViewerActions(Node srcFileNode, int selectedFileCount) {
782  List<Action> actionsList = new ArrayList<>();
783  if (srcFileNode != null) {
784  actionsList.add(new NewWindowViewAction(Bundle.BlackboardArtifactNode_getSrcContentViewerActions_viewInNewWin(), srcFileNode));
785  if (selectedFileCount == 1) {
786  actionsList.add(new ExternalViewerAction(Bundle.BlackboardArtifactNode_getSrcContentViewerActions_openInExtViewer(), srcFileNode));
787  } else {
788  actionsList.add(ExternalViewerShortcutAction.getInstance());
789  }
790  }
791  return actionsList;
792  }
793 
802  @NbBundle.Messages({
803  "# {0} - contentType",
804  "BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName=View Source {0} in Timeline... "
805  })
806  private Action getTimelineSrcContentAction(Content srcContent) {
807  if (srcContent instanceof AbstractFile) {
808  return new ViewFileInTimelineAction((AbstractFile) srcContent,
809  Bundle.BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName(
810  getContentTypeStr(srcContent)));
811  } else if (srcContent instanceof DataArtifact) {
812  try {
813  if (ViewArtifactInTimelineAction.hasSupportedTimeStamp((BlackboardArtifact) srcContent)) {
814  return new ViewArtifactInTimelineAction((BlackboardArtifact) srcContent,
815  Bundle.BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName(
816  getContentTypeStr(srcContent)));
817  }
818  } catch (TskCoreException ex) {
819  logger.log(Level.SEVERE, MessageFormat.format("Error getting source data artifact timestamp (artifact objID={0})", srcContent.getId()), ex); //NON-NLS
820  }
821  }
822 
823  return null;
824  }
825 
834  @Messages({
835  "BlackboardArtifactNode_getTimelineArtifactAction_displayName=View Selected Item in Timeline... "
836  })
837  private Action getTimelineArtifactAction(BlackboardArtifact art) {
838  try {
839  // don't show ViewArtifactInTimelineAction for AnalysisResults.
840  if (!(art instanceof AnalysisResult) && ViewArtifactInTimelineAction.hasSupportedTimeStamp(art)) {
841  return new ViewArtifactInTimelineAction(art, Bundle.BlackboardArtifactNode_getTimelineArtifactAction_displayName());
842  }
843  } catch (TskCoreException ex) {
844  logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact timestamp (artifact objID={0})", art.getId()), ex); //NON-NLS
845  }
846 
847  return null;
848  }
849 
856  public String getSourceName() {
857  return srcContent.getName();
858  }
859 
860  @NbBundle.Messages({
861  "BlackboardArtifactNode.createSheet.srcFile.name=Source Name",
862  "BlackboardArtifactNode.createSheet.srcFile.displayName=Source Name",
863  "BlackboardArtifactNode.createSheet.srcFile.origName=Original Name",
864  "BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name",
865  "BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
866  "BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
867  "BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details",
868  "BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details",
869  "BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash",
870  "BlackboardArtifactNode.createSheet.artifactMD5.name=MD5 Hash",
871  "BlackboardArtifactNode.createSheet.fileSize.name=Size",
872  "BlackboardArtifactNode.createSheet.fileSize.displayName=Size",
873  "BlackboardArtifactNode.createSheet.path.displayName=Path",
874  "BlackboardArtifactNode.createSheet.path.name=Path"
875  })
876  /*
877  * @SuppressWarnings("deprecation") - we need to support already existing
878  * interesting file and artifact hits.
879  */
880  @SuppressWarnings("deprecation")
881  @Override
882  protected Sheet createSheet() {
883  /*
884  * Create an empty property sheet.
885  */
886  Sheet sheet = super.createSheet();
887  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
888  if (sheetSet == null) {
889  sheetSet = Sheet.createPropertiesSet();
890  sheet.put(sheetSet);
891  }
892 
893  /*
894  * Add the name of the source content of the artifact represented by
895  * this node to the sheet. The value of this property is the same as the
896  * display name of the node and this a "special" property that displays
897  * the node's icon as well as the display name.
898  */
899  sheetSet.put(new NodeProperty<>(
900  Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
901  Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
902  NO_DESCR,
903  getDisplayName()));
904 
905  GetSCOTask scoTask;
906  if (artifact instanceof AnalysisResult
907  && !(artifactType.getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()
908  || artifactType.getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID())) {
909  scoTask = updateSheetForAnalysisResult((AnalysisResult) artifact, sheetSet);
910  } else {
911  scoTask = addSCOColumns(sheetSet);
912  }
913 
915  /*
916  * If machine translation is configured, add the original name of
917  * the of the source content of the artifact represented by this
918  * node to the sheet.
919  */
920  sheetSet.put(new NodeProperty<>(
921  Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
922  Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
923  NO_DESCR,
924  translatedSourceName != null ? srcContent.getName() : ""));
925  if (translatedSourceName == null) {
926  /*
927  * NOTE: The task makes its own weak reference to the listener.
928  */
929  new FileNameTransTask(srcContent.getName(), this, listener).submit();
930  }
931  }
932 
933  /*
934  * If the artifact represented by this node is an interesting artifact
935  * hit, add the type and description of the interesting artifact to the
936  * sheet.
937  */
938  if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() || artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ITEM.getTypeID()) {
939  try {
940  BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
941  if (attribute != null) {
942  BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
943  sheetSet.put(new NodeProperty<>(
944  NbBundle.getMessage(BlackboardArtifactNode.class,
945  "BlackboardArtifactNode.createSheet.artifactType.name"),
946  NbBundle.getMessage(BlackboardArtifactNode.class,
947  "BlackboardArtifactNode.createSheet.artifactType.displayName"),
948  NO_DESCR,
949  associatedArtifact.getDisplayName()));
950  sheetSet.put(new NodeProperty<>(
951  NbBundle.getMessage(BlackboardArtifactNode.class,
952  "BlackboardArtifactNode.createSheet.artifactDetails.name"),
953  NbBundle.getMessage(BlackboardArtifactNode.class,
954  "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
955  NO_DESCR,
956  associatedArtifact.getShortDescription()));
957  }
958  } catch (TskCoreException | NoCurrentCaseException ex) {
959  logger.log(Level.SEVERE, MessageFormat.format("Error getting associated artifact with type " + artifact.getArtifactTypeName() + " artifact (objID={0}))", artifact.getId()), ex); //NON-NLS
960  }
961  }
962 
963  /*
964  * Add the attributes of the artifact represented by this node to the
965  * sheet.
966  */
967  Map<String, Object> map = new LinkedHashMap<>();
968  fillPropertyMap(map, artifact);
969  for (Map.Entry<String, Object> entry : map.entrySet()) {
970  sheetSet.put(new NodeProperty<>(entry.getKey(),
971  entry.getKey(),
972  NO_DESCR,
973  entry.getValue()));
974  }
975 
976  /*
977  * Add any "custom properties" for the node to the sheet.
978  */
979  if (customProperties != null) {
980  for (NodeProperty<? extends Object> np : customProperties) {
981  sheetSet.put(np);
982  }
983  }
984 
985  /*
986  * If the artifact represented by this node is a file extension mismatch
987  * artifact, add the extension and type of the artifact's source file to
988  * the sheet.
989  */
990  final int artifactTypeId = artifact.getArtifactTypeID();
991  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
992  String ext = ""; //NON-NLS
993  String actualMimeType = ""; //NON-NLS
994  if (srcContent instanceof AbstractFile) {
995  AbstractFile file = (AbstractFile) srcContent;
996  ext = file.getNameExtension();
997  actualMimeType = file.getMIMEType();
998  if (actualMimeType == null) {
999  actualMimeType = ""; //NON-NLS
1000 
1001  }
1002  }
1003  sheetSet.put(new NodeProperty<>(
1004  NbBundle.getMessage(BlackboardArtifactNode.class,
1005  "BlackboardArtifactNode.createSheet.ext.name"),
1006  NbBundle.getMessage(BlackboardArtifactNode.class,
1007  "BlackboardArtifactNode.createSheet.ext.displayName"),
1008  NO_DESCR,
1009  ext));
1010  sheetSet.put(new NodeProperty<>(
1011  NbBundle.getMessage(BlackboardArtifactNode.class,
1012  "BlackboardArtifactNode.createSheet.mimeType.name"),
1013  NbBundle.getMessage(BlackboardArtifactNode.class,
1014  "BlackboardArtifactNode.createSheet.mimeType.displayName"),
1015  NO_DESCR,
1016  actualMimeType));
1017  }
1018 
1019  /*
1020  * If the type of the artifact represented by this node dictates the
1021  * addition of the source content's unique path, add it to the sheet.
1022  */
1023  if (artifactType != null && artifactType.getCategory() == Category.ANALYSIS_RESULT) {
1024  String sourcePath = ""; //NON-NLS
1025  try {
1026  sourcePath = srcContent.getUniquePath();
1027  } catch (TskCoreException ex) {
1028  logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1029 
1030  }
1031 
1032  if (sourcePath.isEmpty() == false) {
1033  sheetSet.put(new NodeProperty<>(
1034  NbBundle.getMessage(BlackboardArtifactNode.class,
1035  "BlackboardArtifactNode.createSheet.filePath.name"),
1036  NbBundle.getMessage(BlackboardArtifactNode.class,
1037  "BlackboardArtifactNode.createSheet.filePath.displayName"),
1038  NO_DESCR,
1039  sourcePath));
1040  }
1041 
1042  /*
1043  * If the type of the artifact represented by this node dictates the
1044  * addition of the source content's file metadata, add it to the
1045  * sheet. Otherwise, add the data source to the sheet.
1046  */
1047  if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
1048  AbstractFile file = srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
1049  sheetSet.put(new NodeProperty<>(
1050  NbBundle.getMessage(BlackboardArtifactNode.class,
1051  "ContentTagNode.createSheet.fileModifiedTime.name"),
1052  NbBundle.getMessage(BlackboardArtifactNode.class,
1053  "ContentTagNode.createSheet.fileModifiedTime.displayName"),
1054  "",
1055  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getMtime())));
1056  sheetSet.put(new NodeProperty<>(
1057  NbBundle.getMessage(BlackboardArtifactNode.class,
1058  "ContentTagNode.createSheet.fileChangedTime.name"),
1059  NbBundle.getMessage(BlackboardArtifactNode.class,
1060  "ContentTagNode.createSheet.fileChangedTime.displayName"),
1061  "",
1062  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCtime())));
1063  sheetSet.put(new NodeProperty<>(
1064  NbBundle.getMessage(BlackboardArtifactNode.class,
1065  "ContentTagNode.createSheet.fileAccessedTime.name"),
1066  NbBundle.getMessage(BlackboardArtifactNode.class,
1067  "ContentTagNode.createSheet.fileAccessedTime.displayName"),
1068  "",
1069  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getAtime())));
1070  sheetSet.put(new NodeProperty<>(
1071  NbBundle.getMessage(BlackboardArtifactNode.class,
1072  "ContentTagNode.createSheet.fileCreatedTime.name"),
1073  NbBundle.getMessage(BlackboardArtifactNode.class,
1074  "ContentTagNode.createSheet.fileCreatedTime.displayName"),
1075  "",
1076  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCrtime())));
1077  sheetSet.put(new NodeProperty<>(
1078  NbBundle.getMessage(BlackboardArtifactNode.class,
1079  "ContentTagNode.createSheet.fileSize.name"),
1080  NbBundle.getMessage(BlackboardArtifactNode.class,
1081  "ContentTagNode.createSheet.fileSize.displayName"),
1082  "",
1083  file == null ? "" : file.getSize()));
1084  sheetSet.put(new NodeProperty<>(
1085  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
1086  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
1087  "",
1088  file == null ? "" : StringUtils.defaultString(file.getMd5Hash())));
1089  }
1090  } else {
1091  String dataSourceStr = "";
1092  try {
1093  Content dataSource = srcContent.getDataSource();
1094  if (dataSource != null) {
1095  dataSourceStr = dataSource.getName();
1096  } else {
1097  dataSourceStr = getRootAncestorName();
1098  }
1099  } catch (TskCoreException ex) {
1100  logger.log(Level.SEVERE, MessageFormat.format("Error getting source data source name (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1101 
1102  }
1103 
1104  if (dataSourceStr.isEmpty() == false) {
1105  sheetSet.put(new NodeProperty<>(
1106  NbBundle.getMessage(BlackboardArtifactNode.class,
1107  "BlackboardArtifactNode.createSheet.dataSrc.name"),
1108  NbBundle.getMessage(BlackboardArtifactNode.class,
1109  "BlackboardArtifactNode.createSheet.dataSrc.displayName"),
1110  NO_DESCR,
1111  dataSourceStr));
1112  }
1113  }
1114 
1115  /*
1116  * If the artifact represented by this node is an EXIF artifact, add the
1117  * source file size and path to the sheet.
1118  */
1119  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
1120  long size = 0;
1121  String path = ""; //NON-NLS
1122  if (srcContent instanceof AbstractFile) {
1123  AbstractFile af = (AbstractFile) srcContent;
1124  size = af.getSize();
1125  try {
1126  path = af.getUniquePath();
1127  } catch (TskCoreException ex) {
1128  path = af.getParentPath();
1129 
1130  }
1131  }
1132  sheetSet.put(new NodeProperty<>(
1133  NbBundle.getMessage(BlackboardArtifactNode.class,
1134  "BlackboardArtifactNode.createSheet.fileSize.name"),
1135  NbBundle.getMessage(BlackboardArtifactNode.class,
1136  "BlackboardArtifactNode.createSheet.fileSize.displayName"),
1137  NO_DESCR,
1138  size));
1139  sheetSet
1140  .put(new NodeProperty<>(
1141  NbBundle.getMessage(BlackboardArtifactNode.class,
1142  "BlackboardArtifactNode.createSheet.path.name"),
1143  NbBundle.getMessage(BlackboardArtifactNode.class,
1144  "BlackboardArtifactNode.createSheet.path.displayName"),
1145  NO_DESCR,
1146  path));
1147  }
1148 
1149  if (scoTask != null) {
1150  backgroundTasksPool.submit(scoTask);
1151  }
1152 
1153  return sheet;
1154  }
1155 
1162  @Override
1163  protected final List<Tag> getAllTagsFromDatabase() {
1164  List<Tag> tags = new ArrayList<>();
1165  try {
1168  } catch (TskCoreException | NoCurrentCaseException ex) {
1169  logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and its source content (artifact objID={0})", artifact.getId()), ex);
1170  }
1171  return tags;
1172  }
1173 
1190  @Override
1191  protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, List<CorrelationAttributeInstance> attributes) {
1192  /*
1193  * Has a tag with a comment been applied to the artifact or its source
1194  * content?
1195  */
1197  for (Tag tag : tags) {
1198  if (!StringUtils.isBlank(tag.getComment())) {
1199  status = HasCommentStatus.TAG_COMMENT;
1200  break;
1201  }
1202  }
1203  /*
1204  * Is there a comment in the CR for anything that matches the value and
1205  * type of the specified attributes.
1206  */
1207  try {
1208  if (CentralRepoDbUtil.commentExistsOnAttributes(attributes)) {
1211  } else {
1213  }
1214  }
1215  } catch (CentralRepoException ex) {
1216  logger.log(Level.SEVERE, "Attempted to Query CR for presence of comments in a Blackboard Artifact node and was unable to perform query, comment column will only reflect caseDB", ex);
1217  }
1218  return status;
1219  }
1220 
1221  @Override
1222  protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute, String defaultDescription) {
1223  Long count = -1L;
1224  String description = defaultDescription;
1225  try {
1226  if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
1228  description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, attribute.getCorrelationType().getDisplayName());
1229  } else if (attribute != null) {
1230  description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description();
1231  }
1232  } catch (CentralRepoException ex) {
1233  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);
1235  logger.log(Level.WARNING, MessageFormat.format("Error normalizing correlation attribute for central repository query (artifact objID={0}, corrAttrType={2}, corrAttrValue={3})", artifact.getId(), attribute.getCorrelationType(), attribute.getCorrelationValue()), ex);
1236  }
1237  return Pair.of(count, description);
1238  }
1239 
1243  private void updateSheet() {
1244  SwingUtilities.invokeLater(() -> {
1245  this.setSheet(createSheet());
1246  });
1247  }
1248 
1255  private String getRootAncestorName() {
1256  String parentName = srcContent.getName();
1257  Content parent = srcContent;
1258  try {
1259  while ((parent = parent.getParent()) != null) {
1260  parentName = parent.getName();
1261  }
1262  } catch (TskCoreException ex) {
1263  logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1264  return "";
1265  }
1266  return parentName;
1267  }
1268 
1275  public void addNodeProperty(NodeProperty<?> property) {
1276  if (customProperties == null) {
1277  customProperties = new ArrayList<>();
1278  }
1279  customProperties.add(property);
1280  }
1281 
1290  @SuppressWarnings("deprecation")
1291  private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifact) {
1292  try {
1293  for (BlackboardAttribute attribute : artifact.getAttributes()) {
1294  final int attributeTypeID = attribute.getAttributeType().getTypeID();
1295  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
1296  || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
1297  || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
1298  || attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
1299  || attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
1300  || attribute.getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
1301  /*
1302  * Do nothing.
1303  */
1304  } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
1305  addEmailMsgProperty(map, attribute);
1306  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
1307  map.put(attribute.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(attribute.getValueLong()));
1308  } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
1309  && attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
1310  /*
1311  * The truncation of text attributes appears to have been
1312  * motivated by the statement that "RegRipper output would
1313  * often cause the UI to get a black line accross it and
1314  * hang if you hovered over large output or selected it.
1315  * This reduces the amount of data in the table. Could
1316  * consider doing this for all fields in the UI."
1317  */
1318  String value = attribute.getDisplayString();
1319  if (value.length() > 512) {
1320  value = value.substring(0, 512);
1321  }
1322  map.put(attribute.getAttributeType().getDisplayName(), value);
1323  } else {
1324  switch (attribute.getAttributeType().getValueType()) {
1325  case INTEGER:
1326  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt());
1327  break;
1328  case DOUBLE:
1329  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble());
1330  break;
1331  case LONG:
1332  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong());
1333  break;
1334  default:
1335  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
1336 
1337  }
1338 
1339  }
1340  }
1341  } catch (TskCoreException ex) {
1342  logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact attributes (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1343  }
1344  }
1345 
1355  private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute) {
1356  final int attributeTypeID = attribute.getAttributeType().getTypeID();
1357  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
1358  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
1359  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
1360  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
1361  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
1362  || attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()) {
1363  /*
1364  * Do nothing.
1365  */
1366  } else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
1367  String value = attribute.getDisplayString();
1368  if (value.length() > 160) {
1369  value = value.substring(0, 160) + "...";
1370  }
1371  map.put(attribute.getAttributeType().getDisplayName(), value);
1372  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
1373  map.put(attribute.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(attribute.getValueLong()));
1374  } else {
1375  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
1376  }
1377  }
1378 
1379  @Override
1380  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1381  return visitor.visit(this);
1382  }
1383 
1384  @Override
1385  public boolean isLeafTypeNode() {
1386  return true;
1387  }
1388 
1389  @Override
1390  public String getItemType() {
1391  return getClass().getName();
1392  }
1393 
1394  @Override
1395  public <T> T accept(ContentNodeVisitor<T> visitor) {
1396  return visitor.visit(this);
1397  }
1398 
1399  @Messages({
1400  "BlackboardArtifactNode_analysisSheet_sourceType_name=Source Type",
1401  "BlackboardArtifactNode_analysisSheet_soureName_name=Source Name",
1402  "BlackboardArtifactNode_analysisSheet_score_name=Score",
1403  "BlackboardArtifactNode_analysisSheet_conclusion_name=Conclusion",
1404  "BlackboardArtifactNode_analysisSheet_configuration_name=Configuration",
1405  "BlackboardArtifactNode_analysisSheet_justifaction_name=Justification"
1406  })
1407 
1414  private GetSCOTask updateSheetForAnalysisResult(AnalysisResult result, Sheet.Set sheetSet) {
1415  sheetSet.put(new NodeProperty<>(
1416  Bundle.BlackboardArtifactNode_analysisSheet_soureName_name(),
1417  Bundle.BlackboardArtifactNode_analysisSheet_soureName_name(),
1418  NO_DESCR,
1419  getDisplayName()));
1420 
1421  GetSCOTask task = addSCOColumns(sheetSet);
1422 
1423  sheetSet.put(new NodeProperty<>(
1424  Bundle.BlackboardArtifactNode_analysisSheet_sourceType_name(),
1425  Bundle.BlackboardArtifactNode_analysisSheet_sourceType_name(),
1426  NO_DESCR,
1428 
1429  sheetSet.put(new NodeProperty<>(
1430  Bundle.BlackboardArtifactNode_analysisSheet_score_name(),
1431  Bundle.BlackboardArtifactNode_analysisSheet_score_name(),
1432  NO_DESCR,
1433  result.getScore().getSignificance().getDisplayName()));
1434 
1435  sheetSet.put(new NodeProperty<>(
1436  Bundle.BlackboardArtifactNode_analysisSheet_conclusion_name(),
1437  Bundle.BlackboardArtifactNode_analysisSheet_conclusion_name(),
1438  NO_DESCR,
1439  result.getConclusion()));
1440 
1441  sheetSet.put(new NodeProperty<>(
1442  Bundle.BlackboardArtifactNode_analysisSheet_configuration_name(),
1443  Bundle.BlackboardArtifactNode_analysisSheet_configuration_name(),
1444  NO_DESCR,
1445  result.getConfiguration()));
1446 
1447  sheetSet.put(new NodeProperty<>(
1448  Bundle.BlackboardArtifactNode_analysisSheet_justifaction_name(),
1449  Bundle.BlackboardArtifactNode_analysisSheet_justifaction_name(),
1450  NO_DESCR,
1451  result.getJustification()));
1452 
1453  return task;
1454  }
1455 
1456  private GetSCOTask addSCOColumns(Sheet.Set sheetSet) {
1458  /*
1459  * Add S(core), C(omments), and O(ther occurences) columns to the
1460  * sheet and start a background task to compute the value of these
1461  * properties for the artifact represented by this node. The task
1462  * will fire a PropertyChangeEvent when the computation is completed
1463  * and this node's PropertyChangeListener will update the sheet.
1464  */
1465  sheetSet.put(new NodeProperty<>(
1466  Bundle.BlackboardArtifactNode_createSheet_score_name(),
1467  Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
1468  VALUE_LOADING,
1469  ""));
1470  sheetSet.put(new NodeProperty<>(
1471  Bundle.BlackboardArtifactNode_createSheet_comment_name(),
1472  Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
1473  VALUE_LOADING,
1474  ""));
1475  if (CentralRepository.isEnabled()) {
1476  sheetSet.put(new NodeProperty<>(
1477  Bundle.BlackboardArtifactNode_createSheet_count_name(),
1478  Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
1479  VALUE_LOADING,
1480  ""));
1481  }
1482  return new GetSCOTask(new WeakReference<>(this), weakListener);
1483  }
1484  return null;
1485  }
1486 
1497  private String getSourceObjType(Content source) {
1498  if (source instanceof BlackboardArtifact) {
1499  BlackboardArtifact srcArtifact = (BlackboardArtifact) source;
1500  try {
1501  return srcArtifact.getType().getDisplayName();
1502  } catch (TskCoreException ex) {
1503  logger.log(Level.SEVERE, "Failed to get custom artifact type id=" + source.getId(), ex);
1504  }
1505  } else if (srcContent instanceof Volume) {
1506  return TskData.ObjectType.VOL.toString();
1507  } else if (srcContent instanceof AbstractFile) {
1508  return TskData.ObjectType.ABSTRACTFILE.toString();
1509  } else if (srcContent instanceof Image) {
1510  return TskData.ObjectType.IMG.toString();
1511  } else if (srcContent instanceof VolumeSystem) {
1512  return TskData.ObjectType.VS.toString();
1513  } else if (srcContent instanceof OsAccount) {
1514  return TskData.ObjectType.OS_ACCOUNT.toString();
1515  } else if (srcContent instanceof HostAddress) {
1516  return TskData.ObjectType.HOST_ADDRESS.toString();
1517  } else if (srcContent instanceof Pool) {
1518  return TskData.ObjectType.POOL.toString();
1519  }
1520  return "";
1521  }
1522 
1528  private void updateSCOColumns(final SCOData scoData) {
1529  // Make sure this happens in the EDT
1530  SwingUtilities.invokeLater(new Runnable() {
1531  @Override
1532  public void run() {
1533  if (scoData.getScoreAndDescription() != null) {
1535  Bundle.BlackboardArtifactNode_createSheet_score_name(),
1536  Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
1537  scoData.getScoreAndDescription().getRight(),
1538  scoData.getScoreAndDescription().getLeft()));
1539  }
1540  if (scoData.getComment() != null) {
1542  Bundle.BlackboardArtifactNode_createSheet_comment_name(),
1543  Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
1544  NO_DESCR, scoData.getComment()));
1545  }
1546  if (scoData.getCountAndDescription() != null) {
1548  Bundle.BlackboardArtifactNode_createSheet_count_name(),
1549  Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
1550  scoData.getCountAndDescription().getRight(),
1551  scoData.getCountAndDescription().getLeft()));
1552  }
1553  }
1554  });
1555  }
1556 
1561  if (srcContent instanceof BlackboardArtifact) {
1562  try {
1563  setDisplayName(((BlackboardArtifact) srcContent).getShortDescription());
1564  } catch (TskCoreException ex) {
1565  // Log the error, but set the display name to
1566  // Content.getName so there is something visible to the user.
1567  logger.log(Level.WARNING, "Failed to get short description for artifact id = " + srcContent.getId(), ex);
1568  setDisplayName(srcContent.getName());
1569  }
1570  } else if (srcContent instanceof OsAccount) {
1571  setDisplayName(((OsAccount) srcContent).getAddr().orElse(srcContent.getName()));
1572  } else {
1573  setDisplayName(srcContent.getName());
1574  }
1575 
1576  setShortDescription(getDisplayName());
1577  }
1578 
1591  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
1592  "BlackboardArtifactNode.createSheet.score.displayName=S",
1593  "BlackboardArtifactNode.createSheet.notableFile.description=Associated file recognized as notable.",
1594  "BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.",
1595  "BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
1596  "BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
1597  "BlackboardArtifactNode.createSheet.noScore.description=No score"})
1598  @Deprecated
1599  protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) {
1600  Pair<Score, String> scoreAndDescription = getScorePropertyAndDescription();
1601  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
1602  }
1603 
1613  @NbBundle.Messages({
1614  "BlackboardArtifactNode.createSheet.tags.displayName=Tags"}
1615  )
1616  @Deprecated
1617  protected void addTagProperty(Sheet.Set sheetSet) throws MissingResourceException {
1618  List<Tag> tags = new ArrayList<>();
1619  try {
1622  } catch (TskCoreException | NoCurrentCaseException ex) {
1623  logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and source content (artifact objID={0})", artifact.getId()), ex);
1624  }
1625  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1626  }
1627 
1639  @Deprecated
1640  protected final void addTagProperty(Sheet.Set sheetSet, List<Tag> tags) {
1641  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1642  }
1643 
1656  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
1657  "BlackboardArtifactNode.createSheet.count.displayName=O",
1658  "BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found",
1659  "BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property",
1660  "# {0} - occurrenceCount",
1661  "# {1} - attributeType",
1662  "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}"})
1663  @Deprecated
1664  protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
1665  Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute, Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description());
1666  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
1667  }
1668 
1683  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
1684  "BlackboardArtifactNode.createSheet.comment.displayName=C"})
1685  @Deprecated
1686  protected final void addCommentProperty(Sheet.Set sheetSet, List<Tag> tags, CorrelationAttributeInstance attribute) {
1687  List<CorrelationAttributeInstance> attributes = new ArrayList<>();
1688  attributes.add(attribute);
1689  HasCommentStatus status = getCommentProperty(tags, attributes);
1690  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, status));
1691  }
1692 }
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 boolean commentExistsOnAttributes(List< CorrelationAttributeInstance > attributes)
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-2022 Basis Technology. Generated on: Tue Jun 27 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.