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;
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")
70 @NbBundle.Messages(
"KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
73 public static final String
NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
90 +
"blackboard_attributes.value_int32, "
91 +
"blackboard_attributes.artifact_id, "
92 +
"blackboard_attributes.attribute_type_id "
93 +
"FROM blackboard_attributes, blackboard_artifacts "
94 +
"WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "
95 +
" AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
96 +
" AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
97 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()
98 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
99 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()
103 return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
124 this.filteringDSObjId = objId;
138 private final Map<String, Map<String, Map<String, Set<Long>>>>
topLevelMap =
new LinkedHashMap<>();
149 List<String> getListNames() {
151 List<String> names =
new ArrayList<>(topLevelMap.keySet());
155 Collections.sort(names,
new Comparator<String>() {
158 public int compare(String o1, String o2) {
161 if (o1.startsWith(
"Single Literal Keyword Search")) {
164 else if (o2.startsWith(
"Single Literal Keyword Search")) {
167 else if (o1.startsWith(
"Single Regular Expression Search")) {
170 else if (o2.startsWith(
"Single Regular Expression Search")) {
173 return o1.compareTo(o2);
189 List<String> getKeywords(String listName) {
190 List<String> keywords;
192 keywords =
new ArrayList<>(topLevelMap.get(listName).keySet());
194 Collections.sort(keywords);
208 List<String> getKeywordInstances(String listName, String keyword) {
209 List<String> instances;
211 instances =
new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
213 Collections.sort(instances);
228 Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
230 return topLevelMap.get(listName).get(keyword).get(keywordInstance);
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<>());
246 instanceMap.computeIfAbsent(keywordInstance, ki ->
new HashSet<>()).add(artifactId);
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<>());
261 instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN ->
new HashSet<>()).add(artifactId);
271 void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
276 Map<String, Map<String, Map<String, Set<Long>>>> listsMap =
new LinkedHashMap<>();
279 Map<String, Map<String, Set<Long>>> literalMap =
new LinkedHashMap<>();
282 Map<String, Map<String, Set<Long>>> regexMap =
new LinkedHashMap<>();
285 topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
286 topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
288 for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
289 long id = art.getKey();
290 Map<Long, String> attributes = art.getValue();
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()));
298 if (listName != null) {
300 Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln ->
new LinkedHashMap<>());
302 if (
"1".equals(kwType) || reg == null) {
309 word = (reg != null) ? reg : word;
310 addNonRegExpMatchToList(listMap, word,
id);
312 addRegExpToList(listMap, reg, word,
id);
315 if (
"1".equals(kwType) || reg == null) {
322 word = (reg != null) ? reg : word;
323 addNonRegExpMatchToList(literalMap, word,
id);
325 addRegExpToList(regexMap, reg, word,
id);
329 topLevelMap.putAll(listsMap);
338 Map<Long, Map<Long, String>> artifactIds =
new LinkedHashMap<>();
340 if (skCase == null) {
345 if (filteringDSObjId > 0) {
346 queryStr +=
" AND blackboard_artifacts.data_source_obj_id = " +
filteringDSObjId;
349 try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
350 ResultSet resultSet = dbQuery.getResultSet();
351 while (resultSet.next()) {
352 long artifactId = resultSet.getLong(
"artifact_id");
353 long typeId = resultSet.getLong(
"attribute_type_id");
354 String valueStr = resultSet.getString(
"value_text");
357 Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai ->
new LinkedHashMap<>());
358 if (StringUtils.isNotEmpty(valueStr)) {
359 attributesByTypeMap.put(typeId, valueStr);
362 Long valueLong = resultSet.getLong(
"value_int32");
363 attributesByTypeMap.put(typeId, valueLong.toString());
366 }
catch (TskCoreException | SQLException ex) {
367 logger.log(Level.WARNING,
"SQL Exception occurred: ", ex);
370 populateTreeMaps(artifactIds);
376 return visitor.
visit(
this);
383 super(Children.create(
new ListFactory(),
true), Lookups.singleton(KEYWORD_HITS));
385 super.setDisplayName(KEYWORD_HITS);
386 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
396 return visitor.
visit(
this);
400 @NbBundle.Messages({
"KeywordHits.createSheet.name.name=Name",
401 "KeywordHits.createSheet.name.displayName=Name",
402 "KeywordHits.createSheet.name.desc=no description"})
404 Sheet sheet = super.createSheet();
405 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
406 if (sheetSet == null) {
407 sheetSet = Sheet.createPropertiesSet();
412 KeywordHits_createSheet_name_name(),
413 KeywordHits_createSheet_name_displayName(),
414 KeywordHits_createSheet_name_desc(),
422 return getClass().getName();
430 keywordResults.addObserver(
this);
435 keywordResults.deleteObserver(
this);
439 public void update(Observable o, Object arg) {
449 private final PropertyChangeListener
pcl =
new PropertyChangeListener() {
451 public void propertyChange(PropertyChangeEvent evt) {
452 String eventType = evt.getPropertyName();
469 if (null != eventData && eventData.
getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
490 && evt.getNewValue() == null) {
516 super.removeNotify();
521 list.addAll(keywordResults.getListNames());
527 return new ListNode(key);
535 super(children, lookup);
545 return getClass().getName();
549 public void update(Observable o, Object arg) {
553 final void updateDisplayName() {
554 super.setDisplayName(displayName +
" (" + countTotalDescendants() +
")");
557 abstract int countTotalDescendants();
564 class ListNode
extends KWHitsNodeBase {
566 private final String listName;
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");
572 this.listName = listName;
574 keywordResults.addObserver(
this);
578 public int countTotalDescendants() {
579 int totalDescendants = 0;
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();
587 return totalDescendants;
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();
605 sheetSet.put(
new NodeProperty<>(
606 KeywordHits_createSheet_listName_name(),
607 KeywordHits_createSheet_listName_displayName(),
608 KeywordHits_createSheet_listName_desc(),
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()));
621 public boolean isLeafTypeNode() {
626 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
627 return visitor.visit(
this);
645 list.addAll(keywordResults.getKeywords(setName));
651 return new TermNode(setName, key);
665 ChildFactory<?> createChildFactory(String setName, String keyword) {
667 return new HitsFactory(setName, keyword, DEFAULT_INSTANCE_NAME);
669 return new RegExpInstancesFactory(setName, keyword);
676 class TermNode
extends KWHitsNodeBase {
678 private final String setName;
679 private final String keyword;
681 private TermNode(String setName, String keyword) {
682 super(Children.create(createChildFactory(setName, keyword),
true), Lookups.singleton(keyword), keyword);
691 super.setName(setName +
"_" + keyword);
692 this.setName = setName;
693 this.keyword = keyword;
694 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
696 keywordResults.addObserver(
this);
700 int countTotalDescendants() {
701 return keywordResults.getKeywordInstances(setName, keyword).stream()
702 .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
707 public boolean isLeafTypeNode() {
713 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
714 return visitor.visit(
this);
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();
728 sheetSet.put(
new NodeProperty<>(
729 KeywordHits_createSheet_listName_name(),
730 KeywordHits_createSheet_listName_displayName(),
731 KeywordHits_createSheet_listName_desc(),
734 sheetSet.put(
new NodeProperty<>(
735 KeywordHits_createSheet_filesWithHits_name(),
736 KeywordHits_createSheet_filesWithHits_displayName(),
737 KeywordHits_createSheet_filesWithHits_desc(),
738 countTotalDescendants()));
761 list.addAll(keywordResults.getKeywordInstances(setName, keyword));
767 return new RegExpInstanceNode(setName, keyword, key);
774 class RegExpInstanceNode
extends KWHitsNodeBase {
776 private final String setName;
777 private final String keyword;
778 private final String instance;
780 private RegExpInstanceNode(String setName, String keyword, String instance) {
781 super(Children.create(
new HitsFactory(setName, keyword, instance),
true), Lookups.singleton(instance), instance);
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");
796 keywordResults.addObserver(
this);
800 int countTotalDescendants() {
801 return keywordResults.getArtifactIds(setName, keyword, instance).size();
805 public boolean isLeafTypeNode() {
810 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
811 return visitor.visit(
this);
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();
823 sheetSet.put(
new NodeProperty<>(
824 KeywordHits_createSheet_listName_name(),
825 KeywordHits_createSheet_listName_displayName(),
826 KeywordHits_createSheet_listName_desc(),
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()));
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"})
857 if (skCase == null) {
865 AbstractFile file = n.getLookup().lookup(AbstractFile.class);
868 file = skCase.getAbstractFileById(art.getObjectID());
869 }
catch (TskCoreException ex) {
870 logger.log(Level.SEVERE,
"TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex);
883 KeywordHits_createNodeForKey_modTime_name(),
884 KeywordHits_createNodeForKey_modTime_displayName(),
885 KeywordHits_createNodeForKey_modTime_desc(),
888 KeywordHits_createNodeForKey_accessTime_name(),
889 KeywordHits_createNodeForKey_accessTime_displayName(),
890 KeywordHits_createNodeForKey_accessTime_desc(),
893 KeywordHits_createNodeForKey_chgTime_name(),
894 KeywordHits_createNodeForKey_chgTime_displayName(),
895 KeywordHits_createNodeForKey_chgTime_desc(),
908 private final Map<Long, BlackboardArtifact>
artifactHits =
new HashMap<>();
910 private HitsFactory(String setName, String keyword, String instance) {
917 super(setName +
"_" + keyword + (DEFAULT_INSTANCE_NAME.equals(instance) ?
"" :
"_" +
instance));
925 if (skCase != null) {
926 keywordResults.getArtifactIds(setName, keyword, instance).forEach((
id) -> {
928 if (!artifactHits.containsKey(
id)) {
929 BlackboardArtifact art = skCase.getBlackboardArtifact(
id);
930 artifactHits.put(
id, art);
932 }
catch (TskCoreException ex) {
933 logger.log(Level.SEVERE,
"TSK Exception occurred", ex);
937 return new ArrayList<>(artifactHits.values());
939 return Collections.emptyList();
949 keywordResults.addObserver(
this);
954 keywordResults.deleteObserver(
this);
958 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
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)
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 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)
void addNodeProperty(NodeProperty<?> np)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
static final String SIMPLE_LITERAL_SEARCH