Autopsy  4.19.0
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 
21 import com.google.common.annotations.Beta;
22 import com.google.common.cache.Cache;
23 import com.google.common.cache.CacheBuilder;
24 import java.beans.PropertyChangeEvent;
25 import java.beans.PropertyChangeListener;
26 import java.lang.ref.WeakReference;
27 import java.text.MessageFormat;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.EnumSet;
31 import java.util.LinkedHashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.MissingResourceException;
35 import java.util.Set;
36 import java.util.concurrent.ExecutionException;
37 import java.util.concurrent.TimeUnit;
38 import java.util.logging.Level;
39 import java.util.stream.Collectors;
40 import javax.swing.Action;
41 import org.apache.commons.lang3.StringUtils;
42 import org.apache.commons.lang3.tuple.Pair;
43 import org.openide.nodes.Sheet;
44 import org.openide.util.Lookup;
45 import org.openide.util.NbBundle;
46 import org.openide.util.WeakListeners;
47 import org.openide.util.lookup.Lookups;
63 import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
65 import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
68 import org.sleuthkit.datamodel.AbstractFile;
69 import org.sleuthkit.datamodel.BlackboardArtifact;
70 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
71 import org.sleuthkit.datamodel.BlackboardAttribute;
72 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
73 import org.sleuthkit.datamodel.Content;
74 import org.sleuthkit.datamodel.Tag;
75 import org.sleuthkit.datamodel.TskCoreException;
82 import org.sleuthkit.datamodel.AnalysisResult;
83 import org.sleuthkit.datamodel.BlackboardArtifact.Category;
84 import org.sleuthkit.datamodel.Score;
85 
90 public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifact> {
91 
92  private static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName());
93 
94  /*
95  * Cache of Content objects used to avoid repeated trips to the case
96  * database to retrieve Content objects that are the source of multiple
97  * artifacts.
98  */
99  private static final Cache<Long, Content> contentCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
100 
101  /*
102  * Case events that indicate an update to the node's property sheet may be
103  * required.
104  */
105  private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
112 
113  /*
114  * Artifact types for which the file metadata of the artifact's source file
115  * should be displayed in the node's property sheet.
116  */
117  private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
118  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
119  };
120 
121  private final BlackboardArtifact artifact;
122  private final BlackboardArtifact.Type artifactType;
123  private Content srcContent;
124  private volatile String translatedSourceName;
125 
126  /*
127  * A method has been provided to allow the injection of properties into this
128  * node for display in the node's property sheet, independent of the
129  * artifact the node represents.
130  */
131  private List<NodeProperty<? extends Object>> customProperties;
132 
133  private final PropertyChangeListener listener = new PropertyChangeListener() {
134  @Override
135  public void propertyChange(PropertyChangeEvent evt) {
136  String eventType = evt.getPropertyName();
137  if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())) {
139  if (event.getAddedTag().getArtifact().equals(artifact)) {
140  updateSheet();
141  }
142  } else if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) {
144  if (event.getDeletedTagInfo().getArtifactID() == artifact.getArtifactID()) {
145  updateSheet();
146  }
147  } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
149  if (event.getAddedTag().getContent().equals(srcContent)) {
150  updateSheet();
151  }
152  } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
154  if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) {
155  updateSheet();
156  }
157  } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
159  if (event.getContentID() == srcContent.getId()) {
160  updateSheet();
161  }
162  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
163  if (evt.getNewValue() == null) {
164  /*
165  * The case has been closed.
166  */
168  contentCache.invalidateAll();
169  }
170  } else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) {
171  SCOData scoData = (SCOData) evt.getNewValue();
172  if (scoData.getScoreAndDescription() != null) {
174  Bundle.BlackboardArtifactNode_createSheet_score_name(),
175  Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
176  scoData.getScoreAndDescription().getRight(),
177  scoData.getScoreAndDescription().getLeft()));
178  }
179  if (scoData.getComment() != null) {
181  Bundle.BlackboardArtifactNode_createSheet_comment_name(),
182  Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
183  NO_DESCR, scoData.getComment()));
184  }
185  if (scoData.getCountAndDescription() != null) {
187  Bundle.BlackboardArtifactNode_createSheet_count_name(),
188  Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
189  scoData.getCountAndDescription().getRight(),
190  scoData.getCountAndDescription().getLeft()));
191  }
192  } else if (eventType.equals(FileNameTransTask.getPropertyName())) {
193  /*
194  * Replace the value of the Source File property with the
195  * translated name via setDisplayName (see note in createSheet),
196  * and put the untranslated name in the Original Name property
197  * and in the tooltip.
198  */
199  String originalName = evt.getOldValue().toString();
200  translatedSourceName = evt.getNewValue().toString();
201  setDisplayName(translatedSourceName);
202  setShortDescription(originalName);
204  Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
205  Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
206  NO_DESCR,
207  originalName));
208  }
209  }
210  };
211 
212  /*
213  * The node's event listener is wrapped in a weak reference that allows the
214  * node to be garbage collected when the NetBeans infrastructure discards
215  * it. If this is not done, it has been shown that strong references to the
216  * listener held by event publishers prevents garbage collection of this
217  * node.
218  */
219  private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
220 
228  public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
229  super(artifact, createLookup(artifact, false));
230  this.artifact = artifact;
231  this.artifactType = getType(artifact);
232 
233  for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
234  if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) {
235  srcContent = lookupContent;
236  try {
237  /*
238  * Calling this getter causes the unique path of the source
239  * content to be cached in the Content object. This is
240  * advantageous as long as this node is constructed in a
241  * background thread instead of a UI thread.
242  */
243  srcContent.getUniquePath();
244  } catch (TskCoreException ex) {
245  logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
246  }
247  break;
248  }
249  }
250  if (srcContent == null) {
251  throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
252  }
253  setName(Long.toString(artifact.getArtifactID()));
254  String displayName = srcContent.getName();
255  setDisplayName(displayName);
256  setShortDescription(displayName);
257  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
259  }
260 
270  @Beta
271  public BlackboardArtifactNode(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) {
272  super(artifact, createLookup(artifact, lookupIsAssociatedFile));
273  this.artifact = artifact;
274  this.artifactType = getType(artifact);
275 
276  try {
277  //The lookup for a file may or may not exist so we define the srcContent as the parent.
278  srcContent = artifact.getParent();
279  } catch (TskCoreException ex) {
280  logger.log(Level.WARNING, MessageFormat.format("Error getting the parent of the artifact for (artifact objID={0})", artifact.getId()), ex);
281  }
282  if (srcContent != null) {
283  try {
284  /*
285  * Calling this getter causes the unique path of the source
286  * content to be cached in the Content object. This is
287  * advantageous as long as this node is constructed in a
288  * background thread instead of a UI thread.
289  */
290  srcContent.getUniquePath();
291  } catch (TskCoreException ex) {
292  logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
293  }
294  } else {
295  throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
296  }
297  setName(Long.toString(artifact.getArtifactID()));
298  String displayName = srcContent.getName();
299  setDisplayName(displayName);
300  setShortDescription(displayName);
301  String iconPath = IconsUtil.getIconFilePath(artifact.getArtifactTypeID());
302  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
304  }
305 
312  public BlackboardArtifactNode(BlackboardArtifact artifact) {
313  this(artifact, IconsUtil.getIconFilePath(artifact.getArtifactTypeID()));
314  }
315 
321  private static BlackboardArtifact.Type getType(BlackboardArtifact artifact) {
322  try {
323  return artifact.getType();
324  } catch (TskCoreException ex) {
325  logger.log(Level.WARNING, MessageFormat.format("Error getting the artifact type for artifact (artifact objID={0})", artifact.getId()), ex);
326  return null;
327  }
328  }
329 
338  private static Lookup createLookup(BlackboardArtifact artifact) {
339  final long objectID = artifact.getObjectID();
340  try {
341  Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
342  if (content == null) {
343  return Lookups.fixed(artifact);
344  } else {
345  return Lookups.fixed(artifact, content);
346  }
347  } catch (ExecutionException ex) {
348  logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
349  return Lookups.fixed(artifact);
350  }
351  }
352 
364  private static Lookup createLookup(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) {
365  Content content = null;
366  if (lookupIsAssociatedFile) {
367  try {
368  content = getPathIdFile(artifact);
369  } catch (ExecutionException ex) {
370  logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
371  content = null;
372  }
373  if (content == null) {
374  return Lookups.fixed(artifact);
375  } else {
376  return Lookups.fixed(artifact, content);
377  }
378  } else {
379  return createLookup(artifact);
380  }
381 
382  }
383 
397  private static Content getPathIdFile(BlackboardArtifact artifact) throws ExecutionException {
398  try {
399  BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
400  if (attribute != null) {
401  return contentCache.get(attribute.getValueLong(), () -> artifact.getSleuthkitCase().getContentById(attribute.getValueLong()));
402  }
403  } catch (TskCoreException ex) {
404  logger.log(Level.WARNING, MessageFormat.format("Error getting content for path id attrbiute for artifact: ", artifact.getId()), ex); //NON-NLS
405  }
406  return null;
407  }
408 
418  @Override
419  protected void finalize() throws Throwable {
420  super.finalize();
422  }
423 
427  private void unregisterListener() {
429  }
430 
436  public BlackboardArtifact getArtifact() {
437  return this.artifact;
438  }
439 
440  @Override
441  public Action[] getActions(boolean context) {
442  List<Action> actionsList = new ArrayList<>();
443  actionsList.addAll(Arrays.asList(super.getActions(context)));
444 
445  /*
446  * If the artifact represented by this node has a timestamp, add an
447  * action to view it in the timeline.
448  */
449  try {
451  // don't show ViewArtifactInTimelineAction for AnalysisResults.
452  (!(this.artifact instanceof AnalysisResult))) {
453 
454  actionsList.add(new ViewArtifactInTimelineAction(artifact));
455  }
456  } catch (TskCoreException ex) {
457  logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact timestamp (artifact objID={0})", artifact.getId()), ex); //NON-NLS
458  }
459 
460  /*
461  * If the artifact represented by this node is linked to a file via a
462  * TSK_PATH_ID attribute, add an action to view the file in the
463  * timeline.
464  */
465  try {
466  AbstractFile linkedFile = findLinked(artifact);
467  if (linkedFile != null) {
468  actionsList.add(ViewFileInTimelineAction.createViewFileAction(linkedFile));
469  }
470  } catch (TskCoreException ex) {
471  logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS
472 
473  }
474 
475  /*
476  * If the source content of the artifact represented by this node is a
477  * file, add an action to view the file in the data source tree.
478  */
479  AbstractFile file = getLookup().lookup(AbstractFile.class
480  );
481  if (null != file) {
483  }
484 
485  return actionsList.toArray(new Action[actionsList.size()]);
486  }
487 
494  public String getSourceName() {
495  return srcContent.getName();
496  }
497 
498  @NbBundle.Messages({
499  "BlackboardArtifactNode.createSheet.srcFile.name=Source File",
500  "BlackboardArtifactNode.createSheet.srcFile.displayName=Source File",
501  "BlackboardArtifactNode.createSheet.srcFile.origName=Original Name",
502  "BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name",
503  "BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
504  "BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
505  "BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details",
506  "BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details",
507  "BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash",
508  "BlackboardArtifactNode.createSheet.artifactMD5.name=MD5 Hash",
509  "BlackboardArtifactNode.createSheet.fileSize.name=Size",
510  "BlackboardArtifactNode.createSheet.fileSize.displayName=Size",
511  "BlackboardArtifactNode.createSheet.path.displayName=Path",
512  "BlackboardArtifactNode.createSheet.path.name=Path"
513  })
514  @Override
515  protected Sheet createSheet() {
516  /*
517  * Create an empty property sheet.
518  */
519  Sheet sheet = super.createSheet();
520  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
521  if (sheetSet == null) {
522  sheetSet = Sheet.createPropertiesSet();
523  sheet.put(sheetSet);
524  }
525 
526  /*
527  * Add the name of the source content of the artifact represented by
528  * this node to the sheet. The value of this property is the same as the
529  * display name of the node and this a "special" property that displays
530  * the node's icon as well as the display name.
531  */
532  sheetSet.put(new NodeProperty<>(
533  Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
534  Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
535  NO_DESCR,
536  getDisplayName()));
537 
539  /*
540  * If machine translation is configured, add the original name of
541  * the of the source content of the artifact represented by this
542  * node to the sheet.
543  */
544  sheetSet.put(new NodeProperty<>(
545  Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
546  Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
547  NO_DESCR,
548  translatedSourceName != null ? srcContent.getName() : ""));
549  if (translatedSourceName == null) {
550  /*
551  * NOTE: The task makes its own weak reference to the listener.
552  */
553  new FileNameTransTask(srcContent.getName(), this, listener).submit();
554  }
555  }
556 
558  /*
559  * Add S(core), C(omments), and O(ther occurences) columns to the
560  * sheet and start a background task to compute the value of these
561  * properties for the artifact represented by this node. The task
562  * will fire a PropertyChangeEvent when the computation is completed
563  * and this node's PropertyChangeListener will update the sheet.
564  */
565  sheetSet.put(new NodeProperty<>(
566  Bundle.BlackboardArtifactNode_createSheet_score_name(),
567  Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
569  ""));
570  sheetSet.put(new NodeProperty<>(
571  Bundle.BlackboardArtifactNode_createSheet_comment_name(),
572  Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
574  ""));
576  sheetSet.put(new NodeProperty<>(
577  Bundle.BlackboardArtifactNode_createSheet_count_name(),
578  Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
580  ""));
581  }
582  backgroundTasksPool.submit(new GetSCOTask(new WeakReference<>(this), weakListener));
583  }
584 
585  /*
586  * If the artifact represented by this node is an interesting artifact
587  * hit, add the type and description of the interesting artifact to the
588  * sheet.
589  */
590  if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
591  try {
592  BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
593  if (attribute != null) {
594  BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
595  sheetSet.put(new NodeProperty<>(
596  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"),
597  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.displayName"),
598  NO_DESCR,
599  associatedArtifact.getDisplayName()));
600  sheetSet.put(new NodeProperty<>(
601  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.name"),
602  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
603  NO_DESCR,
604  associatedArtifact.getShortDescription()));
605  }
606  } catch (TskCoreException | NoCurrentCaseException ex) {
607  logger.log(Level.SEVERE, MessageFormat.format("Error getting associated artifact of TSK_INTERESTING_ARTIFACT_HIT artifact (objID={0}))", artifact.getId()), ex); //NON-NLS
608  }
609  }
610 
611  /*
612  * Add the attributes of the artifact represented by this node to the
613  * sheet.
614  */
615  Map<String, Object> map = new LinkedHashMap<>();
616  fillPropertyMap(map, artifact);
617  for (Map.Entry<String, Object> entry : map.entrySet()) {
618  sheetSet.put(new NodeProperty<>(entry.getKey(),
619  entry.getKey(),
620  NO_DESCR,
621  entry.getValue()));
622  }
623 
624  /*
625  * Add any "custom properties" for the node to the sheet.
626  */
627  if (customProperties != null) {
628  for (NodeProperty<? extends Object> np : customProperties) {
629  sheetSet.put(np);
630  }
631  }
632 
633  /*
634  * If the artifact represented by this node is a file extension mismatch
635  * artifact, add the extension and type of the artifact's source file to
636  * the sheet.
637  */
638  final int artifactTypeId = artifact.getArtifactTypeID();
639  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
640  String ext = ""; //NON-NLS
641  String actualMimeType = ""; //NON-NLS
642  if (srcContent instanceof AbstractFile) {
643  AbstractFile file = (AbstractFile) srcContent;
644  ext = file.getNameExtension();
645  actualMimeType = file.getMIMEType();
646  if (actualMimeType == null) {
647  actualMimeType = ""; //NON-NLS
648 
649  }
650  }
651  sheetSet.put(new NodeProperty<>(
652  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.name"),
653  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.displayName"),
654  NO_DESCR,
655  ext));
656  sheetSet.put(new NodeProperty<>(
657  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.name"),
658  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.displayName"),
659  NO_DESCR,
660  actualMimeType));
661  }
662 
663  /*
664  * If the type of the artifact represented by this node dictates the
665  * addition of the source content's unique path, add it to the sheet.
666  */
667  if (artifactType != null && artifactType.getCategory() == Category.ANALYSIS_RESULT) {
668  String sourcePath = ""; //NON-NLS
669  try {
670  sourcePath = srcContent.getUniquePath();
671  } catch (TskCoreException ex) {
672  logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
673 
674  }
675 
676  if (sourcePath.isEmpty() == false) {
677  sheetSet.put(new NodeProperty<>(
678  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.name"),
679  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.displayName"),
680  NO_DESCR,
681  sourcePath));
682  }
683 
684  /*
685  * If the type of the artifact represented by this node dictates the
686  * addition of the source content's file metadata, add it to the
687  * sheet. Otherwise, add the data source to the sheet.
688  */
689  if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
690  AbstractFile file = srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
691  sheetSet.put(new NodeProperty<>(
692  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
693  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
694  "",
695  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getMtime())));
696  sheetSet.put(new NodeProperty<>(
697  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
698  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"),
699  "",
700  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCtime())));
701  sheetSet.put(new NodeProperty<>(
702  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
703  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"),
704  "",
705  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getAtime())));
706  sheetSet.put(new NodeProperty<>(
707  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
708  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"),
709  "",
710  file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCrtime())));
711  sheetSet.put(new NodeProperty<>(
712  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
713  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
714  "",
715  file == null ? "" : file.getSize()));
716  sheetSet.put(new NodeProperty<>(
717  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
718  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
719  "",
720  file == null ? "" : StringUtils.defaultString(file.getMd5Hash())));
721  }
722  } else {
723  String dataSourceStr = "";
724  try {
725  Content dataSource = srcContent.getDataSource();
726  if (dataSource != null) {
727  dataSourceStr = dataSource.getName();
728  } else {
729  dataSourceStr = getRootAncestorName();
730  }
731  } catch (TskCoreException ex) {
732  logger.log(Level.SEVERE, MessageFormat.format("Error getting source data source name (artifact objID={0})", artifact.getId()), ex); //NON-NLS
733 
734  }
735 
736  if (dataSourceStr.isEmpty() == false) {
737  sheetSet.put(new NodeProperty<>(
738  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.name"),
739  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.displayName"),
740  NO_DESCR,
741  dataSourceStr));
742  }
743  }
744 
745  /*
746  * If the artifact represented by this node is an EXIF artifact, add the
747  * source file size and path to the sheet.
748  */
749  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
750  long size = 0;
751  String path = ""; //NON-NLS
752  if (srcContent instanceof AbstractFile) {
753  AbstractFile af = (AbstractFile) srcContent;
754  size = af.getSize();
755  try {
756  path = af.getUniquePath();
757  } catch (TskCoreException ex) {
758  path = af.getParentPath();
759 
760  }
761  }
762  sheetSet.put(new NodeProperty<>(
763  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.name"),
764  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.displayName"),
765  NO_DESCR,
766  size));
767  sheetSet
768  .put(new NodeProperty<>(
769  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
770  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
771  NO_DESCR,
772  path));
773  }
774 
775  return sheet;
776  }
777 
784  @Override
785  protected final List<Tag> getAllTagsFromDatabase() {
786  List<Tag> tags = new ArrayList<>();
787  try {
790  } catch (TskCoreException | NoCurrentCaseException ex) {
791  logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and its source content (artifact objID={0})", artifact.getId()), ex);
792  }
793  return tags;
794  }
795 
804  @Override
806  CorrelationAttributeInstance correlationAttribute = null;
807  if (CentralRepository.isEnabled() && srcContent instanceof AbstractFile) {
808  correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile) srcContent);
809  }
810  return correlationAttribute;
811  }
812 
828  @Override
830 
831  /*
832  * Has a tag with a comment been applied to the artifact or its source
833  * content?
834  */
836  for (Tag tag : tags) {
837  if (!StringUtils.isBlank(tag.getComment())) {
838  status = HasCommentStatus.TAG_COMMENT;
839  break;
840  }
841  }
842 
843  /*
844  * Does the given correlation attribute instance have a comment in the
845  * central repository?
846  */
847  if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
848  if (status == HasCommentStatus.TAG_COMMENT) {
850  } else {
851  status = HasCommentStatus.CR_COMMENT;
852  }
853  }
854 
855  return status;
856  }
857 
876  @Override
877  protected Pair<Long, String> getCountPropertyAndDescription(Type corrAttrType, String attributeValue, String defaultDescription) {
878  Long count = -1L;
879  String description = defaultDescription;
880  try {
881  if (corrAttrType != null && StringUtils.isNotBlank(attributeValue)) {
883  description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, corrAttrType.getDisplayName());
884  } else if (corrAttrType != null) {
885  description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description();
886  }
887  } catch (CentralRepoException ex) {
888  logger.log(Level.SEVERE, MessageFormat.format("Error querying central repository for other occurences count (artifact objID={0}, corrAttrType={1}, corrAttrValue={2})", artifact.getId(), corrAttrType, attributeValue), ex);
890  logger.log(Level.SEVERE, MessageFormat.format("Error normalizing correlation attribute for central repository query (artifact objID={0}, corrAttrType={2}, corrAttrValue={3})", artifact.getId(), corrAttrType, attributeValue), ex);
891  }
892  return Pair.of(count, description);
893  }
894 
898  private void updateSheet() {
899  this.setSheet(createSheet());
900  }
901 
908  private String getRootAncestorName() {
909  String parentName = srcContent.getName();
910  Content parent = srcContent;
911  try {
912  while ((parent = parent.getParent()) != null) {
913  parentName = parent.getName();
914  }
915  } catch (TskCoreException ex) {
916  logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
917  return "";
918  }
919  return parentName;
920  }
921 
928  public void addNodeProperty(NodeProperty<?> property) {
929  if (customProperties == null) {
930  customProperties = new ArrayList<>();
931  }
932  customProperties.add(property);
933  }
934 
943  @SuppressWarnings("deprecation")
944  private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifact) {
945  try {
946  for (BlackboardAttribute attribute : artifact.getAttributes()) {
947  final int attributeTypeID = attribute.getAttributeType().getTypeID();
948  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
949  || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
950  || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
951  || attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
952  || attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
953  || attribute.getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
954  /*
955  * Do nothing.
956  */
957  } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
958  addEmailMsgProperty(map, attribute);
959  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
960  map.put(attribute.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(attribute.getValueLong()));
961  } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
962  && attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
963  /*
964  * The truncation of text attributes appears to have been
965  * motivated by the statement that "RegRipper output would
966  * often cause the UI to get a black line accross it and
967  * hang if you hovered over large output or selected it.
968  * This reduces the amount of data in the table. Could
969  * consider doing this for all fields in the UI."
970  */
971  String value = attribute.getDisplayString();
972  if (value.length() > 512) {
973  value = value.substring(0, 512);
974  }
975  map.put(attribute.getAttributeType().getDisplayName(), value);
976  } else {
977  switch (attribute.getAttributeType().getValueType()) {
978  case INTEGER:
979  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt());
980  break;
981  case DOUBLE:
982  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble());
983  break;
984  case LONG:
985  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong());
986  break;
987  default:
988  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
989 
990  }
991 
992  }
993  }
994  } catch (TskCoreException ex) {
995  logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact attributes (artifact objID={0})", artifact.getId()), ex); //NON-NLS
996  }
997  }
998 
1008  private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute) {
1009  final int attributeTypeID = attribute.getAttributeType().getTypeID();
1010  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
1011  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
1012  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
1013  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
1014  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
1015  || attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()) {
1016  /*
1017  * Do nothing.
1018  */
1019  } else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
1020  String value = attribute.getDisplayString();
1021  if (value.length() > 160) {
1022  value = value.substring(0, 160) + "...";
1023  }
1024  map.put(attribute.getAttributeType().getDisplayName(), value);
1025  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
1026  map.put(attribute.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(attribute.getValueLong()));
1027  } else {
1028  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
1029  }
1030  }
1031 
1032  @Override
1033  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1034  return visitor.visit(this);
1035  }
1036 
1037  @Override
1038  public boolean isLeafTypeNode() {
1039  return true;
1040  }
1041 
1042  @Override
1043  public String getItemType() {
1044  return getClass().getName();
1045  }
1046 
1047  @Override
1048  public <T> T accept(ContentNodeVisitor<T> visitor) {
1049  return visitor.visit(this);
1050  }
1051 
1064  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
1065  "BlackboardArtifactNode.createSheet.score.displayName=S",
1066  "BlackboardArtifactNode.createSheet.notableFile.description=Associated file recognized as notable.",
1067  "BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.",
1068  "BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
1069  "BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
1070  "BlackboardArtifactNode.createSheet.noScore.description=No score"})
1071  @Deprecated
1072  protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) {
1073  Pair<Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
1074  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
1075  }
1076 
1086  @NbBundle.Messages({
1087  "BlackboardArtifactNode.createSheet.tags.displayName=Tags"}
1088  )
1089  @Deprecated
1090  protected void addTagProperty(Sheet.Set sheetSet) throws MissingResourceException {
1091  List<Tag> tags = new ArrayList<>();
1092  try {
1095  } catch (TskCoreException | NoCurrentCaseException ex) {
1096  logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and source content (artifact objID={0})", artifact.getId()), ex);
1097  }
1098  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1099  }
1100 
1112  @Deprecated
1113  protected final void addTagProperty(Sheet.Set sheetSet, List<Tag> tags) {
1114  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1115  }
1116 
1129  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
1130  "BlackboardArtifactNode.createSheet.count.displayName=O",
1131  "BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found",
1132  "BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property",
1133  "# {0} - occurrenceCount",
1134  "# {1} - attributeType",
1135  "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}"})
1136  @Deprecated
1137  protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
1138  Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute.getCorrelationType(), attribute.getCorrelationValue(), Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description());
1139  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
1140  }
1141 
1156  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
1157  "BlackboardArtifactNode.createSheet.comment.displayName=C"})
1158  @Deprecated
1159  protected final void addCommentProperty(Sheet.Set sheetSet, List<Tag> tags, CorrelationAttributeInstance attribute) {
1160  HasCommentStatus status = getCommentProperty(tags, attribute);
1161  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, status));
1162  }
1163 
1164 }
static BlackboardArtifact.Type getType(BlackboardArtifact artifact)
final void addTagProperty(Sheet.Set sheetSet, List< Tag > tags)
void fillPropertyMap(Map< String, Object > map, BlackboardArtifact artifact)
static Lookup createLookup(BlackboardArtifact artifact, boolean lookupIsAssociatedFile)
static String getFormattedTime(long epochTime)
BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath)
List< ContentTag > getContentTagsByContent(Content content)
static Lookup createLookup(BlackboardArtifact artifact)
Pair< Long, String > getCountPropertyAndDescription(Type corrAttrType, String attributeValue, String defaultDescription)
static ViewFileInTimelineAction createViewSourceFileAction(AbstractFile file)
final void addScorePropertyAndDescription(Sheet.Set sheetSet, List< Tag > tags)
static Content getPathIdFile(BlackboardArtifact artifact)
static CorrelationAttributeInstance getCorrAttrForFile(AbstractFile file)
void addEmailMsgProperty(Map< String, Object > map, BlackboardAttribute attribute)
final void addCommentProperty(Sheet.Set sheetSet, List< Tag > tags, CorrelationAttributeInstance attribute)
final CorrelationAttributeInstance getCorrelationAttributeInstance()
DataResultViewerTable.HasCommentStatus getCommentProperty(List< Tag > tags, CorrelationAttributeInstance attribute)
final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:711
BlackboardArtifactNode(BlackboardArtifact artifact, boolean lookupIsAssociatedFile)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:756
Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value)
static ViewFileInTimelineAction createViewFileAction(AbstractFile file)
List< BlackboardArtifactTag > getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact)

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