Autopsy  4.12.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 
65 @ServiceProvider(service = DataContentViewer.class, position = 7)
66 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
67 public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer {
68 
69  @NbBundle.Messages({
70  "DataContentViewerArtifact.attrsTableHeader.type=Type",
71  "DataContentViewerArtifact.attrsTableHeader.value=Value",
72  "DataContentViewerArtifact.attrsTableHeader.sources=Source(s)",
73  "DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
74  "DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
75  })
76  private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName());
77  private final static String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText");
78  private final static String ERROR_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.errorText");
79  private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed.
80  private int currentPage = 1;
81  private final Object lock = new Object();
82  private List<ResultsTableArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents()
83  SwingWorker<ViewUpdate, Void> currentTask; // Accessed by multiple threads, use startNewTask()
84  private static final String[] COLUMN_HEADERS = {
85  Bundle.DataContentViewerArtifact_attrsTableHeader_type(),
86  Bundle.DataContentViewerArtifact_attrsTableHeader_value(),
87  Bundle.DataContentViewerArtifact_attrsTableHeader_sources()};
88  private static final int[] COLUMN_WIDTHS = {100, 800, 100};
89  private static final int CELL_BOTTOM_MARGIN = 5;
90  private static final int CELL_RIGHT_MARGIN = 1;
91 
93  initResultsTable();
94  initComponents();
95  resultsTableScrollPane.setViewportView(resultsTable);
96  customizeComponents();
97  resetComponents();
98  resultsTable.setDefaultRenderer(Object.class, new MultiLineTableCellRenderer());
99  }
100 
101  private void initResultsTable() {
102  resultsTable = new ETable();
103  resultsTable.setModel(new javax.swing.table.DefaultTableModel() {
104  private static final long serialVersionUID = 1L;
105 
106  public boolean isCellEditable(int rowIndex, int columnIndex) {
107  return false;
108  }
109  });
110  resultsTable.setCellSelectionEnabled(true);
111  resultsTable.getTableHeader().setReorderingAllowed(false);
112  resultsTable.setColumnHidingAllowed(false);
113  resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
114  resultsTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
115 
116  @Override
117  public void columnAdded(TableColumnModelEvent e) {
118  }
119 
120  @Override
121  public void columnRemoved(TableColumnModelEvent e) {
122  }
123 
124  @Override
125  public void columnMoved(TableColumnModelEvent e) {
126 
127  }
128 
129  @Override
130  public void columnMarginChanged(ChangeEvent e) {
131  updateRowHeights(); //When the user changes column width we may need to resize row height
132  }
133 
134  @Override
135  public void columnSelectionChanged(ListSelectionEvent e) {
136  }
137  });
138  resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN);
139 
140  }
141 
145  private void updateRowHeights() {
146  int valueColIndex = -1;
147  for (int col = 0; col < resultsTable.getColumnCount(); col++) {
148  if (resultsTable.getColumnName(col).equals(COLUMN_HEADERS[1])) {
149  valueColIndex = col;
150  }
151  }
152  if (valueColIndex != -1) {
153  for (int row = 0; row < resultsTable.getRowCount(); row++) {
154  Component comp = resultsTable.prepareRenderer(
155  resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex);
156  final int rowHeight;
157  if (comp instanceof JTextArea) {
158  final JTextArea tc = (JTextArea) comp;
159  final View rootView = tc.getUI().getRootView(tc);
160  java.awt.Insets i = tc.getInsets();
161  rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex)
162  .getWidth() - (i.left + i.right +CELL_RIGHT_MARGIN), //current width minus borders
163  Integer.MAX_VALUE);
164  rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS);
165  } else {
166  rowHeight = comp.getPreferredSize().height;
167  }
168  if (rowHeight > 0) {
169  resultsTable.setRowHeight(row, rowHeight + CELL_BOTTOM_MARGIN);
170  }
171  }
172  }
173  }
174 
178  private void updateColumnSizes() {
179  Enumeration<TableColumn> columns = resultsTable.getColumnModel().getColumns();
180  while (columns.hasMoreElements()) {
181  TableColumn col = columns.nextElement();
182  if (col.getHeaderValue().equals(COLUMN_HEADERS[0])) {
183  col.setPreferredWidth(COLUMN_WIDTHS[0]);
184  } else if (col.getHeaderValue().equals(COLUMN_HEADERS[1])) {
185  col.setPreferredWidth(COLUMN_WIDTHS[1]);
186  } else if (col.getHeaderValue().equals(COLUMN_HEADERS[2])) {
187  col.setPreferredWidth(COLUMN_WIDTHS[2]);
188  }
189  }
190  }
191 
197  @SuppressWarnings("unchecked")
198  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
199  private void initComponents() {
200 
201  rightClickMenu = new javax.swing.JPopupMenu();
202  copyMenuItem = new javax.swing.JMenuItem();
203  selectAllMenuItem = new javax.swing.JMenuItem();
204  jScrollPane1 = new javax.swing.JScrollPane();
205  jPanel1 = new javax.swing.JPanel();
206  totalPageLabel = new javax.swing.JLabel();
207  ofLabel = new javax.swing.JLabel();
208  currentPageLabel = new javax.swing.JLabel();
209  pageLabel = new javax.swing.JLabel();
210  nextPageButton = new javax.swing.JButton();
211  pageLabel2 = new javax.swing.JLabel();
212  prevPageButton = new javax.swing.JButton();
213  artifactLabel = new javax.swing.JLabel();
214  resultsTableScrollPane = new javax.swing.JScrollPane();
215 
216  copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.copyMenuItem.text")); // NOI18N
217  rightClickMenu.add(copyMenuItem);
218 
219  selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.selectAllMenuItem.text")); // NOI18N
220  rightClickMenu.add(selectAllMenuItem);
221 
222  setPreferredSize(new java.awt.Dimension(100, 58));
223 
224  jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
225  jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
226 
227  jPanel1.setPreferredSize(new java.awt.Dimension(620, 58));
228 
229  totalPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.totalPageLabel.text")); // NOI18N
230 
231  ofLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.ofLabel.text")); // NOI18N
232 
233  currentPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.currentPageLabel.text")); // NOI18N
234  currentPageLabel.setMaximumSize(new java.awt.Dimension(18, 14));
235  currentPageLabel.setMinimumSize(new java.awt.Dimension(18, 14));
236  currentPageLabel.setPreferredSize(new java.awt.Dimension(18, 14));
237 
238  pageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel.text")); // NOI18N
239 
240  nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
241  nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.nextPageButton.text")); // NOI18N
242  nextPageButton.setBorderPainted(false);
243  nextPageButton.setContentAreaFilled(false);
244  nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
245  nextPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
246  nextPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
247  nextPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
248  nextPageButton.addActionListener(new java.awt.event.ActionListener() {
249  public void actionPerformed(java.awt.event.ActionEvent evt) {
250  nextPageButtonActionPerformed(evt);
251  }
252  });
253 
254  pageLabel2.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel2.text")); // NOI18N
255  pageLabel2.setMaximumSize(new java.awt.Dimension(29, 14));
256  pageLabel2.setMinimumSize(new java.awt.Dimension(29, 14));
257 
258  prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
259  prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.prevPageButton.text")); // NOI18N
260  prevPageButton.setBorderPainted(false);
261  prevPageButton.setContentAreaFilled(false);
262  prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
263  prevPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
264  prevPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
265  prevPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
266  prevPageButton.addActionListener(new java.awt.event.ActionListener() {
267  public void actionPerformed(java.awt.event.ActionEvent evt) {
268  prevPageButtonActionPerformed(evt);
269  }
270  });
271 
272  javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
273  jPanel1.setLayout(jPanel1Layout);
274  jPanel1Layout.setHorizontalGroup(
275  jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
276  .addGroup(jPanel1Layout.createSequentialGroup()
277  .addContainerGap()
278  .addComponent(pageLabel)
279  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
280  .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
281  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
282  .addComponent(ofLabel)
283  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
284  .addComponent(totalPageLabel)
285  .addGap(41, 41, 41)
286  .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
287  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
288  .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
289  .addGap(0, 0, 0)
290  .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
291  .addContainerGap(383, Short.MAX_VALUE))
292  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
293  .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
294  .addContainerGap(280, Short.MAX_VALUE)
295  .addComponent(artifactLabel)
296  .addContainerGap(84, Short.MAX_VALUE)))
297  );
298  jPanel1Layout.setVerticalGroup(
299  jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
300  .addGroup(jPanel1Layout.createSequentialGroup()
301  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
302  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
303  .addComponent(pageLabel)
304  .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
305  .addComponent(ofLabel)
306  .addComponent(totalPageLabel))
307  .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
308  .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
309  .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
310  .addContainerGap(35, Short.MAX_VALUE))
311  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
312  .addGroup(jPanel1Layout.createSequentialGroup()
313  .addComponent(artifactLabel)
314  .addGap(0, 58, Short.MAX_VALUE)))
315  );
316 
317  jScrollPane1.setViewportView(jPanel1);
318 
319  resultsTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
320  resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
321  resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(620, 34));
322 
323  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
324  this.setLayout(layout);
325  layout.setHorizontalGroup(
326  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
327  .addComponent(jScrollPane1)
328  .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
329  );
330  layout.setVerticalGroup(
331  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
332  .addGroup(layout.createSequentialGroup()
333  .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)
334  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
335  .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
336  );
337  }// </editor-fold>//GEN-END:initComponents
338 
339  private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
340  currentPage = currentPage + 1;
341  currentPageLabel.setText(Integer.toString(currentPage));
342  artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName());
343  startNewTask(new SelectedArtifactChangedTask(currentPage));
344  }//GEN-LAST:event_nextPageButtonActionPerformed
345 
346  private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
347  currentPage = currentPage - 1;
348  currentPageLabel.setText(Integer.toString(currentPage));
349  artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName());
350  startNewTask(new SelectedArtifactChangedTask(currentPage));
351  }//GEN-LAST:event_prevPageButtonActionPerformed
352 
353  // Variables declaration - do not modify//GEN-BEGIN:variables
354  private javax.swing.JLabel artifactLabel;
355  private javax.swing.JMenuItem copyMenuItem;
356  private javax.swing.JLabel currentPageLabel;
357  private javax.swing.JPanel jPanel1;
358  private javax.swing.JScrollPane jScrollPane1;
359  private javax.swing.JButton nextPageButton;
360  private javax.swing.JLabel ofLabel;
361  private javax.swing.JLabel pageLabel;
362  private javax.swing.JLabel pageLabel2;
363  private javax.swing.JButton prevPageButton;
364  private javax.swing.JScrollPane resultsTableScrollPane;
365  private javax.swing.JPopupMenu rightClickMenu;
366  private javax.swing.JMenuItem selectAllMenuItem;
367  private javax.swing.JLabel totalPageLabel;
368  // End of variables declaration//GEN-END:variables
369  private ETable resultsTable;
370 
371  private void customizeComponents() {
372  resultsTable.setComponentPopupMenu(rightClickMenu);
373  ActionListener actList = new ActionListener() {
374  @Override
375  public void actionPerformed(ActionEvent e) {
376  JMenuItem jmi = (JMenuItem) e.getSource();
377  if (jmi.equals(copyMenuItem)) {
378  StringBuilder selectedText = new StringBuilder(512);
379  for (int row : resultsTable.getSelectedRows()) {
380  for (int col : resultsTable.getSelectedColumns()) {
381  selectedText.append((String) resultsTable.getValueAt(row, col));
382  selectedText.append("\t");
383  }
384  //if its the last row selected don't add a new line
385  if (row != resultsTable.getSelectedRows()[resultsTable.getSelectedRows().length - 1]) {
386  selectedText.append(System.lineSeparator());
387  }
388  }
389  Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selectedText.toString()), null);
390  } else if (jmi.equals(selectAllMenuItem)) {
391  resultsTable.selectAll();
392  }
393  }
394  };
395  copyMenuItem.addActionListener(actList);
396 
397  selectAllMenuItem.addActionListener(actList);
398  }
399 
403  private void resetComponents() {
404  currentPage = 1;
405  currentPageLabel.setText("");
406  artifactLabel.setText("");
407  totalPageLabel.setText("");
408  ((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
409  prevPageButton.setEnabled(false);
410  nextPageButton.setEnabled(false);
411  currentNode = null;
412  }
413 
414  @Override
415  public void setNode(Node selectedNode) {
416  if (currentNode == selectedNode) {
417  return;
418  }
419  currentNode = selectedNode;
420 
421  // Make sure there is a node. Null might be passed to reset the viewer.
422  if (selectedNode == null) {
423  return;
424  }
425 
426  // Make sure the node is of the correct type.
427  Lookup lookup = selectedNode.getLookup();
428  Content content = lookup.lookup(Content.class);
429  if (content == null) {
430  return;
431  }
432 
433  startNewTask(new SelectedNodeChangedTask(selectedNode));
434  }
435 
436  @Override
437  public String getTitle() {
438  return NbBundle.getMessage(this.getClass(), "DataContentViewerArtifact.title");
439  }
440 
441  @Override
442  public String getToolTip() {
443  return NbBundle.getMessage(this.getClass(), "DataContentViewerArtifact.toolTip");
444  }
445 
446  @Override
447  public DataContentViewer createInstance() {
448  return new DataContentViewerArtifact();
449  }
450 
451  @Override
452  public Component getComponent() {
453  return this;
454  }
455 
456  @Override
457  public void resetComponent() {
458  resetComponents();
459  }
460 
461  @Override
462  public boolean isSupported(Node node) {
463  if (node == null) {
464  return false;
465  }
466 
467  for (Content content : node.getLookup().lookupAll(Content.class)) {
468  if ( (content != null) && (!(content instanceof BlackboardArtifact)) ){
469  try {
470  return content.getAllArtifactsCount() > 0;
471  } catch (TskException ex) {
472  logger.log(Level.SEVERE, "Couldn't get count of BlackboardArtifacts for content", ex); //NON-NLS
473  }
474  }
475  }
476  return false;
477  }
478 
479  @Override
480  public int isPreferred(Node node) {
481  BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
482  // low priority if node doesn't have an artifact (meaning it was found from normal directory
483  // browsing, or if the artifact is something that means the user really wants to see the original
484  // file and not more details about the artifact
485  if ((artifact == null)
486  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID())
487  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID())
488  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())
489  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID())
490  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID())
491  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID())) {
492  return 3;
493  } else {
494  return 6;
495  }
496  }
497 
502  private class ResultsTableArtifact {
503 
504  private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
505  private String[][] rowData = null;
506  private final String artifactDisplayName;
507  private final Content content;
508 
509  ResultsTableArtifact(BlackboardArtifact artifact, Content content) {
510  artifactDisplayName = artifact.getDisplayName();
511  this.content = content;
512  addRows(artifact);
513  }
514 
515  ResultsTableArtifact(String errorMsg) {
516  artifactDisplayName = errorMsg;
517  rowData = new String[1][3];
518  rowData[0] = new String[]{"", errorMsg, ""};
519  content = null;
520  }
521 
522  private String[][] getRows() {
523  return rowData;
524  }
525 
526  private void addRows(BlackboardArtifact artifact) {
527  List<String[]> rowsToAdd = new ArrayList<>();
528  try {
529  /*
530  * Add rows for each attribute.
531  */
532  for (BlackboardAttribute attr : artifact.getAttributes()) {
533  /*
534  * Attribute value column.
535  */
536  String value = "";
537  switch (attr.getAttributeType().getValueType()) {
538  case STRING:
539  case INTEGER:
540  case LONG:
541  case DOUBLE:
542  case BYTE:
543  default:
544  value = attr.getDisplayString();
545  break;
546  // Use Autopsy date formatting settings, not TSK defaults
547  case DATETIME:
548  long epoch = attr.getValueLong();
549  value = "0000-00-00 00:00:00";
550  if (null != content && 0 != epoch) {
551  dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
552  value = dateFormatter.format(new java.util.Date(epoch * 1000));
553  }
554  break;
555  }
556  /*
557  * Attribute sources column.
558  */
559  String sources = StringUtils.join(attr.getSources(), ", ");
560  rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources});
561  }
562  /*
563  * Add a row for the source content path.
564  */
565  String path = "";
566  try {
567  if (null != content) {
568  path = content.getUniquePath();
569  }
570  } catch (TskCoreException ex) {
571  logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
572  path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message();
573  }
574  rowsToAdd.add(new String[]{"Source File Path", path, ""});
575  /*
576  * Add a row for the artifact id.
577  */
578  rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""});
579  } catch (TskCoreException ex) {
580  rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""});
581  }
582  rowData = rowsToAdd.toArray(new String[0][0]);
583  }
584 
588  String getArtifactDisplayName() {
589  return artifactDisplayName;
590  }
591  }
592 
597  private class ViewUpdate {
598 
599  int numberOfPages;
600  int currentPage;
601  ResultsTableArtifact tableContents;
602 
603  ViewUpdate(int numberOfPages, int currentPage, ResultsTableArtifact contents) {
604  this.currentPage = currentPage;
605  this.numberOfPages = numberOfPages;
606  this.tableContents = contents;
607  }
608 
609  ViewUpdate(int numberOfPages, int currentPage, String errorMsg) {
610  this.currentPage = currentPage;
611  this.numberOfPages = numberOfPages;
612  this.tableContents = new ResultsTableArtifact(errorMsg);
613  }
614  }
615 
623  private void updateView(ViewUpdate viewUpdate) {
624  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
625 
626  nextPageButton.setEnabled(viewUpdate.currentPage < viewUpdate.numberOfPages);
627  prevPageButton.setEnabled(viewUpdate.currentPage > 1);
628  currentPage = viewUpdate.currentPage;
629  totalPageLabel.setText(Integer.toString(viewUpdate.numberOfPages));
630  currentPageLabel.setText(Integer.toString(currentPage));
631  artifactLabel.setText(viewUpdate.tableContents.getArtifactDisplayName());
632  DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
633  tModel.setDataVector(viewUpdate.tableContents.getRows(), COLUMN_HEADERS);
634  updateColumnSizes();
635  updateRowHeights();
636  resultsTable.clearSelection();
637 
638  this.setCursor(null);
639  }
640 
647  private synchronized void startNewTask(SwingWorker<ViewUpdate, Void> task) {
648  String[][] waitRow = new String[1][3];
649  waitRow[0] = new String[]{"", WAIT_TEXT, ""};
650  DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
651  tModel.setDataVector(waitRow, COLUMN_HEADERS);
652  updateColumnSizes();
653  updateRowHeights();
654  resultsTable.clearSelection();
655  // The output of the previous task is no longer relevant.
656  if (currentTask != null) {
657  // This call sets a cancellation flag. It does not terminate the background thread running the task.
658  // The task must check the cancellation flag and react appropriately.
659  currentTask.cancel(false);
660  }
661 
662  // Start the new task.
663  currentTask = task;
664  currentTask.execute();
665  }
666 
673  private void setArtifactContents(List<ResultsTableArtifact> artifactList) {
674  synchronized (lock) {
675  this.artifactTableContents = artifactList;
676  }
677  }
678 
684  private List<ResultsTableArtifact> getArtifactContents() {
685  synchronized (lock) {
686  return artifactTableContents;
687  }
688  }
689 
695  private class SelectedNodeChangedTask extends SwingWorker<ViewUpdate, Void> {
696 
697  private final Node selectedNode;
698 
699  SelectedNodeChangedTask(Node selectedNode) {
700  this.selectedNode = selectedNode;
701  }
702 
703  @Override
705  // Get the lookup for the node for access to its underlying content and
706  // blackboard artifact, if any.
707  Lookup lookup = selectedNode.getLookup();
708 
709  // Get the content. We may get BlackboardArtifacts, ignore those here.
710  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
711  Collection<? extends Content> contents = lookup.lookupAll(Content.class);
712  if (contents.isEmpty()) {
713  return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
714  }
715  Content underlyingContent = null;
716  for (Content content : contents) {
717  if ( (content != null) && (!(content instanceof BlackboardArtifact)) ) {
718  // Get all of the blackboard artifacts associated with the content. These are what this
719  // viewer displays.
720  try {
721  artifacts = content.getAllArtifacts();
722  underlyingContent = content;
723  break;
724  } catch (TskException ex) {
725  logger.log(Level.SEVERE, "Couldn't get artifacts", ex); //NON-NLS
726  return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
727  }
728  }
729  }
730 
731  if (isCancelled()) {
732  return null;
733  }
734 
735  // Build the new artifact contents cache.
736  ArrayList<ResultsTableArtifact> artifactContents = new ArrayList<>();
737  for (BlackboardArtifact artifact : artifacts) {
738  artifactContents.add(new ResultsTableArtifact(artifact, underlyingContent));
739  }
740 
741  // If the node has an underlying blackboard artifact, show it. If not,
742  // show the first artifact.
743  int index = 0;
744  BlackboardArtifact artifact = lookup.lookup(BlackboardArtifact.class);
745  if (artifact != null) {
746  index = artifacts.indexOf(artifact);
747  if (index == -1) {
748  index = 0;
749  } else {
750  // if the artifact has an ASSOCIATED ARTIFACT, then we display the associated artifact instead
751  try {
752  for (BlackboardAttribute attr : artifact.getAttributes()) {
753  if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
754  long assocArtifactId = attr.getValueLong();
755  int assocArtifactIndex = -1;
756  for (BlackboardArtifact art : artifacts) {
757  if (assocArtifactId == art.getArtifactID()) {
758  assocArtifactIndex = artifacts.indexOf(art);
759  break;
760  }
761  }
762  if (assocArtifactIndex >= 0) {
763  index = assocArtifactIndex;
764  }
765  break;
766  }
767  }
768  } catch (TskCoreException ex) {
769  logger.log(Level.WARNING, "Couldn't get associated artifact to display in Content Viewer.", ex); //NON-NLS
770  }
771  }
772 
773  }
774 
775  if (isCancelled()) {
776  return null;
777  }
778 
779  // Add one to the index of the artifact content for the corresponding page index.
780  ViewUpdate viewUpdate = new ViewUpdate(artifactContents.size(), index + 1, artifactContents.get(index));
781 
782  // It may take a considerable amount of time to fetch the attributes of the selected artifact
783  if (isCancelled()) {
784  return null;
785  }
786 
787  // Update the artifact contents cache.
788  setArtifactContents(artifactContents);
789 
790  return viewUpdate;
791  }
792 
793  @Override
794  protected void done() {
795  if (!isCancelled()) {
796  try {
797  ViewUpdate viewUpdate = get();
798  if (viewUpdate != null) {
799  updateView(viewUpdate);
800  }
801  } catch (InterruptedException | ExecutionException ex) {
802  logger.log(Level.WARNING, "Artifact display task unexpectedly interrupted or failed", ex); //NON-NLS
803  }
804  }
805  }
806  }
807 
813  private class SelectedArtifactChangedTask extends SwingWorker<ViewUpdate, Void> {
814 
815  private final int pageIndex;
816 
817  SelectedArtifactChangedTask(final int pageIndex) {
818  this.pageIndex = pageIndex;
819  }
820 
821  @Override
823  // Get the artifact content to display from the cache. Note that one must be subtracted from the
824  // page index to get the corresponding artifact content index.
825  List<ResultsTableArtifact> artifactContents = getArtifactContents();
826  ResultsTableArtifact artifactContent = artifactContents.get(pageIndex - 1);
827 
828  // It may take a considerable amount of time to fetch the attributes of the selected artifact so check for cancellation.
829  if (isCancelled()) {
830  return null;
831  }
832 
833  return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent);
834  }
835 
836  @Override
837  protected void done() {
838  if (!isCancelled()) {
839  try {
840  ViewUpdate viewUpdate = get();
841  if (viewUpdate != null) {
842  updateView(viewUpdate);
843  }
844  } catch (InterruptedException | ExecutionException ex) {
845  logger.log(Level.WARNING, "Artifact display task unexpectedly interrupted or failed", ex); //NON-NLS
846  }
847  }
848  }
849  }
850 
854  private class MultiLineTableCellRenderer implements javax.swing.table.TableCellRenderer {
855 
856  @Override
857  public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
858  javax.swing.JTextArea jtex = new javax.swing.JTextArea();
859  if (value instanceof String) {
860  jtex.setText((String) value);
861  jtex.setLineWrap(true);
862  jtex.setWrapStyleWord(false);
863  }
864  //cell backgroud color when selected
865  if (isSelected) {
866  jtex.setBackground(javax.swing.UIManager.getColor("Table.selectionBackground"));
867  } else {
868  jtex.setBackground(javax.swing.UIManager.getColor("Table.background"));
869  }
870  return jtex;
871  }
872  }
873 }
synchronized void startNewTask(SwingWorker< ViewUpdate, Void > task)
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-2018 Basis Technology. Generated on: Wed Sep 18 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.