Autopsy  4.21.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-2021 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.WeakListeners;
46 import org.openide.util.lookup.Lookups;
51 import static org.sleuthkit.autopsy.datamodel.Bundle.*;
54 import org.sleuthkit.datamodel.AbstractFile;
55 import org.sleuthkit.datamodel.BlackboardArtifact;
56 import org.sleuthkit.datamodel.BlackboardAttribute;
57 import org.sleuthkit.datamodel.SleuthkitCase;
58 import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
59 import org.sleuthkit.datamodel.TskCoreException;
60 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
62 import org.sleuthkit.datamodel.AnalysisResult;
63 
67 public class KeywordHits implements AutopsyVisitableItem {
68 
69  private static final Logger logger = Logger.getLogger(KeywordHits.class.getName());
72  @NbBundle.Messages("KeywordHits.kwHits.text=Keyword Hits")
73  private static final String KEYWORD_HITS = KeywordHits_kwHits_text();
74  @NbBundle.Messages("KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
75  private static final String SIMPLE_LITERAL_SEARCH = KeywordHits_simpleLiteralSearch_text();
76  @NbBundle.Messages("KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
77  private static final String SIMPLE_REGEX_SEARCH = KeywordHits_singleRegexSearch_text();
78 
79  public static final String NAME = BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeName();
80 
81  private SleuthkitCase skCase;
83  private final long filteringDSObjId; // 0 if not filtering/grouping by data source
84 
90  private static final String DEFAULT_INSTANCE_NAME = "DEFAULT_INSTANCE_NAME";
91 
95  private static final String KEYWORD_HIT_ATTRIBUTES_QUERY = "SELECT blackboard_attributes.value_text, "//NON-NLS
96  + "blackboard_attributes.value_int32, "//NON-NLS
97  + "blackboard_artifacts.artifact_obj_id, " //NON-NLS
98  + "blackboard_attributes.attribute_type_id "//NON-NLS
99  + "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS
100  + "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS
101  + " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
102  + " AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()//NON-NLS
103  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()//NON-NLS
104  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()//NON-NLS
105  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()//NON-NLS
106  + ")"; //NON-NLS
107 
108  static private boolean isOnlyDefaultInstance(List<String> instances) {
109  return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
110  }
111 
117  KeywordHits(SleuthkitCase skCase) {
118  this(skCase, 0);
119  }
120 
128  public KeywordHits(SleuthkitCase skCase, long objId) {
129  this.skCase = skCase;
130  this.filteringDSObjId = objId;
131  keywordResults = new KeywordResults();
132  }
133 
134  /*
135  * All of these maps and code assume the following: Regexps will have an
136  * 'instance' layer that shows the specific words that matched the regexp
137  * Exact match and substring will not have the instance layer and instead
138  * will have the specific hits below their term.
139  */
140  private final class KeywordResults extends Observable {
141 
142  // Map from listName/Type to Map of keywords/regexp to Map of instance terms to Set of artifact Ids
143  // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
144  private final Map<String, Map<String, Map<String, Set<Long>>>> topLevelMap = new LinkedHashMap<>();
145 
146  KeywordResults() {
147  update();
148  }
149 
155  List<String> getListNames() {
156  synchronized (topLevelMap) {
157  List<String> names = new ArrayList<>(topLevelMap.keySet());
158 
159  // sort the list names, but ensure that the special lists
160  // stay at the top.
161  Collections.sort(names, new Comparator<String>() {
162 
163  @Override
164  public int compare(String o1, String o2) {
165  // ideally, they would not be hard coded, but this module
166  // doesn't know about Keyword Search NBM
167  if (o1.startsWith("Single Literal Keyword Search")) {
168  return -1;
169  } else if (o2.startsWith("Single Literal Keyword Search")) {
170  return 1;
171  } else if (o1.startsWith("Single Regular Expression Search")) {
172  return -1;
173  } else if (o2.startsWith("Single Regular Expression Search")) {
174  return 1;
175  }
176  return o1.compareTo(o2);
177  }
178  });
179 
180  return names;
181  }
182  }
183 
192  List<String> getKeywords(String listName) {
193  List<String> keywords;
194  synchronized (topLevelMap) {
195  keywords = new ArrayList<>(topLevelMap.get(listName).keySet());
196  }
197  Collections.sort(keywords);
198  return keywords;
199  }
200 
211  List<String> getKeywordInstances(String listName, String keyword) {
212  List<String> instances;
213  synchronized (topLevelMap) {
214  instances = new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
215  }
216  Collections.sort(instances);
217  return instances;
218  }
219 
231  Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
232  synchronized (topLevelMap) {
233  return topLevelMap.get(listName).get(keyword).get(keywordInstance);
234  }
235  }
236 
246  void addRegExpToList(Map<String, Map<String, Set<Long>>> listMap, String regExp, String keywordInstance, Long artifactId) {
247  Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(regExp, r -> new LinkedHashMap<>());
248  // add this ID to the instances entry, creating one if needed
249  instanceMap.computeIfAbsent(keywordInstance, ki -> new HashSet<>()).add(artifactId);
250  }
251 
260  void addNonRegExpMatchToList(Map<String, Map<String, Set<Long>>> listMap, String keyWord, Long artifactId) {
261  Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(keyWord, k -> new LinkedHashMap<>());
262 
263  // Use the default instance name, since we don't need that level in the tree
264  instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN -> new HashSet<>()).add(artifactId);
265  }
266 
274  void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
275  synchronized (topLevelMap) {
276  topLevelMap.clear();
277 
278  // map of list name to keword to artifact IDs
279  Map<String, Map<String, Map<String, Set<Long>>>> listsMap = new LinkedHashMap<>();
280 
281  // Map from from literal keyword to instances (which will be empty) to artifact IDs
282  Map<String, Map<String, Set<Long>>> literalMap = new LinkedHashMap<>();
283 
284  // Map from regex keyword artifact to instances to artifact IDs
285  Map<String, Map<String, Set<Long>>> regexMap = new LinkedHashMap<>();
286 
287  // top-level nodes
288  topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
289  topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
290 
291  for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
292  long id = art.getKey();
293  Map<Long, String> attributes = art.getValue();
294 
295  // I think we can use attributes.remove(...) here? - why should bwe use remove?
296  String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
297  String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
298  String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
299  String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()));
300 
301  if (listName != null) { // part of a list
302  // get or create list entry
303  Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln -> new LinkedHashMap<>());
304 
305  if ("1".equals(kwType) || reg == null) { //literal, substring or exact
306  /*
307  * Substring, treated same as exact match. "1" is
308  * the ordinal value for substring as defined in
309  * KeywordSearch.java. The original term should be
310  * stored in reg
311  */
312  word = (reg != null) ? reg : word; //use original term if it there.
313  addNonRegExpMatchToList(listMap, word, id);
314  } else {
315  addRegExpToList(listMap, reg, word, id);
316  }
317  } else {//single term
318  if ("1".equals(kwType) || reg == null) { //literal, substring or exact
319  /*
320  * Substring, treated same as exact match. "1" is
321  * the ordinal value for substring as defined in
322  * KeywordSearch.java. The original term should be
323  * stored in reg
324  */
325  word = (reg != null) ? reg : word; //use original term if it there.
326  addNonRegExpMatchToList(literalMap, word, id);
327  } else {
328  addRegExpToList(regexMap, reg, word, id);
329  }
330  }
331  }
332  topLevelMap.putAll(listsMap);
333  }
334 
335  setChanged();
336  notifyObservers();
337  }
338 
339  public void update() {
340  // maps Artifact ID to map of attribute types to attribute values
341  Map<Long, Map<Long, String>> artifactIds = new LinkedHashMap<>();
342 
343  if (skCase == null) {
344  return;
345  }
346 
347  String queryStr = KEYWORD_HIT_ATTRIBUTES_QUERY;
348  if (filteringDSObjId > 0) {
349  queryStr += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
350  }
351 
352  try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
353  ResultSet resultSet = dbQuery.getResultSet();
354  while (resultSet.next()) {
355  long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS
356  long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS
357  String valueStr = resultSet.getString("value_text"); //NON-NLS
358 
359  //get the map of attributes for this artifact
360  Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactObjId, ai -> new LinkedHashMap<>());
361  if (StringUtils.isNotEmpty(valueStr)) {
362  attributesByTypeMap.put(typeId, valueStr);
363  } else {
364  // Keyword Search Type is an int
365  Long valueLong = resultSet.getLong("value_int32");
366  attributesByTypeMap.put(typeId, valueLong.toString());
367  }
368  }
369  } catch (TskCoreException | SQLException ex) {
370  logger.log(Level.WARNING, "SQL Exception occurred: ", ex); //NON-NLS
371  }
372 
373  populateTreeMaps(artifactIds);
374  }
375  }
376 
377  @Override
378  public <T> T accept(AutopsyItemVisitor<T> visitor) {
379  return visitor.visit(this);
380  }
381 
382  // Created by CreateAutopsyNodeVisitor
383  public class RootNode extends UpdatableCountTypeNode {
384 
385  public RootNode() {
386  super(Children.create(new ListFactory(), true),
387  Lookups.singleton(KEYWORD_HITS),
388  KEYWORD_HITS,
390  TSK_KEYWORD_HIT);
391 
392  super.setName(NAME);
393  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
394  }
395 
396  @Override
397  public boolean isLeafTypeNode() {
398  return false;
399  }
400 
401  @Override
402  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
403  return visitor.visit(this);
404  }
405 
406  @Override
407  @NbBundle.Messages({"KeywordHits.createSheet.name.name=Name",
408  "KeywordHits.createSheet.name.displayName=Name",
409  "KeywordHits.createSheet.name.desc=no description"})
410  protected Sheet createSheet() {
411  Sheet sheet = super.createSheet();
412  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
413  if (sheetSet == null) {
414  sheetSet = Sheet.createPropertiesSet();
415  sheet.put(sheetSet);
416  }
417 
418  sheetSet.put(new NodeProperty<>(
419  KeywordHits_createSheet_name_name(),
420  KeywordHits_createSheet_name_displayName(),
421  KeywordHits_createSheet_name_desc(),
422  getName()));
423 
424  return sheet;
425  }
426 
427  @Override
428  public String getItemType() {
429  return getClass().getName();
430  }
431  }
432 
433  private abstract class DetachableObserverChildFactory<X> extends ChildFactory.Detachable<X> implements Observer {
434 
435  @Override
436  protected void addNotify() {
437  keywordResults.addObserver(this);
438  }
439 
440  @Override
441  protected void finalize() throws Throwable {
442  super.finalize();
443  keywordResults.deleteObserver(this);
444  }
445 
446  @Override
447  public void update(Observable o, Object arg) {
448  refresh(true);
449  }
450  }
451 
455  private class ListFactory extends DetachableObserverChildFactory<String> {
456 
457  private final PropertyChangeListener pcl = new PropertyChangeListener() {
458  @Override
459  public void propertyChange(PropertyChangeEvent evt) {
460  String eventType = evt.getPropertyName();
461  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
468  try {
476  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
477  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
478  keywordResults.update();
479  }
480  } catch (NoCurrentCaseException notUsed) {
481  // Case is closed, do nothing.
482  }
483  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
484  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
491  try {
493  keywordResults.update();
494  } catch (NoCurrentCaseException notUsed) {
495  // Case is closed, do nothing.
496  }
497  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())
498  && evt.getNewValue() == null) {
499  /*
500  * Case was closed. Remove listeners so that we don't get
501  * called with a stale case handle
502  */
503  removeNotify();
504  skCase = null;
505  }
506 
507  }
508  };
509 
510  private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
511 
512  @Override
513  protected void addNotify() {
517  keywordResults.update();
518  super.addNotify();
519  }
520 
521  @Override
522  protected void finalize() throws Throwable{
526  super.finalize();
527  }
528 
529  @Override
530  protected boolean createKeys(List<String> list) {
531  list.addAll(keywordResults.getListNames());
532  return true;
533  }
534 
535  @Override
536  protected Node createNodeForKey(String key) {
537  return new ListNode(key);
538  }
539  }
540 
541  private abstract class KWHitsNodeBase extends DisplayableItemNode implements Observer {
542 
543  private String displayName;
544 
545  private KWHitsNodeBase(Children children, Lookup lookup, String displayName) {
546  super(children, lookup);
547  this.displayName = displayName;
548  }
549 
550  private KWHitsNodeBase(Children children) {
551  super(children);
552  }
553 
554  @Override
555  public String getItemType() {
556  return getClass().getName();
557  }
558 
559  @Override
560  public void update(Observable o, Object arg) {
561  updateDisplayName();
562  }
563 
564  final void updateDisplayName() {
565  super.setDisplayName(displayName + " (" + countTotalDescendants() + ")");
566  }
567 
568  abstract int countTotalDescendants();
569  }
570 
575  class ListNode extends KWHitsNodeBase {
576 
577  private final String listName;
578 
579  private ListNode(String listName) {
580  super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName), listName);
581  super.setName(listName);
582  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
583  this.listName = listName;
584  updateDisplayName();
585  keywordResults.addObserver(this);
586  }
587 
588  @Override
589  public int countTotalDescendants() {
590  int totalDescendants = 0;
591 
592  for (String word : keywordResults.getKeywords(listName)) {
593  for (String instance : keywordResults.getKeywordInstances(listName, word)) {
594  Set<Long> ids = keywordResults.getArtifactIds(listName, word, instance);
595  totalDescendants += ids.size();
596  }
597  }
598  return totalDescendants;
599  }
600 
601  @Override
602  @NbBundle.Messages({"KeywordHits.createSheet.listName.name=List Name",
603  "KeywordHits.createSheet.listName.displayName=List Name",
604  "KeywordHits.createSheet.listName.desc=no description",
605  "KeywordHits.createSheet.numChildren.name=Number of Children",
606  "KeywordHits.createSheet.numChildren.displayName=Number of Children",
607  "KeywordHits.createSheet.numChildren.desc=no description"})
608  protected Sheet createSheet() {
609  Sheet sheet = super.createSheet();
610  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
611  if (sheetSet == null) {
612  sheetSet = Sheet.createPropertiesSet();
613  sheet.put(sheetSet);
614  }
615 
616  sheetSet.put(new NodeProperty<>(
617  KeywordHits_createSheet_listName_name(),
618  KeywordHits_createSheet_listName_displayName(),
619  KeywordHits_createSheet_listName_desc(),
620  listName));
621 
622  sheetSet.put(new NodeProperty<>(
623  KeywordHits_createSheet_numChildren_name(),
624  KeywordHits_createSheet_numChildren_displayName(),
625  KeywordHits_createSheet_numChildren_desc(),
626  keywordResults.getKeywords(listName).size()));
627 
628  return sheet;
629  }
630 
631  @Override
632  public boolean isLeafTypeNode() {
633  return false;
634  }
635 
636  @Override
637  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
638  return visitor.visit(this);
639  }
640  }
641 
645  private class TermFactory extends DetachableObserverChildFactory<String> {
646 
647  private final String setName;
648 
649  private TermFactory(String setName) {
650  super();
651  this.setName = setName;
652  }
653 
654  @Override
655  protected boolean createKeys(List<String> list) {
656  list.addAll(keywordResults.getKeywords(setName));
657  return true;
658  }
659 
660  @Override
661  protected Node createNodeForKey(String key) {
662  return new TermNode(setName, key);
663  }
664  }
665 
676  ChildFactory<?> createChildFactory(String setName, String keyword) {
677  if (isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword))) {
678  return new HitsFactory(setName, keyword, DEFAULT_INSTANCE_NAME);
679  } else {
680  return new RegExpInstancesFactory(setName, keyword);
681  }
682  }
683 
687  class TermNode extends KWHitsNodeBase {
688 
689  private final String setName;
690  private final String keyword;
691 
692  private TermNode(String setName, String keyword) {
693  super(Children.create(createChildFactory(setName, keyword), true), Lookups.singleton(keyword), keyword);
694 
702  super.setName(setName + "_" + keyword);
703  this.setName = setName;
704  this.keyword = keyword;
705  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
706  updateDisplayName();
707  keywordResults.addObserver(this);
708  }
709 
710  @Override
711  int countTotalDescendants() {
712  return keywordResults.getKeywordInstances(setName, keyword).stream()
713  .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
714  .sum();
715  }
716 
717  @Override
718  public boolean isLeafTypeNode() {
719  // is this an exact/substring match (i.e. did we use the DEFAULT name)?
720  return isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword));
721  }
722 
723  @Override
724  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
725  return visitor.visit(this);
726  }
727 
728  @Override
729  @NbBundle.Messages({"KeywordHits.createSheet.filesWithHits.name=Files with Hits",
730  "KeywordHits.createSheet.filesWithHits.displayName=Files with Hits",
731  "KeywordHits.createSheet.filesWithHits.desc=no description"})
732  protected Sheet createSheet() {
733  Sheet sheet = super.createSheet();
734  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
735  if (sheetSet == null) {
736  sheetSet = Sheet.createPropertiesSet();
737  sheet.put(sheetSet);
738  }
739  sheetSet.put(new NodeProperty<>(
740  KeywordHits_createSheet_listName_name(),
741  KeywordHits_createSheet_listName_displayName(),
742  KeywordHits_createSheet_listName_desc(),
743  getDisplayName()));
744 
745  sheetSet.put(new NodeProperty<>(
746  KeywordHits_createSheet_filesWithHits_name(),
747  KeywordHits_createSheet_filesWithHits_displayName(),
748  KeywordHits_createSheet_filesWithHits_desc(),
749  countTotalDescendants()));
750 
751  return sheet;
752  }
753  }
754 
760 
761  private final String keyword;
762  private final String setName;
763 
764  private RegExpInstancesFactory(String setName, String keyword) {
765  super();
766  this.setName = setName;
767  this.keyword = keyword;
768  }
769 
770  @Override
771  protected boolean createKeys(List<String> list) {
772  list.addAll(keywordResults.getKeywordInstances(setName, keyword));
773  return true;
774  }
775 
776  @Override
777  protected Node createNodeForKey(String key) {
778  return new RegExpInstanceNode(setName, keyword, key);
779  }
780  }
781 
785  class RegExpInstanceNode extends KWHitsNodeBase {
786 
787  private final String setName;
788  private final String keyword;
789  private final String instance;
790 
791  private RegExpInstanceNode(String setName, String keyword, String instance) {
792  super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance), instance);
793 
801  super.setName(setName + "_" + keyword + "_" + instance);
802  this.setName = setName;
803  this.keyword = keyword;
804  this.instance = instance;
805  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
806  updateDisplayName();
807  keywordResults.addObserver(this);
808  }
809 
810  @Override
811  int countTotalDescendants() {
812  return keywordResults.getArtifactIds(setName, keyword, instance).size();
813  }
814 
815  @Override
816  public boolean isLeafTypeNode() {
817  return true;
818  }
819 
820  @Override
821  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
822  return visitor.visit(this);
823  }
824 
825  @Override
826  protected Sheet createSheet() {
827  Sheet sheet = super.createSheet();
828  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
829  if (sheetSet == null) {
830  sheetSet = Sheet.createPropertiesSet();
831  sheet.put(sheetSet);
832  }
833 
834  sheetSet.put(new NodeProperty<>(
835  KeywordHits_createSheet_listName_name(),
836  KeywordHits_createSheet_listName_displayName(),
837  KeywordHits_createSheet_listName_desc(),
838  getDisplayName()));
839 
840  sheetSet.put(new NodeProperty<>(
841  KeywordHits_createSheet_filesWithHits_name(),
842  KeywordHits_createSheet_filesWithHits_displayName(),
843  KeywordHits_createSheet_filesWithHits_desc(),
844  keywordResults.getArtifactIds(setName, keyword, instance).size()));
845 
846  return sheet;
847  }
848 
849  }
850 
858  @NbBundle.Messages({"KeywordHits.createNodeForKey.modTime.name=ModifiedTime",
859  "KeywordHits.createNodeForKey.modTime.displayName=Modified Time",
860  "KeywordHits.createNodeForKey.modTime.desc=Modified Time",
861  "KeywordHits.createNodeForKey.accessTime.name=AccessTime",
862  "KeywordHits.createNodeForKey.accessTime.displayName=Access Time",
863  "KeywordHits.createNodeForKey.accessTime.desc=Access Time",
864  "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
865  "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
866  "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
868  if (skCase == null) {
869  return null;
870  }
871 
872  BlackboardArtifactNode n = new BlackboardArtifactNode(art); //NON-NLS
873 
874  // The associated file should be available through the Lookup that
875  // gets created when the BlackboardArtifactNode is constructed.
876  AbstractFile file = n.getLookup().lookup(AbstractFile.class);
877  if (file == null) {
878  try {
879  file = skCase.getAbstractFileById(art.getObjectID());
880  } catch (TskCoreException ex) {
881  logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex); //NON-NLS
882  return n;
883  }
884  }
885  /*
886  * It is possible to get a keyword hit on artifacts generated for the
887  * underlying image in which case MAC times are not
888  * available/applicable/useful.
889  */
890  if (file == null) {
891  return n;
892  }
894  KeywordHits_createNodeForKey_modTime_name(),
895  KeywordHits_createNodeForKey_modTime_displayName(),
896  KeywordHits_createNodeForKey_modTime_desc(),
897  TimeZoneUtils.getFormattedTime(file.getMtime())));
899  KeywordHits_createNodeForKey_accessTime_name(),
900  KeywordHits_createNodeForKey_accessTime_displayName(),
901  KeywordHits_createNodeForKey_accessTime_desc(),
902  TimeZoneUtils.getFormattedTime(file.getAtime())));
904  KeywordHits_createNodeForKey_chgTime_name(),
905  KeywordHits_createNodeForKey_chgTime_displayName(),
906  KeywordHits_createNodeForKey_chgTime_desc(),
907  TimeZoneUtils.getFormattedTime(file.getCtime())));
908  return n;
909  }
910 
914  private class HitsFactory extends BaseChildFactory<AnalysisResult> implements Observer {
915 
916  private final String keyword;
917  private final String setName;
918  private final String instance;
919  private final Map<Long, AnalysisResult> artifactHits = new HashMap<>();
920 
921  private HitsFactory(String setName, String keyword, String instance) {
928  super(setName + "_" + keyword + (DEFAULT_INSTANCE_NAME.equals(instance) ? "" : "_" + instance));
929  this.setName = setName;
930  this.keyword = keyword;
931  this.instance = instance;
932  }
933 
934  @Override
935  protected List<AnalysisResult> makeKeys() {
936  if (skCase != null) {
937  keywordResults.getArtifactIds(setName, keyword, instance).forEach((id) -> {
938  try {
939  if (!artifactHits.containsKey(id)) {
940  AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id);
941  //Cache attributes while we are off the EDT.
942  //See JIRA-5969
943  art.getAttributes();
944  artifactHits.put(id, art);
945  }
946  } catch (TskCoreException ex) {
947  logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
948  }
949  });
950 
951  return new ArrayList<>(artifactHits.values());
952  }
953  return Collections.emptyList();
954  }
955 
956  @Override
957  protected Node createNodeForKey(AnalysisResult art) {
958  return createBlackboardArtifactNode(art);
959  }
960 
961  @Override
962  protected void onAdd() {
963  keywordResults.addObserver(this);
964  }
965 
966  @Override
967  protected void onRemove() {
968  keywordResults.deleteObserver(this);
969  }
970 
971  @Override
972  public void update(Observable o, Object arg) {
973  refresh(true);
974  }
975  }
976 }
static boolean isOnlyDefaultInstance(List< String > instances)
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized IngestManager getInstance()
static String getFormattedTime(long epochTime)
final Map< String, Map< String, Map< String, Set< Long > > > > topLevelMap
BlackboardArtifactNode createBlackboardArtifactNode(AnalysisResult art)
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:708
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:753

Copyright © 2012-2022 Basis Technology. Generated on: Tue Feb 6 2024
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.