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

Copyright © 2012-2015 Basis Technology. Generated on: Wed Apr 6 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.