Autopsy  4.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
BlackboardArtifactNode.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2017 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.datamodel;
20 
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.text.MessageFormat;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.logging.Level;
30 import java.util.stream.Collectors;
31 import javax.swing.Action;
32 import org.apache.commons.lang3.StringUtils;
33 import org.openide.nodes.Children;
34 import org.openide.nodes.Sheet;
35 import org.openide.util.Lookup;
36 import org.openide.util.NbBundle;
37 import org.openide.util.lookup.Lookups;
53 import org.sleuthkit.datamodel.Tag;
55 
61 
63  private final Content associated;
64  private List<NodeProperty<? extends Object>> customProperties;
65  private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName());
66  /*
67  * Artifact types which should have the full unique path of the associated
68  * content as a property.
69  */
70  private static final Integer[] SHOW_UNIQUE_PATH = new Integer[]{
75 
76  // TODO (RC): This is an unattractive alternative to subclassing BlackboardArtifactNode,
77  // cut from the same cloth as the equally unattractive SHOW_UNIQUE_PATH array
78  // above. It should be removed when and if the subclassing is implemented.
79  private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
81 
82  private final PropertyChangeListener pcl = new PropertyChangeListener() {
83  @Override
84  public void propertyChange(PropertyChangeEvent evt) {
85  String eventType = evt.getPropertyName();
86  if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())) {
88  if (event.getAddedTag().getArtifact().equals(artifact)) {
89  updateSheet();
90  }
91  } else if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) {
93  if (event.getDeletedTagInfo().getArtifactID() == artifact.getArtifactID()) {
94  updateSheet();
95  }
96  } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
98  if (event.getAddedTag().getContent().equals(associated)) {
99  updateSheet();
100  }
101  } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
103  if (event.getDeletedTagInfo().getContentID()== associated.getId()) {
104  updateSheet();
105  }
106  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
107  if (evt.getNewValue() == null) {
108  // case was closed. Remove listeners so that we don't get called with a stale case handle
109  removeListeners();
110  }
111  }
112  }
113  };
114 
122  public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
123  super(Children.LEAF, createLookup(artifact));
124 
125  this.artifact = artifact;
126  //this.associated = getAssociatedContent(artifact);
127  this.associated = this.getLookup().lookup(Content.class);
128  this.setName(Long.toString(artifact.getArtifactID()));
129  this.setDisplayName();
130  this.setIconBaseWithExtension(iconPath);
132  }
133 
141  super(Children.LEAF, createLookup(artifact));
142 
143  this.artifact = artifact;
144  //this.associated = getAssociatedContent(artifact);
145  this.associated = this.getLookup().lookup(Content.class);
146  this.setName(Long.toString(artifact.getArtifactID()));
147  this.setDisplayName();
148  this.setIconBaseWithExtension(ExtractedContent.getIconFilePath(artifact.getArtifactTypeID())); //NON-NLS
150  }
151 
152  private void removeListeners() {
154  }
155 
156  @Override
157  @NbBundle.Messages({
158  "BlackboardArtifactNode.getAction.errorTitle=Error getting actions",
159  "BlackboardArtifactNode.getAction.resultErrorMessage=There was a problem getting actions for the selected result."
160  + " The 'View Result in Timeline' action will not be available.",
161  "BlackboardArtifactNode.getAction.linkedFileMessage=There was a problem getting actions for the selected result. "
162  + " The 'View File in Timeline' action will not be available."})
163  public Action[] getActions(boolean context) {
164  List<Action> actionsList = new ArrayList<>();
165  actionsList.addAll(Arrays.asList(super.getActions(context)));
166 
167  //if this artifact has a time stamp add the action to view it in the timeline
168  try {
170  actionsList.add(new ViewArtifactInTimelineAction(artifact));
171  }
172  } catch (TskCoreException ex) {
173  LOGGER.log(Level.SEVERE, MessageFormat.format("Error getting arttribute(s) from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
174  MessageNotifyUtil.Notify.error(Bundle.BlackboardArtifactNode_getAction_errorTitle(), Bundle.BlackboardArtifactNode_getAction_resultErrorMessage());
175  }
176 
177  // if the artifact links to another file, add an action to go to that file
178  try {
179  AbstractFile c = findLinked(artifact);
180  if (c != null) {
182  }
183  } catch (TskCoreException ex) {
184  LOGGER.log(Level.SEVERE, MessageFormat.format("Error getting linked file from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
185  MessageNotifyUtil.Notify.error(Bundle.BlackboardArtifactNode_getAction_errorTitle(), Bundle.BlackboardArtifactNode_getAction_linkedFileMessage());
186  }
187 
188  //if this artifact has associated content, add the action to view the content in the timeline
189  AbstractFile file = getLookup().lookup(AbstractFile.class);
190  if (null != file) {
192  }
193 
194  return actionsList.toArray(new Action[actionsList.size()]);
195  }
196 
202  private void setDisplayName() {
203  String displayName = ""; //NON-NLS
204  if (associated != null) {
205  displayName = associated.getName();
206  }
207 
208  // If this is a node for a keyword hit on an artifact, we set the
209  // display name to be the artifact type name followed by " Artifact"
210  // e.g. "Messages Artifact".
211  if (artifact != null &&
212  (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() ||
213  artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID())) {
214  try {
215  for (BlackboardAttribute attribute : artifact.getAttributes()) {
216  if (attribute.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
217  BlackboardArtifact associatedArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
218  if (associatedArtifact != null) {
219  displayName = associatedArtifact.getDisplayName() + " Artifact";
220  }
221  }
222  }
223  } catch (TskCoreException ex) {
224  // Do nothing since the display name will be set to the file name.
225  }
226  }
227  this.setDisplayName(displayName);
228  }
229 
230  @Override
231  protected Sheet createSheet() {
232  Sheet s = super.createSheet();
233  Sheet.Set ss = s.get(Sheet.PROPERTIES);
234  if (ss == null) {
235  ss = Sheet.createPropertiesSet();
236  s.put(ss);
237  }
238  final String NO_DESCR = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.noDesc.text");
239 
240  Map<String, Object> map = new LinkedHashMap<>();
241  fillPropertyMap(map, artifact);
242 
243  ss.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.name"),
244  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.displayName"),
245  NO_DESCR,
246  this.getDisplayName()));
247 
248  for (Map.Entry<String, Object> entry : map.entrySet()) {
249  ss.put(new NodeProperty<>(entry.getKey(),
250  entry.getKey(),
251  NO_DESCR,
252  entry.getValue()));
253  }
254 
255  //append custom node properties
256  if (customProperties != null) {
257  for (NodeProperty<? extends Object> np : customProperties) {
258  ss.put(np);
259  }
260  }
261 
262  final int artifactTypeId = artifact.getArtifactTypeID();
263 
264  // If mismatch, add props for extension and file type
265  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
266  String ext = ""; //NON-NLS
267  String actualMimeType = ""; //NON-NLS
268  if (associated instanceof AbstractFile) {
269  AbstractFile af = (AbstractFile) associated;
270  ext = af.getNameExtension();
271  actualMimeType = af.getMIMEType();
272  if (actualMimeType == null) {
273  actualMimeType = ""; //NON-NLS
274  }
275  }
276  ss.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.name"),
277  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.displayName"),
278  NO_DESCR,
279  ext));
280  ss.put(new NodeProperty<>(
281  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.name"),
282  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.displayName"),
283  NO_DESCR,
284  actualMimeType));
285  }
286 
287  if (Arrays.asList(SHOW_UNIQUE_PATH).contains(artifactTypeId)) {
288  String sourcePath = ""; //NON-NLS
289  try {
290  sourcePath = associated.getUniquePath();
291  } catch (TskCoreException ex) {
292  LOGGER.log(Level.WARNING, "Failed to get unique path from: {0}", associated.getName()); //NON-NLS
293  }
294 
295  if (sourcePath.isEmpty() == false) {
296  ss.put(new NodeProperty<>(
297  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.name"),
298  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.displayName"),
299  NO_DESCR,
300  sourcePath));
301  }
302 
303  if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
304  AbstractFile file = associated instanceof AbstractFile ? (AbstractFile) associated : null;
305  ss.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
306  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
307  "",
308  file != null ? ContentUtils.getStringTime(file.getMtime(), file) : ""));
309  ss.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
310  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"),
311  "",
312  file != null ? ContentUtils.getStringTime(file.getCtime(), file) : ""));
313  ss.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
314  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"),
315  "",
316  file != null ? ContentUtils.getStringTime(file.getAtime(), file) : ""));
317  ss.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
318  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"),
319  "",
320  file != null ? ContentUtils.getStringTime(file.getCrtime(), file) : ""));
321  ss.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
322  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
323  "",
324  associated.getSize()));
325  }
326  } else {
327  String dataSourceStr = "";
328  try {
329  Content dataSource = associated.getDataSource();
330  if (dataSource != null) {
331  dataSourceStr = dataSource.getName();
332  } else {
333  dataSourceStr = getRootParentName();
334  }
335  } catch (TskCoreException ex) {
336  LOGGER.log(Level.WARNING, "Failed to get image name from {0}", associated.getName()); //NON-NLS
337  }
338 
339  if (dataSourceStr.isEmpty() == false) {
340  ss.put(new NodeProperty<>(
341  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.name"),
342  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.displayName"),
343  NO_DESCR,
344  dataSourceStr));
345  }
346  }
347 
348  // add properties for tags
349  List<Tag> tags = new ArrayList<>();
350  try {
353  } catch (TskCoreException ex) {
354  LOGGER.log(Level.SEVERE, "Failed to get tags for artifact " + artifact.getDisplayName(), ex);
355  }
356  ss.put(new NodeProperty<>("Tags", NbBundle.getMessage(AbstractAbstractFileNode.class, "BlackboardArtifactNode.createSheet.tags.displayName"),
357  NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
358 
359  return s;
360  }
361 
362  private void updateSheet() {
363  this.setSheet(createSheet());
364  }
365 
366  private String getRootParentName() {
367  String parentName = associated.getName();
368  Content parent = associated;
369  try {
370  while ((parent = parent.getParent()) != null) {
371  parentName = parent.getName();
372  }
373  } catch (TskCoreException ex) {
374  LOGGER.log(Level.WARNING, "Failed to get parent name from {0}", associated.getName()); //NON-NLS
375  return "";
376  }
377  return parentName;
378  }
379 
387  if (null == customProperties) {
388  //lazy create the list
389  customProperties = new ArrayList<>();
390  }
391  customProperties.add(np);
392  }
393 
401  @SuppressWarnings("deprecation")
402  private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifact) {
403  try {
404  for (BlackboardAttribute attribute : artifact.getAttributes()) {
405  final int attributeTypeID = attribute.getAttributeType().getTypeID();
406  //skip some internal attributes that user shouldn't see
407  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
408  || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
409  || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
410  || attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
411  || attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()) {
412  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
413  map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
414  } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
415  && attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
416  /*
417  * This was added because the RegRipper output would often
418  * cause the UI to get a black line accross it and hang if
419  * you hovered over large output or selected it. This
420  * reduces the amount of data in the table. Could consider
421  * doing this for all fields in the UI.
422  */
423  String value = attribute.getDisplayString();
424  if (value.length() > 512) {
425  value = value.substring(0, 512);
426  }
427  map.put(attribute.getAttributeType().getDisplayName(), value);
428  } else {
429  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
430  }
431  }
432  } catch (TskCoreException ex) {
433  LOGGER.log(Level.SEVERE, "Getting attributes failed", ex); //NON-NLS
434  }
435  }
436 
437  @Override
438  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
439  return v.visit(this);
440  }
441 
449  private static Lookup createLookup(BlackboardArtifact artifact) {
450  List<Object> forLookup = new ArrayList<>();
451  forLookup.add(artifact);
452 
453  // Add the content the artifact is associated with
454  Content content = getAssociatedContent(artifact);
455  if (content != null) {
456  forLookup.add(content);
457  }
458 
459  return Lookups.fixed(forLookup.toArray(new Object[forLookup.size()]));
460  }
461 
463  try {
464  return artifact.getSleuthkitCase().getContentById(artifact.getObjectID());
465  } catch (TskCoreException ex) {
466  LOGGER.log(Level.WARNING, "Getting file failed", ex); //NON-NLS
467  }
468  throw new IllegalArgumentException(
469  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.getAssocCont.exception.msg"));
470  }
471 
472 
473  @Override
474  public boolean isLeafTypeNode() {
475  return true;
476  }
477 
478  @Override
479  public String getItemType() {
480  return getClass().getName();
481  }
482 }
void fillPropertyMap(Map< String, Object > map, BlackboardArtifact artifact)
static String getStringTime(long epochSeconds, TimeZone tzone)
static void removePropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:384
BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath)
static Lookup createLookup(BlackboardArtifact artifact)
synchronized List< BlackboardArtifactTag > getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact)
BlackboardArtifact getBlackboardArtifact(long artifactID)
static Content getAssociatedContent(BlackboardArtifact artifact)
static ViewFileInTimelineAction createViewSourceFileAction(AbstractFile file)
static void addPropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:372
List< BlackboardAttribute > getAttributes()
synchronized static Logger getLogger(String name)
Definition: Logger.java:161
synchronized List< ContentTag > getContentTagsByContent(Content content)
static ViewFileInTimelineAction createViewFileAction(AbstractFile file)

Copyright © 2012-2016 Basis Technology. Generated on: Mon Apr 24 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.