Autopsy  4.14.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
DataContentViewerArtifact.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2018 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.corecomponents;
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.Collection;
30 import java.util.Enumeration;
31 import java.util.List;
32 import java.util.concurrent.ExecutionException;
33 import java.util.logging.Level;
34 import javax.swing.JMenuItem;
35 import javax.swing.JTextArea;
36 import javax.swing.SwingWorker;
37 import javax.swing.event.ChangeEvent;
38 import javax.swing.event.ListSelectionEvent;
39 import javax.swing.event.TableColumnModelEvent;
40 import javax.swing.table.DefaultTableModel;
41 import javax.swing.table.TableColumn;
42 import javax.swing.event.TableColumnModelListener;
43 import javax.swing.text.View;
44 import org.apache.commons.lang.StringUtils;
45 import org.openide.nodes.Node;
46 import org.openide.util.Lookup;
47 import org.openide.util.NbBundle;
48 import org.openide.util.lookup.ServiceProvider;
52 import org.sleuthkit.datamodel.BlackboardArtifact;
53 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
54 import org.sleuthkit.datamodel.BlackboardAttribute;
55 import org.sleuthkit.datamodel.Content;
56 import org.sleuthkit.datamodel.TskCoreException;
57 import org.sleuthkit.datamodel.TskException;
58 import org.netbeans.swing.etable.ETable;
59 import com.google.gson.JsonElement;
60 import com.google.gson.JsonObject;
61 import com.google.gson.JsonParser;
62 import com.google.gson.JsonArray;
63 import java.util.Map;
64 
70 @ServiceProvider(service = DataContentViewer.class, position = 7)
71 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
72 public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer {
73 
74  @NbBundle.Messages({
75  "DataContentViewerArtifact.attrsTableHeader.type=Type",
76  "DataContentViewerArtifact.attrsTableHeader.value=Value",
77  "DataContentViewerArtifact.attrsTableHeader.sources=Source(s)",
78  "DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
79  "DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
80  })
81  private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName());
82  private final static String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText");
83  private final static String ERROR_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.errorText");
84  private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed.
85  private int currentPage = 1;
86  private final Object lock = new Object();
87  private List<ResultsTableArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents()
88  SwingWorker<ViewUpdate, Void> currentTask; // Accessed by multiple threads, use startNewTask()
89  private static final String[] COLUMN_HEADERS = {
90  Bundle.DataContentViewerArtifact_attrsTableHeader_type(),
91  Bundle.DataContentViewerArtifact_attrsTableHeader_value(),
92  Bundle.DataContentViewerArtifact_attrsTableHeader_sources()};
93  private static final int[] COLUMN_WIDTHS = {100, 800, 100};
94  private static final int CELL_BOTTOM_MARGIN = 5;
95  private static final int CELL_RIGHT_MARGIN = 1;
96 
98  initResultsTable();
99  initComponents();
100  resultsTableScrollPane.setViewportView(resultsTable);
101  customizeComponents();
102  resetComponents();
103  resultsTable.setDefaultRenderer(Object.class, new MultiLineTableCellRenderer());
104  }
105 
106  private void initResultsTable() {
107  resultsTable = new ETable();
108  resultsTable.setModel(new javax.swing.table.DefaultTableModel() {
109  private static final long serialVersionUID = 1L;
110 
111  public boolean isCellEditable(int rowIndex, int columnIndex) {
112  return false;
113  }
114  });
115  resultsTable.setCellSelectionEnabled(true);
116  resultsTable.getTableHeader().setReorderingAllowed(false);
117  resultsTable.setColumnHidingAllowed(false);
118  resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
119  resultsTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
120 
121  @Override
122  public void columnAdded(TableColumnModelEvent e) {
123  }
124 
125  @Override
126  public void columnRemoved(TableColumnModelEvent e) {
127  }
128 
129  @Override
130  public void columnMoved(TableColumnModelEvent e) {
131 
132  }
133 
134  @Override
135  public void columnMarginChanged(ChangeEvent e) {
136  updateRowHeights(); //When the user changes column width we may need to resize row height
137  }
138 
139  @Override
140  public void columnSelectionChanged(ListSelectionEvent e) {
141  }
142  });
143  resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN);
144 
145  }
146 
150  private void updateRowHeights() {
151  int valueColIndex = -1;
152  for (int col = 0; col < resultsTable.getColumnCount(); col++) {
153  if (resultsTable.getColumnName(col).equals(COLUMN_HEADERS[1])) {
154  valueColIndex = col;
155  }
156  }
157  if (valueColIndex != -1) {
158  for (int row = 0; row < resultsTable.getRowCount(); row++) {
159  Component comp = resultsTable.prepareRenderer(
160  resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex);
161  final int rowHeight;
162  if (comp instanceof JTextArea) {
163  final JTextArea tc = (JTextArea) comp;
164  final View rootView = tc.getUI().getRootView(tc);
165  java.awt.Insets i = tc.getInsets();
166  rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex)
167  .getWidth() - (i.left + i.right +CELL_RIGHT_MARGIN), //current width minus borders
168  Integer.MAX_VALUE);
169  rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS);
170  } else {
171  rowHeight = comp.getPreferredSize().height;
172  }
173  if (rowHeight > 0) {
174  resultsTable.setRowHeight(row, rowHeight + CELL_BOTTOM_MARGIN);
175  }
176  }
177  }
178  }
179 
183  private void updateColumnSizes() {
184  Enumeration<TableColumn> columns = resultsTable.getColumnModel().getColumns();
185  while (columns.hasMoreElements()) {
186  TableColumn col = columns.nextElement();
187  if (col.getHeaderValue().equals(COLUMN_HEADERS[0])) {
188  col.setPreferredWidth(COLUMN_WIDTHS[0]);
189  } else if (col.getHeaderValue().equals(COLUMN_HEADERS[1])) {
190  col.setPreferredWidth(COLUMN_WIDTHS[1]);
191  } else if (col.getHeaderValue().equals(COLUMN_HEADERS[2])) {
192  col.setPreferredWidth(COLUMN_WIDTHS[2]);
193  }
194  }
195  }
196 
202  @SuppressWarnings("unchecked")
203  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
204  private void initComponents() {
205 
206  rightClickMenu = new javax.swing.JPopupMenu();
207  copyMenuItem = new javax.swing.JMenuItem();
208  selectAllMenuItem = new javax.swing.JMenuItem();
209  jScrollPane1 = new javax.swing.JScrollPane();
210  jPanel1 = new javax.swing.JPanel();
211  totalPageLabel = new javax.swing.JLabel();
212  ofLabel = new javax.swing.JLabel();
213  currentPageLabel = new javax.swing.JLabel();
214  pageLabel = new javax.swing.JLabel();
215  nextPageButton = new javax.swing.JButton();
216  pageLabel2 = new javax.swing.JLabel();
217  prevPageButton = new javax.swing.JButton();
218  artifactLabel = new javax.swing.JLabel();
219  resultsTableScrollPane = new javax.swing.JScrollPane();
220 
221  copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.copyMenuItem.text")); // NOI18N
222  rightClickMenu.add(copyMenuItem);
223 
224  selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.selectAllMenuItem.text")); // NOI18N
225  rightClickMenu.add(selectAllMenuItem);
226 
227  setPreferredSize(new java.awt.Dimension(100, 58));
228 
229  jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
230  jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
231 
232  jPanel1.setPreferredSize(new java.awt.Dimension(620, 58));
233 
234  totalPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.totalPageLabel.text")); // NOI18N
235 
236  ofLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.ofLabel.text")); // NOI18N
237 
238  currentPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.currentPageLabel.text")); // NOI18N
239  currentPageLabel.setMaximumSize(new java.awt.Dimension(18, 14));
240  currentPageLabel.setMinimumSize(new java.awt.Dimension(18, 14));
241  currentPageLabel.setPreferredSize(new java.awt.Dimension(18, 14));
242 
243  pageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel.text")); // NOI18N
244 
245  nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
246  nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.nextPageButton.text")); // NOI18N
247  nextPageButton.setBorderPainted(false);
248  nextPageButton.setContentAreaFilled(false);
249  nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
250  nextPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
251  nextPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
252  nextPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
253  nextPageButton.addActionListener(new java.awt.event.ActionListener() {
254  public void actionPerformed(java.awt.event.ActionEvent evt) {
255  nextPageButtonActionPerformed(evt);
256  }
257  });
258 
259  pageLabel2.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel2.text")); // NOI18N
260  pageLabel2.setMaximumSize(new java.awt.Dimension(29, 14));
261  pageLabel2.setMinimumSize(new java.awt.Dimension(29, 14));
262 
263  prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
264  prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.prevPageButton.text")); // NOI18N
265  prevPageButton.setBorderPainted(false);
266  prevPageButton.setContentAreaFilled(false);
267  prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
268  prevPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
269  prevPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
270  prevPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
271  prevPageButton.addActionListener(new java.awt.event.ActionListener() {
272  public void actionPerformed(java.awt.event.ActionEvent evt) {
273  prevPageButtonActionPerformed(evt);
274  }
275  });
276 
277  javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
278  jPanel1.setLayout(jPanel1Layout);
279  jPanel1Layout.setHorizontalGroup(
280  jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
281  .addGroup(jPanel1Layout.createSequentialGroup()
282  .addContainerGap()
283  .addComponent(pageLabel)
284  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
285  .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
286  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
287  .addComponent(ofLabel)
288  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
289  .addComponent(totalPageLabel)
290  .addGap(41, 41, 41)
291  .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
292  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
293  .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
294  .addGap(0, 0, 0)
295  .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
296  .addContainerGap(383, Short.MAX_VALUE))
297  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
298  .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
299  .addContainerGap(280, Short.MAX_VALUE)
300  .addComponent(artifactLabel)
301  .addContainerGap(84, Short.MAX_VALUE)))
302  );
303  jPanel1Layout.setVerticalGroup(
304  jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
305  .addGroup(jPanel1Layout.createSequentialGroup()
306  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
307  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
308  .addComponent(pageLabel)
309  .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
310  .addComponent(ofLabel)
311  .addComponent(totalPageLabel))
312  .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
313  .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
314  .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
315  .addContainerGap(35, Short.MAX_VALUE))
316  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
317  .addGroup(jPanel1Layout.createSequentialGroup()
318  .addComponent(artifactLabel)
319  .addGap(0, 58, Short.MAX_VALUE)))
320  );
321 
322  jScrollPane1.setViewportView(jPanel1);
323 
324  resultsTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
325  resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
326  resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(620, 34));
327 
328  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
329  this.setLayout(layout);
330  layout.setHorizontalGroup(
331  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
332  .addComponent(jScrollPane1)
333  .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
334  );
335  layout.setVerticalGroup(
336  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
337  .addGroup(layout.createSequentialGroup()
338  .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)
339  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
340  .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
341  );
342  }// </editor-fold>//GEN-END:initComponents
343 
344  private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
345  currentPage = currentPage + 1;
346  currentPageLabel.setText(Integer.toString(currentPage));
347  artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName());
348  startNewTask(new SelectedArtifactChangedTask(currentPage));
349  }//GEN-LAST:event_nextPageButtonActionPerformed
350 
351  private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
352  currentPage = currentPage - 1;
353  currentPageLabel.setText(Integer.toString(currentPage));
354  artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName());
355  startNewTask(new SelectedArtifactChangedTask(currentPage));
356  }//GEN-LAST:event_prevPageButtonActionPerformed
357 
358  // Variables declaration - do not modify//GEN-BEGIN:variables
359  private javax.swing.JLabel artifactLabel;
360  private javax.swing.JMenuItem copyMenuItem;
361  private javax.swing.JLabel currentPageLabel;
362  private javax.swing.JPanel jPanel1;
363  private javax.swing.JScrollPane jScrollPane1;
364  private javax.swing.JButton nextPageButton;
365  private javax.swing.JLabel ofLabel;
366  private javax.swing.JLabel pageLabel;
367  private javax.swing.JLabel pageLabel2;
368  private javax.swing.JButton prevPageButton;
369  private javax.swing.JScrollPane resultsTableScrollPane;
370  private javax.swing.JPopupMenu rightClickMenu;
371  private javax.swing.JMenuItem selectAllMenuItem;
372  private javax.swing.JLabel totalPageLabel;
373  // End of variables declaration//GEN-END:variables
374  private ETable resultsTable;
375 
376  private void customizeComponents() {
377  resultsTable.setComponentPopupMenu(rightClickMenu);
378  ActionListener actList = new ActionListener() {
379  @Override
380  public void actionPerformed(ActionEvent e) {
381  JMenuItem jmi = (JMenuItem) e.getSource();
382  if (jmi.equals(copyMenuItem)) {
383  StringBuilder selectedText = new StringBuilder(512);
384  for (int row : resultsTable.getSelectedRows()) {
385  for (int col : resultsTable.getSelectedColumns()) {
386  selectedText.append((String) resultsTable.getValueAt(row, col));
387  selectedText.append("\t");
388  }
389  //if its the last row selected don't add a new line
390  if (row != resultsTable.getSelectedRows()[resultsTable.getSelectedRows().length - 1]) {
391  selectedText.append(System.lineSeparator());
392  }
393  }
394  Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selectedText.toString()), null);
395  } else if (jmi.equals(selectAllMenuItem)) {
396  resultsTable.selectAll();
397  }
398  }
399  };
400  copyMenuItem.addActionListener(actList);
401 
402  selectAllMenuItem.addActionListener(actList);
403  }
404 
408  private void resetComponents() {
409  currentPage = 1;
410  currentPageLabel.setText("");
411  artifactLabel.setText("");
412  totalPageLabel.setText("");
413  ((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
414  prevPageButton.setEnabled(false);
415  nextPageButton.setEnabled(false);
416  currentNode = null;
417  }
418 
419  @Override
420  public void setNode(Node selectedNode) {
421  if (currentNode == selectedNode) {
422  return;
423  }
424  currentNode = selectedNode;
425 
426  // Make sure there is a node. Null might be passed to reset the viewer.
427  if (selectedNode == null) {
428  return;
429  }
430 
431  // Make sure the node is of the correct type.
432  Lookup lookup = selectedNode.getLookup();
433  Content content = lookup.lookup(Content.class);
434  if (content == null) {
435  return;
436  }
437 
438  startNewTask(new SelectedNodeChangedTask(selectedNode));
439  }
440 
441  @Override
442  public String getTitle() {
443  return NbBundle.getMessage(this.getClass(), "DataContentViewerArtifact.title");
444  }
445 
446  @Override
447  public String getToolTip() {
448  return NbBundle.getMessage(this.getClass(), "DataContentViewerArtifact.toolTip");
449  }
450 
451  @Override
452  public DataContentViewer createInstance() {
453  return new DataContentViewerArtifact();
454  }
455 
456  @Override
457  public Component getComponent() {
458  return this;
459  }
460 
461  @Override
462  public void resetComponent() {
463  resetComponents();
464  }
465 
466  @Override
467  public boolean isSupported(Node node) {
468  if (node == null) {
469  return false;
470  }
471 
472  for (Content content : node.getLookup().lookupAll(Content.class)) {
473  if ( (content != null) && (!(content instanceof BlackboardArtifact)) ){
474  try {
475  return content.getAllArtifactsCount() > 0;
476  } catch (TskException ex) {
477  logger.log(Level.SEVERE, "Couldn't get count of BlackboardArtifacts for content", ex); //NON-NLS
478  }
479  }
480  }
481  return false;
482  }
483 
484  @Override
485  public int isPreferred(Node node) {
486  BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
487  // low priority if node doesn't have an artifact (meaning it was found from normal directory
488  // browsing, or if the artifact is something that means the user really wants to see the original
489  // file and not more details about the artifact
490  if ((artifact == null)
491  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID())
492  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID())
493  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())
494  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID())
495  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID())
496  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID())) {
497  return 3;
498  } else {
499  return 6;
500  }
501  }
502 
507  private class ResultsTableArtifact {
508 
509  private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
510  private String[][] rowData = null;
511  private final String artifactDisplayName;
512  private final Content content;
513 
514  ResultsTableArtifact(BlackboardArtifact artifact, Content content) {
515  artifactDisplayName = artifact.getDisplayName();
516  this.content = content;
517  addRows(artifact);
518  }
519 
520  ResultsTableArtifact(String errorMsg) {
521  artifactDisplayName = errorMsg;
522  rowData = new String[1][3];
523  rowData[0] = new String[]{"", errorMsg, ""};
524  content = null;
525  }
526 
527  private String[][] getRows() {
528  return rowData;
529  }
530 
531  private void addRows(BlackboardArtifact artifact) {
532  List<String[]> rowsToAdd = new ArrayList<>();
533  try {
534  /*
535  * Add rows for each attribute.
536  */
537  for (BlackboardAttribute attr : artifact.getAttributes()) {
538  /*
539  * Attribute value column.
540  */
541  String value = "";
542  switch (attr.getAttributeType().getValueType()) {
543  case STRING:
544  case INTEGER:
545  case LONG:
546  case DOUBLE:
547  case BYTE:
548  default:
549  value = attr.getDisplayString();
550  break;
551  // Use Autopsy date formatting settings, not TSK defaults
552  case DATETIME:
553  value = epochTimeToString(attr.getValueLong());
554  break;
555  case JSON:
556  // Get the attribute's JSON value and convert to indented multiline display string
557  String jsonVal = attr.getValueString();
558  JsonParser parser = new JsonParser();
559  JsonObject json = parser.parse(jsonVal).getAsJsonObject();
560 
561  value = toJsonDisplayString(json, "");
562  break;
563  }
564  /*
565  * Attribute sources column.
566  */
567  String sources = StringUtils.join(attr.getSources(), ", ");
568  rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources});
569  }
570  /*
571  * Add a row for the source content path.
572  */
573  String path = "";
574  try {
575  if (null != content) {
576  path = content.getUniquePath();
577  }
578  } catch (TskCoreException ex) {
579  logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
580  path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message();
581  }
582  rowsToAdd.add(new String[]{"Source File Path", path, ""});
583  /*
584  * Add a row for the artifact id.
585  */
586  rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""});
587  } catch (TskCoreException ex) {
588  rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""});
589  }
590  rowData = rowsToAdd.toArray(new String[0][0]);
591  }
592 
596  String getArtifactDisplayName() {
597  return artifactDisplayName;
598  }
599 
600  private static final String INDENT_RIGHT = " ";
601  private static final String NEW_LINE = "\n";
602 
612  private String toJsonDisplayString(JsonElement element, String startIndent) {
613 
614  StringBuilder sb = new StringBuilder("");
615  JsonObject obj = element.getAsJsonObject();
616 
617  for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
618  appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb );
619  }
620 
621  String returnString = sb.toString();
622  if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
623  returnString = returnString.substring(NEW_LINE.length());
624  }
625  return returnString;
626  }
627 
628 
637  private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) {
638  if (jsonElement.isJsonArray()) {
639  JsonArray jsonArray = jsonElement.getAsJsonArray();
640  if (jsonArray.size() > 0) {
641  int count = 1;
642  sb.append(NEW_LINE).append(String.format("%s%s", startIndent, jsonKey));
643  for (JsonElement arrayMember : jsonArray) {
644  sb.append(NEW_LINE).append(String.format("%s%d", startIndent.concat(INDENT_RIGHT), count));
645  sb.append(toJsonDisplayString(arrayMember, startIndent.concat(INDENT_RIGHT).concat(INDENT_RIGHT)));
646  count++;
647  }
648  }
649  } else if (jsonElement.isJsonObject()) {
650  sb.append(NEW_LINE).append(String.format("%s%s %s", startIndent, jsonKey, toJsonDisplayString(jsonElement.getAsJsonObject(), startIndent + INDENT_RIGHT)));
651  } else if (jsonElement.isJsonPrimitive()) {
652  String attributeName = jsonKey;
653  String attributeValue;
654  if (attributeName.toUpperCase().contains("DATETIME")) {
655  attributeValue = epochTimeToString(Long.parseLong(jsonElement.getAsString()));
656  } else {
657  attributeValue = jsonElement.getAsString();
658  }
659  sb.append(NEW_LINE).append(String.format("%s%s = %s", startIndent, attributeName, attributeValue));
660  } else if (jsonElement.isJsonNull()) {
661  sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
662  }
663  }
664 
671  private String epochTimeToString(long epochTime) {
672  String dateTimeString = "0000-00-00 00:00:00";
673  if (null != content && 0 != epochTime) {
674  dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
675  dateTimeString = dateFormatter.format(new java.util.Date(epochTime * 1000));
676  }
677  return dateTimeString;
678  }
679 
680  }
681 
686  private class ViewUpdate {
687 
688  int numberOfPages;
689  int currentPage;
690  ResultsTableArtifact tableContents;
691 
692  ViewUpdate(int numberOfPages, int currentPage, ResultsTableArtifact contents) {
693  this.currentPage = currentPage;
694  this.numberOfPages = numberOfPages;
695  this.tableContents = contents;
696  }
697 
698  ViewUpdate(int numberOfPages, int currentPage, String errorMsg) {
699  this.currentPage = currentPage;
700  this.numberOfPages = numberOfPages;
701  this.tableContents = new ResultsTableArtifact(errorMsg);
702  }
703  }
704 
712  private void updateView(ViewUpdate viewUpdate) {
713  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
714 
715  nextPageButton.setEnabled(viewUpdate.currentPage < viewUpdate.numberOfPages);
716  prevPageButton.setEnabled(viewUpdate.currentPage > 1);
717  currentPage = viewUpdate.currentPage;
718  totalPageLabel.setText(Integer.toString(viewUpdate.numberOfPages));
719  currentPageLabel.setText(Integer.toString(currentPage));
720  artifactLabel.setText(viewUpdate.tableContents.getArtifactDisplayName());
721  DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
722  tModel.setDataVector(viewUpdate.tableContents.getRows(), COLUMN_HEADERS);
723  updateColumnSizes();
724  updateRowHeights();
725  resultsTable.clearSelection();
726 
727  this.setCursor(null);
728  }
729 
736  private synchronized void startNewTask(SwingWorker<ViewUpdate, Void> task) {
737  String[][] waitRow = new String[1][3];
738  waitRow[0] = new String[]{"", WAIT_TEXT, ""};
739  DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
740  tModel.setDataVector(waitRow, COLUMN_HEADERS);
741  updateColumnSizes();
742  updateRowHeights();
743  resultsTable.clearSelection();
744  // The output of the previous task is no longer relevant.
745  if (currentTask != null) {
746  // This call sets a cancellation flag. It does not terminate the background thread running the task.
747  // The task must check the cancellation flag and react appropriately.
748  currentTask.cancel(false);
749  }
750 
751  // Start the new task.
752  currentTask = task;
753  currentTask.execute();
754  }
755 
762  private void setArtifactContents(List<ResultsTableArtifact> artifactList) {
763  synchronized (lock) {
764  this.artifactTableContents = artifactList;
765  }
766  }
767 
773  private List<ResultsTableArtifact> getArtifactContents() {
774  synchronized (lock) {
775  return artifactTableContents;
776  }
777  }
778 
784  private class SelectedNodeChangedTask extends SwingWorker<ViewUpdate, Void> {
785 
786  private final Node selectedNode;
787 
788  SelectedNodeChangedTask(Node selectedNode) {
789  this.selectedNode = selectedNode;
790  }
791 
792  @Override
794  // Get the lookup for the node for access to its underlying content and
795  // blackboard artifact, if any.
796  Lookup lookup = selectedNode.getLookup();
797 
798  // Get the content. We may get BlackboardArtifacts, ignore those here.
799  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
800  Collection<? extends Content> contents = lookup.lookupAll(Content.class);
801  if (contents.isEmpty()) {
802  return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
803  }
804  Content underlyingContent = null;
805  for (Content content : contents) {
806  if ( (content != null) && (!(content instanceof BlackboardArtifact)) ) {
807  // Get all of the blackboard artifacts associated with the content. These are what this
808  // viewer displays.
809  try {
810  artifacts = content.getAllArtifacts();
811  underlyingContent = content;
812  break;
813  } catch (TskException ex) {
814  logger.log(Level.SEVERE, "Couldn't get artifacts", ex); //NON-NLS
815  return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
816  }
817  }
818  }
819 
820  if (isCancelled()) {
821  return null;
822  }
823 
824  // Build the new artifact contents cache.
825  ArrayList<ResultsTableArtifact> artifactContents = new ArrayList<>();
826  for (BlackboardArtifact artifact : artifacts) {
827  artifactContents.add(new ResultsTableArtifact(artifact, underlyingContent));
828  }
829 
830  // If the node has an underlying blackboard artifact, show it. If not,
831  // show the first artifact.
832  int index = 0;
833  BlackboardArtifact artifact = lookup.lookup(BlackboardArtifact.class);
834  if (artifact != null) {
835  index = artifacts.indexOf(artifact);
836  if (index == -1) {
837  index = 0;
838  } else {
839  // if the artifact has an ASSOCIATED ARTIFACT, then we display the associated artifact instead
840  try {
841  for (BlackboardAttribute attr : artifact.getAttributes()) {
842  if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
843  long assocArtifactId = attr.getValueLong();
844  int assocArtifactIndex = -1;
845  for (BlackboardArtifact art : artifacts) {
846  if (assocArtifactId == art.getArtifactID()) {
847  assocArtifactIndex = artifacts.indexOf(art);
848  break;
849  }
850  }
851  if (assocArtifactIndex >= 0) {
852  index = assocArtifactIndex;
853  }
854  break;
855  }
856  }
857  } catch (TskCoreException ex) {
858  logger.log(Level.WARNING, "Couldn't get associated artifact to display in Content Viewer.", ex); //NON-NLS
859  }
860  }
861 
862  }
863 
864  if (isCancelled()) {
865  return null;
866  }
867 
868  // Add one to the index of the artifact content for the corresponding page index.
869  ViewUpdate viewUpdate = new ViewUpdate(artifactContents.size(), index + 1, artifactContents.get(index));
870 
871  // It may take a considerable amount of time to fetch the attributes of the selected artifact
872  if (isCancelled()) {
873  return null;
874  }
875 
876  // Update the artifact contents cache.
877  setArtifactContents(artifactContents);
878 
879  return viewUpdate;
880  }
881 
882  @Override
883  protected void done() {
884  if (!isCancelled()) {
885  try {
886  ViewUpdate viewUpdate = get();
887  if (viewUpdate != null) {
888  updateView(viewUpdate);
889  }
890  } catch (InterruptedException | ExecutionException ex) {
891  logger.log(Level.WARNING, "Artifact display task unexpectedly interrupted or failed", ex); //NON-NLS
892  }
893  }
894  }
895  }
896 
902  private class SelectedArtifactChangedTask extends SwingWorker<ViewUpdate, Void> {
903 
904  private final int pageIndex;
905 
906  SelectedArtifactChangedTask(final int pageIndex) {
907  this.pageIndex = pageIndex;
908  }
909 
910  @Override
912  // Get the artifact content to display from the cache. Note that one must be subtracted from the
913  // page index to get the corresponding artifact content index.
914  List<ResultsTableArtifact> artifactContents = getArtifactContents();
915  ResultsTableArtifact artifactContent = artifactContents.get(pageIndex - 1);
916 
917  // It may take a considerable amount of time to fetch the attributes of the selected artifact so check for cancellation.
918  if (isCancelled()) {
919  return null;
920  }
921 
922  return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent);
923  }
924 
925  @Override
926  protected void done() {
927  if (!isCancelled()) {
928  try {
929  ViewUpdate viewUpdate = get();
930  if (viewUpdate != null) {
931  updateView(viewUpdate);
932  }
933  } catch (InterruptedException | ExecutionException ex) {
934  logger.log(Level.WARNING, "Artifact display task unexpectedly interrupted or failed", ex); //NON-NLS
935  }
936  }
937  }
938  }
939 
943  private class MultiLineTableCellRenderer implements javax.swing.table.TableCellRenderer {
944 
945  @Override
946  public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
947  javax.swing.JTextArea jtex = new javax.swing.JTextArea();
948  if (value instanceof String) {
949  jtex.setText((String) value);
950  jtex.setLineWrap(true);
951  jtex.setWrapStyleWord(false);
952  }
953  //cell backgroud color when selected
954  if (isSelected) {
955  jtex.setBackground(javax.swing.UIManager.getColor("Table.selectionBackground"));
956  } else {
957  jtex.setBackground(javax.swing.UIManager.getColor("Table.background"));
958  }
959  return jtex;
960  }
961  }
962 }
synchronized void startNewTask(SwingWorker< ViewUpdate, Void > task)
void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb)
void setArtifactContents(List< ResultsTableArtifact > artifactList)
static TimeZone getTimeZone(Content content)
Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

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