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

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