Autopsy  4.17.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-2020 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.Comparator;
28 import java.util.EnumSet;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.LinkedHashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Observable;
35 import java.util.Observer;
36 import java.util.Set;
37 import java.util.logging.Level;
38 import org.apache.commons.lang3.StringUtils;
39 import org.openide.nodes.ChildFactory;
40 import org.openide.nodes.Children;
41 import org.openide.nodes.Node;
42 import org.openide.nodes.Sheet;
43 import org.openide.util.Lookup;
44 import org.openide.util.NbBundle;
45 import org.openide.util.lookup.Lookups;
49 import static org.sleuthkit.autopsy.datamodel.Bundle.*;
52 import org.sleuthkit.datamodel.AbstractFile;
53 import org.sleuthkit.datamodel.BlackboardArtifact;
54 import org.sleuthkit.datamodel.BlackboardAttribute;
55 import org.sleuthkit.datamodel.SleuthkitCase;
56 import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
57 import org.sleuthkit.datamodel.TskCoreException;
58 
62 public class KeywordHits implements AutopsyVisitableItem {
63 
64  private static final Logger logger = Logger.getLogger(KeywordHits.class.getName());
67  @NbBundle.Messages("KeywordHits.kwHits.text=Keyword Hits")
68  private static final String KEYWORD_HITS = KeywordHits_kwHits_text();
69  @NbBundle.Messages("KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
70  private static final String SIMPLE_LITERAL_SEARCH = KeywordHits_simpleLiteralSearch_text();
71  @NbBundle.Messages("KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
72  private static final String SIMPLE_REGEX_SEARCH = KeywordHits_singleRegexSearch_text();
73 
74  public static final String NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
75 
76  private SleuthkitCase skCase;
78  private final long filteringDSObjId; // 0 if not filtering/grouping by data source
79 
85  private static final String DEFAULT_INSTANCE_NAME = "DEFAULT_INSTANCE_NAME";
86 
90  private static final String KEYWORD_HIT_ATTRIBUTES_QUERY = "SELECT blackboard_attributes.value_text, "//NON-NLS
91  + "blackboard_attributes.value_int32, "//NON-NLS
92  + "blackboard_attributes.artifact_id, " //NON-NLS
93  + "blackboard_attributes.attribute_type_id "//NON-NLS
94  + "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS
95  + "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS
96  + " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
97  + " AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()//NON-NLS
98  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()//NON-NLS
99  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()//NON-NLS
100  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()//NON-NLS
101  + ")"; //NON-NLS
102 
103  static private boolean isOnlyDefaultInstance(List<String> instances) {
104  return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
105  }
106 
112  KeywordHits(SleuthkitCase skCase) {
113  this(skCase, 0);
114  }
115 
123  public KeywordHits(SleuthkitCase skCase, long objId) {
124  this.skCase = skCase;
125  this.filteringDSObjId = objId;
126  keywordResults = new KeywordResults();
127  }
128 
129  /*
130  * All of these maps and code assume the following: Regexps will have an
131  * 'instance' layer that shows the specific words that matched the regexp
132  * Exact match and substring will not have the instance layer and instead
133  * will have the specific hits below their term.
134  */
135  private final class KeywordResults extends Observable {
136 
137  // Map from listName/Type to Map of keywords/regexp to Map of instance terms to Set of artifact Ids
138  // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
139  private final Map<String, Map<String, Map<String, Set<Long>>>> topLevelMap = new LinkedHashMap<>();
140 
141  KeywordResults() {
142  update();
143  }
144 
150  List<String> getListNames() {
151  synchronized (topLevelMap) {
152  List<String> names = new ArrayList<>(topLevelMap.keySet());
153 
154  // sort the list names, but ensure that the special lists
155  // stay at the top.
156  Collections.sort(names, new Comparator<String>() {
157 
158  @Override
159  public int compare(String o1, String o2) {
160  // ideally, they would not be hard coded, but this module
161  // doesn't know about Keyword Search NBM
162  if (o1.startsWith("Single Literal Keyword Search")) {
163  return -1;
164  } else if (o2.startsWith("Single Literal Keyword Search")) {
165  return 1;
166  } else if (o1.startsWith("Single Regular Expression Search")) {
167  return -1;
168  } else if (o2.startsWith("Single Regular Expression Search")) {
169  return 1;
170  }
171  return o1.compareTo(o2);
172  }
173  });
174 
175  return names;
176  }
177  }
178 
187  List<String> getKeywords(String listName) {
188  List<String> keywords;
189  synchronized (topLevelMap) {
190  keywords = new ArrayList<>(topLevelMap.get(listName).keySet());
191  }
192  Collections.sort(keywords);
193  return keywords;
194  }
195 
206  List<String> getKeywordInstances(String listName, String keyword) {
207  List<String> instances;
208  synchronized (topLevelMap) {
209  instances = new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
210  }
211  Collections.sort(instances);
212  return instances;
213  }
214 
226  Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
227  synchronized (topLevelMap) {
228  return topLevelMap.get(listName).get(keyword).get(keywordInstance);
229  }
230  }
231 
241  void addRegExpToList(Map<String, Map<String, Set<Long>>> listMap, String regExp, String keywordInstance, Long artifactId) {
242  Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(regExp, r -> new LinkedHashMap<>());
243  // add this ID to the instances entry, creating one if needed
244  instanceMap.computeIfAbsent(keywordInstance, ki -> new HashSet<>()).add(artifactId);
245  }
246 
255  void addNonRegExpMatchToList(Map<String, Map<String, Set<Long>>> listMap, String keyWord, Long artifactId) {
256  Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(keyWord, k -> new LinkedHashMap<>());
257 
258  // Use the default instance name, since we don't need that level in the tree
259  instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN -> new HashSet<>()).add(artifactId);
260  }
261 
269  void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
270  synchronized (topLevelMap) {
271  topLevelMap.clear();
272 
273  // map of list name to keword to artifact IDs
274  Map<String, Map<String, Map<String, Set<Long>>>> listsMap = new LinkedHashMap<>();
275 
276  // Map from from literal keyword to instances (which will be empty) to artifact IDs
277  Map<String, Map<String, Set<Long>>> literalMap = new LinkedHashMap<>();
278 
279  // Map from regex keyword artifact to instances to artifact IDs
280  Map<String, Map<String, Set<Long>>> regexMap = new LinkedHashMap<>();
281 
282  // top-level nodes
283  topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
284  topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
285 
286  for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
287  long id = art.getKey();
288  Map<Long, String> attributes = art.getValue();
289 
290  // I think we can use attributes.remove(...) here? - why should bwe use remove?
291  String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
292  String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
293  String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
294  String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()));
295 
296  if (listName != null) { // part of a list
297  // get or create list entry
298  Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln -> new LinkedHashMap<>());
299 
300  if ("1".equals(kwType) || reg == null) { //literal, substring or exact
301  /*
302  * Substring, treated same as exact match. "1" is
303  * the ordinal value for substring as defined in
304  * KeywordSearch.java. The original term should be
305  * stored in reg
306  */
307  word = (reg != null) ? reg : word; //use original term if it there.
308  addNonRegExpMatchToList(listMap, word, id);
309  } else {
310  addRegExpToList(listMap, reg, word, id);
311  }
312  } else {//single term
313  if ("1".equals(kwType) || reg == null) { //literal, substring or exact
314  /*
315  * Substring, treated same as exact match. "1" is
316  * the ordinal value for substring as defined in
317  * KeywordSearch.java. The original term should be
318  * stored in reg
319  */
320  word = (reg != null) ? reg : word; //use original term if it there.
321  addNonRegExpMatchToList(literalMap, word, id);
322  } else {
323  addRegExpToList(regexMap, reg, word, id);
324  }
325  }
326  }
327  topLevelMap.putAll(listsMap);
328  }
329 
330  setChanged();
331  notifyObservers();
332  }
333 
334  public void update() {
335  // maps Artifact ID to map of attribute types to attribute values
336  Map<Long, Map<Long, String>> artifactIds = new LinkedHashMap<>();
337 
338  if (skCase == null) {
339  return;
340  }
341 
342  String queryStr = KEYWORD_HIT_ATTRIBUTES_QUERY;
343  if (filteringDSObjId > 0) {
344  queryStr += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
345  }
346 
347  try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
348  ResultSet resultSet = dbQuery.getResultSet();
349  while (resultSet.next()) {
350  long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
351  long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS
352  String valueStr = resultSet.getString("value_text"); //NON-NLS
353 
354  //get the map of attributes for this artifact
355  Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai -> new LinkedHashMap<>());
356  if (StringUtils.isNotEmpty(valueStr)) {
357  attributesByTypeMap.put(typeId, valueStr);
358  } else {
359  // Keyword Search Type is an int
360  Long valueLong = resultSet.getLong("value_int32");
361  attributesByTypeMap.put(typeId, valueLong.toString());
362  }
363  }
364  } catch (TskCoreException | SQLException ex) {
365  logger.log(Level.WARNING, "SQL Exception occurred: ", ex); //NON-NLS
366  }
367 
368  populateTreeMaps(artifactIds);
369  }
370  }
371 
372  @Override
373  public <T> T accept(AutopsyItemVisitor<T> visitor) {
374  return visitor.visit(this);
375  }
376 
377  // Created by CreateAutopsyNodeVisitor
378  public class RootNode extends DisplayableItemNode {
379 
380  public RootNode() {
381  super(Children.create(new ListFactory(), true), Lookups.singleton(KEYWORD_HITS));
382  super.setName(NAME);
383  super.setDisplayName(KEYWORD_HITS);
384  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
385  }
386 
387  @Override
388  public boolean isLeafTypeNode() {
389  return false;
390  }
391 
392  @Override
393  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
394  return visitor.visit(this);
395  }
396 
397  @Override
398  @NbBundle.Messages({"KeywordHits.createSheet.name.name=Name",
399  "KeywordHits.createSheet.name.displayName=Name",
400  "KeywordHits.createSheet.name.desc=no description"})
401  protected Sheet createSheet() {
402  Sheet sheet = super.createSheet();
403  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
404  if (sheetSet == null) {
405  sheetSet = Sheet.createPropertiesSet();
406  sheet.put(sheetSet);
407  }
408 
409  sheetSet.put(new NodeProperty<>(
410  KeywordHits_createSheet_name_name(),
411  KeywordHits_createSheet_name_displayName(),
412  KeywordHits_createSheet_name_desc(),
413  getName()));
414 
415  return sheet;
416  }
417 
418  @Override
419  public String getItemType() {
420  return getClass().getName();
421  }
422  }
423 
424  private abstract class DetachableObserverChildFactory<X> extends ChildFactory.Detachable<X> implements Observer {
425 
426  @Override
427  protected void addNotify() {
428  keywordResults.addObserver(this);
429  }
430 
431  @Override
432  protected void removeNotify() {
433  keywordResults.deleteObserver(this);
434  }
435 
436  @Override
437  public void update(Observable o, Object arg) {
438  refresh(true);
439  }
440  }
441 
445  private class ListFactory extends DetachableObserverChildFactory<String> {
446 
447  private final PropertyChangeListener pcl = new PropertyChangeListener() {
448  @Override
449  public void propertyChange(PropertyChangeEvent evt) {
450  String eventType = evt.getPropertyName();
451  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
458  try {
466  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
467  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
468  keywordResults.update();
469  }
470  } catch (NoCurrentCaseException notUsed) {
471  // Case is closed, do nothing.
472  }
473  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
474  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
481  try {
483  keywordResults.update();
484  } catch (NoCurrentCaseException notUsed) {
485  // Case is closed, do nothing.
486  }
487  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())
488  && evt.getNewValue() == null) {
489  /*
490  * Case was closed. Remove listeners so that we don't get
491  * called with a stale case handle
492  */
493  removeNotify();
494  skCase = null;
495  }
496 
497  }
498  };
499 
500  @Override
501  protected void addNotify() {
505  keywordResults.update();
506  super.addNotify();
507  }
508 
509  @Override
510  protected void removeNotify() {
514  super.removeNotify();
515  }
516 
517  @Override
518  protected boolean createKeys(List<String> list) {
519  list.addAll(keywordResults.getListNames());
520  return true;
521  }
522 
523  @Override
524  protected Node createNodeForKey(String key) {
525  return new ListNode(key);
526  }
527  }
528 
529  private abstract class KWHitsNodeBase extends DisplayableItemNode implements Observer {
530 
531  private String displayName;
532 
533  private KWHitsNodeBase(Children children, Lookup lookup, String displayName) {
534  super(children, lookup);
535  this.displayName = displayName;
536  }
537 
538  private KWHitsNodeBase(Children children) {
539  super(children);
540  }
541 
542  @Override
543  public String getItemType() {
544  return getClass().getName();
545  }
546 
547  @Override
548  public void update(Observable o, Object arg) {
549  updateDisplayName();
550  }
551 
552  final void updateDisplayName() {
553  super.setDisplayName(displayName + " (" + countTotalDescendants() + ")");
554  }
555 
556  abstract int countTotalDescendants();
557  }
558 
563  class ListNode extends KWHitsNodeBase {
564 
565  private final String listName;
566 
567  private ListNode(String listName) {
568  super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName), listName);
569  super.setName(listName);
570  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
571  this.listName = listName;
572  updateDisplayName();
573  keywordResults.addObserver(this);
574  }
575 
576  @Override
577  public int countTotalDescendants() {
578  int totalDescendants = 0;
579 
580  for (String word : keywordResults.getKeywords(listName)) {
581  for (String instance : keywordResults.getKeywordInstances(listName, word)) {
582  Set<Long> ids = keywordResults.getArtifactIds(listName, word, instance);
583  totalDescendants += ids.size();
584  }
585  }
586  return totalDescendants;
587  }
588 
589  @Override
590  @NbBundle.Messages({"KeywordHits.createSheet.listName.name=List Name",
591  "KeywordHits.createSheet.listName.displayName=List Name",
592  "KeywordHits.createSheet.listName.desc=no description",
593  "KeywordHits.createSheet.numChildren.name=Number of Children",
594  "KeywordHits.createSheet.numChildren.displayName=Number of Children",
595  "KeywordHits.createSheet.numChildren.desc=no description"})
596  protected Sheet createSheet() {
597  Sheet sheet = super.createSheet();
598  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
599  if (sheetSet == null) {
600  sheetSet = Sheet.createPropertiesSet();
601  sheet.put(sheetSet);
602  }
603 
604  sheetSet.put(new NodeProperty<>(
605  KeywordHits_createSheet_listName_name(),
606  KeywordHits_createSheet_listName_displayName(),
607  KeywordHits_createSheet_listName_desc(),
608  listName));
609 
610  sheetSet.put(new NodeProperty<>(
611  KeywordHits_createSheet_numChildren_name(),
612  KeywordHits_createSheet_numChildren_displayName(),
613  KeywordHits_createSheet_numChildren_desc(),
614  keywordResults.getKeywords(listName).size()));
615 
616  return sheet;
617  }
618 
619  @Override
620  public boolean isLeafTypeNode() {
621  return false;
622  }
623 
624  @Override
625  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
626  return visitor.visit(this);
627  }
628  }
629 
633  private class TermFactory extends DetachableObserverChildFactory<String> {
634 
635  private final String setName;
636 
637  private TermFactory(String setName) {
638  super();
639  this.setName = setName;
640  }
641 
642  @Override
643  protected boolean createKeys(List<String> list) {
644  list.addAll(keywordResults.getKeywords(setName));
645  return true;
646  }
647 
648  @Override
649  protected Node createNodeForKey(String key) {
650  return new TermNode(setName, key);
651  }
652  }
653 
664  ChildFactory<?> createChildFactory(String setName, String keyword) {
665  if (isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword))) {
666  return new HitsFactory(setName, keyword, DEFAULT_INSTANCE_NAME);
667  } else {
668  return new RegExpInstancesFactory(setName, keyword);
669  }
670  }
671 
675  class TermNode extends KWHitsNodeBase {
676 
677  private final String setName;
678  private final String keyword;
679 
680  private TermNode(String setName, String keyword) {
681  super(Children.create(createChildFactory(setName, keyword), true), Lookups.singleton(keyword), keyword);
682 
690  super.setName(setName + "_" + keyword);
691  this.setName = setName;
692  this.keyword = keyword;
693  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
694  updateDisplayName();
695  keywordResults.addObserver(this);
696  }
697 
698  @Override
699  int countTotalDescendants() {
700  return keywordResults.getKeywordInstances(setName, keyword).stream()
701  .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
702  .sum();
703  }
704 
705  @Override
706  public boolean isLeafTypeNode() {
707  // is this an exact/substring match (i.e. did we use the DEFAULT name)?
708  return isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword));
709  }
710 
711  @Override
712  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
713  return visitor.visit(this);
714  }
715 
716  @Override
717  @NbBundle.Messages({"KeywordHits.createSheet.filesWithHits.name=Files with Hits",
718  "KeywordHits.createSheet.filesWithHits.displayName=Files with Hits",
719  "KeywordHits.createSheet.filesWithHits.desc=no description"})
720  protected Sheet createSheet() {
721  Sheet sheet = super.createSheet();
722  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
723  if (sheetSet == null) {
724  sheetSet = Sheet.createPropertiesSet();
725  sheet.put(sheetSet);
726  }
727  sheetSet.put(new NodeProperty<>(
728  KeywordHits_createSheet_listName_name(),
729  KeywordHits_createSheet_listName_displayName(),
730  KeywordHits_createSheet_listName_desc(),
731  getDisplayName()));
732 
733  sheetSet.put(new NodeProperty<>(
734  KeywordHits_createSheet_filesWithHits_name(),
735  KeywordHits_createSheet_filesWithHits_displayName(),
736  KeywordHits_createSheet_filesWithHits_desc(),
737  countTotalDescendants()));
738 
739  return sheet;
740  }
741  }
742 
748 
749  private final String keyword;
750  private final String setName;
751 
752  private RegExpInstancesFactory(String setName, String keyword) {
753  super();
754  this.setName = setName;
755  this.keyword = keyword;
756  }
757 
758  @Override
759  protected boolean createKeys(List<String> list) {
760  list.addAll(keywordResults.getKeywordInstances(setName, keyword));
761  return true;
762  }
763 
764  @Override
765  protected Node createNodeForKey(String key) {
766  return new RegExpInstanceNode(setName, keyword, key);
767  }
768  }
769 
773  class RegExpInstanceNode extends KWHitsNodeBase {
774 
775  private final String setName;
776  private final String keyword;
777  private final String instance;
778 
779  private RegExpInstanceNode(String setName, String keyword, String instance) {
780  super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance), instance);
781 
789  super.setName(setName + "_" + keyword + "_" + instance);
790  this.setName = setName;
791  this.keyword = keyword;
792  this.instance = instance;
793  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
794  updateDisplayName();
795  keywordResults.addObserver(this);
796  }
797 
798  @Override
799  int countTotalDescendants() {
800  return keywordResults.getArtifactIds(setName, keyword, instance).size();
801  }
802 
803  @Override
804  public boolean isLeafTypeNode() {
805  return true;
806  }
807 
808  @Override
809  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
810  return visitor.visit(this);
811  }
812 
813  @Override
814  protected Sheet createSheet() {
815  Sheet sheet = super.createSheet();
816  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
817  if (sheetSet == null) {
818  sheetSet = Sheet.createPropertiesSet();
819  sheet.put(sheetSet);
820  }
821 
822  sheetSet.put(new NodeProperty<>(
823  KeywordHits_createSheet_listName_name(),
824  KeywordHits_createSheet_listName_displayName(),
825  KeywordHits_createSheet_listName_desc(),
826  getDisplayName()));
827 
828  sheetSet.put(new NodeProperty<>(
829  KeywordHits_createSheet_filesWithHits_name(),
830  KeywordHits_createSheet_filesWithHits_displayName(),
831  KeywordHits_createSheet_filesWithHits_desc(),
832  keywordResults.getArtifactIds(setName, keyword, instance).size()));
833 
834  return sheet;
835  }
836 
837  }
838 
846  @NbBundle.Messages({"KeywordHits.createNodeForKey.modTime.name=ModifiedTime",
847  "KeywordHits.createNodeForKey.modTime.displayName=Modified Time",
848  "KeywordHits.createNodeForKey.modTime.desc=Modified Time",
849  "KeywordHits.createNodeForKey.accessTime.name=AccessTime",
850  "KeywordHits.createNodeForKey.accessTime.displayName=Access Time",
851  "KeywordHits.createNodeForKey.accessTime.desc=Access Time",
852  "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
853  "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
854  "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
855  private BlackboardArtifactNode createBlackboardArtifactNode(BlackboardArtifact art) {
856  if (skCase == null) {
857  return null;
858  }
859 
860  BlackboardArtifactNode n = new BlackboardArtifactNode(art); //NON-NLS
861 
862  // The associated file should be available through the Lookup that
863  // gets created when the BlackboardArtifactNode is constructed.
864  AbstractFile file = n.getLookup().lookup(AbstractFile.class);
865  if (file == null) {
866  try {
867  file = skCase.getAbstractFileById(art.getObjectID());
868  } catch (TskCoreException ex) {
869  logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex); //NON-NLS
870  return n;
871  }
872  }
873  /*
874  * It is possible to get a keyword hit on artifacts generated for the
875  * underlying image in which case MAC times are not
876  * available/applicable/useful.
877  */
878  if (file == null) {
879  return n;
880  }
882  KeywordHits_createNodeForKey_modTime_name(),
883  KeywordHits_createNodeForKey_modTime_displayName(),
884  KeywordHits_createNodeForKey_modTime_desc(),
885  ContentUtils.getStringTime(file.getMtime(), file)));
887  KeywordHits_createNodeForKey_accessTime_name(),
888  KeywordHits_createNodeForKey_accessTime_displayName(),
889  KeywordHits_createNodeForKey_accessTime_desc(),
890  ContentUtils.getStringTime(file.getAtime(), file)));
892  KeywordHits_createNodeForKey_chgTime_name(),
893  KeywordHits_createNodeForKey_chgTime_displayName(),
894  KeywordHits_createNodeForKey_chgTime_desc(),
895  ContentUtils.getStringTime(file.getCtime(), file)));
896  return n;
897  }
898 
902  private class HitsFactory extends BaseChildFactory<BlackboardArtifact> implements Observer {
903 
904  private final String keyword;
905  private final String setName;
906  private final String instance;
907  private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
908 
909  private HitsFactory(String setName, String keyword, String instance) {
916  super(setName + "_" + keyword + (DEFAULT_INSTANCE_NAME.equals(instance) ? "" : "_" + instance));
917  this.setName = setName;
918  this.keyword = keyword;
919  this.instance = instance;
920  }
921 
922  @Override
923  protected List<BlackboardArtifact> makeKeys() {
924  if (skCase != null) {
925  keywordResults.getArtifactIds(setName, keyword, instance).forEach((id) -> {
926  try {
927  if (!artifactHits.containsKey(id)) {
928  BlackboardArtifact art = skCase.getBlackboardArtifact(id);
929  //Cache attributes while we are off the EDT.
930  //See JIRA-5969
931  art.getAttributes();
932  artifactHits.put(id, art);
933  }
934  } catch (TskCoreException ex) {
935  logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
936  }
937  });
938 
939  return new ArrayList<>(artifactHits.values());
940  }
941  return Collections.emptyList();
942  }
943 
944  @Override
945  protected Node createNodeForKey(BlackboardArtifact art) {
946  return createBlackboardArtifactNode(art);
947  }
948 
949  @Override
950  protected void onAdd() {
951  keywordResults.addObserver(this);
952  }
953 
954  @Override
955  protected void onRemove() {
956  keywordResults.deleteObserver(this);
957  }
958 
959  @Override
960  public void update(Observable o, Object arg) {
961  refresh(true);
962  }
963  }
964 }
static boolean isOnlyDefaultInstance(List< String > instances)
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
BlackboardArtifactNode createBlackboardArtifactNode(BlackboardArtifact art)
static String getStringTime(long epochSeconds, TimeZone tzone)
static synchronized IngestManager getInstance()
final Map< Long, BlackboardArtifact > artifactHits
final Map< String, Map< String, Map< String, Set< Long > > > > topLevelMap
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
void addIngestModuleEventListener(final PropertyChangeListener listener)
KeywordHits(SleuthkitCase skCase, long objId)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:491
HitsFactory(String setName, String keyword, String instance)
KWHitsNodeBase(Children children, Lookup lookup, String displayName)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:536

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