Autopsy  4.8.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExtractedContentViewer.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.keywordsearch;
20 
21 import java.awt.Component;
22 import java.awt.Cursor;
23 import java.awt.event.ActionEvent;
24 import java.awt.event.ActionListener;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.List;
28 import java.util.logging.Level;
29 import org.openide.nodes.Node;
30 import org.openide.util.Lookup;
31 import org.openide.util.NbBundle;
32 import org.openide.util.lookup.ServiceProvider;
38 import org.sleuthkit.datamodel.AbstractFile;
39 import org.sleuthkit.datamodel.Account;
40 import org.sleuthkit.datamodel.BlackboardArtifact;
41 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT;
42 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
43 import org.sleuthkit.datamodel.BlackboardAttribute;
44 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
45 import org.sleuthkit.datamodel.Content;
46 import org.sleuthkit.datamodel.Report;
47 import org.sleuthkit.datamodel.TskCoreException;
48 
53 @ServiceProvider(service = DataContentViewer.class, position = 4)
54 public class ExtractedContentViewer implements DataContentViewer {
55 
56  private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName());
57 
58  private static final BlackboardAttribute.Type TSK_ASSOCIATED_ARTIFACT_TYPE = new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT);
59  private static final BlackboardAttribute.Type TSK_ACCOUNT_TYPE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE);
60 
61  private ExtractedContentPanel panel;
62  private volatile Node currentNode = null;
63  private IndexedText currentSource = null;
64 
71  }
72 
78  @Override
79  public void setNode(final Node node) {
80  // Clear the viewer.
81  if (node == null) {
82  currentNode = null;
83  resetComponent();
84  return;
85  }
86 
87  /*
88  * This deals with the known bug with an unknown cause where setNode is
89  * sometimes called twice for the same node.
90  */
91  if (node == currentNode) {
92  return;
93  } else {
94  currentNode = node;
95  }
96 
97  /*
98  * Assemble a collection of all of the indexed text "sources" for the
99  * node.
100  */
101  List<IndexedText> sources = new ArrayList<>();
102  Lookup nodeLookup = node.getLookup();
103 
108  AdHocQueryResult adHocQueryResult = nodeLookup.lookup(AdHocQueryResult.class);
109  AbstractFile file = nodeLookup.lookup(AbstractFile.class);
110  BlackboardArtifact artifact = nodeLookup.lookup(BlackboardArtifact.class);
111  Report report = nodeLookup.lookup(Report.class);
112 
113  /*
114  * First, get text with highlighted hits if this node is for a search
115  * result.
116  */
117  IndexedText highlightedHitText = null;
118  if (adHocQueryResult != null) {
119  /*
120  * The node is an ad hoc search result node.
121  */
122  highlightedHitText = new HighlightedText(adHocQueryResult.getSolrObjectId(), adHocQueryResult.getResults());
123  } else if (artifact != null) {
124  if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) {
125  /*
126  * The node is a keyword hit artifact node.
127  */
128  try {
129  highlightedHitText = new HighlightedText(artifact);
130  } catch (TskCoreException ex) {
131  logger.log(Level.SEVERE, "Failed to create HighlightedText for " + artifact, ex); //NON-NLS
132  }
133  } else if (artifact.getArtifactTypeID() == TSK_ACCOUNT.getTypeID() && file != null) {
134  try {
135  BlackboardAttribute attribute = artifact.getAttribute(TSK_ACCOUNT_TYPE);
136  if (attribute != null && Account.Type.CREDIT_CARD.getTypeName().equals(attribute.getValueString())) {
137  /*
138  * The node is an credit card account node.
139  */
140  highlightedHitText = getAccountsText(file, nodeLookup);
141  }
142  } catch (TskCoreException ex) {
143  logger.log(Level.SEVERE, "Failed to create AccountsText for " + file, ex); //NON-NLS
144  }
145  }
146  }
147  if (highlightedHitText != null) {
148  sources.add(highlightedHitText);
149  }
150 
151  /*
152  * Next, add the "raw" (not highlighted) text, if any, for any file
153  * associated with the node.
154  */
155  IndexedText rawContentText = null;
156  if (file != null) {
157  rawContentText = new RawText(file, file.getId());
158  sources.add(rawContentText);
159  }
160 
161  /*
162  * Add the "raw" (not highlighted) text, if any, for any report
163  * associated with the node.
164  */
165  if (report != null) {
166  rawContentText = new RawText(report, report.getId());
167  sources.add(rawContentText);
168  }
169 
170  /*
171  * Finally, add the "raw" (not highlighted) text, if any, for any
172  * artifact associated with the node.
173  */
174  IndexedText rawArtifactText = null;
175  try {
176  rawArtifactText = getRawArtifactText(artifact);
177  if (rawArtifactText != null) {
178  sources.add(rawArtifactText);
179  }
180  } catch (TskCoreException | NoCurrentCaseException ex) {
181  logger.log(Level.SEVERE, "Error creating RawText for " + file, ex); //NON-NLS
182  }
183 
184  // Now set the default source to be displayed.
185  if (highlightedHitText != null) {
186  currentSource = highlightedHitText;
187  } else if (rawArtifactText != null) {
188  currentSource = rawArtifactText;
189  } else {
190  currentSource = rawContentText;
191  }
192 
193  // Push the text sources into the panel.
194  for (IndexedText source : sources) {
195  int currentPage = source.getCurrentPage();
196  if (currentPage == 0 && source.hasNextPage()) {
197  source.nextPage();
198  }
199  }
200  panel.updateControls(currentSource);
201 
202  String contentName = "";
203  if (file != null) {
204  contentName = file.getName();
205  }
206  setPanel(contentName, sources);
207 
208  }
209 
210  static private IndexedText getRawArtifactText(BlackboardArtifact artifact) throws TskCoreException, NoCurrentCaseException {
211  IndexedText rawArtifactText = null;
212  if (null != artifact) {
213  /*
214  * For keyword hit artifacts, add the text of the artifact that hit,
215  * not the hit artifact; otherwise add the text for the artifact.
216  */
217  if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()
218  || artifact.getArtifactTypeID() == TSK_ACCOUNT.getTypeID()) {
219 
220  BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_ARTIFACT_TYPE);
221  if (attribute != null) {
222  long artifactId = attribute.getValueLong();
223  BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(artifactId);
224  rawArtifactText = new RawText(associatedArtifact, associatedArtifact.getArtifactID());
225 
226  }
227 
228  } else {
229  rawArtifactText = new RawText(artifact, artifact.getArtifactID());
230  }
231  }
232  return rawArtifactText;
233  }
234 
235  static private IndexedText getAccountsText(Content content, Lookup nodeLookup) throws TskCoreException {
236  /*
237  * get all the credit card artifacts
238  */
239  //if the node had artifacts in the lookup use them, other wise look up all credit card artifacts for the content.
240  Collection<? extends BlackboardArtifact> artifacts = nodeLookup.lookupAll(BlackboardArtifact.class);
241  artifacts = (artifacts == null || artifacts.isEmpty())
242  ? content.getArtifacts(TSK_ACCOUNT)
243  : artifacts;
244 
245  return new AccountsText(content.getId(), artifacts);
246  }
247 
248  private void scrollToCurrentHit() {
249  final IndexedText source = panel.getSelectedSource();
250  if (source == null || !source.isSearchable()) {
251  return;
252  }
253 
254  panel.scrollToAnchor(source.getAnchorPrefix() + Integer.toString(source.currentItem()));
255  }
256 
257  @Override
258  public String getTitle() {
259  return NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.getTitle");
260  }
261 
262  @Override
263  public String getToolTip() {
264  return NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.toolTip");
265  }
266 
267  @Override
268  public DataContentViewer createInstance() {
269  return new ExtractedContentViewer();
270  }
271 
272  @Override
273  public synchronized Component getComponent() {
274  if (panel == null) {
275  panel = new ExtractedContentPanel();
276  panel.addPrevMatchControlListener(new PrevFindActionListener());
277  panel.addNextMatchControlListener(new NextFindActionListener());
278  panel.addPrevPageControlListener(new PrevPageActionListener());
279  panel.addNextPageControlListener(new NextPageActionListener());
280  panel.addSourceComboControlListener(new SourceChangeActionListener());
281  }
282  return panel;
283  }
284 
285  @Override
286  public void resetComponent() {
287 
288  panel.resetDisplay();
289  currentNode = null;
290  currentSource = null;
291  }
292 
293  @Override
294  public boolean isSupported(Node node) {
295  if (node == null) {
296  return false;
297  }
298 
299  /*
300  * If the lookup of the node contains an ad hoc search result object,
301  * then there must be indexed text that produced the hit.
302  */
303  AdHocQueryResult adHocQueryResult = node.getLookup().lookup(AdHocQueryResult.class);
304  if (adHocQueryResult != null) {
305  return true;
306  }
307 
308  /*
309  * If the lookup of the node contains either a keyword hit artifact or a
310  * credit card account artifact from a credit card account numbers
311  * search, then there must be indexed text that produced the hit(s).
312  */
313  BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
314  if (artifact != null) {
315  final int artifactTypeID = artifact.getArtifactTypeID();
316  if (artifactTypeID == TSK_KEYWORD_HIT.getTypeID()) {
317  return true;
318  } else if (artifactTypeID == TSK_ACCOUNT.getTypeID()) {
319  try {
320  BlackboardAttribute attribute = artifact.getAttribute(TSK_ACCOUNT_TYPE);
321  if (attribute != null && Account.Type.CREDIT_CARD.getTypeName().equals(attribute.getValueString())) {
322  return true;
323  }
324  } catch (TskCoreException ex) {
325  /*
326  * If there was an error checking the account type, fall
327  * back to the check below for the file associated with the
328  * account (if there is one).
329  */
330  logger.log(Level.SEVERE, "Error getting TSK_ACCOUNT_TYPE attribute from artifact " + artifact.getArtifactID(), ex);
331  }
332  }
333  }
334 
335  /*
336  * If the lookup of the node contains a file, check to see if there is
337  * indexed text for the file. Note that there should be a file in the
338  * lookup of all nodes except artifact nodes that are associated with a
339  * data source instead of a file.
340  */
341  AbstractFile file = node.getLookup().lookup(AbstractFile.class);
342  if (file != null && solrHasContent(file.getId())) {
343  return true;
344  }
345 
346  /*
347  * If the lookup of the node contains an artifact that is neither a
348  * keyword hit artifact nor a credit card account artifact, and the
349  * artifact is not associated with a file, check to see if there is
350  * indexed text for the artifact.
351  */
352  if (artifact != null) {
353  return solrHasContent(artifact.getArtifactID());
354  }
355 
356  /*
357  * If the lookup of the node contains no artifacts or file but does
358  * contain a report, check to see if there is indexed text for the
359  * report.
360  */
361  Report report = node.getLookup().lookup(Report.class);
362  if (report != null) {
363  return solrHasContent(report.getId());
364  }
365 
366  /*
367  * If the lookup of the node contains neither ad hoc search results, nor
368  * artifacts, nor a file, nor a report, there is no indexed text.
369  */
370  return false;
371  }
372 
373  @Override
374  public int isPreferred(Node node) {
375  AdHocQueryResult adhocResult = node.getLookup().lookup(AdHocQueryResult.class);
376  if (adhocResult != null) {
377  //The presence of an AdHocQueryResult indicates that this is a Keyword Hit and has a high preference for this content viewer
378  return 7;
379  }
380 
381  BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
382  if (artifact == null) {
383  return 4;
384  } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
385  return 7;
386  } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
387  try {
388  BlackboardAttribute attribute = artifact.getAttribute(TSK_ACCOUNT_TYPE);
389  if (attribute != null && Account.Type.CREDIT_CARD.getTypeName().equals(attribute.getValueString())) {
390  return 7;
391  } else {
392  return 4;
393  }
394  } catch (TskCoreException ex) {
395  logger.log(Level.SEVERE, "Error getting TSK_ACCOUNT_TYPE attribute from artifact " + artifact.getArtifactID(), ex);
396  return 4;
397  }
398  } else {
399  return 4;
400  }
401  }
402 
411  private void setPanel(String contentName, List<IndexedText> sources) {
412  if (panel != null) {
413  panel.setSources(contentName, sources);
414  }
415  }
416 
424  private boolean solrHasContent(Long objectId) {
425  final Server solrServer = KeywordSearch.getServer();
426  if (solrServer.coreIsOpen() == false) {
427  return false;
428  }
429 
430  try {
431  return solrServer.queryIsIndexed(objectId);
433  logger.log(Level.SEVERE, "Error querying Solr server", ex); //NON-NLS
434  return false;
435  }
436  }
437 
438  private class NextFindActionListener implements ActionListener {
439 
440  @Override
441  public void actionPerformed(ActionEvent e) {
442  IndexedText source = panel.getSelectedSource();
443  if (source == null) {
444  // reset
445  panel.updateControls(null);
446  return;
447  }
448  final boolean hasNextItem = source.hasNextItem();
449  final boolean hasNextPage = source.hasNextPage();
450  int indexVal;
451  if (hasNextItem || hasNextPage) {
452  if (!hasNextItem) {
453  //flip the page
454  nextPage();
455  indexVal = source.currentItem();
456  } else {
457  indexVal = source.nextItem();
458  }
459 
460  //scroll
461  panel.scrollToAnchor(source.getAnchorPrefix() + Integer.toString(indexVal));
462 
463  //update display
464  panel.updateCurrentMatchDisplay(source.currentItem());
465  panel.updateTotaMatcheslDisplay(source.getNumberHits());
466 
467  //update controls if needed
468  if (!source.hasNextItem() && !source.hasNextPage()) {
469  panel.enableNextMatchControl(false);
470  }
471  if (source.hasPreviousItem() || source.hasPreviousPage()) {
472  panel.enablePrevMatchControl(true);
473  }
474  }
475  }
476  }
477 
478  private class PrevFindActionListener implements ActionListener {
479 
480  @Override
481  public void actionPerformed(ActionEvent e) {
482  IndexedText source = panel.getSelectedSource();
483  final boolean hasPreviousItem = source.hasPreviousItem();
484  final boolean hasPreviousPage = source.hasPreviousPage();
485  int indexVal;
486  if (hasPreviousItem || hasPreviousPage) {
487  if (!hasPreviousItem) {
488  //flip the page
489  previousPage();
490  indexVal = source.currentItem();
491  } else {
492  indexVal = source.previousItem();
493  }
494 
495  //scroll
496  panel.scrollToAnchor(source.getAnchorPrefix() + Integer.toString(indexVal));
497 
498  //update display
499  panel.updateCurrentMatchDisplay(source.currentItem());
500  panel.updateTotaMatcheslDisplay(source.getNumberHits());
501 
502  //update controls if needed
503  if (!source.hasPreviousItem() && !source.hasPreviousPage()) {
504  panel.enablePrevMatchControl(false);
505  }
506  if (source.hasNextItem() || source.hasNextPage()) {
507  panel.enableNextMatchControl(true);
508  }
509  }
510  }
511  }
512 
513  private class SourceChangeActionListener implements ActionListener {
514 
515  @Override
516  public void actionPerformed(ActionEvent e) {
517  currentSource = panel.getSelectedSource();
518 
519  if (currentSource == null) {
520  //TODO might need to reset something
521  return;
522  }
523 
524  panel.updateControls(currentSource);
525  panel.updateSearchControls(currentSource);
526  }
527  }
528 
529  private void nextPage() {
530  // we should never have gotten here -- reset
531  if (currentSource == null) {
532  panel.updateControls(null);
533  return;
534  }
535 
536  if (currentSource.hasNextPage()) {
537  currentSource.nextPage();
538 
539  //set new text
540  panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
541  panel.refreshCurrentMarkup();
542  panel.setCursor(null);
543 
544  //update display
545  panel.updateCurrentPageDisplay(currentSource.getCurrentPage());
546 
547  //scroll to current selection
549 
550  //update controls if needed
551  if (!currentSource.hasNextPage()) {
552  panel.enableNextPageControl(false);
553  }
554  if (currentSource.hasPreviousPage()) {
555  panel.enablePrevPageControl(true);
556  }
557 
558  panel.updateSearchControls(currentSource);
559  }
560  }
561 
562  private void previousPage() {
563  // reset, we should have never gotten here if null
564  if (currentSource == null) {
565  panel.updateControls(null);
566  return;
567  }
568 
569  if (currentSource.hasPreviousPage()) {
570  currentSource.previousPage();
571 
572  //set new text
573  panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
574  panel.refreshCurrentMarkup();
575  panel.setCursor(null);
576 
577  //update display
578  panel.updateCurrentPageDisplay(currentSource.getCurrentPage());
579 
580  //scroll to current selection
582 
583  //update controls if needed
584  if (!currentSource.hasPreviousPage()) {
585  panel.enablePrevPageControl(false);
586  }
587  if (currentSource.hasNextPage()) {
588  panel.enableNextPageControl(true);
589  }
590 
591  panel.updateSearchControls(currentSource);
592 
593  }
594  }
595 
596  private class NextPageActionListener implements ActionListener {
597 
598  @Override
599  public void actionPerformed(ActionEvent e) {
600  nextPage();
601  }
602  }
603 
604  private class PrevPageActionListener implements ActionListener {
605 
606  @Override
607  public void actionPerformed(ActionEvent e) {
608  previousPage();
609  }
610  }
611 }
static IndexedText getAccountsText(Content content, Lookup nodeLookup)
static IndexedText getRawArtifactText(BlackboardArtifact artifact)
void setPanel(String contentName, List< IndexedText > sources)
boolean queryIsIndexed(long contentID)
Definition: Server.java:1092
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2018 Basis Technology. Generated on: Thu Oct 4 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.