Autopsy  3.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-2014 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;
52 
56 public class KeywordHits implements AutopsyVisitableItem {
57 
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  // Map from listName/Type to Map of keyword to set of artifact Ids
75  private final Map<String, Map<String, Set<Long>>> topLevelMap;
76 
77  KeywordResults() {
78  topLevelMap = new LinkedHashMap<>();
79  update();
80  }
81 
82  List<String> getListNames() {
83  List <String> names = new ArrayList<>(topLevelMap.keySet());
84  // this causes the "Single ..." terms to be in the middle of the results,
85  // which is wierd. Make a custom comparator or do something else to maek them on top
86  //Collections.sort(names);
87  return names;
88  }
89 
90  List<String> getKeywords(String listName) {
91  List <String> keywords = new ArrayList<>(topLevelMap.get(listName).keySet());
92  Collections.sort(keywords);
93  return keywords;
94  }
95 
96  Set<Long> getArtifactIds(String listName, String keyword) {
97  return topLevelMap.get(listName).get(keyword);
98  }
99 
100  // populate maps based on artifactIds
101  void populateMaps(Map<Long, Map<Long, String>> artifactIds) {
102  topLevelMap.clear();
103 
104  // map of list name to keword to artifact IDs
105  Map<String, Map<String, Set<Long>>> listsMap = new LinkedHashMap<>();
106 
107  // Map from from literal keyword to artifact IDs
108  Map<String, Set<Long>> literalMap = new LinkedHashMap<>();
109 
110  // Map from regex keyword artifact IDs
111  Map<String, Set<Long>> regexMap = new LinkedHashMap<>();
112 
113  // top-level nodes
114  topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
115  topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
116 
117  for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
118  long id = art.getKey();
119  Map<Long, String> attributes = art.getValue();
120 
121  // I think we can use attributes.remove(...) here?
122  String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
123  String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
124  String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
125 
126  // part of a list
127  if (listName != null) {
128  if (listsMap.containsKey(listName) == false) {
129  listsMap.put(listName, new LinkedHashMap<String, Set<Long>>());
130  }
131 
132  Map<String, Set<Long>> listMap = listsMap.get(listName);
133  if (listMap.containsKey(word) == false) {
134  listMap.put(word, new HashSet<Long>());
135  }
136 
137  listMap.get(word).add(id);
138  }
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  }
146  // literal, single term
147  else {
148  if (literalMap.containsKey(word) == false) {
149  literalMap.put(word, new HashSet<Long>());
150  }
151  literalMap.get(word).add(id);
152  }
153  topLevelMap.putAll(listsMap);
154  }
155 
156  setChanged();
157  notifyObservers();
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  private class ListFactory extends ChildFactory.Detachable<String> implements Observer {
244 
245  private final PropertyChangeListener pcl = new PropertyChangeListener() {
246  @Override
247  public void propertyChange(PropertyChangeEvent evt) {
248  String eventType = evt.getPropertyName();
249 
250  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
251  if (((ModuleDataEvent) evt.getOldValue()).getArtifactType() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT) {
252  keywordResults.update();
253  }
254  }
255  else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
256  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
257  keywordResults.update();
258  }
259  else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
260  // case was closed. Remove listeners so that we don't get called with a stale case handle
261  if (evt.getNewValue() == null) {
262  removeNotify();
263  skCase = null;
264  }
265  }
266  }
267  };
268 
269  @Override
270  protected void addNotify() {
274  keywordResults.update();
275  keywordResults.addObserver(this);
276  }
277 
278  @Override
279  protected void removeNotify() {
283  keywordResults.deleteObserver(this);
284  }
285 
286  @Override
287  protected boolean createKeys(List<String> list) {
288  list.addAll(keywordResults.getListNames());
289  return true;
290  }
291 
292  @Override
293  protected Node createNodeForKey(String key) {
294  return new ListNode(key);
295  }
296 
297  @Override
298  public void update(Observable o, Object arg) {
299  refresh(true);
300  }
301  }
302 
303  public class ListNode extends DisplayableItemNode implements Observer {
304  private String listName;
305 
306  public ListNode(String listName) {
307  super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName));
308  super.setName(listName);
309  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
310  this.listName = listName;
312  keywordResults.addObserver(this);
313  }
314 
315  private void updateDisplayName() {
316  int totalDescendants = 0;
317  for (String word : keywordResults.getKeywords(listName)) {
318  Set<Long> ids = keywordResults.getArtifactIds(listName, word);
319  totalDescendants += ids.size();
320  }
321  super.setDisplayName(listName + " (" + totalDescendants + ")");
322  }
323 
324  @Override
325  protected Sheet createSheet() {
326  Sheet s = super.createSheet();
327  Sheet.Set ss = s.get(Sheet.PROPERTIES);
328  if (ss == null) {
329  ss = Sheet.createPropertiesSet();
330  s.put(ss);
331  }
332 
333  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.name"),
334  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.displayName"),
335  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.desc"),
336  listName));
337 
338 
339  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.numChildren.name"),
340  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.numChildren.displayName"),
341  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.numChildren.desc"),
342  keywordResults.getKeywords(listName).size()));
343 
344  return s;
345  }
346 
347  @Override
348  public boolean isLeafTypeNode() {
349  return false;
350  }
351 
352  @Override
353  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
354  return v.visit(this);
355  }
356 
357  @Override
358  public void update(Observable o, Object arg) {
360  }
361  }
362 
363  private class TermFactory extends ChildFactory.Detachable<String> implements Observer {
364  private String setName;
365 
366  private TermFactory(String setName) {
367  super();
368  this.setName = setName;
369  }
370 
371  @Override
372  protected void addNotify() {
373  keywordResults.addObserver(this);
374  }
375 
376  @Override
377  protected void removeNotify() {
378  keywordResults.deleteObserver(this);
379  }
380 
381  @Override
382  protected boolean createKeys(List<String> list) {
383  list.addAll(keywordResults.getKeywords(setName));
384  return true;
385  }
386 
387  @Override
388  protected Node createNodeForKey(String key) {
389  return new TermNode(setName, key);
390  }
391 
392  @Override
393  public void update(Observable o, Object arg) {
394  refresh(true);
395  }
396  }
397 
398  public class TermNode extends DisplayableItemNode implements Observer {
399 
400  private String setName;
401  private String keyword;
402 
403  public TermNode(String setName, String keyword) {
404  super(Children.create(new HitsFactory (setName, keyword), true), Lookups.singleton(keyword));
405  super.setName(keyword);
406  this.setName = setName;
407  this.keyword = keyword;
408  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
410  keywordResults.addObserver(this);
411  }
412 
413  private void updateDisplayName() {
414  super.setDisplayName(keyword + " (" + keywordResults.getArtifactIds(setName, keyword).size() + ")");
415  }
416 
417 
418  @Override
419  public void update(Observable o, Object arg) {
421  }
422 
423  @Override
424  public boolean isLeafTypeNode() {
425  return true;
426  }
427 
428  @Override
429  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
430  return v.visit(this);
431  }
432 
433  @Override
434  protected Sheet createSheet() {
435  Sheet s = super.createSheet();
436  Sheet.Set ss = s.get(Sheet.PROPERTIES);
437  if (ss == null) {
438  ss = Sheet.createPropertiesSet();
439  s.put(ss);
440  }
441 
442  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.name"),
443  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.displayName"),
444  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.desc"),
445  getDisplayName()));
446 
447  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.name"),
448  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.displayName"),
449  NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.desc"),
450  keywordResults.getArtifactIds(setName, keyword).size()));
451 
452  return s;
453  }
454  }
455 
456  public class HitsFactory extends ChildFactory.Detachable<Long> implements Observer {
457  private String keyword;
458  private String setName;
459 
460  public HitsFactory(String setName, String keyword) {
461  super();
462  this.setName = setName;
463  this.keyword = keyword;
464  }
465 
466  @Override
467  protected void addNotify() {
468  keywordResults.addObserver(this);
469  }
470 
471  @Override
472  protected void removeNotify() {
473  keywordResults.deleteObserver(this);
474  }
475 
476  @Override
477  protected boolean createKeys(List<Long> list) {
478  list.addAll(keywordResults.getArtifactIds(setName, keyword));
479  return true;
480  }
481 
482 
483  @Override
484  protected Node createNodeForKey(Long artifactId) {
485  if (skCase == null) {
486  return null;
487  }
488 
489  try {
490  BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId);
492  AbstractFile file;
493  try {
494  file = skCase.getAbstractFileById(art.getObjectID());
495  } catch (TskCoreException ex) {
496  logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren"); //NON-NLS
497  return n;
498  }
499 
500  // It is possible to get a keyword hit on artifacts generated
501  // for the underlying image in which case MAC times are not
502  // available/applicable/useful.
503  if (file == null)
504  return n;
505 
506  n.addNodeProperty(new NodeProperty<>(
507  NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.modTime.name"),
508  NbBundle.getMessage(this.getClass(),
509  "KeywordHits.createNodeForKey.modTime.displayName"),
510  NbBundle.getMessage(this.getClass(),
511  "KeywordHits.createNodeForKey.modTime.desc"),
512  ContentUtils.getStringTime(file.getMtime(), file)));
513  n.addNodeProperty(new NodeProperty<>(
514  NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.accessTime.name"),
515  NbBundle.getMessage(this.getClass(),
516  "KeywordHits.createNodeForKey.accessTime.displayName"),
517  NbBundle.getMessage(this.getClass(),
518  "KeywordHits.createNodeForKey.accessTime.desc"),
519  ContentUtils.getStringTime(file.getAtime(), file)));
520  n.addNodeProperty(new NodeProperty<>(
521  NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.chgTime.name"),
522  NbBundle.getMessage(this.getClass(),
523  "KeywordHits.createNodeForKey.chgTime.displayName"),
524  NbBundle.getMessage(this.getClass(),
525  "KeywordHits.createNodeForKey.chgTime.desc"),
526  ContentUtils.getStringTime(file.getCtime(), file)));
527  return n;
528  } catch (TskException ex) {
529  logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS
530  }
531  return null;
532  }
533 
534  @Override
535  public void update(Observable o, Object arg) {
536  refresh(true);
537  }
538  }
539 }
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static String getStringTime(long epochSeconds, TimeZone tzone)
static synchronized IngestManager getInstance()
BlackboardArtifact getBlackboardArtifact(long artifactID)
final Map< String, Map< String, Set< Long > > > topLevelMap
AbstractFile getAbstractFileById(long id)
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
static synchronized void removePropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:837
void addIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized void addPropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:833
static Logger getLogger(String name)
Definition: Logger.java:131
CaseDbQuery executeQuery(String query)

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