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;
43 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
45 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
51 import org.
sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
58 @ServiceProvider(service = DataContentViewer.class, position = 4)
59 @SuppressWarnings(
"PMD.SingularField")
71 customizeComponents();
80 @SuppressWarnings(
"unchecked")
82 private
void initComponents() {
84 jPopupMenu1 =
new javax.swing.JPopupMenu();
85 jScrollPane2 =
new javax.swing.JScrollPane();
86 jTextPane1 =
new javax.swing.JTextPane();
88 setPreferredSize(
new java.awt.Dimension(100, 52));
90 jScrollPane2.setPreferredSize(
new java.awt.Dimension(610, 52));
92 jTextPane1.setEditable(
false);
93 jTextPane1.setPreferredSize(
new java.awt.Dimension(600, 52));
94 jScrollPane2.setViewportView(jTextPane1);
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)
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)
128 jTextPane1.setText(
"<html><head></head><body>" + str +
"</body></html>");
131 private void addHeader(StringBuilder sb, String header,
boolean spaced) {
132 sb.append(MessageFormat.format(
"<div class=\"{0}\"><h1 class=\"{1}\">{2}</h1></div>",
139 sb.append(MessageFormat.format(
"<table class=\"{0}\" valign=\"top\" align=\"left\"><tbody>",
144 sb.append(
"</tbody></table>");
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>",
158 sb.append(MessageFormat.format(
"<tr><td class=\"{0}\"><span class=\"{1}\">{2}</span></td></tr>",
166 String[] safeValues = values == null || values.length < 1 ?
new String[]{
""} : values;
168 addRow(sb, key, safeValues[0]);
169 Stream.of(safeValues)
171 .filter(line -> line != null)
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..."})
193 if (worker != null) {
199 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
200 setText(Bundle.Metadata_nodeText_loading());
205 if (!isCancelled()) {
207 jTextPane1.setCaretPosition(0);
209 }
catch (InterruptedException | ExecutionException ex) {
210 LOGGER.log(Level.SEVERE,
"Failed to get metaData for node " + node.getName(), ex);
213 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
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());
250 if (dataSource != null) {
252 String details = dataSource.getAcquisitionDetails();
253 if (StringUtils.isEmpty(details)) {
254 details = Bundle.Metadata_nodeText_unknown();
256 String[] lines = (details != null) ? details.split(
"\n") :
new String[]{
""};
257 addRowWithMultipleValues(sb,
258 NbBundle.getMessage(
this.getClass(),
"Metadata.tableRowTitle.acquisitionDetails"),
261 }
catch (TskCoreException ex) {
262 LOGGER.log(Level.SEVERE,
"Error reading acquisition details from case database", ex);
269 return getTitle(null);
273 "Metadata_dataArtifactTitle=Source File Metadata"
277 if (node != null && !node.getLookup().lookupAll(DataArtifact.class).isEmpty()) {
278 return Bundle.Metadata_dataArtifactTitle();
280 return NbBundle.getMessage(this.getClass(),
"Metadata.title");
286 return NbBundle.getMessage(this.getClass(),
"Metadata.toolTip");
306 Image image = node.getLookup().lookup(Image.class);
307 AbstractFile file = node.getLookup().lookup(AbstractFile.class);
308 return (file != null) || (image != null);
327 @Messages(
"MetadataWorker.doInBackground.noDataMsg=No Data")
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");
337 StringBuilder sb =
new StringBuilder();
338 addHeader(sb, Bundle.Metadata_headerTitle(),
false);
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());
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());
358 String md5 = file.getMd5Hash();
360 md5 = NbBundle.getMessage(this.getClass(),
"Metadata.tableRowContent.md5notCalc");
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");
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);
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());
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);
387 }
catch (TskCoreException ex) {
388 sb.append(NbBundle.getMessage(
this.getClass(),
"Metadata.nodeText.exceptionNotice.text")).append(ex.getLocalizedMessage());
397 if (file instanceof FsContent) {
398 FsContent fsFile = (FsContent) file;
400 addHeader(sb, NbBundle.getMessage(
this.getClass(),
"Metadata.nodeText.text"),
true);
402 List<String> istatStrings = Collections.emptyList();
404 istatStrings = fsFile.getMetaDataText();
405 }
catch (TskCoreException ex) {
406 istatStrings = Arrays.asList(NbBundle.getMessage(
this.getClass(),
"Metadata.nodeText.exceptionNotice.text") + ex.getLocalizedMessage());
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()));
417 for (String str : istatStrings) {
418 addMonospacedRow(sb, str);
424 if (sb.length() > 50000) {
425 addMonospacedRow(sb, NbBundle.getMessage(
this.getClass(),
"Metadata.nodeText.truncated"));
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());
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()));
444 String md5 = image.getMd5();
445 if (md5 == null || md5.isEmpty()) {
446 md5 = NbBundle.getMessage(this.getClass(),
"Metadata.tableRowContent.md5notCalc");
448 addRow(sb, NbBundle.getMessage(
this.getClass(),
"Metadata.tableRowTitle.md5"), md5);
450 String sha1 = image.getSha1();
451 if (sha1 == null || sha1.isEmpty()) {
452 sha1 = NbBundle.getMessage(this.getClass(),
"Metadata.tableRowContent.md5notCalc");
454 addRow(sb, NbBundle.getMessage(
this.getClass(),
"Metadata.tableRowTitle.sha1"), sha1);
456 String sha256 = image.getSha256();
457 if (sha256 == null || sha256.isEmpty()) {
458 sha256 = NbBundle.getMessage(this.getClass(),
"Metadata.tableRowContent.md5notCalc");
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());
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()));
471 String[] imagePaths = image.getPaths();
473 if (imagePaths.length > 0) {
474 addRowWithMultipleValues(sb,
475 NbBundle.getMessage(
this.getClass(),
"Metadata.tableRowTitle.localPath"),
478 addRow(sb, NbBundle.getMessage(
this.getClass(),
"Metadata.tableRowTitle.localPath"),
479 NbBundle.getMessage(
this.getClass(),
"Metadata.nodeText.none"));
489 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)