Autopsy  4.18.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
Metadata.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-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.contentviewers;
20 
21 import java.awt.Component;
22 import java.awt.Cursor;
23 import java.text.MessageFormat;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.concurrent.ExecutionException;
28 import java.util.logging.Level;
29 import java.util.stream.Stream;
30 import javax.swing.SwingWorker;
31 import org.apache.commons.lang3.StringUtils;
32 import org.openide.nodes.Node;
33 import org.openide.util.NbBundle;
34 import org.openide.util.NbBundle.Messages;
35 import org.openide.util.lookup.ServiceProvider;
41 import org.sleuthkit.datamodel.AbstractFile;
42 import org.sleuthkit.datamodel.BlackboardArtifact;
43 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
44 import org.sleuthkit.datamodel.BlackboardAttribute;
45 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
46 import org.sleuthkit.datamodel.DataArtifact;
47 import org.sleuthkit.datamodel.DataSource;
48 import org.sleuthkit.datamodel.Image;
49 import org.sleuthkit.datamodel.FsContent;
50 import org.sleuthkit.datamodel.TskCoreException;
51 import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
52 
58 @ServiceProvider(service = DataContentViewer.class, position = 4)
59 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
60 public class Metadata extends javax.swing.JPanel implements DataContentViewer {
61 
62  private static final Logger LOGGER = Logger.getLogger(Metadata.class.getName());
63 
65 
69  public Metadata() {
70  initComponents();
71  customizeComponents();
73  }
74 
80  @SuppressWarnings("unchecked")
81  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
82  private void initComponents() {
83 
84  jPopupMenu1 = new javax.swing.JPopupMenu();
85  jScrollPane2 = new javax.swing.JScrollPane();
86  jTextPane1 = new javax.swing.JTextPane();
87 
88  setPreferredSize(new java.awt.Dimension(100, 52));
89 
90  jScrollPane2.setPreferredSize(new java.awt.Dimension(610, 52));
91 
92  jTextPane1.setEditable(false);
93  jTextPane1.setPreferredSize(new java.awt.Dimension(600, 52));
94  jScrollPane2.setViewportView(jTextPane1);
95 
96  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
97  this.setLayout(layout);
98  layout.setHorizontalGroup(
99  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
100  .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
101  );
102  layout.setVerticalGroup(
103  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
104  .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
105  );
106  }// </editor-fold>//GEN-END:initComponents
107  // Variables declaration - do not modify//GEN-BEGIN:variables
108  private javax.swing.JPopupMenu jPopupMenu1;
109  private javax.swing.JScrollPane jScrollPane2;
110  private javax.swing.JTextPane jTextPane1;
111  // End of variables declaration//GEN-END:variables
112 
113  private void customizeComponents() {
114  /*
115  * jTextPane1.setComponentPopupMenu(rightClickMenu); ActionListener
116  * actList = new ActionListener(){ @Override public void
117  * actionPerformed(ActionEvent e){ JMenuItem jmi = (JMenuItem)
118  * e.getSource(); if(jmi.equals(copyMenuItem)) outputViewPane.copy();
119  * else if(jmi.equals(selectAllMenuItem)) outputViewPane.selectAll(); }
120  * }; copyMenuItem.addActionListener(actList);
121  * selectAllMenuItem.addActionListener(actList);
122  */
123 
124  }
125 
126  private void setText(String str) {
128  jTextPane1.setText("<html><head></head><body>" + str + "</body></html>"); //NON-NLS
129  }
130 
131  private void addHeader(StringBuilder sb, String header, boolean spaced) {
132  sb.append(MessageFormat.format("<div class=\"{0}\"><h1 class=\"{1}\">{2}</h1></div>",
135  header));
136  }
137 
138  private void startTable(StringBuilder sb) {
139  sb.append(MessageFormat.format("<table class=\"{0}\" valign=\"top\" align=\"left\"><tbody>",
141  }
142 
143  private void endTable(StringBuilder sb) {
144  sb.append("</tbody></table>"); //NON-NLS
145  }
146 
147  private void addRow(StringBuilder sb, String key, String value) {
148  sb.append(MessageFormat.format("<tr><td class=\"{0}\"><span class=\"{1}\">{2}:</span></td><td class=\"{3}\">{4}</td></tr>",
151  EscapeUtil.escapeHtml(key),
153  EscapeUtil.escapeHtml(value)
154  ));
155  }
156 
157  private void addMonospacedRow(StringBuilder sb, String key) {
158  sb.append(MessageFormat.format("<tr><td class=\"{0}\"><span class=\"{1}\">{2}</span></td></tr>",
162  ));
163  }
164 
165  private void addRowWithMultipleValues(StringBuilder sb, String key, String[] values) {
166  String[] safeValues = values == null || values.length < 1 ? new String[]{""} : values;
167 
168  addRow(sb, key, safeValues[0]);
169  Stream.of(safeValues)
170  .skip(1)
171  .filter(line -> line != null)
172  .forEach(line -> addRow(sb, "", EscapeUtil.escapeHtml(line)));
173  }
174 
175  @Messages({
176  "Metadata.headerTitle=Metadata",
177  "Metadata.tableRowTitle.mimeType=MIME Type",
178  "Metadata.nodeText.truncated=(results truncated)",
179  "Metadata.tableRowTitle.sha1=SHA1",
180  "Metadata.tableRowTitle.sha256=SHA-256",
181  "Metadata.tableRowTitle.imageType=Type",
182  "Metadata.tableRowTitle.sectorSize=Sector Size",
183  "Metadata.tableRowTitle.timezone=Time Zone",
184  "Metadata.tableRowTitle.deviceId=Device ID",
185  "Metadata.tableRowTitle.acquisitionDetails=Acquisition Details",
186  "Metadata.tableRowTitle.downloadSource=Downloaded From",
187  "Metadata.nodeText.unknown=Unknown",
188  "Metadata.nodeText.none=None",
189  "Metadata.nodeText.loading=Metadata loading..."})
190  @Override
191  public void setNode(Node node) {
192 
193  if (worker != null) {
194  worker.cancel(true);
195  worker = null;
196  }
197 
198  if (node != null) {
199  setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
200  setText(Bundle.Metadata_nodeText_loading());
201  worker = new MetaDataWorker(node) {
202  @Override
203  public void done() {
204  try {
205  if (!isCancelled()) {
206  setText(get());
207  jTextPane1.setCaretPosition(0);
208  }
209  } catch (InterruptedException | ExecutionException ex) {
210  LOGGER.log(Level.SEVERE, "Failed to get metaData for node " + node.getName(), ex);
211  }
212 
213  setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
214  }
215  };
216 
217  worker.execute();
218  } else {
219  setText("");
220  }
221  }
222 
232  private void addDownloadSourceRow(StringBuilder sb, BlackboardArtifact associatedArtifact) throws TskCoreException {
233  if (associatedArtifact != null
234  && ((associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID())
235  || (associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID()))) {
236  BlackboardAttribute urlAttr = associatedArtifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL));
237  if (urlAttr != null) {
238  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.downloadSource"), urlAttr.getValueString());
239  }
240  }
241  }
242 
249  private void addAcquisitionDetails(StringBuilder sb, DataSource dataSource) {
250  if (dataSource != null) {
251  try {
252  String details = dataSource.getAcquisitionDetails();
253  if (StringUtils.isEmpty(details)) {
254  details = Bundle.Metadata_nodeText_unknown();
255  }
256  String[] lines = (details != null) ? details.split("\n") : new String[]{""};
257  addRowWithMultipleValues(sb,
258  NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.acquisitionDetails"),
259  lines);
260 
261  } catch (TskCoreException ex) {
262  LOGGER.log(Level.SEVERE, "Error reading acquisition details from case database", ex); //NON-NLS
263  }
264  }
265  }
266 
267  @Override
268  public String getTitle() {
269  return getTitle(null);
270  }
271 
272  @Messages({
273  "Metadata_dataArtifactTitle=Source File Metadata"
274  })
275  @Override
276  public String getTitle(Node node) {
277  if (node != null && !node.getLookup().lookupAll(DataArtifact.class).isEmpty()) {
278  return Bundle.Metadata_dataArtifactTitle();
279  } else {
280  return NbBundle.getMessage(this.getClass(), "Metadata.title");
281  }
282  }
283 
284  @Override
285  public String getToolTip() {
286  return NbBundle.getMessage(this.getClass(), "Metadata.toolTip");
287  }
288 
289  @Override
290  public DataContentViewer createInstance() {
291  return new Metadata();
292  }
293 
294  @Override
295  public Component getComponent() {
296  return this;
297  }
298 
299  @Override
300  public void resetComponent() {
301  setText("");
302  }
303 
304  @Override
305  public boolean isSupported(Node node) {
306  Image image = node.getLookup().lookup(Image.class);
307  AbstractFile file = node.getLookup().lookup(AbstractFile.class);
308  return (file != null) || (image != null);
309  }
310 
311  @Override
312  public int isPreferred(Node node) {
313  return 1;
314  }
315 
319  private class MetaDataWorker extends SwingWorker<String, Void> {
320 
321  private final Node node;
322 
323  MetaDataWorker(Node node) {
324  this.node = node;
325  }
326 
327  @Override
328  protected String doInBackground() throws Exception {
329  AbstractFile file = node.getLookup().lookup(AbstractFile.class);
330  Image image = node.getLookup().lookup(Image.class);
331  DataSource dataSource = node.getLookup().lookup(DataSource.class);
332  if (file == null && image == null) {
333  return NbBundle.getMessage(this.getClass(), "Metadata.nodeText.nonFilePassedIn");
334  }
335 
336  StringBuilder sb = new StringBuilder();
337  addHeader(sb, Bundle.Metadata_headerTitle(), false);
338  startTable(sb);
339 
340  if (file != null) {
341  try {
342  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), file.getUniquePath());
343  } catch (TskCoreException ex) {
344  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), file.getParentPath() + "/" + file.getName());
345  }
346 
347  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.type"), file.getType().getName());
348  addRow(sb, Bundle.Metadata_tableRowTitle_mimeType(), file.getMIMEType());
349  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.size"), Long.toString(file.getSize()));
350  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.fileNameAlloc"), file.getDirFlagAsString());
351  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.metadataAlloc"), file.getMetaFlagsAsString());
352  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.modified"), TimeZoneUtils.getFormattedTime(file.getMtime()));
353  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.accessed"), TimeZoneUtils.getFormattedTime(file.getAtime()));
354  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.created"), TimeZoneUtils.getFormattedTime(file.getCrtime()));
355  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.changed"), TimeZoneUtils.getFormattedTime(file.getCtime()));
356 
357  String md5 = file.getMd5Hash();
358  if (md5 == null) {
359  md5 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
360  }
361  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5);
362  String sha256 = file.getSha256Hash();
363  if (sha256 == null) {
364  sha256 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
365  }
366  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sha256"), sha256);
367  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.hashLookupResults"), file.getKnown().toString());
368  addAcquisitionDetails(sb, dataSource);
369 
370  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.internalid"), Long.toString(file.getId()));
371  if (file.getType().compareTo(TSK_DB_FILES_TYPE_ENUM.LOCAL) == 0) {
372  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"), file.getLocalAbsPath());
373  }
374 
375  try {
376  List<BlackboardArtifact> associatedObjectArtifacts = file.getArtifacts(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
377  if (!associatedObjectArtifacts.isEmpty()) {
378  BlackboardArtifact artifact = associatedObjectArtifacts.get(0);
379  BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
380  if (associatedArtifactAttribute != null) {
381  long artifactId = associatedArtifactAttribute.getValueLong();
382  BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId);
383  addDownloadSourceRow(sb, associatedArtifact);
384  }
385  }
386  } catch (TskCoreException ex) {
387  sb.append(NbBundle.getMessage(this.getClass(), "Metadata.nodeText.exceptionNotice.text")).append(ex.getLocalizedMessage());
388  }
389 
390  endTable(sb);
391 
392  /*
393  * If we have a file system file, grab the more detailed
394  * metadata text too
395  */
396  if (file instanceof FsContent) {
397  FsContent fsFile = (FsContent) file;
398 
399  addHeader(sb, NbBundle.getMessage(this.getClass(), "Metadata.nodeText.text"), true);
400  startTable(sb);
401 
402  List<String> istatStrings = Collections.emptyList();
403  try {
404  istatStrings = fsFile.getMetaDataText();
405  } catch (TskCoreException ex) {
406  istatStrings = Arrays.asList(NbBundle.getMessage(this.getClass(), "Metadata.nodeText.exceptionNotice.text") + ex.getLocalizedMessage());
407  }
408 
409  for (String str : istatStrings) {
410  addMonospacedRow(sb, str);
411 
412  /*
413  * Very long results can cause the UI to hang before
414  * displaying, so truncate the results if necessary.
415  */
416  if (sb.length() > 50000) {
417  addMonospacedRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.nodeText.truncated"));
418  break;
419  }
420  }
421 
422  endTable(sb);
423  }
424 
425  } else {
426  try {
427  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), image.getUniquePath());
428  } catch (TskCoreException ex) {
429  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), image.getName());
430  }
431  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.imageType"), image.getType().getName());
432  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.size"), Long.toString(image.getSize()));
433 
434  try {
435  String md5 = image.getMd5();
436  if (md5 == null || md5.isEmpty()) {
437  md5 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
438  }
439  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5);
440 
441  String sha1 = image.getSha1();
442  if (sha1 == null || sha1.isEmpty()) {
443  sha1 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
444  }
445  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sha1"), sha1);
446 
447  String sha256 = image.getSha256();
448  if (sha256 == null || sha256.isEmpty()) {
449  sha256 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
450  }
451  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sha256"), sha256);
452  } catch (TskCoreException ex) {
453  sb.append(NbBundle.getMessage(this.getClass(), "Metadata.nodeText.exceptionNotice.text")).append(ex.getLocalizedMessage());
454  }
455  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sectorSize"), Long.toString(image.getSsize()));
456  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.timezone"), image.getTimeZone());
457  addAcquisitionDetails(sb, dataSource);
458  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.deviceId"), image.getDeviceId());
459  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.internalid"), Long.toString(image.getId()));
460 
461  // Add all the data source paths to the "Local Path" value cell.
462  String[] imagePaths = image.getPaths();
463 
464 
465  if (imagePaths.length > 0) {
466  addRowWithMultipleValues(sb,
467  NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"),
468  imagePaths);
469  } else {
470  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"),
471  NbBundle.getMessage(this.getClass(), "Metadata.nodeText.none"));
472  }
473 
474  endTable(sb);
475  }
476 
477  if (isCancelled()) {
478  return "";
479  }
480 
481  return sb.toString();
482  }
483  }
484 }
static String escapeHtml(String toEscape)
Definition: EscapeUtil.java:75
void addRow(StringBuilder sb, String key, String value)
Definition: Metadata.java:147
void addAcquisitionDetails(StringBuilder sb, DataSource dataSource)
Definition: Metadata.java:249
void addRowWithMultipleValues(StringBuilder sb, String key, String[] values)
Definition: Metadata.java:165
static String getFormattedTime(long epochTime)
void addHeader(StringBuilder sb, String header, boolean spaced)
Definition: Metadata.java:131
void addDownloadSourceRow(StringBuilder sb, BlackboardArtifact associatedArtifact)
Definition: Metadata.java:232
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void addMonospacedRow(StringBuilder sb, String key)
Definition: Metadata.java:157

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