19 package org.sleuthkit.autopsy.datamodel;
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;
34 import java.util.Observable;
35 import java.util.Observer;
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;
56 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
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")
71 @NbBundle.Messages(
"KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
74 public static final String
NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
91 +
"blackboard_attributes.value_int32, "
92 +
"blackboard_attributes.artifact_id, "
93 +
"blackboard_attributes.attribute_type_id "
94 +
"FROM blackboard_attributes, blackboard_artifacts "
95 +
"WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "
96 +
" AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
97 +
" AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
98 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()
99 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
100 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()
104 return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
125 this.filteringDSObjId = objId;
139 private final Map<String, Map<String, Map<String, Set<Long>>>>
topLevelMap =
new LinkedHashMap<>();
150 List<String> getListNames() {
152 List<String> names =
new ArrayList<>(topLevelMap.keySet());
156 Collections.sort(names,
new Comparator<String>() {
159 public int compare(String o1, String o2) {
162 if (o1.startsWith(
"Single Literal Keyword Search")) {
164 }
else if (o2.startsWith(
"Single Literal Keyword Search")) {
166 }
else if (o1.startsWith(
"Single Regular Expression Search")) {
168 }
else if (o2.startsWith(
"Single Regular Expression Search")) {
171 return o1.compareTo(o2);
187 List<String> getKeywords(String listName) {
188 List<String> keywords;
190 keywords =
new ArrayList<>(topLevelMap.get(listName).keySet());
192 Collections.sort(keywords);
206 List<String> getKeywordInstances(String listName, String keyword) {
207 List<String> instances;
209 instances =
new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
211 Collections.sort(instances);
226 Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
228 return topLevelMap.get(listName).get(keyword).get(keywordInstance);
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<>());
244 instanceMap.computeIfAbsent(keywordInstance, ki ->
new HashSet<>()).add(artifactId);
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<>());
259 instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN ->
new HashSet<>()).add(artifactId);
269 void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
274 Map<String, Map<String, Map<String, Set<Long>>>> listsMap =
new LinkedHashMap<>();
277 Map<String, Map<String, Set<Long>>> literalMap =
new LinkedHashMap<>();
280 Map<String, Map<String, Set<Long>>> regexMap =
new LinkedHashMap<>();
283 topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
284 topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
286 for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
287 long id = art.getKey();
288 Map<Long, String> attributes = art.getValue();
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()));
296 if (listName != null) {
298 Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln ->
new LinkedHashMap<>());
300 if (
"1".equals(kwType) || reg == null) {
307 word = (reg != null) ? reg : word;
308 addNonRegExpMatchToList(listMap, word,
id);
310 addRegExpToList(listMap, reg, word,
id);
313 if (
"1".equals(kwType) || reg == null) {
320 word = (reg != null) ? reg : word;
321 addNonRegExpMatchToList(literalMap, word,
id);
323 addRegExpToList(regexMap, reg, word,
id);
327 topLevelMap.putAll(listsMap);
336 Map<Long, Map<Long, String>> artifactIds =
new LinkedHashMap<>();
338 if (skCase == null) {
343 if (filteringDSObjId > 0) {
344 queryStr +=
" AND blackboard_artifacts.data_source_obj_id = " +
filteringDSObjId;
347 try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
348 ResultSet resultSet = dbQuery.getResultSet();
349 while (resultSet.next()) {
350 long artifactId = resultSet.getLong(
"artifact_id");
351 long typeId = resultSet.getLong(
"attribute_type_id");
352 String valueStr = resultSet.getString(
"value_text");
355 Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai ->
new LinkedHashMap<>());
356 if (StringUtils.isNotEmpty(valueStr)) {
357 attributesByTypeMap.put(typeId, valueStr);
360 Long valueLong = resultSet.getLong(
"value_int32");
361 attributesByTypeMap.put(typeId, valueLong.toString());
364 }
catch (TskCoreException | SQLException ex) {
365 logger.log(Level.WARNING,
"SQL Exception occurred: ", ex);
368 populateTreeMaps(artifactIds);
374 return visitor.
visit(
this);
381 super(Children.create(
new ListFactory(),
true), Lookups.singleton(KEYWORD_HITS));
383 super.setDisplayName(KEYWORD_HITS);
384 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
394 return visitor.
visit(
this);
398 @NbBundle.Messages({
"KeywordHits.createSheet.name.name=Name",
399 "KeywordHits.createSheet.name.displayName=Name",
400 "KeywordHits.createSheet.name.desc=no description"})
402 Sheet sheet = super.createSheet();
403 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
404 if (sheetSet == null) {
405 sheetSet = Sheet.createPropertiesSet();
410 KeywordHits_createSheet_name_name(),
411 KeywordHits_createSheet_name_displayName(),
412 KeywordHits_createSheet_name_desc(),
420 return getClass().getName();
428 keywordResults.addObserver(
this);
433 keywordResults.deleteObserver(
this);
437 public void update(Observable o, Object arg) {
447 private final PropertyChangeListener
pcl =
new PropertyChangeListener() {
449 public void propertyChange(PropertyChangeEvent evt) {
450 String eventType = evt.getPropertyName();
467 if (null != eventData && eventData.
getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
488 && evt.getNewValue() == null) {
514 super.removeNotify();
519 list.addAll(keywordResults.getListNames());
525 return new ListNode(key);
534 super(children, lookup);
544 return getClass().getName();
548 public void update(Observable o, Object arg) {
552 final void updateDisplayName() {
553 super.setDisplayName(displayName +
" (" + countTotalDescendants() +
")");
556 abstract int countTotalDescendants();
563 class ListNode
extends KWHitsNodeBase {
565 private final String listName;
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");
571 this.listName = listName;
573 keywordResults.addObserver(
this);
577 public int countTotalDescendants() {
578 int totalDescendants = 0;
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();
586 return totalDescendants;
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();
604 sheetSet.put(
new NodeProperty<>(
605 KeywordHits_createSheet_listName_name(),
606 KeywordHits_createSheet_listName_displayName(),
607 KeywordHits_createSheet_listName_desc(),
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()));
620 public boolean isLeafTypeNode() {
625 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
626 return visitor.visit(
this);
644 list.addAll(keywordResults.getKeywords(setName));
650 return new TermNode(setName, key);
664 ChildFactory<?> createChildFactory(String setName, String keyword) {
666 return new HitsFactory(setName, keyword, DEFAULT_INSTANCE_NAME);
668 return new RegExpInstancesFactory(setName, keyword);
675 class TermNode
extends KWHitsNodeBase {
677 private final String setName;
678 private final String keyword;
680 private TermNode(String setName, String keyword) {
681 super(Children.create(createChildFactory(setName, keyword),
true), Lookups.singleton(keyword), keyword);
690 super.setName(setName +
"_" + keyword);
691 this.setName = setName;
692 this.keyword = keyword;
693 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
695 keywordResults.addObserver(
this);
699 int countTotalDescendants() {
700 return keywordResults.getKeywordInstances(setName, keyword).stream()
701 .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
706 public boolean isLeafTypeNode() {
712 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
713 return visitor.visit(
this);
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();
727 sheetSet.put(
new NodeProperty<>(
728 KeywordHits_createSheet_listName_name(),
729 KeywordHits_createSheet_listName_displayName(),
730 KeywordHits_createSheet_listName_desc(),
733 sheetSet.put(
new NodeProperty<>(
734 KeywordHits_createSheet_filesWithHits_name(),
735 KeywordHits_createSheet_filesWithHits_displayName(),
736 KeywordHits_createSheet_filesWithHits_desc(),
737 countTotalDescendants()));
760 list.addAll(keywordResults.getKeywordInstances(setName, keyword));
766 return new RegExpInstanceNode(setName, keyword, key);
773 class RegExpInstanceNode
extends KWHitsNodeBase {
775 private final String setName;
776 private final String keyword;
777 private final String instance;
779 private RegExpInstanceNode(String setName, String keyword, String instance) {
780 super(Children.create(
new HitsFactory(setName, keyword, instance),
true), Lookups.singleton(instance), instance);
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");
795 keywordResults.addObserver(
this);
799 int countTotalDescendants() {
800 return keywordResults.getArtifactIds(setName, keyword, instance).size();
804 public boolean isLeafTypeNode() {
809 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
810 return visitor.visit(
this);
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();
822 sheetSet.put(
new NodeProperty<>(
823 KeywordHits_createSheet_listName_name(),
824 KeywordHits_createSheet_listName_displayName(),
825 KeywordHits_createSheet_listName_desc(),
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()));
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"})
856 if (skCase == null) {
864 AbstractFile file = n.getLookup().lookup(AbstractFile.class);
867 file = skCase.getAbstractFileById(art.getObjectID());
868 }
catch (TskCoreException ex) {
869 logger.log(Level.SEVERE,
"TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex);
882 KeywordHits_createNodeForKey_modTime_name(),
883 KeywordHits_createNodeForKey_modTime_displayName(),
884 KeywordHits_createNodeForKey_modTime_desc(),
887 KeywordHits_createNodeForKey_accessTime_name(),
888 KeywordHits_createNodeForKey_accessTime_displayName(),
889 KeywordHits_createNodeForKey_accessTime_desc(),
892 KeywordHits_createNodeForKey_chgTime_name(),
893 KeywordHits_createNodeForKey_chgTime_displayName(),
894 KeywordHits_createNodeForKey_chgTime_desc(),
907 private final Map<Long, BlackboardArtifact>
artifactHits =
new HashMap<>();
909 private HitsFactory(String setName, String keyword, String instance) {
916 super(setName +
"_" + keyword + (DEFAULT_INSTANCE_NAME.equals(instance) ?
"" :
"_" +
instance));
924 if (skCase != null) {
925 keywordResults.getArtifactIds(setName, keyword, instance).forEach((
id) -> {
927 if (!artifactHits.containsKey(
id)) {
928 BlackboardArtifact art = skCase.getBlackboardArtifact(
id);
932 artifactHits.put(
id, art);
934 }
catch (TskCoreException ex) {
935 logger.log(Level.SEVERE,
"TSK Exception occurred", ex);
939 return new ArrayList<>(artifactHits.values());
941 return Collections.emptyList();
951 keywordResults.addObserver(
this);
956 keywordResults.deleteObserver(
this);
960 public void update(Observable o, Object arg) {
final PropertyChangeListener pcl
static boolean isOnlyDefaultInstance(List< String > instances)
static final String KEYWORD_HITS
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
BlackboardArtifactNode createBlackboardArtifactNode(BlackboardArtifact art)
static String getStringTime(long epochSeconds, TimeZone tzone)
Node createNodeForKey(String key)
static synchronized IngestManager getInstance()
void update(Observable o, Object arg)
final KeywordResults keywordResults
static final String SIMPLE_REGEX_SEARCH
final Map< Long, BlackboardArtifact > artifactHits
TermFactory(String setName)
final Map< String, Map< String, Map< String, Set< Long > > > > topLevelMap
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
List< BlackboardArtifact > makeKeys()
T visit(DataSourcesNode in)
boolean createKeys(List< String > list)
void removeIngestJobEventListener(final PropertyChangeListener listener)
KWHitsNodeBase(Children children)
boolean createKeys(List< String > list)
void addNodeProperty(NodeProperty<?> property)
boolean createKeys(List< String > list)
void update(Observable o, Object arg)
RegExpInstancesFactory(String setName, String keyword)
void addIngestJobEventListener(final PropertyChangeListener listener)
static final String DEFAULT_INSTANCE_NAME
Node createNodeForKey(String key)
Node createNodeForKey(BlackboardArtifact art)
static final Logger logger
Node createNodeForKey(String key)
static final String KEYWORD_HIT_ATTRIBUTES_QUERY
void addIngestModuleEventListener(final PropertyChangeListener listener)
KeywordHits(SleuthkitCase skCase, long objId)
synchronized static Logger getLogger(String name)
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
static Case getCurrentCaseThrows()
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
void update(Observable o, Object arg)
final long filteringDSObjId
HitsFactory(String setName, String keyword, String instance)
KWHitsNodeBase(Children children, Lookup lookup, String displayName)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
static final String SIMPLE_LITERAL_SEARCH