Autopsy  4.15.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
DefaultArtifactContentViewer.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2020 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.artifactviewers;
20 
21 import java.awt.Component;
22 import java.awt.Cursor;
23 import java.awt.Toolkit;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.awt.datatransfer.StringSelection;
27 import java.text.SimpleDateFormat;
28 import java.util.ArrayList;
29 import java.util.Enumeration;
30 import java.util.List;
31 import java.util.logging.Level;
32 import javax.swing.JMenuItem;
33 import javax.swing.JTextArea;
34 import javax.swing.event.ChangeEvent;
35 import javax.swing.event.ListSelectionEvent;
36 import javax.swing.event.TableColumnModelEvent;
37 import javax.swing.table.DefaultTableModel;
38 import javax.swing.table.TableColumn;
39 import javax.swing.event.TableColumnModelListener;
40 import javax.swing.text.View;
41 import org.apache.commons.lang.StringUtils;
42 import org.openide.util.NbBundle;
45 import org.sleuthkit.datamodel.BlackboardArtifact;
46 import org.sleuthkit.datamodel.BlackboardAttribute;
47 import org.sleuthkit.datamodel.Content;
48 import org.sleuthkit.datamodel.TskCoreException;
49 import org.netbeans.swing.etable.ETable;
50 import com.google.gson.JsonElement;
51 import com.google.gson.JsonObject;
52 import com.google.gson.JsonParser;
53 import com.google.gson.JsonArray;
54 import java.util.Locale;
55 import java.util.Map;
56 import javax.swing.SwingUtilities;
57 //import org.sleuthkit.autopsy.contentviewers.Bundle;
58 
64 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
65 public class DefaultArtifactContentViewer extends javax.swing.JPanel implements ArtifactContentViewer {
66 
67  @NbBundle.Messages({
68  "DefaultArtifactContentViewer.attrsTableHeader.type=Type",
69  "DefaultArtifactContentViewer.attrsTableHeader.value=Value",
70  "DefaultArtifactContentViewer.attrsTableHeader.sources=Source(s)",
71  "DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
72  "DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
73  })
74 
75  private final static Logger logger = Logger.getLogger(DefaultArtifactContentViewer.class.getName());
76 
77  private static final long serialVersionUID = 1L;
78 
79  private static final String[] COLUMN_HEADERS = {
80  Bundle.DefaultArtifactContentViewer_attrsTableHeader_type(),
81  Bundle.DefaultArtifactContentViewer_attrsTableHeader_value(),
82  Bundle.DefaultArtifactContentViewer_attrsTableHeader_sources()};
83  private static final int[] COLUMN_WIDTHS = {100, 800, 100};
84  private static final int CELL_BOTTOM_MARGIN = 5;
85  private static final int CELL_RIGHT_MARGIN = 1;
86 
88  initResultsTable();
89  initComponents();
90  resultsTableScrollPane.setViewportView(resultsTable);
91  customizeComponents();
92  resetComponents();
93  resultsTable.setDefaultRenderer(Object.class, new MultiLineTableCellRenderer());
94  }
95 
96  private void initResultsTable() {
97  resultsTable = new ETable();
98  resultsTable.setModel(new javax.swing.table.DefaultTableModel() {
99  private static final long serialVersionUID = 1L;
100 
101  @Override
102  public boolean isCellEditable(int rowIndex, int columnIndex) {
103  return false;
104  }
105  });
106  resultsTable.setCellSelectionEnabled(true);
107  resultsTable.getTableHeader().setReorderingAllowed(false);
108  resultsTable.setColumnHidingAllowed(false);
109  resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
110  resultsTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
111 
112  @Override
113  public void columnAdded(TableColumnModelEvent e) {
114  // do nothing
115  }
116 
117  @Override
118  public void columnRemoved(TableColumnModelEvent e) {
119  // do nothing
120  }
121 
122  @Override
123  public void columnMoved(TableColumnModelEvent e) {
124  // do nothing
125  }
126 
127  @Override
128  public void columnMarginChanged(ChangeEvent e) {
129  updateRowHeights(); //When the user changes column width we may need to resize row height
130  }
131 
132  @Override
133  public void columnSelectionChanged(ListSelectionEvent e) {
134  // do nothing
135  }
136  });
137  resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN);
138 
139  }
140 
144  private void updateRowHeights() {
145  int valueColIndex = -1;
146  for (int col = 0; col < resultsTable.getColumnCount(); col++) {
147  if (resultsTable.getColumnName(col).equals(COLUMN_HEADERS[1])) {
148  valueColIndex = col;
149  }
150  }
151  if (valueColIndex != -1) {
152  for (int row = 0; row < resultsTable.getRowCount(); row++) {
153  Component comp = resultsTable.prepareRenderer(
154  resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex);
155  final int rowHeight;
156  if (comp instanceof JTextArea) {
157  final JTextArea tc = (JTextArea) comp;
158  final View rootView = tc.getUI().getRootView(tc);
159  java.awt.Insets i = tc.getInsets();
160  rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex)
161  .getWidth() - (i.left + i.right +CELL_RIGHT_MARGIN), //current width minus borders
162  Integer.MAX_VALUE);
163  rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS);
164  } else {
165  rowHeight = comp.getPreferredSize().height;
166  }
167  if (rowHeight > 0) {
168  resultsTable.setRowHeight(row, rowHeight + CELL_BOTTOM_MARGIN);
169  }
170  }
171  }
172  }
173 
177  private void updateColumnSizes() {
178  Enumeration<TableColumn> columns = resultsTable.getColumnModel().getColumns();
179  while (columns.hasMoreElements()) {
180  TableColumn col = columns.nextElement();
181  if (col.getHeaderValue().equals(COLUMN_HEADERS[0])) {
182  col.setPreferredWidth(COLUMN_WIDTHS[0]);
183  } else if (col.getHeaderValue().equals(COLUMN_HEADERS[1])) {
184  col.setPreferredWidth(COLUMN_WIDTHS[1]);
185  } else if (col.getHeaderValue().equals(COLUMN_HEADERS[2])) {
186  col.setPreferredWidth(COLUMN_WIDTHS[2]);
187  }
188  }
189  }
190 
196  @SuppressWarnings("unchecked")
197  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
198  private void initComponents() {
199 
200  rightClickMenu = new javax.swing.JPopupMenu();
201  copyMenuItem = new javax.swing.JMenuItem();
202  selectAllMenuItem = new javax.swing.JMenuItem();
203  resultsTableScrollPane = new javax.swing.JScrollPane();
204 
205  copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultArtifactContentViewer.class, "DefaultArtifactContentViewer.copyMenuItem.text")); // NOI18N
206  rightClickMenu.add(copyMenuItem);
207 
208  selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultArtifactContentViewer.class, "DefaultArtifactContentViewer.selectAllMenuItem.text")); // NOI18N
209  rightClickMenu.add(selectAllMenuItem);
210 
211  setPreferredSize(new java.awt.Dimension(100, 58));
212 
213  resultsTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
214  resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
215  resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(620, 34));
216 
217  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
218  this.setLayout(layout);
219  layout.setHorizontalGroup(
220  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
221  .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
222  );
223  layout.setVerticalGroup(
224  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
225  .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 58, Short.MAX_VALUE)
226  );
227  }// </editor-fold>//GEN-END:initComponents
228 
229  // Variables declaration - do not modify//GEN-BEGIN:variables
230  private javax.swing.JMenuItem copyMenuItem;
231  private javax.swing.JScrollPane resultsTableScrollPane;
232  private javax.swing.JPopupMenu rightClickMenu;
233  private javax.swing.JMenuItem selectAllMenuItem;
234  // End of variables declaration//GEN-END:variables
235  private ETable resultsTable;
236 
237  private void customizeComponents() {
238  resultsTable.setComponentPopupMenu(rightClickMenu);
239  ActionListener actList = new ActionListener() {
240  @Override
241  public void actionPerformed(ActionEvent e) {
242  JMenuItem jmi = (JMenuItem) e.getSource();
243  if (jmi.equals(copyMenuItem)) {
244  StringBuilder selectedText = new StringBuilder(512);
245  for (int row : resultsTable.getSelectedRows()) {
246  for (int col : resultsTable.getSelectedColumns()) {
247  selectedText.append((String) resultsTable.getValueAt(row, col));
248  selectedText.append('\t');
249  }
250  //if its the last row selected don't add a new line
251  if (row != resultsTable.getSelectedRows()[resultsTable.getSelectedRows().length - 1]) {
252  selectedText.append(System.lineSeparator());
253  }
254  }
255  Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selectedText.toString()), null);
256  } else if (jmi.equals(selectAllMenuItem)) {
257  resultsTable.selectAll();
258  }
259  }
260  };
261  copyMenuItem.addActionListener(actList);
262 
263  selectAllMenuItem.addActionListener(actList);
264  }
265 
269  private void resetComponents() {
270 
271  ((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
272  }
273 
274  @Override
275  public Component getComponent() {
276  return this;
277  }
278 
279  @Override
280  public void setArtifact(BlackboardArtifact artifact) {
281  try {
282  ResultsTableArtifact resultsTableArtifact = new ResultsTableArtifact(artifact, artifact.getParent());
283 
284  SwingUtilities.invokeLater(new Runnable() {
285  @Override
286  public void run() {
287  updateView(resultsTableArtifact);
288  }
289  });
290 
291  } catch (TskCoreException ex) {
292  logger.log(Level.SEVERE, String.format("Error getting parent content for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
293  }
294 
295  }
296 
297  @Override
298  public boolean isSupported(BlackboardArtifact artifact) {
299  // This viewer supports all artifacts.
300  return true;
301  }
302 
307  private class ResultsTableArtifact {
308 
309  private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
310  private String[][] rowData = null;
311  private final String artifactDisplayName;
312  private final Content content;
313 
314  ResultsTableArtifact(BlackboardArtifact artifact, Content content) {
315  artifactDisplayName = artifact.getDisplayName();
316  this.content = content;
317  addRows(artifact);
318  }
319 
320  ResultsTableArtifact(String errorMsg) {
321  artifactDisplayName = errorMsg;
322  rowData = new String[1][3];
323  rowData[0] = new String[]{"", errorMsg, ""};
324  content = null;
325  }
326 
327  private String[][] getRows() {
328  return rowData;
329  }
330 
331  private void addRows(BlackboardArtifact artifact) {
332  List<String[]> rowsToAdd = new ArrayList<>();
333  try {
334  /*
335  * Add rows for each attribute.
336  */
337  for (BlackboardAttribute attr : artifact.getAttributes()) {
338  /*
339  * Attribute value column.
340  */
341  String value;
342  switch (attr.getAttributeType().getValueType()) {
343 
344  // Use Autopsy date formatting settings, not TSK defaults
345  case DATETIME:
346  value = epochTimeToString(attr.getValueLong());
347  break;
348  case JSON:
349  // Get the attribute's JSON value and convert to indented multiline display string
350  String jsonVal = attr.getValueString();
351  JsonParser parser = new JsonParser();
352  JsonObject json = parser.parse(jsonVal).getAsJsonObject();
353 
354  value = toJsonDisplayString(json, "");
355  break;
356 
357  case STRING:
358  case INTEGER:
359  case LONG:
360  case DOUBLE:
361  case BYTE:
362  default:
363  value = attr.getDisplayString();
364  break;
365  }
366  /*
367  * Attribute sources column.
368  */
369  String sources = StringUtils.join(attr.getSources(), ", ");
370  rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources});
371  }
372  /*
373  * Add a row for the source content path.
374  */
375  String path = "";
376  try {
377  if (null != content) {
378  path = content.getUniquePath();
379  }
380  } catch (TskCoreException ex) {
381  logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
382  path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message();
383  }
384  rowsToAdd.add(new String[]{"Source File Path", path, ""});
385  /*
386  * Add a row for the artifact id.
387  */
388  rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""});
389  } catch (TskCoreException ex) {
390  rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""});
391  }
392  rowData = rowsToAdd.toArray(new String[0][0]);
393  }
394 
398  String getArtifactDisplayName() {
399  return artifactDisplayName;
400  }
401 
402  private static final String INDENT_RIGHT = " ";
403  private static final String NEW_LINE = "\n";
404 
414  private String toJsonDisplayString(JsonElement element, String startIndent) {
415 
416  StringBuilder sb = new StringBuilder("");
417  JsonObject obj = element.getAsJsonObject();
418 
419  for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
420  appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb );
421  }
422 
423  String returnString = sb.toString();
424  if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
425  returnString = returnString.substring(NEW_LINE.length());
426  }
427  return returnString;
428  }
429 
430 
439  private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) {
440  if (jsonElement.isJsonArray()) {
441  JsonArray jsonArray = jsonElement.getAsJsonArray();
442  if (jsonArray.size() > 0) {
443  int count = 1;
444  sb.append(NEW_LINE).append(String.format("%s%s", startIndent, jsonKey));
445  for (JsonElement arrayMember : jsonArray) {
446  sb.append(NEW_LINE).append(String.format("%s%d", startIndent.concat(INDENT_RIGHT), count));
447  sb.append(toJsonDisplayString(arrayMember, startIndent.concat(INDENT_RIGHT).concat(INDENT_RIGHT)));
448  count++;
449  }
450  }
451  } else if (jsonElement.isJsonObject()) {
452  sb.append(NEW_LINE).append(String.format("%s%s %s", startIndent, jsonKey, toJsonDisplayString(jsonElement.getAsJsonObject(), startIndent + INDENT_RIGHT)));
453  } else if (jsonElement.isJsonPrimitive()) {
454  String attributeName = jsonKey;
455  String attributeValue;
456  if (attributeName.toUpperCase().contains("DATETIME")) {
457  attributeValue = epochTimeToString(Long.parseLong(jsonElement.getAsString()));
458  } else {
459  attributeValue = jsonElement.getAsString();
460  }
461  sb.append(NEW_LINE).append(String.format("%s%s = %s", startIndent, attributeName, attributeValue));
462  } else if (jsonElement.isJsonNull()) {
463  sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
464  }
465  }
466 
473  private String epochTimeToString(long epochTime) {
474  String dateTimeString = "0000-00-00 00:00:00";
475  if (null != content && 0 != epochTime) {
476  dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
477  dateTimeString = dateFormatter.format(new java.util.Date(epochTime * 1000));
478  }
479  return dateTimeString;
480  }
481 
482  }
483 
491  private void updateView(ResultsTableArtifact resultsTableArtifact) {
492  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
493 
494  DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
495  tModel.setDataVector(resultsTableArtifact.getRows(), COLUMN_HEADERS);
496  updateColumnSizes();
497  updateRowHeights();
498  resultsTable.clearSelection();
499 
500  this.setCursor(null);
501  }
502 
506  private class MultiLineTableCellRenderer implements javax.swing.table.TableCellRenderer {
507 
508  @Override
509  public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
510  javax.swing.JTextArea jtex = new javax.swing.JTextArea();
511  if (value instanceof String) {
512  jtex.setText((String) value);
513  jtex.setLineWrap(true);
514  jtex.setWrapStyleWord(false);
515  }
516  //cell backgroud color when selected
517  if (isSelected) {
518  jtex.setBackground(javax.swing.UIManager.getColor("Table.selectionBackground"));
519  } else {
520  jtex.setBackground(javax.swing.UIManager.getColor("Table.background"));
521  }
522  return jtex;
523  }
524  }
525 }
Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
static TimeZone getTimeZone(Content content)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb)

Copyright © 2012-2020 Basis Technology. Generated on: Mon Jul 6 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.