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

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.