Autopsy  4.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
KeywordHits.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.datamodel;
20 
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.sql.ResultSet;
24 import java.sql.SQLException;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.LinkedHashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Observable;
32 import java.util.Observer;
33 import java.util.Set;
34 import java.util.logging.Level;
35 import org.openide.nodes.ChildFactory;
36 import org.openide.nodes.Children;
37 import org.openide.nodes.Node;
38 import org.openide.nodes.Sheet;
39 import org.openide.util.NbBundle;
40 import org.openide.util.lookup.Lookups;
45 import org.sleuthkit.datamodel.AbstractFile;
46 import org.sleuthkit.datamodel.BlackboardArtifact;
47 import org.sleuthkit.datamodel.BlackboardAttribute;
48 import org.sleuthkit.datamodel.SleuthkitCase;
49 import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
50 import org.sleuthkit.datamodel.TskCoreException;
51 import org.sleuthkit.datamodel.TskException;
52 
56 public class KeywordHits implements AutopsyVisitableItem {
57 
58  private SleuthkitCase skCase;
59  private static final Logger logger = Logger.getLogger(KeywordHits.class.getName());
60  private static final String KEYWORD_HITS = NbBundle.getMessage(KeywordHits.class, "KeywordHits.kwHits.text");
61  public static final String NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
62  public static final String SIMPLE_LITERAL_SEARCH = NbBundle
63  .getMessage(KeywordHits.class, "KeywordHits.simpleLiteralSearch.text");
64  public static final String SIMPLE_REGEX_SEARCH = NbBundle
65  .getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text");
67 
68  public KeywordHits(SleuthkitCase skCase) {
69  this.skCase = skCase;
70  keywordResults = new KeywordResults();
71  }
72 
73  private final class KeywordResults extends Observable {
74 
75  // Map from listName/Type to Map of keyword to set of artifact Ids
76  // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
77  private final Map<String, Map<String, Set<Long>>> topLevelMap = new LinkedHashMap<>();
78 
79  KeywordResults() {
80  update();
81  }
82 
83  List<String> getListNames() {
84  synchronized (topLevelMap) {
85  List<String> names = new ArrayList<>(topLevelMap.keySet());
86  // this causes the "Single ..." terms to be in the middle of the results,
87  // which is wierd. Make a custom comparator or do something else to maek them on top
88  //Collections.sort(names);
89  return names;
90  }
91  }
92 
93  List<String> getKeywords(String listName) {
94  List<String> keywords;
95  synchronized (topLevelMap) {
96  keywords = new ArrayList<>(topLevelMap.get(listName).keySet());
97  }
98  Collections.sort(keywords);
99  return keywords;
100  }
101 
102  Set<Long> getArtifactIds(String listName, String keyword) {
103  synchronized (topLevelMap) {
104  return topLevelMap.get(listName).get(keyword);
105  }
106  }
107 
108  // populate maps based on artifactIds
109  void populateMaps(Map<Long, Map<Long, String>> artifactIds) {
110  synchronized (topLevelMap) {
111  topLevelMap.clear();
112 
113  // map of list name to keword to artifact IDs
114  Map<String, Map<String, Set<Long>>> listsMap = new LinkedHashMap<>();
115 
116  // Map from from literal keyword to artifact IDs
117  Map<String, Set<Long>> literalMap = new LinkedHashMap<>();
118 
119  // Map from regex keyword artifact IDs
120  Map<String, Set<Long>> regexMap = new LinkedHashMap<>();
121 
122  // top-level nodes
123  topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
124  topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
125 
126  for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
127  long id = art.getKey();
128  Map<Long, String> attributes = art.getValue();
129 
130  // I think we can use attributes.remove(...) here?
131  String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
132  String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
133  String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
134 
135  // part of a list
136  if (listName != null) {
137  if (listsMap.containsKey(listName) == false) {
138  listsMap.put(listName, new LinkedHashMap<String, Set<Long>>());
139  }
140 
141  Map<String, Set<Long>> listMap = listsMap.get(listName);
142  if (listMap.containsKey(word) == false) {
143  listMap.put(word, new HashSet<Long>());
144  }
145 
146  listMap.get(word).add(id);
147  } // regular expression, single term
148  else if (reg != null) {
149  if (regexMap.containsKey(reg) == false) {
150  regexMap.put(reg, new HashSet<Long>());
151  }
152  regexMap.get(reg).add(id);
153  } // literal, single term
154  else {
155  if (literalMap.containsKey(word) == false) {
156  literalMap.put(word, new HashSet<Long>());
157  }
158  literalMap.get(word).add(id);
159  }
160  topLevelMap.putAll(listsMap);
161  }
162  }
163 
164  setChanged();
165  notifyObservers();
166  }
167 
168  @SuppressWarnings("deprecation")
169  public void update() {
170  Map<Long, Map<Long, String>> artifactIds = new LinkedHashMap<>();
171 
172  if (skCase == null) {
173  return;
174  }
175 
176  int setId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID();
177  int wordId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID();
178  int regexId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID();
179  int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID();
180  String query = "SELECT blackboard_attributes.value_text,blackboard_attributes.artifact_id," //NON-NLS
181  + "blackboard_attributes.attribute_type_id FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
182  + "(blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id AND " //NON-NLS
183  + "blackboard_artifacts.artifact_type_id=" + artId //NON-NLS
184  + ") AND (attribute_type_id=" + setId + " OR " //NON-NLS
185  + "attribute_type_id=" + wordId + " OR " //NON-NLS
186  + "attribute_type_id=" + regexId + ")"; //NON-NLS
187 
188  try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
189  ResultSet resultSet = dbQuery.getResultSet();
190  while (resultSet.next()) {
191  String value = resultSet.getString("value_text"); //NON-NLS
192  long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
193  long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS
194  if (!artifactIds.containsKey(artifactId)) {
195  artifactIds.put(artifactId, new LinkedHashMap<Long, String>());
196  }
197  if (!value.equals("")) {
198  artifactIds.get(artifactId).put(typeId, value);
199  }
200  }
201  } catch (TskCoreException | SQLException ex) {
202  logger.log(Level.WARNING, "SQL Exception occurred: ", ex); //NON-NLS
203  }
204 
205  populateMaps(artifactIds);
206  }
207  }
208 
209  @Override
210  public <T> T accept(AutopsyItemVisitor<T> v) {
211  return v.visit(this);
212  }
213 
214  // Created by CreateAutopsyNodeVisitor
215  public class RootNode extends DisplayableItemNode {
216 
217  public RootNode() {
218  super(Children.create(new ListFactory(), true), Lookups.singleton(KEYWORD_HITS));
219  super.setName(NAME);
220  super.setDisplayName(KEYWORD_HITS);
221  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
222  }
223 
224  @Override
225  public boolean isLeafTypeNode() {
226  return false;
227  }
228 
229  @Override
230  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
231  return v.visit(this);
232  }
233 
234  @Override
235  protected Sheet createSheet() {
236  Sheet s = super.createSheet();
237  Sheet.Set ss = s.get(Sheet.PROPERTIES);
238  if (ss == null) {
239  ss = Sheet.createPropertiesSet();
240  s.put(ss);
241  }
242 
243  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.name.name"),
244  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.name.displayName"),
245  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.name.desc"),
246  getName()));
247 
248  return s;
249  }
250 
251  @Override
252  public String getItemType() {
253  return getClass().getName();
254  }
255  }
256 
257  private class ListFactory extends ChildFactory.Detachable<String> implements Observer {
258 
259  private final PropertyChangeListener pcl = new PropertyChangeListener() {
260  @Override
261  public void propertyChange(PropertyChangeEvent evt) {
262  String eventType = evt.getPropertyName();
263  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
270  try {
278  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
279  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
280  keywordResults.update();
281  }
282  } catch (IllegalStateException notUsed) {
286  }
287  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
288  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
295  try {
297  keywordResults.update();
298  } catch (IllegalStateException notUsed) {
302  }
303  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
304  // case was closed. Remove listeners so that we don't get called with a stale case handle
305  if (evt.getNewValue() == null) {
306  removeNotify();
307  skCase = null;
308  }
309  }
310  }
311  };
312 
313  @Override
314  protected void addNotify() {
318  keywordResults.update();
319  keywordResults.addObserver(this);
320  }
321 
322  @Override
323  protected void removeNotify() {
327  keywordResults.deleteObserver(this);
328  }
329 
330  @Override
331  protected boolean createKeys(List<String> list) {
332  list.addAll(keywordResults.getListNames());
333  return true;
334  }
335 
336  @Override
337  protected Node createNodeForKey(String key) {
338  return new ListNode(key);
339  }
340 
341  @Override
342  public void update(Observable o, Object arg) {
343  refresh(true);
344  }
345  }
346 
347  public class ListNode extends DisplayableItemNode implements Observer {
348 
349  private final String listName;
350 
351  public ListNode(String listName) {
352  super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName));
353  super.setName(listName);
354  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
355  this.listName = listName;
357  keywordResults.addObserver(this);
358  }
359 
360  private void updateDisplayName() {
361  int totalDescendants = 0;
362  for (String word : keywordResults.getKeywords(listName)) {
363  Set<Long> ids = keywordResults.getArtifactIds(listName, word);
364  totalDescendants += ids.size();
365  }
366  super.setDisplayName(listName + " (" + totalDescendants + ")");
367  }
368 
369  @Override
370  protected Sheet createSheet() {
371  Sheet s = super.createSheet();
372  Sheet.Set ss = s.get(Sheet.PROPERTIES);
373  if (ss == null) {
374  ss = Sheet.createPropertiesSet();
375  s.put(ss);
376  }
377 
378  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.name"),
379  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.displayName"),
380  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.desc"),
381  listName));
382 
383  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.numChildren.name"),
384  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.numChildren.displayName"),
385  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.numChildren.desc"),
386  keywordResults.getKeywords(listName).size()));
387 
388  return s;
389  }
390 
391  @Override
392  public boolean isLeafTypeNode() {
393  return false;
394  }
395 
396  @Override
397  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
398  return v.visit(this);
399  }
400 
401  @Override
402  public void update(Observable o, Object arg) {
404  }
405 
406  @Override
407  public String getItemType() {
408  return getClass().getName();
409  }
410  }
411 
412  private class TermFactory extends ChildFactory.Detachable<String> implements Observer {
413 
414  private final String setName;
415 
416  private TermFactory(String setName) {
417  super();
418  this.setName = setName;
419  }
420 
421  @Override
422  protected void addNotify() {
423  keywordResults.addObserver(this);
424  }
425 
426  @Override
427  protected void removeNotify() {
428  keywordResults.deleteObserver(this);
429  }
430 
431  @Override
432  protected boolean createKeys(List<String> list) {
433  list.addAll(keywordResults.getKeywords(setName));
434  return true;
435  }
436 
437  @Override
438  protected Node createNodeForKey(String key) {
439  return new TermNode(setName, key);
440  }
441 
442  @Override
443  public void update(Observable o, Object arg) {
444  refresh(true);
445  }
446  }
447 
448  public class TermNode extends DisplayableItemNode implements Observer {
449 
450  private final String setName;
451  private final String keyword;
452 
453  public TermNode(String setName, String keyword) {
454  super(Children.create(new HitsFactory(setName, keyword), true), Lookups.singleton(keyword));
455  super.setName(keyword);
456  this.setName = setName;
457  this.keyword = keyword;
458  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
460  keywordResults.addObserver(this);
461  }
462 
463  private void updateDisplayName() {
464  super.setDisplayName(keyword + " (" + keywordResults.getArtifactIds(setName, keyword).size() + ")");
465  }
466 
467  @Override
468  public void update(Observable o, Object arg) {
470  }
471 
472  @Override
473  public boolean isLeafTypeNode() {
474  return true;
475  }
476 
477  @Override
478  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
479  return v.visit(this);
480  }
481 
482  @Override
483  protected Sheet createSheet() {
484  Sheet s = super.createSheet();
485  Sheet.Set ss = s.get(Sheet.PROPERTIES);
486  if (ss == null) {
487  ss = Sheet.createPropertiesSet();
488  s.put(ss);
489  }
490 
491  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.name"),
492  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.displayName"),
493  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.desc"),
494  getDisplayName()));
495 
496  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.name"),
497  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.displayName"),
498  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.desc"),
499  keywordResults.getArtifactIds(setName, keyword).size()));
500 
501  return s;
502  }
503 
504  @Override
505  public String getItemType() {
506  return getClass().getName();
507  }
508  }
509 
510  public class HitsFactory extends ChildFactory.Detachable<Long> implements Observer {
511 
512  private final String keyword;
513  private final String setName;
514 
515  public HitsFactory(String setName, String keyword) {
516  super();
517  this.setName = setName;
518  this.keyword = keyword;
519  }
520 
521  @Override
522  protected void addNotify() {
523  keywordResults.addObserver(this);
524  }
525 
526  @Override
527  protected void removeNotify() {
528  keywordResults.deleteObserver(this);
529  }
530 
531  @Override
532  protected boolean createKeys(List<Long> list) {
533  list.addAll(keywordResults.getArtifactIds(setName, keyword));
534  return true;
535  }
536 
537  @Override
538  protected Node createNodeForKey(Long artifactId) {
539  if (skCase == null) {
540  return null;
541  }
542 
543  try {
544  BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId);
546  AbstractFile file;
547  try {
548  file = skCase.getAbstractFileById(art.getObjectID());
549  } catch (TskCoreException ex) {
550  logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren"); //NON-NLS
551  return n;
552  }
553 
554  // It is possible to get a keyword hit on artifacts generated
555  // for the underlying image in which case MAC times are not
556  // available/applicable/useful.
557  if (file == null) {
558  return n;
559  }
560 
562  NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.modTime.name"),
563  NbBundle.getMessage(this.getClass(),
564  "KeywordHits.createNodeForKey.modTime.displayName"),
565  NbBundle.getMessage(this.getClass(),
566  "KeywordHits.createNodeForKey.modTime.desc"),
567  ContentUtils.getStringTime(file.getMtime(), file)));
569  NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.accessTime.name"),
570  NbBundle.getMessage(this.getClass(),
571  "KeywordHits.createNodeForKey.accessTime.displayName"),
572  NbBundle.getMessage(this.getClass(),
573  "KeywordHits.createNodeForKey.accessTime.desc"),
574  ContentUtils.getStringTime(file.getAtime(), file)));
576  NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.chgTime.name"),
577  NbBundle.getMessage(this.getClass(),
578  "KeywordHits.createNodeForKey.chgTime.displayName"),
579  NbBundle.getMessage(this.getClass(),
580  "KeywordHits.createNodeForKey.chgTime.desc"),
581  ContentUtils.getStringTime(file.getCtime(), file)));
582  return n;
583  } catch (TskException ex) {
584  logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS
585  }
586  return null;
587  }
588 
589  @Override
590  public void update(Observable o, Object arg) {
591  refresh(true);
592  }
593  }
594 }
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static String getStringTime(long epochSeconds, TimeZone tzone)
static synchronized IngestManager getInstance()
static void removePropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:318
final Map< String, Map< String, Set< Long > > > topLevelMap
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
static void addPropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:306
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:161

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