Autopsy  4.1
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-2016 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.HashSet;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.logging.Level;
31 import org.apache.commons.lang.StringUtils;
32 import org.openide.nodes.Node;
33 import org.openide.util.Lookup;
34 import org.openide.util.NbBundle;
35 import org.openide.util.lookup.ServiceProvider;
39 import org.sleuthkit.datamodel.BlackboardArtifact;
40 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT;
41 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
42 import org.sleuthkit.datamodel.BlackboardAttribute;
43 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
44 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
45 import org.sleuthkit.datamodel.Content;
46 import org.sleuthkit.datamodel.TskCoreException;
47 
52 @ServiceProvider(service = DataContentViewer.class, position = 4)
53 public class ExtractedContentViewer implements DataContentViewer {
54 
55  private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName());
56 
57  private static final long INVALID_DOCUMENT_ID = 0L;
58  private static final BlackboardAttribute.Type TSK_ASSOCIATED_ARTIFACT_TYPE = new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT);
59 
60  private ExtractedContentPanel panel;
61  private volatile Node currentNode = null;
62  private IndexedText currentSource = null;
63 
70  }
71 
77  @Override
78  public void setNode(final Node node) {
79  // Clear the viewer.
80  if (node == null) {
81  currentNode = null;
82  resetComponent();
83  return;
84  }
85 
86  /*
87  * This deals with the known bug with an unknown cause where setNode is
88  * sometimes called twice for the same node.
89  */
90  if (node == currentNode) {
91  return;
92  } else {
93  currentNode = node;
94  }
95 
96  Lookup nodeLookup = node.getLookup();
97  Content content = nodeLookup.lookup(Content.class);
98 
99 
100  /*
101  * Assemble a collection of all of the indexed text "sources" associated
102  * with the node.
103  */
104  List<IndexedText> sources = new ArrayList<>();
105  IndexedText highlightedHitText = null;
106  IndexedText rawContentText = null;
107  IndexedText rawArtifactText = null;
108 
109  /*
110  * First add the text marked up with HTML to highlight keyword hits that
111  * will be present in the selected node's lookup if the node is for a
112  * keyword hit artifact or account.
113  */
114  sources.addAll(nodeLookup.lookupAll(IndexedText.class));
115 
116  if (!sources.isEmpty()) {
117  //if the look up had any sources use them and don't make a new one.
118  highlightedHitText = sources.get(0);
119  } else if (null != content && solrHasContent(content.getId())) {//if the lookup didn't have any sources, and solr has indexed the content...
120  /*
121  * get all the credit card artifacts and make a AccountsText object
122  * that will highlight them.
123  */
124  String solrDocumentID = String.valueOf(content.getId()); //grab the object id as the solrDocumentID
125  Set<String> accountNumbers = new HashSet<>();
126  try {
127  //if the node had artifacts in the lookup use them, other wise look up all credit card artifacts for the content.
128  Collection<? extends BlackboardArtifact> artifacts = nodeLookup.lookupAll(BlackboardArtifact.class);
129  artifacts = (artifacts == null || artifacts.isEmpty())
130  ? content.getArtifacts(TSK_ACCOUNT)
131  : artifacts;
132 
133  /*
134  * For each artifact add the account number to the list of
135  * accountNumbers to highlight, and use the solrDocumentId
136  * attribute(in place of the content's object Id) if it exists
137  *
138  * NOTE: this assumes all the artifacts will be from the same
139  * solrDocumentId
140  */
141  for (BlackboardArtifact artifact : artifacts) {
142  try {
143  BlackboardAttribute solrIDAttr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID));
144  if (solrIDAttr != null) {
145  String valueString = solrIDAttr.getValueString();
146  if (StringUtils.isNotBlank(valueString)) {
147  solrDocumentID = valueString;
148  }
149  }
150 
151  BlackboardAttribute keyWordAttr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER));
152  if (keyWordAttr != null) {
153  String valueString = keyWordAttr.getValueString();
154  if (StringUtils.isNotBlank(valueString)) {
155  accountNumbers.add(valueString);
156  }
157  }
158 
159  } catch (TskCoreException ex) {
160  logger.log(Level.SEVERE, "Failed to retrieve Blackboard Attributes", ex); //NON-NLS
161  }
162  }
163  if (accountNumbers.isEmpty() == false) {
164  highlightedHitText = new AccountsText(solrDocumentID, accountNumbers);
165  sources.add(highlightedHitText);
166  }
167  } catch (TskCoreException ex) {
168  logger.log(Level.SEVERE, "Failed to retrieve Blackboard Artifacts", ex); //NON-NLS
169  }
170  }
171 
172  /*
173  * Next, add the "raw" (not highlighted) text, if any, for any content
174  * associated with the node.
175  */
176  if (null != content && solrHasContent(content.getId())) {
177  rawContentText = new RawText(content, content.getId());
178  sources.add(rawContentText);
179  }
180 
181  /*
182  * Finally, add the "raw" (not highlighted) text, if any, for any
183  * artifact associated with the node.
184  */
185  BlackboardArtifact artifact = nodeLookup.lookup(BlackboardArtifact.class);
186  if (null != artifact) {
187  /*
188  * For keyword hit artifacts, add the text of the artifact that hit,
189  * not the hit artifact; otherwise add the text for the artifact.
190  */
191  if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID() || artifact.getArtifactTypeID() == TSK_ACCOUNT.getTypeID()) {
192  try {
193  BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_ARTIFACT_TYPE);
194  if (attribute != null) {
195  long artifactId = attribute.getValueLong();
196  BlackboardArtifact associatedArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(artifactId);
197  rawArtifactText = new RawText(associatedArtifact, associatedArtifact.getArtifactID());
198  sources.add(rawArtifactText);
199  }
200  } catch (TskCoreException ex) {
201  logger.log(Level.SEVERE, "Error getting associated artifact attributes", ex); //NON-NLS
202  }
203  } else {
204  rawArtifactText = new RawText(artifact, artifact.getArtifactID());
205  sources.add(rawArtifactText);
206  }
207 
208  }
209 
210  // Now set the default source to be displayed.
211  if (null != highlightedHitText) {
212  currentSource = highlightedHitText;
213  } else if (null != rawContentText) {
214  currentSource = rawContentText;
215  } else {
216  currentSource = rawArtifactText;
217  }
218 
219  // Push the text sources into the panel.
220  for (IndexedText source : sources) {
221  int currentPage = source.getCurrentPage();
222  if (currentPage == 0 && source.hasNextPage()) {
223  source.nextPage();
224  }
225  }
226  updatePageControls();
227  setPanel(sources);
228  }
229 
230  private void scrollToCurrentHit() {
231  final IndexedText source = panel.getSelectedSource();
232  if (source == null || !source.isSearchable()) {
233  return;
234  }
235 
236  panel.scrollToAnchor(source.getAnchorPrefix() + Integer.toString(source.currentItem()));
237 
238  }
239 
240  @Override
241  public String getTitle() {
242  return NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.getTitle");
243  }
244 
245  @Override
246  public String getToolTip() {
247  return NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.toolTip");
248  }
249 
250  @Override
251  public DataContentViewer createInstance() {
252  return new ExtractedContentViewer();
253  }
254 
255  @Override
256  public synchronized Component getComponent() {
257  if (panel == null) {
258  panel = new ExtractedContentPanel();
259  panel.addPrevMatchControlListener(new PrevFindActionListener());
260  panel.addNextMatchControlListener(new NextFindActionListener());
261  panel.addPrevPageControlListener(new PrevPageActionListener());
262  panel.addNextPageControlListener(new NextPageActionListener());
263  panel.addSourceComboControlListener(new SourceChangeActionListener());
264  }
265  return panel;
266  }
267 
268  @Override
269  public void resetComponent() {
270  setPanel(new ArrayList<>());
271  panel.resetDisplay();
272  currentNode = null;
273  currentSource = null;
274  }
275 
276  @Override
277  public boolean isSupported(Node node) {
278  if (node == null) {
279  return false;
280  }
281 
288  Collection<? extends IndexedText> sources = node.getLookup().lookupAll(IndexedText.class);
289  if (sources.isEmpty() == false) {
290  return true;
291  }
292 
293  /*
294  * Is there a credit card artifact in the lookup
295  */
296  Collection<? extends BlackboardArtifact> artifacts = node.getLookup().lookupAll(BlackboardArtifact.class);
297  if (artifacts != null) {
298  for (BlackboardArtifact art : artifacts) {
299  if (art.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
300  return true;
301  }
302  }
303  }
304 
305  /*
306  * No highlighted text for a keyword hit, so is there any indexed text
307  * at all for this node?
308  */
309  long documentID = getDocumentId(node);
310  if (INVALID_DOCUMENT_ID == documentID) {
311  return false;
312  }
313 
314  return solrHasContent(documentID);
315  }
316 
317  @Override
318  public int isPreferred(Node node) {
319  BlackboardArtifact art = node.getLookup().lookup(BlackboardArtifact.class);
320 
321  if (art == null) {
322  return 4;
323  } else if (art.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
324  || art.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
325  return 6;
326  } else {
327  return 4;
328  }
329  }
330 
337  private void setPanel(List<IndexedText> sources) {
338  if (panel != null) {
339  panel.setSources(sources);
340  }
341  }
342 
350  private boolean solrHasContent(Long objectId) {
351  final Server solrServer = KeywordSearch.getServer();
352  try {
353  return solrServer.queryIsIndexed(objectId);
355  logger.log(Level.SEVERE, "Error querying Solr server", ex); //NON-NLS
356  return false;
357  }
358  }
359 
368  private Long getDocumentId(Node node) {
377  BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
378  if (null != artifact) {
379  if (artifact.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
380  return artifact.getArtifactID();
381  } else {
382  try {
383  // Get the associated artifact attribute and return its value as the ID
384  BlackboardAttribute blackboardAttribute = artifact.getAttribute(TSK_ASSOCIATED_ARTIFACT_TYPE);
385  if (blackboardAttribute != null) {
386  return blackboardAttribute.getValueLong();
387  }
388  } catch (TskCoreException ex) {
389  logger.log(Level.SEVERE, "Error getting associated artifact attributes", ex); //NON-NLS
390  }
391  }
392  }
393 
394  /*
395  * For keyword search hit artifact nodes and all other nodes, the
396  * document ID for the extracted text is the ID of the associated
397  * content, if any, unless there is an associated artifact, which is
398  * handled above.
399  */
400  Content content = node.getLookup().lookup(Content.class);
401  if (content != null) {
402  return content.getId();
403  }
404 
405  /*
406  * No extracted text, return an invalid docuemnt ID.
407  */
408  return 0L;
409  }
410 
411  private class NextFindActionListener implements ActionListener {
412 
413  @Override
414  public void actionPerformed(ActionEvent e) {
415  IndexedText source = panel.getSelectedSource();
416  if (source == null) {
417  // reset
418  panel.updateControls(null);
419  return;
420  }
421  final boolean hasNextItem = source.hasNextItem();
422  final boolean hasNextPage = source.hasNextPage();
423  int indexVal = 0;
424  if (hasNextItem || hasNextPage) {
425  if (!hasNextItem) {
426  //flip the page
427  nextPage();
428  indexVal = source.currentItem();
429  } else {
430  indexVal = source.nextItem();
431  }
432 
433  //scroll
434  panel.scrollToAnchor(source.getAnchorPrefix() + Integer.toString(indexVal));
435 
436  //update display
437  panel.updateCurrentMatchDisplay(source.currentItem());
438  panel.updateTotaMatcheslDisplay(source.getNumberHits());
439 
440  //update controls if needed
441  if (!source.hasNextItem() && !source.hasNextPage()) {
442  panel.enableNextMatchControl(false);
443  }
444  if (source.hasPreviousItem() || source.hasPreviousPage()) {
445  panel.enablePrevMatchControl(true);
446  }
447  }
448  }
449  }
450 
451  private class PrevFindActionListener implements ActionListener {
452 
453  @Override
454  public void actionPerformed(ActionEvent e) {
455  IndexedText source = panel.getSelectedSource();
456  final boolean hasPreviousItem = source.hasPreviousItem();
457  final boolean hasPreviousPage = source.hasPreviousPage();
458  int indexVal = 0;
459  if (hasPreviousItem || hasPreviousPage) {
460  if (!hasPreviousItem) {
461  //flip the page
462  previousPage();
463  indexVal = source.currentItem();
464  } else {
465  indexVal = source.previousItem();
466  }
467 
468  //scroll
469  panel.scrollToAnchor(source.getAnchorPrefix() + Integer.toString(indexVal));
470 
471  //update display
472  panel.updateCurrentMatchDisplay(source.currentItem());
473  panel.updateTotaMatcheslDisplay(source.getNumberHits());
474 
475  //update controls if needed
476  if (!source.hasPreviousItem() && !source.hasPreviousPage()) {
477  panel.enablePrevMatchControl(false);
478  }
479  if (source.hasNextItem() || source.hasNextPage()) {
480  panel.enableNextMatchControl(true);
481  }
482  }
483  }
484  }
485 
486  private class SourceChangeActionListener implements ActionListener {
487 
488  @Override
489  public void actionPerformed(ActionEvent e) {
490 
491  currentSource = panel.getSelectedSource();
492 
493  if (currentSource == null) {
494  //TODO might need to reset something
495  return;
496  }
497 
498  updatePageControls();
499  updateSearchControls();
500 
501  }
502  }
503 
504  private void updateSearchControls() {
505  panel.updateSearchControls(currentSource);
506  }
507 
508  private void updatePageControls() {
509  panel.updateControls(currentSource);
510  }
511 
512  private void nextPage() {
513  // we should never have gotten here -- reset
514  if (currentSource == null) {
515  panel.updateControls(null);
516  return;
517  }
518 
519  if (currentSource.hasNextPage()) {
520  currentSource.nextPage();
521 
522  //set new text
523  panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
524  panel.refreshCurrentMarkup();
525  panel.setCursor(null);
526 
527  //update display
528  panel.updateCurrentPageDisplay(currentSource.getCurrentPage());
529 
530  //scroll to current selection
532 
533  //update controls if needed
534  if (!currentSource.hasNextPage()) {
535  panel.enableNextPageControl(false);
536  }
537  if (currentSource.hasPreviousPage()) {
538  panel.enablePrevPageControl(true);
539  }
540 
541  updateSearchControls();
542  }
543  }
544 
545  private void previousPage() {
546  // reset, we should have never gotten here if null
547  if (currentSource == null) {
548  panel.updateControls(null);
549  return;
550  }
551 
552  if (currentSource.hasPreviousPage()) {
553  currentSource.previousPage();
554 
555  //set new text
556  panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
557  panel.refreshCurrentMarkup();
558  panel.setCursor(null);
559 
560  //update display
561  panel.updateCurrentPageDisplay(currentSource.getCurrentPage());
562 
563  //scroll to current selection
565 
566  //update controls if needed
567  if (!currentSource.hasPreviousPage()) {
568  panel.enablePrevPageControl(false);
569  }
570  if (currentSource.hasNextPage()) {
571  panel.enableNextPageControl(true);
572  }
573 
574  updateSearchControls();
575 
576  }
577  }
578 
579  class NextPageActionListener implements ActionListener {
580 
581  @Override
582  public void actionPerformed(ActionEvent e) {
583  nextPage();
584  }
585  }
586 
587  private class PrevPageActionListener implements ActionListener {
588 
589  @Override
590  public void actionPerformed(ActionEvent e) {
591  previousPage();
592  }
593  }
594 }
boolean queryIsIndexed(long contentID)
Definition: Server.java:832
synchronized static Logger getLogger(String name)
Definition: Logger.java:161

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