Autopsy 4.22.1
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 */
19package org.sleuthkit.autopsy.contentviewers;
20
21import java.awt.Component;
22import java.awt.Cursor;
23import java.text.MessageFormat;
24import java.util.Arrays;
25import java.util.Collections;
26import java.util.List;
27import java.util.concurrent.ExecutionException;
28import java.util.logging.Level;
29import java.util.stream.Stream;
30import javax.swing.SwingWorker;
31import org.apache.commons.lang3.StringUtils;
32import org.openide.nodes.Node;
33import org.openide.util.NbBundle;
34import org.openide.util.NbBundle.Messages;
35import org.openide.util.lookup.ServiceProvider;
36import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerHtmlStyles;
37import org.sleuthkit.autopsy.contentviewers.utils.ViewerPriority;
38import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
39import org.sleuthkit.autopsy.coreutils.EscapeUtil;
40import org.sleuthkit.autopsy.coreutils.Logger;
41import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
42import org.sleuthkit.datamodel.AbstractFile;
43import org.sleuthkit.datamodel.BlackboardArtifact;
44import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
45import org.sleuthkit.datamodel.BlackboardAttribute;
46import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
47import org.sleuthkit.datamodel.DataArtifact;
48import org.sleuthkit.datamodel.DataSource;
49import org.sleuthkit.datamodel.Image;
50import org.sleuthkit.datamodel.FsContent;
51import org.sleuthkit.datamodel.TskCoreException;
52import 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
61public class Metadata extends javax.swing.JPanel implements DataContentViewer {
62
63 private static final Logger LOGGER = Logger.getLogger(Metadata.class.getName());
64
66
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>",
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[]{""};
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
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) {
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}
void addAcquisitionDetails(StringBuilder sb, DataSource dataSource)
void addMonospacedRow(StringBuilder sb, String key)
void addRowWithMultipleValues(StringBuilder sb, String key, String[] values)
void addRow(StringBuilder sb, String key, String value)
void addDownloadSourceRow(StringBuilder sb, BlackboardArtifact associatedArtifact)
void addHeader(StringBuilder sb, String header, boolean spaced)
static String escapeHtml(String toEscape)
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static String getFormattedTime(long epochTime)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.