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

Copyright © 2012-2018 Basis Technology. Generated on: Fri Jun 21 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.