19 package org.sleuthkit.autopsy.contentviewers;
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;
44 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
46 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
52 import org.
sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
59 @ServiceProvider(service = DataContentViewer.class, position = 4)
60 @SuppressWarnings(
"PMD.SingularField")
72 customizeComponents();
81 @SuppressWarnings(
"unchecked")
83 private
void initComponents() {
85 jPopupMenu1 =
new javax.swing.JPopupMenu();
86 jScrollPane2 =
new javax.swing.JScrollPane();
87 jTextPane1 =
new javax.swing.JTextPane();
89 setPreferredSize(
new java.awt.Dimension(100, 52));
91 jScrollPane2.setPreferredSize(
new java.awt.Dimension(610, 52));
93 jTextPane1.setEditable(
false);
94 jTextPane1.setPreferredSize(
new java.awt.Dimension(600, 52));
95 jScrollPane2.setViewportView(jTextPane1);
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)
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)
129 jTextPane1.setText(
"<html><head></head><body>" + str +
"</body></html>");
132 private void addHeader(StringBuilder sb, String header,
boolean spaced) {
133 sb.append(MessageFormat.format(
"<div class=\"{0}\"><h1 class=\"{1}\">{2}</h1></div>",
140 sb.append(MessageFormat.format(
"<table class=\"{0}\" valign=\"top\" align=\"left\"><tbody>",
145 sb.append(
"</tbody></table>");
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>",
159 sb.append(MessageFormat.format(
"<tr><td class=\"{0}\"><span class=\"{1}\">{2}</span></td></tr>",
167 String[] safeValues = values == null || values.length < 1 ?
new String[]{
""} : values;
169 addRow(sb, key, safeValues[0]);
170 Stream.of(safeValues)
172 .filter(line -> line != null)
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..."})
194 if (worker != null) {
200 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
201 setText(Bundle.Metadata_nodeText_loading());
206 if (!isCancelled()) {
208 jTextPane1.setCaretPosition(0);
210 }
catch (InterruptedException | ExecutionException ex) {
211 LOGGER.log(Level.SEVERE,
"Failed to get metaData for node " + node.getName(), ex);
214 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
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());
251 if (dataSource != null) {
253 String details = dataSource.getAcquisitionDetails();
254 if (StringUtils.isEmpty(details)) {
255 details = Bundle.Metadata_nodeText_unknown();
257 String[] lines = (details != null) ? details.split(
"\n") :
new String[]{
""};
258 addRowWithMultipleValues(sb,
259 NbBundle.getMessage(
this.getClass(),
"Metadata.tableRowTitle.acquisitionDetails"),
262 }
catch (TskCoreException ex) {
263 LOGGER.log(Level.SEVERE,
"Error reading acquisition details from case database", ex);
270 return getTitle(null);
274 "Metadata_dataArtifactTitle=Source File Metadata"
278 if (node != null && !node.getLookup().lookupAll(DataArtifact.class).isEmpty()) {
279 return Bundle.Metadata_dataArtifactTitle();
281 return NbBundle.getMessage(this.getClass(),
"Metadata.title");
287 return NbBundle.getMessage(this.getClass(),
"Metadata.toolTip");
307 Image image = node.getLookup().lookup(Image.class);
308 AbstractFile file = node.getLookup().lookup(AbstractFile.class);
309 return (file != null) || (image != null);
328 @Messages(
"MetadataWorker.doInBackground.noDataMsg=No Data")
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");
338 StringBuilder sb =
new StringBuilder();
339 addHeader(sb, Bundle.Metadata_headerTitle(),
false);
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());
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());
359 String md5 = file.getMd5Hash();
361 md5 = NbBundle.getMessage(this.getClass(),
"Metadata.tableRowContent.md5notCalc");
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");
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);
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());
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);
388 }
catch (TskCoreException ex) {
389 sb.append(NbBundle.getMessage(
this.getClass(),
"Metadata.nodeText.exceptionNotice.text")).append(ex.getLocalizedMessage());
398 if (file instanceof FsContent) {
399 FsContent fsFile = (FsContent) file;
401 addHeader(sb, NbBundle.getMessage(
this.getClass(),
"Metadata.nodeText.text"),
true);
403 List<String> istatStrings = Collections.emptyList();
405 istatStrings = fsFile.getMetaDataText();
406 }
catch (TskCoreException ex) {
407 istatStrings = Arrays.asList(NbBundle.getMessage(
this.getClass(),
"Metadata.nodeText.exceptionNotice.text") + ex.getLocalizedMessage());
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()));
418 for (String str : istatStrings) {
419 addMonospacedRow(sb, str);
425 if (sb.length() > 50000) {
426 addMonospacedRow(sb, NbBundle.getMessage(
this.getClass(),
"Metadata.nodeText.truncated"));
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());
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()));
445 String md5 = image.getMd5();
446 if (md5 == null || md5.isEmpty()) {
447 md5 = NbBundle.getMessage(this.getClass(),
"Metadata.tableRowContent.md5notCalc");
449 addRow(sb, NbBundle.getMessage(
this.getClass(),
"Metadata.tableRowTitle.md5"), md5);
451 String sha1 = image.getSha1();
452 if (sha1 == null || sha1.isEmpty()) {
453 sha1 = NbBundle.getMessage(this.getClass(),
"Metadata.tableRowContent.md5notCalc");
455 addRow(sb, NbBundle.getMessage(
this.getClass(),
"Metadata.tableRowTitle.sha1"), sha1);
457 String sha256 = image.getSha256();
458 if (sha256 == null || sha256.isEmpty()) {
459 sha256 = NbBundle.getMessage(this.getClass(),
"Metadata.tableRowContent.md5notCalc");
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());
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()));
472 String[] imagePaths = image.getPaths();
474 if (imagePaths.length > 0) {
475 addRowWithMultipleValues(sb,
476 NbBundle.getMessage(
this.getClass(),
"Metadata.tableRowTitle.localPath"),
479 addRow(sb, NbBundle.getMessage(
this.getClass(),
"Metadata.tableRowTitle.localPath"),
480 NbBundle.getMessage(
this.getClass(),
"Metadata.nodeText.none"));
490 return sb.toString();
static String escapeHtml(String toEscape)
void endTable(StringBuilder sb)
static String getSpacedSectionClassName()
void addRow(StringBuilder sb, String key, String value)
static String getTextClassName()
void addAcquisitionDetails(StringBuilder sb, DataSource dataSource)
void addRowWithMultipleValues(StringBuilder sb, String key, String[] values)
static String getFormattedTime(long epochTime)
javax.swing.JTextPane jTextPane1
javax.swing.JPopupMenu jPopupMenu1
static String getKeyColumnClassName()
static String getIndentedClassName()
void customizeComponents()
String getTitle(Node node)
boolean isSupported(Node node)
void addHeader(StringBuilder sb, String header, boolean spaced)
void addDownloadSourceRow(StringBuilder sb, BlackboardArtifact associatedArtifact)
static void setupHtmlJTextPane(JTextPane textPane)
DataContentViewer createInstance()
int isPreferred(Node node)
static String getHeaderClassName()
javax.swing.JScrollPane jScrollPane2
static String getMonospacedClassName()
synchronized static Logger getLogger(String name)
void startTable(StringBuilder sb)
void addMonospacedRow(StringBuilder sb, String key)