Autopsy  4.19.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  @Messages("MetadataWorker.doInBackground.noDataMsg=No Data")
328  @Override
329  protected String doInBackground() throws Exception {
330  AbstractFile file = node.getLookup().lookup(AbstractFile.class);
331  Image image = node.getLookup().lookup(Image.class);
332  DataSource dataSource = node.getLookup().lookup(DataSource.class);
333  if (file == null && image == null) {
334  return NbBundle.getMessage(this.getClass(), "Metadata.nodeText.nonFilePassedIn");
335  }
336 
337  StringBuilder sb = new StringBuilder();
338  addHeader(sb, Bundle.Metadata_headerTitle(), false);
339  startTable(sb);
340 
341  if (file != null) {
342  try {
343  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), file.getUniquePath());
344  } catch (TskCoreException ex) {
345  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), file.getParentPath() + "/" + file.getName());
346  }
347 
348  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.type"), file.getType().getName());
349  addRow(sb, Bundle.Metadata_tableRowTitle_mimeType(), file.getMIMEType());
350  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.size"), Long.toString(file.getSize()));
351  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.fileNameAlloc"), file.getDirFlagAsString());
352  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.metadataAlloc"), file.getMetaFlagsAsString());
353  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.modified"), TimeZoneUtils.getFormattedTime(file.getMtime()));
354  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.accessed"), TimeZoneUtils.getFormattedTime(file.getAtime()));
355  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.created"), TimeZoneUtils.getFormattedTime(file.getCrtime()));
356  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.changed"), TimeZoneUtils.getFormattedTime(file.getCtime()));
357 
358  String md5 = file.getMd5Hash();
359  if (md5 == null) {
360  md5 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
361  }
362  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5);
363  String sha256 = file.getSha256Hash();
364  if (sha256 == null) {
365  sha256 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
366  }
367  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sha256"), sha256);
368  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.hashLookupResults"), file.getKnown().toString());
369  addAcquisitionDetails(sb, dataSource);
370 
371  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.internalid"), Long.toString(file.getId()));
372  if (file.getType().compareTo(TSK_DB_FILES_TYPE_ENUM.LOCAL) == 0) {
373  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"), file.getLocalAbsPath());
374  }
375 
376  try {
377  List<BlackboardArtifact> associatedObjectArtifacts = file.getArtifacts(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
378  if (!associatedObjectArtifacts.isEmpty()) {
379  BlackboardArtifact artifact = associatedObjectArtifacts.get(0);
380  BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
381  if (associatedArtifactAttribute != null) {
382  long artifactId = associatedArtifactAttribute.getValueLong();
383  BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId);
384  addDownloadSourceRow(sb, associatedArtifact);
385  }
386  }
387  } catch (TskCoreException ex) {
388  sb.append(NbBundle.getMessage(this.getClass(), "Metadata.nodeText.exceptionNotice.text")).append(ex.getLocalizedMessage());
389  }
390 
391  endTable(sb);
392 
393  /*
394  * If we have a file system file, grab the more detailed
395  * metadata text too
396  */
397  if (file instanceof FsContent) {
398  FsContent fsFile = (FsContent) file;
399 
400  addHeader(sb, NbBundle.getMessage(this.getClass(), "Metadata.nodeText.text"), true);
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  if (istatStrings.isEmpty() || (istatStrings.size() == 1 && StringUtils.isEmpty(istatStrings.get(0)))) {
410  sb.append(MessageFormat.format("<div class=\"{0}\"><p class=\"{1}\">{2}</p><div>",
413  Bundle.MetadataWorker_doInBackground_noDataMsg()));
414  } else {
415  startTable(sb);
416 
417  for (String str : istatStrings) {
418  addMonospacedRow(sb, str);
419 
420  /*
421  * Very long results can cause the UI to hang before
422  * displaying, so truncate the results if necessary.
423  */
424  if (sb.length() > 50000) {
425  addMonospacedRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.nodeText.truncated"));
426  break;
427  }
428  }
429 
430  endTable(sb);
431  }
432  }
433 
434  } else {
435  try {
436  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), image.getUniquePath());
437  } catch (TskCoreException ex) {
438  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), image.getName());
439  }
440  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.imageType"), image.getType().getName());
441  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.size"), Long.toString(image.getSize()));
442 
443  try {
444  String md5 = image.getMd5();
445  if (md5 == null || md5.isEmpty()) {
446  md5 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
447  }
448  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5);
449 
450  String sha1 = image.getSha1();
451  if (sha1 == null || sha1.isEmpty()) {
452  sha1 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
453  }
454  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sha1"), sha1);
455 
456  String sha256 = image.getSha256();
457  if (sha256 == null || sha256.isEmpty()) {
458  sha256 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
459  }
460  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sha256"), sha256);
461  } catch (TskCoreException ex) {
462  sb.append(NbBundle.getMessage(this.getClass(), "Metadata.nodeText.exceptionNotice.text")).append(ex.getLocalizedMessage());
463  }
464  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sectorSize"), Long.toString(image.getSsize()));
465  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.timezone"), image.getTimeZone());
466  addAcquisitionDetails(sb, dataSource);
467  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.deviceId"), image.getDeviceId());
468  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.internalid"), Long.toString(image.getId()));
469 
470  // Add all the data source paths to the "Local Path" value cell.
471  String[] imagePaths = image.getPaths();
472 
473  if (imagePaths.length > 0) {
474  addRowWithMultipleValues(sb,
475  NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"),
476  imagePaths);
477  } else {
478  addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"),
479  NbBundle.getMessage(this.getClass(), "Metadata.nodeText.none"));
480  }
481 
482  endTable(sb);
483  }
484 
485  if (isCancelled()) {
486  return "";
487  }
488 
489  return sb.toString();
490  }
491  }
492 }
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: Fri Aug 6 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.