Autopsy  4.6.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-2018 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.EnumSet;
28 import java.util.HashSet;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Observable;
33 import java.util.Observer;
34 import java.util.Set;
35 import java.util.logging.Level;
36 import java.util.stream.Collectors;
37 import org.apache.commons.lang3.StringUtils;
38 import org.openide.nodes.ChildFactory;
39 import org.openide.nodes.Children;
40 import org.openide.nodes.Node;
41 import org.openide.nodes.Sheet;
42 import org.openide.util.Lookup;
43 import org.openide.util.NbBundle;
44 import org.openide.util.lookup.Lookups;
48 import static org.sleuthkit.autopsy.datamodel.Bundle.*;
51 import org.sleuthkit.datamodel.AbstractFile;
52 import org.sleuthkit.datamodel.BlackboardArtifact;
53 import org.sleuthkit.datamodel.BlackboardAttribute;
54 import org.sleuthkit.datamodel.SleuthkitCase;
55 import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
56 import org.sleuthkit.datamodel.TskCoreException;
57 
61 public class KeywordHits implements AutopsyVisitableItem {
62 
63  private static final Logger logger = Logger.getLogger(KeywordHits.class.getName());
64 
65  @NbBundle.Messages("KeywordHits.kwHits.text=Keyword Hits")
66  private static final String KEYWORD_HITS = KeywordHits_kwHits_text();
67  @NbBundle.Messages("KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
68  private static final String SIMPLE_LITERAL_SEARCH = KeywordHits_simpleLiteralSearch_text();
69  @NbBundle.Messages("KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
70  private static final String SIMPLE_REGEX_SEARCH = KeywordHits_singleRegexSearch_text();
71 
72  public static final String NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
73 
74  private SleuthkitCase skCase;
76 
82  private static final String DEFAULT_INSTANCE_NAME = "DEFAULT_INSTANCE_NAME";
83 
87  private static final String KEYWORD_HIT_ATTRIBUTES_QUERY = "SELECT blackboard_attributes.value_text, "//NON-NLS
88  + "blackboard_attributes.value_int32, "//NON-NLS
89  + "blackboard_attributes.artifact_id, " //NON-NLS
90  + "blackboard_attributes.attribute_type_id "//NON-NLS
91  + "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS
92  + "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS
93  + " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
94  + " AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()//NON-NLS
95  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()//NON-NLS
96  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()//NON-NLS
97  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()//NON-NLS
98  + ")"; //NON-NLS
99 
100  static private boolean isOnlyDefaultInstance(List<String> instances) {
101  return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
102  }
103 
104  public KeywordHits(SleuthkitCase skCase) {
105  this.skCase = skCase;
106  keywordResults = new KeywordResults();
107  }
108 
109  /*
110  * All of these maps and code assume the following: Regexps will have an
111  * 'instance' layer that shows the specific words that matched the regexp
112  * Exact match and substring will not have the instance layer and instead
113  * will have the specific hits below their term.
114  */
115  private final class KeywordResults extends Observable {
116 
117  // Map from listName/Type to Map of keywords/regexp to Map of instance terms to Set of artifact Ids
118  // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
119  private final Map<String, Map<String, Map<String, Set<Long>>>> topLevelMap = new LinkedHashMap<>();
120 
121  KeywordResults() {
122  update();
123  }
124 
130  List<String> getListNames() {
131  synchronized (topLevelMap) {
132  List<String> names = new ArrayList<>(topLevelMap.keySet());
133  // this causes the "Single ..." terms to be in the middle of the results,
134  // which is wierd. Make a custom comparator or do something else to maek them on top
135  //Collections.sort(names);
136  return names;
137  }
138  }
139 
148  List<String> getKeywords(String listName) {
149  List<String> keywords;
150  synchronized (topLevelMap) {
151  keywords = new ArrayList<>(topLevelMap.get(listName).keySet());
152  }
153  Collections.sort(keywords);
154  return keywords;
155  }
156 
167  List<String> getKeywordInstances(String listName, String keyword) {
168  List<String> instances;
169  synchronized (topLevelMap) {
170  instances = new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
171  }
172  Collections.sort(instances);
173  return instances;
174  }
175 
187  Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
188  synchronized (topLevelMap) {
189  return topLevelMap.get(listName).get(keyword).get(keywordInstance);
190  }
191  }
192 
202  void addRegExpToList(Map<String, Map<String, Set<Long>>> listMap, String regExp, String keywordInstance, Long artifactId) {
203  Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(regExp, r -> new LinkedHashMap<>());
204  // add this ID to the instances entry, creating one if needed
205  instanceMap.computeIfAbsent(keywordInstance, ki -> new HashSet<>()).add(artifactId);
206  }
207 
216  void addNonRegExpMatchToList(Map<String, Map<String, Set<Long>>> listMap, String keyWord, Long artifactId) {
217  Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(keyWord, k -> new LinkedHashMap<>());
218 
219  // Use the default instance name, since we don't need that level in the tree
220  instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN -> new HashSet<>()).add(artifactId);
221  }
222 
230  void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
231  synchronized (topLevelMap) {
232  topLevelMap.clear();
233 
234  // map of list name to keword to artifact IDs
235  Map<String, Map<String, Map<String, Set<Long>>>> listsMap = new LinkedHashMap<>();
236 
237  // Map from from literal keyword to instances (which will be empty) to artifact IDs
238  Map<String, Map<String, Set<Long>>> literalMap = new LinkedHashMap<>();
239 
240  // Map from regex keyword artifact to instances to artifact IDs
241  Map<String, Map<String, Set<Long>>> regexMap = new LinkedHashMap<>();
242 
243  // top-level nodes
244  topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
245  topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
246 
247  for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
248  long id = art.getKey();
249  Map<Long, String> attributes = art.getValue();
250 
251  // I think we can use attributes.remove(...) here? - why should bwe use remove?
252  String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
253  String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
254  String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
255  String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()));
256 
257  if (listName != null) { // part of a list
258  // get or create list entry
259  Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln -> new LinkedHashMap<>());
260 
261  if ("1".equals(kwType) || reg == null) { //literal, substring or exact
262  /*
263  * Substring, treated same as exact match. "1" is
264  * the ordinal value for substring as defined in
265  * KeywordSearch.java. The original term should be
266  * stored in reg
267  */
268  word = (reg != null) ? reg : word; //use original term if it there.
269  addNonRegExpMatchToList(listMap, word, id);
270  } else {
271  addRegExpToList(listMap, reg, word, id);
272  }
273  } else {//single term
274  if ("1".equals(kwType) || reg == null) { //literal, substring or exact
275  /*
276  * Substring, treated same as exact match. "1" is
277  * the ordinal value for substring as defined in
278  * KeywordSearch.java. The original term should be
279  * stored in reg
280  */
281  word = (reg != null) ? reg : word; //use original term if it there.
282  addNonRegExpMatchToList(literalMap, word, id);
283  } else {
284  addRegExpToList(regexMap, reg, word, id);
285  }
286  }
287  }
288  topLevelMap.putAll(listsMap);
289  }
290 
291  setChanged();
292  notifyObservers();
293  }
294 
295  public void update() {
296  // maps Artifact ID to map of attribute types to attribute values
297  Map<Long, Map<Long, String>> artifactIds = new LinkedHashMap<>();
298 
299  if (skCase == null) {
300  return;
301  }
302 
303  try (CaseDbQuery dbQuery = skCase.executeQuery(KEYWORD_HIT_ATTRIBUTES_QUERY)) {
304  ResultSet resultSet = dbQuery.getResultSet();
305  while (resultSet.next()) {
306  long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
307  long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS
308  String valueStr = resultSet.getString("value_text"); //NON-NLS
309 
310  //get the map of attributes for this artifact
311  Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai -> new LinkedHashMap<>());
312  if (StringUtils.isNotEmpty(valueStr)) {
313  attributesByTypeMap.put(typeId, valueStr);
314  } else {
315  // Keyword Search Type is an int
316  Long valueLong = resultSet.getLong("value_int32");
317  attributesByTypeMap.put(typeId, valueLong.toString());
318  }
319  }
320  } catch (TskCoreException | SQLException ex) {
321  logger.log(Level.WARNING, "SQL Exception occurred: ", ex); //NON-NLS
322  }
323 
324  populateTreeMaps(artifactIds);
325  }
326  }
327 
328  @Override
329  public <T> T accept(AutopsyItemVisitor<T> visitor) {
330  return visitor.visit(this);
331  }
332 
333  // Created by CreateAutopsyNodeVisitor
334  public class RootNode extends DisplayableItemNode {
335 
336  public RootNode() {
337  super(Children.create(new ListFactory(), true), Lookups.singleton(KEYWORD_HITS));
338  super.setName(NAME);
339  super.setDisplayName(KEYWORD_HITS);
340  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
341  }
342 
343  @Override
344  public boolean isLeafTypeNode() {
345  return false;
346  }
347 
348  @Override
349  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
350  return visitor.visit(this);
351  }
352 
353  @Override
354  @NbBundle.Messages({"KeywordHits.createSheet.name.name=Name",
355  "KeywordHits.createSheet.name.displayName=Name",
356  "KeywordHits.createSheet.name.desc=no description"})
357  protected Sheet createSheet() {
358  Sheet sheet = super.createSheet();
359  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
360  if (sheetSet == null) {
361  sheetSet = Sheet.createPropertiesSet();
362  sheet.put(sheetSet);
363  }
364 
365  sheetSet.put(new NodeProperty<>(
366  KeywordHits_createSheet_name_name(),
367  KeywordHits_createSheet_name_displayName(),
368  KeywordHits_createSheet_name_desc(),
369  getName()));
370 
371  return sheet;
372  }
373 
374  @Override
375  public String getItemType() {
376  return getClass().getName();
377  }
378  }
379 
380  private abstract class DetachableObserverChildFactory<X> extends ChildFactory.Detachable<X> implements Observer {
381 
382  @Override
383  protected void addNotify() {
384  keywordResults.addObserver(this);
385  }
386 
387  @Override
388  protected void removeNotify() {
389  keywordResults.deleteObserver(this);
390  }
391 
392  @Override
393  public void update(Observable o, Object arg) {
394  refresh(true);
395  }
396  }
397 
401  private class ListFactory extends DetachableObserverChildFactory<String> {
402 
403  private final PropertyChangeListener pcl = new PropertyChangeListener() {
404  @Override
405  public void propertyChange(PropertyChangeEvent evt) {
406  String eventType = evt.getPropertyName();
407  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
414  try {
415  Case.getOpenCase();
422  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
423  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
424  keywordResults.update();
425  }
426  } catch (NoCurrentCaseException notUsed) {
427  // Case is closed, do nothing.
428  }
429  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
430  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
437  try {
438  Case.getOpenCase();
439  keywordResults.update();
440  } catch (NoCurrentCaseException notUsed) {
441  // Case is closed, do nothing.
442  }
443  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())
444  && evt.getNewValue() == null) {
445  /*
446  * Case was closed. Remove listeners so that we don't get
447  * called with a stale case handle
448  */
449  removeNotify();
450  skCase = null;
451  }
452 
453  }
454  };
455 
456  @Override
457  protected void addNotify() {
461  keywordResults.update();
462  super.addNotify();
463  }
464 
465  @Override
466  protected void removeNotify() {
470  super.removeNotify();
471  }
472 
473  @Override
474  protected boolean createKeys(List<String> list) {
475  list.addAll(keywordResults.getListNames());
476  return true;
477  }
478 
479  @Override
480  protected Node createNodeForKey(String key) {
481  return new ListNode(key);
482  }
483  }
484 
485  private abstract class KWHitsNodeBase extends DisplayableItemNode implements Observer {
486 
487  private KWHitsNodeBase(Children children, Lookup lookup) {
488  super(children, lookup);
489  }
490 
491  private KWHitsNodeBase(Children children) {
492  super(children);
493  }
494 
495  @Override
496  public String getItemType() {
497  return getClass().getName();
498  }
499 
500  @Override
501  public void update(Observable o, Object arg) {
502  updateDisplayName();
503  }
504 
505  final void updateDisplayName() {
506  super.setDisplayName(getName() + " (" + countTotalDescendants() + ")");
507  }
508 
509  abstract int countTotalDescendants();
510  }
511 
516  class ListNode extends KWHitsNodeBase {
517 
518  private final String listName;
519 
520  private ListNode(String listName) {
521  super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName));
522  super.setName(listName);
523  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
524  this.listName = listName;
525  updateDisplayName();
526  keywordResults.addObserver(this);
527  }
528 
529  @Override
530  public int countTotalDescendants() {
531  int totalDescendants = 0;
532 
533  for (String word : keywordResults.getKeywords(listName)) {
534  for (String instance : keywordResults.getKeywordInstances(listName, word)) {
535  Set<Long> ids = keywordResults.getArtifactIds(listName, word, instance);
536  totalDescendants += ids.size();
537  }
538  }
539  return totalDescendants;
540  }
541 
542  @Override
543  @NbBundle.Messages({"KeywordHits.createSheet.listName.name=List Name",
544  "KeywordHits.createSheet.listName.displayName=List Name",
545  "KeywordHits.createSheet.listName.desc=no description",
546  "KeywordHits.createSheet.numChildren.name=Number of Children",
547  "KeywordHits.createSheet.numChildren.displayName=Number of Children",
548  "KeywordHits.createSheet.numChildren.desc=no description"})
549  protected Sheet createSheet() {
550  Sheet sheet = super.createSheet();
551  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
552  if (sheetSet == null) {
553  sheetSet = Sheet.createPropertiesSet();
554  sheet.put(sheetSet);
555  }
556 
557  sheetSet.put(new NodeProperty<>(
558  KeywordHits_createSheet_listName_name(),
559  KeywordHits_createSheet_listName_displayName(),
560  KeywordHits_createSheet_listName_desc(),
561  listName));
562 
563  sheetSet.put(new NodeProperty<>(
564  KeywordHits_createSheet_numChildren_name(),
565  KeywordHits_createSheet_numChildren_displayName(),
566  KeywordHits_createSheet_numChildren_desc(),
567  keywordResults.getKeywords(listName).size()));
568 
569  return sheet;
570  }
571 
572  @Override
573  public boolean isLeafTypeNode() {
574  return false;
575  }
576 
577  @Override
578  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
579  return visitor.visit(this);
580  }
581  }
582 
586  private class TermFactory extends DetachableObserverChildFactory<String> {
587 
588  private final String setName;
589 
590  private TermFactory(String setName) {
591  super();
592  this.setName = setName;
593  }
594 
595  @Override
596  protected boolean createKeys(List<String> list) {
597  list.addAll(keywordResults.getKeywords(setName));
598  return true;
599  }
600 
601  @Override
602  protected Node createNodeForKey(String key) {
603  return new TermNode(setName, key);
604  }
605  }
606 
610  class TermNode extends KWHitsNodeBase {
611 
612  private final String setName;
613  private final String keyword;
614 
615  private TermNode(String setName, String keyword) {
616  super(Children.create(new RegExpInstancesFactory(setName, keyword), true), Lookups.singleton(keyword));
617  super.setName(keyword);
618  this.setName = setName;
619  this.keyword = keyword;
620  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
621  updateDisplayName();
622  keywordResults.addObserver(this);
623  }
624 
625  @Override
626  int countTotalDescendants() {
627  return keywordResults.getKeywordInstances(setName, keyword).stream()
628  .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
629  .sum();
630  }
631 
632  @Override
633  public boolean isLeafTypeNode() {
634  // is this an exact/substring match (i.e. did we use the DEFAULT name)?
635  return isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword));
636  }
637 
638  @Override
639  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
640  return visitor.visit(this);
641  }
642 
643  @Override
644  @NbBundle.Messages({"KeywordHits.createSheet.filesWithHits.name=Files with Hits",
645  "KeywordHits.createSheet.filesWithHits.displayName=Files with Hits",
646  "KeywordHits.createSheet.filesWithHits.desc=no description"})
647  protected Sheet createSheet() {
648  Sheet sheet = super.createSheet();
649  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
650  if (sheetSet == null) {
651  sheetSet = Sheet.createPropertiesSet();
652  sheet.put(sheetSet);
653  }
654  sheetSet.put(new NodeProperty<>(
655  KeywordHits_createSheet_listName_name(),
656  KeywordHits_createSheet_listName_displayName(),
657  KeywordHits_createSheet_listName_desc(),
658  getDisplayName()));
659 
660  sheetSet.put(new NodeProperty<>(
661  KeywordHits_createSheet_filesWithHits_name(),
662  KeywordHits_createSheet_filesWithHits_displayName(),
663  KeywordHits_createSheet_filesWithHits_desc(),
664  countTotalDescendants()));
665 
666  return sheet;
667  }
668  }
669 
675  private class RegExpInstanceKey {
676 
677  private final boolean isRegExp;
678  private String strKey;
679  private Long longKey;
680 
681  RegExpInstanceKey(String key) {
682  isRegExp = true;
683  strKey = key;
684  }
685 
686  RegExpInstanceKey(Long key) {
687  isRegExp = false;
688  longKey = key;
689  }
690 
691  boolean isRegExp() {
692  return isRegExp;
693  }
694 
695  Long getIdKey() {
696  return longKey;
697  }
698 
699  String getRegExpKey() {
700  return strKey;
701  }
702  }
703 
708  private class RegExpInstancesFactory extends DetachableObserverChildFactory<RegExpInstanceKey> {
709 
710  private final String keyword;
711  private final String setName;
712 
713  private RegExpInstancesFactory(String setName, String keyword) {
714  super();
715  this.setName = setName;
716  this.keyword = keyword;
717  }
718 
719  @Override
720  protected boolean createKeys(List<RegExpInstanceKey> list) {
721  List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
722  // The keys are different depending on what we are displaying.
723  // regexp get another layer to show instances.
724  // Exact/substring matches don't.
725  if (isOnlyDefaultInstance(instances)) {
726  list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream()
727  .map(RegExpInstanceKey::new)
728  .collect(Collectors.toList()));
729  } else {
730  list.addAll(instances.stream()
731  .map(RegExpInstanceKey::new)
732  .collect(Collectors.toList()));
733  }
734  return true;
735  }
736 
737  @Override
738  protected Node createNodeForKey(RegExpInstanceKey key) {
739  if (key.isRegExp()) {
740  return new RegExpInstanceNode(setName, keyword, key.getRegExpKey());
741  } else {
742  // if it isn't a regexp, then skip the 'instance' layer of the tree
743  return createBlackboardArtifactNode(key.getIdKey());
744  }
745  }
746 
747  }
748 
752  class RegExpInstanceNode extends KWHitsNodeBase {
753 
754  private final String setName;
755  private final String keyword;
756  private final String instance;
757 
758  private RegExpInstanceNode(String setName, String keyword, String instance) {
759  super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance));
760  super.setName(instance); //the instance represents the name of the keyword hit at this point as the keyword is the regex
761  this.setName = setName;
762  this.keyword = keyword;
763  this.instance = instance;
764  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
765  updateDisplayName();
766  keywordResults.addObserver(this);
767  }
768 
769  @Override
770  int countTotalDescendants() {
771  return keywordResults.getArtifactIds(setName, keyword, instance).size();
772  }
773 
774  @Override
775  public boolean isLeafTypeNode() {
776  return true;
777  }
778 
779  @Override
780  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
781  return visitor.visit(this);
782  }
783 
784  @Override
785  protected Sheet createSheet() {
786  Sheet sheet = super.createSheet();
787  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
788  if (sheetSet == null) {
789  sheetSet = Sheet.createPropertiesSet();
790  sheet.put(sheetSet);
791  }
792 
793  sheetSet.put(new NodeProperty<>(
794  KeywordHits_createSheet_listName_name(),
795  KeywordHits_createSheet_listName_displayName(),
796  KeywordHits_createSheet_listName_desc(),
797  getDisplayName()));
798 
799  sheetSet.put(new NodeProperty<>(
800  KeywordHits_createSheet_filesWithHits_name(),
801  KeywordHits_createSheet_filesWithHits_displayName(),
802  KeywordHits_createSheet_filesWithHits_desc(),
803  keywordResults.getArtifactIds(setName, keyword, instance).size()));
804 
805  return sheet;
806  }
807 
808  }
809 
817  @NbBundle.Messages({"KeywordHits.createNodeForKey.modTime.name=ModifiedTime",
818  "KeywordHits.createNodeForKey.modTime.displayName=Modified Time",
819  "KeywordHits.createNodeForKey.modTime.desc=Modified Time",
820  "KeywordHits.createNodeForKey.accessTime.name=AccessTime",
821  "KeywordHits.createNodeForKey.accessTime.displayName=Access Time",
822  "KeywordHits.createNodeForKey.accessTime.desc=Access Time",
823  "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
824  "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
825  "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
827  if (skCase == null) {
828  return null;
829  }
830 
831  try {
832  BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId);
834  AbstractFile file;
835  try {
836  file = skCase.getAbstractFileById(art.getObjectID());
837  } catch (TskCoreException ex) {
838  logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex); //NON-NLS
839  return n;
840  }
841 
842  /*
843  * It is possible to get a keyword hit on artifacts generated for
844  * the underlying image in which case MAC times are not
845  * available/applicable/useful.
846  */
847  if (file == null) {
848  return n;
849  }
850 
852  KeywordHits_createNodeForKey_modTime_name(),
853  KeywordHits_createNodeForKey_modTime_displayName(),
854  KeywordHits_createNodeForKey_modTime_desc(),
855  ContentUtils.getStringTime(file.getMtime(), file)));
857  KeywordHits_createNodeForKey_accessTime_name(),
858  KeywordHits_createNodeForKey_accessTime_displayName(),
859  KeywordHits_createNodeForKey_accessTime_desc(),
860  ContentUtils.getStringTime(file.getAtime(), file)));
862  KeywordHits_createNodeForKey_chgTime_name(),
863  KeywordHits_createNodeForKey_chgTime_displayName(),
864  KeywordHits_createNodeForKey_chgTime_desc(),
865  ContentUtils.getStringTime(file.getCtime(), file)));
866  return n;
867  } catch (TskCoreException ex) {
868  logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS
869  }
870  return null;
871  }
872 
876  private class HitsFactory extends DetachableObserverChildFactory<Long> {
877 
878  private final String keyword;
879  private final String setName;
880  private final String instance;
881 
882  private HitsFactory(String setName, String keyword, String instance) {
883  super();
884  this.setName = setName;
885  this.keyword = keyword;
886  this.instance = instance;
887  }
888 
889  @Override
890  protected boolean createKeys(List<Long> list) {
891  list.addAll(keywordResults.getArtifactIds(setName, keyword, instance));
892  return true;
893  }
894 
895  @Override
896  protected Node createNodeForKey(Long artifactId) {
897  return createBlackboardArtifactNode(artifactId);
898  }
899  }
900 }
static boolean isOnlyDefaultInstance(List< String > instances)
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static String getStringTime(long epochSeconds, TimeZone tzone)
static synchronized IngestManager getInstance()
BlackboardArtifactNode createBlackboardArtifactNode(Long artifactId)
final Map< String, Map< String, Map< String, Set< Long > > > > topLevelMap
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:420
HitsFactory(String setName, String keyword, String instance)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:465

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