Autopsy  4.8.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.Objects;
33 import java.util.Observable;
34 import java.util.Observer;
35 import java.util.Set;
36 import java.util.logging.Level;
37 import java.util.stream.Collectors;
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;
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 
64 public class KeywordHits implements AutopsyVisitableItem {
65 
66  private static final Logger logger = Logger.getLogger(KeywordHits.class.getName());
67 
68  @NbBundle.Messages("KeywordHits.kwHits.text=Keyword Hits")
69  private static final String KEYWORD_HITS = KeywordHits_kwHits_text();
70  @NbBundle.Messages("KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
71  private static final String SIMPLE_LITERAL_SEARCH = KeywordHits_simpleLiteralSearch_text();
72  @NbBundle.Messages("KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
73  private static final String SIMPLE_REGEX_SEARCH = KeywordHits_singleRegexSearch_text();
74 
75  public static final String NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
76 
77  private SleuthkitCase skCase;
79  private final long datasourceObjId;
80 
86  private static final String DEFAULT_INSTANCE_NAME = "DEFAULT_INSTANCE_NAME";
87 
88 
92  private static final String KEYWORD_HIT_ATTRIBUTES_QUERY = "SELECT blackboard_attributes.value_text, "//NON-NLS
93  + "blackboard_attributes.value_int32, "//NON-NLS
94  + "blackboard_attributes.artifact_id, " //NON-NLS
95  + "blackboard_attributes.attribute_type_id "//NON-NLS
96  + "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS
97  + "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS
98  + " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
99  + " AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()//NON-NLS
100  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()//NON-NLS
101  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()//NON-NLS
102  + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()//NON-NLS
103  + ")"; //NON-NLS
104 
105  static private boolean isOnlyDefaultInstance(List<String> instances) {
106  return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
107  }
108 
114  KeywordHits(SleuthkitCase skCase) {
115  this(skCase, 0);
116  }
117 
125  public KeywordHits(SleuthkitCase skCase, long objId) {
126  this.skCase = skCase;
127  this.datasourceObjId = objId;
128  keywordResults = new KeywordResults();
129  }
130 
131  /*
132  * All of these maps and code assume the following: Regexps will have an
133  * 'instance' layer that shows the specific words that matched the regexp
134  * Exact match and substring will not have the instance layer and instead
135  * will have the specific hits below their term.
136  */
137  private final class KeywordResults extends Observable {
138 
139  // Map from listName/Type to Map of keywords/regexp to Map of instance terms to Set of artifact Ids
140  // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
141  private final Map<String, Map<String, Map<String, Set<Long>>>> topLevelMap = new LinkedHashMap<>();
142 
143  KeywordResults() {
144  update();
145  }
146 
152  List<String> getListNames() {
153  synchronized (topLevelMap) {
154  List<String> names = new ArrayList<>(topLevelMap.keySet());
155  // this causes the "Single ..." terms to be in the middle of the results,
156  // which is wierd. Make a custom comparator or do something else to maek them on top
157  //Collections.sort(names);
158  return names;
159  }
160  }
161 
170  List<String> getKeywords(String listName) {
171  List<String> keywords;
172  synchronized (topLevelMap) {
173  keywords = new ArrayList<>(topLevelMap.get(listName).keySet());
174  }
175  Collections.sort(keywords);
176  return keywords;
177  }
178 
189  List<String> getKeywordInstances(String listName, String keyword) {
190  List<String> instances;
191  synchronized (topLevelMap) {
192  instances = new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
193  }
194  Collections.sort(instances);
195  return instances;
196  }
197 
209  Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
210  synchronized (topLevelMap) {
211  return topLevelMap.get(listName).get(keyword).get(keywordInstance);
212  }
213  }
214 
224  void addRegExpToList(Map<String, Map<String, Set<Long>>> listMap, String regExp, String keywordInstance, Long artifactId) {
225  Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(regExp, r -> new LinkedHashMap<>());
226  // add this ID to the instances entry, creating one if needed
227  instanceMap.computeIfAbsent(keywordInstance, ki -> new HashSet<>()).add(artifactId);
228  }
229 
238  void addNonRegExpMatchToList(Map<String, Map<String, Set<Long>>> listMap, String keyWord, Long artifactId) {
239  Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(keyWord, k -> new LinkedHashMap<>());
240 
241  // Use the default instance name, since we don't need that level in the tree
242  instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN -> new HashSet<>()).add(artifactId);
243  }
244 
252  void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
253  synchronized (topLevelMap) {
254  topLevelMap.clear();
255 
256  // map of list name to keword to artifact IDs
257  Map<String, Map<String, Map<String, Set<Long>>>> listsMap = new LinkedHashMap<>();
258 
259  // Map from from literal keyword to instances (which will be empty) to artifact IDs
260  Map<String, Map<String, Set<Long>>> literalMap = new LinkedHashMap<>();
261 
262  // Map from regex keyword artifact to instances to artifact IDs
263  Map<String, Map<String, Set<Long>>> regexMap = new LinkedHashMap<>();
264 
265  // top-level nodes
266  topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
267  topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
268 
269  for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
270  long id = art.getKey();
271  Map<Long, String> attributes = art.getValue();
272 
273  // I think we can use attributes.remove(...) here? - why should bwe use remove?
274  String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
275  String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
276  String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
277  String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()));
278 
279  if (listName != null) { // part of a list
280  // get or create list entry
281  Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln -> new LinkedHashMap<>());
282 
283  if ("1".equals(kwType) || reg == null) { //literal, substring or exact
284  /*
285  * Substring, treated same as exact match. "1" is
286  * the ordinal value for substring as defined in
287  * KeywordSearch.java. The original term should be
288  * stored in reg
289  */
290  word = (reg != null) ? reg : word; //use original term if it there.
291  addNonRegExpMatchToList(listMap, word, id);
292  } else {
293  addRegExpToList(listMap, reg, word, id);
294  }
295  } else {//single term
296  if ("1".equals(kwType) || reg == null) { //literal, substring or exact
297  /*
298  * Substring, treated same as exact match. "1" is
299  * the ordinal value for substring as defined in
300  * KeywordSearch.java. The original term should be
301  * stored in reg
302  */
303  word = (reg != null) ? reg : word; //use original term if it there.
304  addNonRegExpMatchToList(literalMap, word, id);
305  } else {
306  addRegExpToList(regexMap, reg, word, id);
307  }
308  }
309  }
310  topLevelMap.putAll(listsMap);
311  }
312 
313  setChanged();
314  notifyObservers();
315  }
316 
317  public void update() {
318  // maps Artifact ID to map of attribute types to attribute values
319  Map<Long, Map<Long, String>> artifactIds = new LinkedHashMap<>();
320 
321  if (skCase == null) {
322  return;
323  }
324 
325  String queryStr = KEYWORD_HIT_ATTRIBUTES_QUERY;
326  if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
327  queryStr += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
328  }
329 
330  try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
331  ResultSet resultSet = dbQuery.getResultSet();
332  while (resultSet.next()) {
333  long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
334  long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS
335  String valueStr = resultSet.getString("value_text"); //NON-NLS
336 
337  //get the map of attributes for this artifact
338  Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai -> new LinkedHashMap<>());
339  if (StringUtils.isNotEmpty(valueStr)) {
340  attributesByTypeMap.put(typeId, valueStr);
341  } else {
342  // Keyword Search Type is an int
343  Long valueLong = resultSet.getLong("value_int32");
344  attributesByTypeMap.put(typeId, valueLong.toString());
345  }
346  }
347  } catch (TskCoreException | SQLException ex) {
348  logger.log(Level.WARNING, "SQL Exception occurred: ", ex); //NON-NLS
349  }
350 
351  populateTreeMaps(artifactIds);
352  }
353  }
354 
355  @Override
356  public <T> T accept(AutopsyItemVisitor<T> visitor) {
357  return visitor.visit(this);
358  }
359 
360  // Created by CreateAutopsyNodeVisitor
361  public class RootNode extends DisplayableItemNode {
362 
363  public RootNode() {
364  super(Children.create(new ListFactory(), true), Lookups.singleton(KEYWORD_HITS));
365  super.setName(NAME);
366  super.setDisplayName(KEYWORD_HITS);
367  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
368  }
369 
370  @Override
371  public boolean isLeafTypeNode() {
372  return false;
373  }
374 
375  @Override
376  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
377  return visitor.visit(this);
378  }
379 
380  @Override
381  @NbBundle.Messages({"KeywordHits.createSheet.name.name=Name",
382  "KeywordHits.createSheet.name.displayName=Name",
383  "KeywordHits.createSheet.name.desc=no description"})
384  protected Sheet createSheet() {
385  Sheet sheet = super.createSheet();
386  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
387  if (sheetSet == null) {
388  sheetSet = Sheet.createPropertiesSet();
389  sheet.put(sheetSet);
390  }
391 
392  sheetSet.put(new NodeProperty<>(
393  KeywordHits_createSheet_name_name(),
394  KeywordHits_createSheet_name_displayName(),
395  KeywordHits_createSheet_name_desc(),
396  getName()));
397 
398  return sheet;
399  }
400 
401  @Override
402  public String getItemType() {
403  return getClass().getName();
404  }
405  }
406 
407  private abstract class DetachableObserverChildFactory<X> extends ChildFactory.Detachable<X> implements Observer {
408 
409  @Override
410  protected void addNotify() {
411  keywordResults.addObserver(this);
412  }
413 
414  @Override
415  protected void removeNotify() {
416  keywordResults.deleteObserver(this);
417  }
418 
419  @Override
420  public void update(Observable o, Object arg) {
421  refresh(true);
422  }
423  }
424 
428  private class ListFactory extends DetachableObserverChildFactory<String> {
429 
430  private final PropertyChangeListener pcl = new PropertyChangeListener() {
431  @Override
432  public void propertyChange(PropertyChangeEvent evt) {
433  String eventType = evt.getPropertyName();
434  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
441  try {
449  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
450  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
451  keywordResults.update();
452  }
453  } catch (NoCurrentCaseException notUsed) {
454  // Case is closed, do nothing.
455  }
456  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
457  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
464  try {
466  keywordResults.update();
467  } catch (NoCurrentCaseException notUsed) {
468  // Case is closed, do nothing.
469  }
470  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())
471  && evt.getNewValue() == null) {
472  /*
473  * Case was closed. Remove listeners so that we don't get
474  * called with a stale case handle
475  */
476  removeNotify();
477  skCase = null;
478  }
479 
480  }
481  };
482 
483  @Override
484  protected void addNotify() {
488  keywordResults.update();
489  super.addNotify();
490  }
491 
492  @Override
493  protected void removeNotify() {
497  super.removeNotify();
498  }
499 
500  @Override
501  protected boolean createKeys(List<String> list) {
502  list.addAll(keywordResults.getListNames());
503  return true;
504  }
505 
506  @Override
507  protected Node createNodeForKey(String key) {
508  return new ListNode(key);
509  }
510  }
511 
512  private abstract class KWHitsNodeBase extends DisplayableItemNode implements Observer {
513 
514  private KWHitsNodeBase(Children children, Lookup lookup) {
515  super(children, lookup);
516  }
517 
518  private KWHitsNodeBase(Children children) {
519  super(children);
520  }
521 
522  @Override
523  public String getItemType() {
524  return getClass().getName();
525  }
526 
527  @Override
528  public void update(Observable o, Object arg) {
529  updateDisplayName();
530  }
531 
532  final void updateDisplayName() {
533  super.setDisplayName(getName() + " (" + countTotalDescendants() + ")");
534  }
535 
536  abstract int countTotalDescendants();
537  }
538 
543  class ListNode extends KWHitsNodeBase {
544 
545  private final String listName;
546 
547  private ListNode(String listName) {
548  super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName));
549  super.setName(listName);
550  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
551  this.listName = listName;
552  updateDisplayName();
553  keywordResults.addObserver(this);
554  }
555 
556  @Override
557  public int countTotalDescendants() {
558  int totalDescendants = 0;
559 
560  for (String word : keywordResults.getKeywords(listName)) {
561  for (String instance : keywordResults.getKeywordInstances(listName, word)) {
562  Set<Long> ids = keywordResults.getArtifactIds(listName, word, instance);
563  totalDescendants += ids.size();
564  }
565  }
566  return totalDescendants;
567  }
568 
569  @Override
570  @NbBundle.Messages({"KeywordHits.createSheet.listName.name=List Name",
571  "KeywordHits.createSheet.listName.displayName=List Name",
572  "KeywordHits.createSheet.listName.desc=no description",
573  "KeywordHits.createSheet.numChildren.name=Number of Children",
574  "KeywordHits.createSheet.numChildren.displayName=Number of Children",
575  "KeywordHits.createSheet.numChildren.desc=no description"})
576  protected Sheet createSheet() {
577  Sheet sheet = super.createSheet();
578  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
579  if (sheetSet == null) {
580  sheetSet = Sheet.createPropertiesSet();
581  sheet.put(sheetSet);
582  }
583 
584  sheetSet.put(new NodeProperty<>(
585  KeywordHits_createSheet_listName_name(),
586  KeywordHits_createSheet_listName_displayName(),
587  KeywordHits_createSheet_listName_desc(),
588  listName));
589 
590  sheetSet.put(new NodeProperty<>(
591  KeywordHits_createSheet_numChildren_name(),
592  KeywordHits_createSheet_numChildren_displayName(),
593  KeywordHits_createSheet_numChildren_desc(),
594  keywordResults.getKeywords(listName).size()));
595 
596  return sheet;
597  }
598 
599  @Override
600  public boolean isLeafTypeNode() {
601  return false;
602  }
603 
604  @Override
605  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
606  return visitor.visit(this);
607  }
608  }
609 
613  private class TermFactory extends DetachableObserverChildFactory<String> {
614 
615  private final String setName;
616 
617  private TermFactory(String setName) {
618  super();
619  this.setName = setName;
620  }
621 
622  @Override
623  protected boolean createKeys(List<String> list) {
624  list.addAll(keywordResults.getKeywords(setName));
625  return true;
626  }
627 
628  @Override
629  protected Node createNodeForKey(String key) {
630  return new TermNode(setName, key);
631  }
632  }
633 
637  class TermNode extends KWHitsNodeBase {
638 
639  private final String setName;
640  private final String keyword;
641 
642  private TermNode(String setName, String keyword) {
643  super(Children.create(new RegExpInstancesFactory(setName, keyword), true), Lookups.singleton(keyword));
644  super.setName(keyword);
645  this.setName = setName;
646  this.keyword = keyword;
647  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
648  updateDisplayName();
649  keywordResults.addObserver(this);
650  }
651 
652  @Override
653  int countTotalDescendants() {
654  return keywordResults.getKeywordInstances(setName, keyword).stream()
655  .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
656  .sum();
657  }
658 
659  @Override
660  public boolean isLeafTypeNode() {
661  // is this an exact/substring match (i.e. did we use the DEFAULT name)?
662  return isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword));
663  }
664 
665  @Override
666  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
667  return visitor.visit(this);
668  }
669 
670  @Override
671  @NbBundle.Messages({"KeywordHits.createSheet.filesWithHits.name=Files with Hits",
672  "KeywordHits.createSheet.filesWithHits.displayName=Files with Hits",
673  "KeywordHits.createSheet.filesWithHits.desc=no description"})
674  protected Sheet createSheet() {
675  Sheet sheet = super.createSheet();
676  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
677  if (sheetSet == null) {
678  sheetSet = Sheet.createPropertiesSet();
679  sheet.put(sheetSet);
680  }
681  sheetSet.put(new NodeProperty<>(
682  KeywordHits_createSheet_listName_name(),
683  KeywordHits_createSheet_listName_displayName(),
684  KeywordHits_createSheet_listName_desc(),
685  getDisplayName()));
686 
687  sheetSet.put(new NodeProperty<>(
688  KeywordHits_createSheet_filesWithHits_name(),
689  KeywordHits_createSheet_filesWithHits_displayName(),
690  KeywordHits_createSheet_filesWithHits_desc(),
691  countTotalDescendants()));
692 
693  return sheet;
694  }
695  }
696 
702  private class RegExpInstanceKey {
703 
704  private final boolean isRegExp;
705  private String strKey;
706  private Long longKey;
707 
708  RegExpInstanceKey(String key) {
709  isRegExp = true;
710  strKey = key;
711  }
712 
713  RegExpInstanceKey(Long key) {
714  isRegExp = false;
715  longKey = key;
716  }
717 
718  boolean isRegExp() {
719  return isRegExp;
720  }
721 
722  Long getIdKey() {
723  return longKey;
724  }
725 
726  String getRegExpKey() {
727  return strKey;
728  }
729  }
730 
735  private class RegExpInstancesFactory extends DetachableObserverChildFactory<RegExpInstanceKey> {
736 
737  private final String keyword;
738  private final String setName;
739 
740  private RegExpInstancesFactory(String setName, String keyword) {
741  super();
742  this.setName = setName;
743  this.keyword = keyword;
744  }
745 
746  @Override
747  protected boolean createKeys(List<RegExpInstanceKey> list) {
748  List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
749  // The keys are different depending on what we are displaying.
750  // regexp get another layer to show instances.
751  // Exact/substring matches don't.
752  if (isOnlyDefaultInstance(instances)) {
753  list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream()
754  .map(RegExpInstanceKey::new)
755  .collect(Collectors.toList()));
756  } else {
757  list.addAll(instances.stream()
758  .map(RegExpInstanceKey::new)
759  .collect(Collectors.toList()));
760  }
761  return true;
762  }
763 
764  @Override
765  protected Node createNodeForKey(RegExpInstanceKey key) {
766  if (key.isRegExp()) {
767  return new RegExpInstanceNode(setName, keyword, key.getRegExpKey());
768  } else {
769  // if it isn't a regexp, then skip the 'instance' layer of the tree
770  return createBlackboardArtifactNode(key.getIdKey());
771  }
772  }
773 
774  }
775 
779  class RegExpInstanceNode extends KWHitsNodeBase {
780 
781  private final String setName;
782  private final String keyword;
783  private final String instance;
784 
785  private RegExpInstanceNode(String setName, String keyword, String instance) {
786  super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance));
787  super.setName(instance); //the instance represents the name of the keyword hit at this point as the keyword is the regex
788  this.setName = setName;
789  this.keyword = keyword;
790  this.instance = instance;
791  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
792  updateDisplayName();
793  keywordResults.addObserver(this);
794  }
795 
796  @Override
797  int countTotalDescendants() {
798  return keywordResults.getArtifactIds(setName, keyword, instance).size();
799  }
800 
801  @Override
802  public boolean isLeafTypeNode() {
803  return true;
804  }
805 
806  @Override
807  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
808  return visitor.visit(this);
809  }
810 
811  @Override
812  protected Sheet createSheet() {
813  Sheet sheet = super.createSheet();
814  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
815  if (sheetSet == null) {
816  sheetSet = Sheet.createPropertiesSet();
817  sheet.put(sheetSet);
818  }
819 
820  sheetSet.put(new NodeProperty<>(
821  KeywordHits_createSheet_listName_name(),
822  KeywordHits_createSheet_listName_displayName(),
823  KeywordHits_createSheet_listName_desc(),
824  getDisplayName()));
825 
826  sheetSet.put(new NodeProperty<>(
827  KeywordHits_createSheet_filesWithHits_name(),
828  KeywordHits_createSheet_filesWithHits_displayName(),
829  KeywordHits_createSheet_filesWithHits_desc(),
830  keywordResults.getArtifactIds(setName, keyword, instance).size()));
831 
832  return sheet;
833  }
834 
835  }
836 
844  @NbBundle.Messages({"KeywordHits.createNodeForKey.modTime.name=ModifiedTime",
845  "KeywordHits.createNodeForKey.modTime.displayName=Modified Time",
846  "KeywordHits.createNodeForKey.modTime.desc=Modified Time",
847  "KeywordHits.createNodeForKey.accessTime.name=AccessTime",
848  "KeywordHits.createNodeForKey.accessTime.displayName=Access Time",
849  "KeywordHits.createNodeForKey.accessTime.desc=Access Time",
850  "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
851  "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
852  "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
854  if (skCase == null) {
855  return null;
856  }
857 
858  try {
859  BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId);
861  AbstractFile file;
862  try {
863  file = skCase.getAbstractFileById(art.getObjectID());
864  } catch (TskCoreException ex) {
865  logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex); //NON-NLS
866  return n;
867  }
868 
869  /*
870  * It is possible to get a keyword hit on artifacts generated for
871  * the underlying image in which case MAC times are not
872  * available/applicable/useful.
873  */
874  if (file == null) {
875  return n;
876  }
877 
879  KeywordHits_createNodeForKey_modTime_name(),
880  KeywordHits_createNodeForKey_modTime_displayName(),
881  KeywordHits_createNodeForKey_modTime_desc(),
882  ContentUtils.getStringTime(file.getMtime(), file)));
884  KeywordHits_createNodeForKey_accessTime_name(),
885  KeywordHits_createNodeForKey_accessTime_displayName(),
886  KeywordHits_createNodeForKey_accessTime_desc(),
887  ContentUtils.getStringTime(file.getAtime(), file)));
889  KeywordHits_createNodeForKey_chgTime_name(),
890  KeywordHits_createNodeForKey_chgTime_displayName(),
891  KeywordHits_createNodeForKey_chgTime_desc(),
892  ContentUtils.getStringTime(file.getCtime(), file)));
893  return n;
894  } catch (TskCoreException ex) {
895  logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS
896  }
897  return null;
898  }
899 
903  private class HitsFactory extends DetachableObserverChildFactory<Long> {
904 
905  private final String keyword;
906  private final String setName;
907  private final String instance;
908 
909  private HitsFactory(String setName, String keyword, String instance) {
910  super();
911  this.setName = setName;
912  this.keyword = keyword;
913  this.instance = instance;
914  }
915 
916  @Override
917  protected boolean createKeys(List<Long> list) {
918  list.addAll(keywordResults.getArtifactIds(setName, keyword, instance));
919  return true;
920  }
921 
922  @Override
923  protected Node createNodeForKey(Long artifactId) {
924  return createBlackboardArtifactNode(artifactId);
925  }
926  }
927 }
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)
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:428
HitsFactory(String setName, String keyword, String instance)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:473

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