Autopsy  4.4
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-2017 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.List;
30 import java.util.concurrent.ExecutionException;
31 import java.util.logging.Level;
32 import javax.swing.JMenuItem;
33 import javax.swing.SwingWorker;
34 import javax.swing.table.DefaultTableModel;
35 import org.apache.commons.lang.StringUtils;
36 import org.openide.nodes.Node;
37 import org.openide.util.Lookup;
38 import org.openide.util.NbBundle;
39 import org.openide.util.lookup.ServiceProvider;
43 import org.sleuthkit.datamodel.BlackboardArtifact;
44 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
45 import org.sleuthkit.datamodel.BlackboardAttribute;
46 import org.sleuthkit.datamodel.Content;
47 import org.sleuthkit.datamodel.TskCoreException;
48 import org.sleuthkit.datamodel.TskException;
49 import org.netbeans.swing.etable.ETable;
50 
56 @ServiceProvider(service = DataContentViewer.class, position = 3)
57 public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer {
58 
59  @NbBundle.Messages({
60  "DataContentViewerArtifact.attrsTableHeader.type=Type",
61  "DataContentViewerArtifact.attrsTableHeader.value=Value",
62  "DataContentViewerArtifact.attrsTableHeader.sources=Source(s)",
63  "DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
64  "DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
65  })
66  private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName());
67  private final static String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText");
68  private final static String ERROR_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.errorText");
69  private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed.
70  private int currentPage = 1;
71  private final Object lock = new Object();
72  private List<ResultsTableArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents()
73  SwingWorker<ViewUpdate, Void> currentTask; // Accessed by multiple threads, use startNewTask()
74  private static final String[] COLUMN_HEADERS = {
75  Bundle.DataContentViewerArtifact_attrsTableHeader_type(),
76  Bundle.DataContentViewerArtifact_attrsTableHeader_value(),
77  Bundle.DataContentViewerArtifact_attrsTableHeader_sources()};
78 
80  initResultsTable();
81  initComponents();
82  resultsTableScrollPane.setViewportView(resultsTable);
83  customizeComponents();
84  resetComponents();
85  }
86 
87  private void initResultsTable() {
88  resultsTable = new ETable();
89  resultsTable.setModel(new javax.swing.table.DefaultTableModel() {
90  private static final long serialVersionUID = 1L;
91 
92  public boolean isCellEditable(int rowIndex, int columnIndex) {
93  return false;
94  }
95  });
96  resultsTable.setCellSelectionEnabled(true);
97  resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
98  updateColumnSizes();
99  }
100 
101  private void updateColumnSizes() {
102  resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN);
103  if (resultsTable.getColumnModel().getColumnCount() > 0) {
104  resultsTable.getColumnModel().getColumn(0).setPreferredWidth(100);
105  resultsTable.getColumnModel().getColumn(1).setPreferredWidth(800);
106  resultsTable.getColumnModel().getColumn(2).setPreferredWidth(100);
107  }
108  }
109 
115  @SuppressWarnings("unchecked")
116  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
117  private void initComponents() {
118 
119  rightClickMenu = new javax.swing.JPopupMenu();
120  copyMenuItem = new javax.swing.JMenuItem();
121  selectAllMenuItem = new javax.swing.JMenuItem();
122  jScrollPane1 = new javax.swing.JScrollPane();
123  jPanel1 = new javax.swing.JPanel();
124  totalPageLabel = new javax.swing.JLabel();
125  ofLabel = new javax.swing.JLabel();
126  currentPageLabel = new javax.swing.JLabel();
127  pageLabel = new javax.swing.JLabel();
128  nextPageButton = new javax.swing.JButton();
129  pageLabel2 = new javax.swing.JLabel();
130  prevPageButton = new javax.swing.JButton();
131  resultsTableScrollPane = new javax.swing.JScrollPane();
132  artifactLabel = new javax.swing.JLabel();
133 
134  copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.copyMenuItem.text")); // NOI18N
135  rightClickMenu.add(copyMenuItem);
136 
137  selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.selectAllMenuItem.text")); // NOI18N
138  rightClickMenu.add(selectAllMenuItem);
139 
140  setPreferredSize(new java.awt.Dimension(622, 58));
141 
142  jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
143  jScrollPane1.setPreferredSize(new java.awt.Dimension(622, 58));
144 
145  jPanel1.setPreferredSize(new java.awt.Dimension(620, 58));
146 
147  totalPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.totalPageLabel.text")); // NOI18N
148 
149  ofLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.ofLabel.text")); // NOI18N
150 
151  currentPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.currentPageLabel.text")); // NOI18N
152  currentPageLabel.setMaximumSize(new java.awt.Dimension(18, 14));
153  currentPageLabel.setMinimumSize(new java.awt.Dimension(18, 14));
154  currentPageLabel.setPreferredSize(new java.awt.Dimension(18, 14));
155 
156  pageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel.text")); // NOI18N
157  pageLabel.setMaximumSize(new java.awt.Dimension(33, 14));
158  pageLabel.setMinimumSize(new java.awt.Dimension(33, 14));
159  pageLabel.setPreferredSize(new java.awt.Dimension(33, 14));
160 
161  nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
162  nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.nextPageButton.text")); // NOI18N
163  nextPageButton.setBorderPainted(false);
164  nextPageButton.setContentAreaFilled(false);
165  nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
166  nextPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
167  nextPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
168  nextPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
169  nextPageButton.addActionListener(new java.awt.event.ActionListener() {
170  public void actionPerformed(java.awt.event.ActionEvent evt) {
171  nextPageButtonActionPerformed(evt);
172  }
173  });
174 
175  pageLabel2.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel2.text")); // NOI18N
176  pageLabel2.setMaximumSize(new java.awt.Dimension(29, 14));
177  pageLabel2.setMinimumSize(new java.awt.Dimension(29, 14));
178  pageLabel2.setPreferredSize(new java.awt.Dimension(29, 14));
179 
180  prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
181  prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.prevPageButton.text")); // NOI18N
182  prevPageButton.setBorderPainted(false);
183  prevPageButton.setContentAreaFilled(false);
184  prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
185  prevPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
186  prevPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
187  prevPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
188  prevPageButton.addActionListener(new java.awt.event.ActionListener() {
189  public void actionPerformed(java.awt.event.ActionEvent evt) {
190  prevPageButtonActionPerformed(evt);
191  }
192  });
193 
194  resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(620, 271));
195 
196  javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
197  jPanel1.setLayout(jPanel1Layout);
198  jPanel1Layout.setHorizontalGroup(
199  jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
200  .addGroup(jPanel1Layout.createSequentialGroup()
201  .addContainerGap()
202  .addComponent(pageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)
203  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
204  .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
205  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
206  .addComponent(ofLabel)
207  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
208  .addComponent(totalPageLabel)
209  .addGap(41, 41, 41)
210  .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 38, javax.swing.GroupLayout.PREFERRED_SIZE)
211  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
212  .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
213  .addGap(0, 0, 0)
214  .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
215  .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
216  .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
217  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
218  .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
219  .addContainerGap(277, Short.MAX_VALUE)
220  .addComponent(artifactLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 258, javax.swing.GroupLayout.PREFERRED_SIZE)
221  .addContainerGap(85, Short.MAX_VALUE)))
222  );
223  jPanel1Layout.setVerticalGroup(
224  jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
225  .addGroup(jPanel1Layout.createSequentialGroup()
226  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
227  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
228  .addComponent(pageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
229  .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
230  .addComponent(ofLabel)
231  .addComponent(totalPageLabel))
232  .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
233  .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
234  .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
235  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
236  .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 34, Short.MAX_VALUE))
237  .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
238  .addGroup(jPanel1Layout.createSequentialGroup()
239  .addComponent(artifactLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
240  .addGap(0, 40, Short.MAX_VALUE)))
241  );
242 
243  jScrollPane1.setViewportView(jPanel1);
244 
245  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
246  this.setLayout(layout);
247  layout.setHorizontalGroup(
248  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
249  .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
250  );
251  layout.setVerticalGroup(
252  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
253  .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
254  );
255  }// </editor-fold>//GEN-END:initComponents
256 
257  private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
258  currentPage = currentPage - 1;
259  currentPageLabel.setText(Integer.toString(currentPage));
260  artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName());
261  startNewTask(new SelectedArtifactChangedTask(currentPage));
262  }//GEN-LAST:event_prevPageButtonActionPerformed
263 
264  private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
265  currentPage = currentPage + 1;
266  currentPageLabel.setText(Integer.toString(currentPage));
267  artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName());
268  startNewTask(new SelectedArtifactChangedTask(currentPage));
269  }//GEN-LAST:event_nextPageButtonActionPerformed
270 
271  // Variables declaration - do not modify//GEN-BEGIN:variables
272  private javax.swing.JLabel artifactLabel;
273  private javax.swing.JMenuItem copyMenuItem;
274  private javax.swing.JLabel currentPageLabel;
275  private javax.swing.JPanel jPanel1;
276  private javax.swing.JScrollPane jScrollPane1;
277  private javax.swing.JButton nextPageButton;
278  private javax.swing.JLabel ofLabel;
279  private javax.swing.JLabel pageLabel;
280  private javax.swing.JLabel pageLabel2;
281  private javax.swing.JButton prevPageButton;
282  private javax.swing.JScrollPane resultsTableScrollPane;
283  private javax.swing.JPopupMenu rightClickMenu;
284  private javax.swing.JMenuItem selectAllMenuItem;
285  private javax.swing.JLabel totalPageLabel;
286  // End of variables declaration//GEN-END:variables
287  private ETable resultsTable;
288 
289  private void customizeComponents() {
290  resultsTable.setComponentPopupMenu(rightClickMenu);
291  ActionListener actList = new ActionListener() {
292  @Override
293  public void actionPerformed(ActionEvent e) {
294  JMenuItem jmi = (JMenuItem) e.getSource();
295  if (jmi.equals(copyMenuItem)) {
296  StringBuilder selectedText = new StringBuilder(512);
297  for (int row : resultsTable.getSelectedRows()) {
298  for (int col : resultsTable.getSelectedColumns()) {
299  selectedText.append((String) resultsTable.getValueAt(row, col));
300  selectedText.append("\t");
301  }
302  //if its the last row selected don't add a new line
303  if (row != resultsTable.getSelectedRows()[resultsTable.getSelectedRows().length - 1]) {
304  selectedText.append(System.lineSeparator());
305  }
306  }
307  Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selectedText.toString()), null);
308  } else if (jmi.equals(selectAllMenuItem)) {
309  resultsTable.selectAll();
310  }
311  }
312  };
313  copyMenuItem.addActionListener(actList);
314 
315  selectAllMenuItem.addActionListener(actList);
316  }
317 
321  private void resetComponents() {
322  currentPage = 1;
323  currentPageLabel.setText("");
324  artifactLabel.setText("");
325  totalPageLabel.setText("");
326  ((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
327  prevPageButton.setEnabled(false);
328  nextPageButton.setEnabled(false);
329  currentNode = null;
330  }
331 
332  @Override
333  public void setNode(Node selectedNode) {
334  if (currentNode == selectedNode) {
335  return;
336  }
337  currentNode = selectedNode;
338 
339  // Make sure there is a node. Null might be passed to reset the viewer.
340  if (selectedNode == null) {
341  return;
342  }
343 
344  // Make sure the node is of the correct type.
345  Lookup lookup = selectedNode.getLookup();
346  Content content = lookup.lookup(Content.class);
347  if (content == null) {
348  return;
349  }
350 
351  startNewTask(new SelectedNodeChangedTask(selectedNode));
352  }
353 
354  @Override
355  public String getTitle() {
356  return NbBundle.getMessage(this.getClass(), "DataContentViewerArtifact.title");
357  }
358 
359  @Override
360  public String getToolTip() {
361  return NbBundle.getMessage(this.getClass(), "DataContentViewerArtifact.toolTip");
362  }
363 
364  @Override
365  public DataContentViewer createInstance() {
366  return new DataContentViewerArtifact();
367  }
368 
369  @Override
370  public Component getComponent() {
371  return this;
372  }
373 
374  @Override
375  public void resetComponent() {
376  resetComponents();
377  }
378 
379  @Override
380  public boolean isSupported(Node node) {
381  if (node == null) {
382  return false;
383  }
384 
385  Content content = node.getLookup().lookup(Content.class);
386  if (content != null) {
387  try {
388  return content.getAllArtifactsCount() > 0;
389  } catch (TskException ex) {
390  logger.log(Level.WARNING, "Couldn't get count of BlackboardArtifacts for content", ex); //NON-NLS
391  }
392  }
393  return false;
394  }
395 
396  @Override
397  public int isPreferred(Node node) {
398  BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
399  // low priority if node doesn't have an artifact (meaning it was found from normal directory
400  // browsing, or if the artifact is something that means the user really wants to see the original
401  // file and not more details about the artifact
402  if ((artifact == null)
403  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID())
404  || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID())) {
405  return 3;
406  } else {
407  return 5;
408  }
409  }
410 
415  private class ResultsTableArtifact {
416 
417  private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
418  private String[][] rowData = null;
419  private final String artifactDisplayName;
420 
421  ResultsTableArtifact(BlackboardArtifact artifact) {
422  artifactDisplayName = artifact.getDisplayName();
423  addRows(artifact);
424 
425  }
426 
427  ResultsTableArtifact(String errorMsg) {
428  artifactDisplayName = errorMsg;
429  rowData = new String[1][3];
430  rowData[0] = new String[]{"", errorMsg, ""};
431 
432  }
433 
434  private String[][] getRows() {
435  return rowData;
436  }
437 
438  private void addRows(BlackboardArtifact artifact) {
439  List<String[]> rowsToAdd = new ArrayList<>();
440  try {
441  Content content = artifact.getSleuthkitCase().getContentById(artifact.getObjectID());
442  /*
443  * Add rows for each attribute.
444  */
445  for (BlackboardAttribute attr : artifact.getAttributes()) {
446  /*
447  * Attribute value column.
448  */
449  String value = "";
450  switch (attr.getAttributeType().getValueType()) {
451  case STRING:
452  case INTEGER:
453  case LONG:
454  case DOUBLE:
455  case BYTE:
456  default:
457  value = attr.getDisplayString();
458  break;
459  // Use Autopsy date formatting settings, not TSK defaults
460  case DATETIME:
461  long epoch = attr.getValueLong();
462  value = "0000-00-00 00:00:00";
463  if (null != content && 0 != epoch) {
464  dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
465  value = dateFormatter.format(new java.util.Date(epoch * 1000));
466  }
467  break;
468  }
469  /*
470  * Attribute sources column.
471  */
472  String sources = StringUtils.join(attr.getSources(), ", ");
473  rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources});
474  }
475  /*
476  * Add a row for the source content path.
477  */
478  String path = "";
479  try {
480  if (null != content) {
481  path = content.getUniquePath();
482  }
483  } catch (TskCoreException ex) {
484  logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
485  path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message();
486  }
487  rowsToAdd.add(new String[]{"Source File Path", path, ""});
488  /*
489  * Add a row for the artifact id.
490  */
491  rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""});
492  } catch (TskCoreException ex) {
493  rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""});
494  }
495  rowData = rowsToAdd.toArray(new String[0][0]);
496  }
497 
501  String getArtifactDisplayName() {
502  return artifactDisplayName;
503  }
504  }
505 
510  private class ViewUpdate {
511 
512  int numberOfPages;
513  int currentPage;
514  ResultsTableArtifact tableContents;
515 
516  ViewUpdate(int numberOfPages, int currentPage, ResultsTableArtifact contents) {
517  this.currentPage = currentPage;
518  this.numberOfPages = numberOfPages;
519  this.tableContents = contents;
520  }
521 
522  ViewUpdate(int numberOfPages, int currentPage, String errorMsg) {
523  this.currentPage = currentPage;
524  this.numberOfPages = numberOfPages;
525  this.tableContents = new ResultsTableArtifact(errorMsg);
526  }
527  }
528 
536  private void updateView(ViewUpdate viewUpdate) {
537  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
538 
539  nextPageButton.setEnabled(viewUpdate.currentPage < viewUpdate.numberOfPages);
540  prevPageButton.setEnabled(viewUpdate.currentPage > 1);
541  currentPage = viewUpdate.currentPage;
542  totalPageLabel.setText(Integer.toString(viewUpdate.numberOfPages));
543  currentPageLabel.setText(Integer.toString(currentPage));
544  artifactLabel.setText(viewUpdate.tableContents.getArtifactDisplayName());
545  // @@@ This can take a long time. Perhaps a faster HTML renderer can be found.
546  // Note that the rendering appears to be done on a background thread, since the
547  // wait cursor reset below happens before the new text hits the JTextPane. On the
548  // other hand, the UI is unresponsive...
549  DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
550  tModel.setDataVector(viewUpdate.tableContents.getRows(), COLUMN_HEADERS);
551  updateColumnSizes();
552  resultsTable.clearSelection();
553 
554  this.setCursor(null);
555  }
556 
563  private synchronized void startNewTask(SwingWorker<ViewUpdate, Void> task) {
564  String[][] waitRow = new String[1][3];
565  waitRow[0] = new String[]{"", WAIT_TEXT, ""};
566  DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
567  tModel.setDataVector(waitRow, COLUMN_HEADERS);
568  updateColumnSizes();
569  resultsTable.clearSelection();
570  // The output of the previous task is no longer relevant.
571  if (currentTask != null) {
572  // This call sets a cancellation flag. It does not terminate the background thread running the task.
573  // The task must check the cancellation flag and react appropriately.
574  currentTask.cancel(false);
575  }
576 
577  // Start the new task.
578  currentTask = task;
579  currentTask.execute();
580  }
581 
588  private void setArtifactContents(List<ResultsTableArtifact> artifactList) {
589  synchronized (lock) {
590  this.artifactTableContents = artifactList;
591  }
592  }
593 
599  private List<ResultsTableArtifact> getArtifactContents() {
600  synchronized (lock) {
601  return artifactTableContents;
602  }
603  }
604 
610  private class SelectedNodeChangedTask extends SwingWorker<ViewUpdate, Void> {
611 
612  private final Node selectedNode;
613 
614  SelectedNodeChangedTask(Node selectedNode) {
615  this.selectedNode = selectedNode;
616  }
617 
618  @Override
620  // Get the lookup for the node for access to its underlying content and
621  // blackboard artifact, if any.
622  Lookup lookup = selectedNode.getLookup();
623 
624  // Get the content.
625  Content content = lookup.lookup(Content.class);
626  if (content == null) {
627  return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
628  }
629 
630  // Get all of the blackboard artifacts associated with the content. These are what this
631  // viewer displays.
632  ArrayList<BlackboardArtifact> artifacts;
633  try {
634  artifacts = content.getAllArtifacts();
635  } catch (TskException ex) {
636  logger.log(Level.WARNING, "Couldn't get artifacts", ex); //NON-NLS
637  return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
638  }
639 
640  if (isCancelled()) {
641  return null;
642  }
643 
644  // Build the new artifact contents cache.
645  ArrayList<ResultsTableArtifact> artifactContents = new ArrayList<>();
646  for (BlackboardArtifact artifact : artifacts) {
647  artifactContents.add(new ResultsTableArtifact(artifact));
648  }
649 
650  // If the node has an underlying blackboard artifact, show it. If not,
651  // show the first artifact.
652  int index = 0;
653  BlackboardArtifact artifact = lookup.lookup(BlackboardArtifact.class);
654  if (artifact != null) {
655  index = artifacts.indexOf(artifact);
656  if (index == -1) {
657  index = 0;
658  } else {
659  // if the artifact has an ASSOCIATED ARTIFACT, then we display the associated artifact instead
660  try {
661  for (BlackboardAttribute attr : artifact.getAttributes()) {
662  if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
663  long assocArtifactId = attr.getValueLong();
664  int assocArtifactIndex = -1;
665  for (BlackboardArtifact art : artifacts) {
666  if (assocArtifactId == art.getArtifactID()) {
667  assocArtifactIndex = artifacts.indexOf(art);
668  break;
669  }
670  }
671  if (assocArtifactIndex >= 0) {
672  index = assocArtifactIndex;
673  }
674  break;
675  }
676  }
677  } catch (TskCoreException ex) {
678  logger.log(Level.WARNING, "Couldn't get associated artifact to display in Content Viewer.", ex); //NON-NLS
679  }
680  }
681 
682  }
683 
684  if (isCancelled()) {
685  return null;
686  }
687 
688  // Add one to the index of the artifact content for the corresponding page index.
689  ViewUpdate viewUpdate = new ViewUpdate(artifactContents.size(), index + 1, artifactContents.get(index));
690 
691  // It may take a considerable amount of time to fetch the attributes of the selected artifact
692  if (isCancelled()) {
693  return null;
694  }
695 
696  // Update the artifact contents cache.
697  setArtifactContents(artifactContents);
698 
699  return viewUpdate;
700  }
701 
702  @Override
703  protected void done() {
704  if (!isCancelled()) {
705  try {
706  ViewUpdate viewUpdate = get();
707  if (viewUpdate != null) {
708  updateView(viewUpdate);
709  }
710  } catch (InterruptedException | ExecutionException ex) {
711  logger.log(Level.WARNING, "Artifact display task unexpectedly interrupted or failed", ex); //NON-NLS
712  }
713  }
714  }
715  }
716 
722  private class SelectedArtifactChangedTask extends SwingWorker<ViewUpdate, Void> {
723 
724  private final int pageIndex;
725 
726  SelectedArtifactChangedTask(final int pageIndex) {
727  this.pageIndex = pageIndex;
728  }
729 
730  @Override
732  // Get the artifact content to display from the cache. Note that one must be subtracted from the
733  // page index to get the corresponding artifact content index.
734  List<ResultsTableArtifact> artifactContents = getArtifactContents();
735  ResultsTableArtifact artifactContent = artifactContents.get(pageIndex - 1);
736 
737  // It may take a considerable amount of time to fetch the attributes of the selected artifact and render them
738  // as HTML, so check for cancellation.
739  if (isCancelled()) {
740  return null;
741  }
742 
743  return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent);
744  }
745 
746  @Override
747  protected void done() {
748  if (!isCancelled()) {
749  try {
750  ViewUpdate viewUpdate = get();
751  if (viewUpdate != null) {
752  updateView(viewUpdate);
753  }
754  } catch (InterruptedException | ExecutionException ex) {
755  logger.log(Level.WARNING, "Artifact display task unexpectedly interrupted or failed", ex); //NON-NLS
756  }
757  }
758  }
759  }
760 }
synchronized void startNewTask(SwingWorker< ViewUpdate, Void > task)
void setArtifactContents(List< ResultsTableArtifact > artifactList)
synchronized static Logger getLogger(String name)
Definition: Logger.java:161

Copyright © 2012-2016 Basis Technology. Generated on: Tue Jun 13 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.