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.WeakListeners;
46 import org.openide.util.lookup.Lookups;
58 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
60 import static org.
sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
72 @NbBundle.Messages(
"KeywordHits.kwHits.text=Keyword Hits")
73 private static final String
KEYWORD_HITS = KeywordHits_kwHits_text();
74 @NbBundle.Messages(
"KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
76 @NbBundle.Messages(
"KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
79 public static final String
NAME = BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeName();
96 +
"blackboard_attributes.value_int32, "
97 +
"blackboard_artifacts.artifact_obj_id, "
98 +
"blackboard_attributes.attribute_type_id "
99 +
"FROM blackboard_attributes, blackboard_artifacts "
100 +
"WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "
101 +
" AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()
102 +
" AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
103 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()
104 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
105 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()
109 return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
130 this.filteringDSObjId = objId;
144 private final Map<String, Map<String, Map<String, Set<Long>>>>
topLevelMap =
new LinkedHashMap<>();
155 List<String> getListNames() {
157 List<String> names =
new ArrayList<>(topLevelMap.keySet());
161 Collections.sort(names,
new Comparator<String>() {
164 public int compare(String o1, String o2) {
167 if (o1.startsWith(
"Single Literal Keyword Search")) {
169 }
else if (o2.startsWith(
"Single Literal Keyword Search")) {
171 }
else if (o1.startsWith(
"Single Regular Expression Search")) {
173 }
else if (o2.startsWith(
"Single Regular Expression Search")) {
176 return o1.compareTo(o2);
192 List<String> getKeywords(String listName) {
193 List<String> keywords;
195 keywords =
new ArrayList<>(topLevelMap.get(listName).keySet());
197 Collections.sort(keywords);
211 List<String> getKeywordInstances(String listName, String keyword) {
212 List<String> instances;
214 instances =
new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
216 Collections.sort(instances);
231 Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
233 return topLevelMap.get(listName).get(keyword).get(keywordInstance);
246 void addRegExpToList(Map<String, Map<String, Set<Long>>> listMap, String regExp, String keywordInstance, Long artifactId) {
247 Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(regExp, r ->
new LinkedHashMap<>());
249 instanceMap.computeIfAbsent(keywordInstance, ki ->
new HashSet<>()).add(artifactId);
260 void addNonRegExpMatchToList(Map<String, Map<String, Set<Long>>> listMap, String keyWord, Long artifactId) {
261 Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(keyWord, k ->
new LinkedHashMap<>());
264 instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN ->
new HashSet<>()).add(artifactId);
274 void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
279 Map<String, Map<String, Map<String, Set<Long>>>> listsMap =
new LinkedHashMap<>();
282 Map<String, Map<String, Set<Long>>> literalMap =
new LinkedHashMap<>();
285 Map<String, Map<String, Set<Long>>> regexMap =
new LinkedHashMap<>();
288 topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
289 topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
291 for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
292 long id = art.getKey();
293 Map<Long, String> attributes = art.getValue();
296 String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
297 String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
298 String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
299 String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()));
301 if (listName != null) {
303 Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln ->
new LinkedHashMap<>());
305 if (
"1".equals(kwType) || reg == null) {
312 word = (reg != null) ? reg : word;
313 addNonRegExpMatchToList(listMap, word,
id);
315 addRegExpToList(listMap, reg, word,
id);
318 if (
"1".equals(kwType) || reg == null) {
325 word = (reg != null) ? reg : word;
326 addNonRegExpMatchToList(literalMap, word,
id);
328 addRegExpToList(regexMap, reg, word,
id);
332 topLevelMap.putAll(listsMap);
341 Map<Long, Map<Long, String>> artifactIds =
new LinkedHashMap<>();
343 if (skCase == null) {
348 if (filteringDSObjId > 0) {
349 queryStr +=
" AND blackboard_artifacts.data_source_obj_id = " +
filteringDSObjId;
352 try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
353 ResultSet resultSet = dbQuery.getResultSet();
354 while (resultSet.next()) {
355 long artifactObjId = resultSet.getLong(
"artifact_obj_id");
356 long typeId = resultSet.getLong(
"attribute_type_id");
357 String valueStr = resultSet.getString(
"value_text");
360 Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactObjId, ai ->
new LinkedHashMap<>());
361 if (StringUtils.isNotEmpty(valueStr)) {
362 attributesByTypeMap.put(typeId, valueStr);
365 Long valueLong = resultSet.getLong(
"value_int32");
366 attributesByTypeMap.put(typeId, valueLong.toString());
369 }
catch (TskCoreException | SQLException ex) {
370 logger.log(Level.WARNING,
"SQL Exception occurred: ", ex);
373 populateTreeMaps(artifactIds);
379 return visitor.
visit(
this);
387 Lookups.singleton(KEYWORD_HITS),
393 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
403 return visitor.
visit(
this);
407 @NbBundle.Messages({
"KeywordHits.createSheet.name.name=Name",
408 "KeywordHits.createSheet.name.displayName=Name",
409 "KeywordHits.createSheet.name.desc=no description"})
411 Sheet sheet = super.createSheet();
412 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
413 if (sheetSet == null) {
414 sheetSet = Sheet.createPropertiesSet();
419 KeywordHits_createSheet_name_name(),
420 KeywordHits_createSheet_name_displayName(),
421 KeywordHits_createSheet_name_desc(),
429 return getClass().getName();
437 keywordResults.addObserver(
this);
443 keywordResults.deleteObserver(
this);
447 public void update(Observable o, Object arg) {
457 private final PropertyChangeListener
pcl =
new PropertyChangeListener() {
459 public void propertyChange(PropertyChangeEvent evt) {
460 String eventType = evt.getPropertyName();
477 if (null != eventData && eventData.
getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
498 && evt.getNewValue() == null) {
510 private final PropertyChangeListener
weakPcl = WeakListeners.propertyChange(pcl, null);
531 list.addAll(keywordResults.getListNames());
537 return new ListNode(key);
546 super(children, lookup);
556 return getClass().getName();
560 public void update(Observable o, Object arg) {
564 final void updateDisplayName() {
565 super.setDisplayName(displayName +
" (" + countTotalDescendants() +
")");
568 abstract int countTotalDescendants();
575 class ListNode
extends KWHitsNodeBase {
577 private final String listName;
579 private ListNode(String listName) {
580 super(Children.create(
new TermFactory(listName),
true), Lookups.singleton(listName), listName);
581 super.setName(listName);
582 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
583 this.listName = listName;
585 keywordResults.addObserver(
this);
589 public int countTotalDescendants() {
590 int totalDescendants = 0;
592 for (String word : keywordResults.getKeywords(listName)) {
593 for (String instance : keywordResults.getKeywordInstances(listName, word)) {
594 Set<Long> ids = keywordResults.getArtifactIds(listName, word, instance);
595 totalDescendants += ids.size();
598 return totalDescendants;
602 @NbBundle.Messages({
"KeywordHits.createSheet.listName.name=List Name",
603 "KeywordHits.createSheet.listName.displayName=List Name",
604 "KeywordHits.createSheet.listName.desc=no description",
605 "KeywordHits.createSheet.numChildren.name=Number of Children",
606 "KeywordHits.createSheet.numChildren.displayName=Number of Children",
607 "KeywordHits.createSheet.numChildren.desc=no description"})
608 protected Sheet createSheet() {
609 Sheet sheet = super.createSheet();
610 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
611 if (sheetSet == null) {
612 sheetSet = Sheet.createPropertiesSet();
616 sheetSet.put(
new NodeProperty<>(
617 KeywordHits_createSheet_listName_name(),
618 KeywordHits_createSheet_listName_displayName(),
619 KeywordHits_createSheet_listName_desc(),
622 sheetSet.put(
new NodeProperty<>(
623 KeywordHits_createSheet_numChildren_name(),
624 KeywordHits_createSheet_numChildren_displayName(),
625 KeywordHits_createSheet_numChildren_desc(),
626 keywordResults.getKeywords(listName).size()));
632 public boolean isLeafTypeNode() {
637 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
638 return visitor.visit(
this);
656 list.addAll(keywordResults.getKeywords(setName));
662 return new TermNode(setName, key);
676 ChildFactory<?> createChildFactory(String setName, String keyword) {
678 return new HitsFactory(setName, keyword, DEFAULT_INSTANCE_NAME);
680 return new RegExpInstancesFactory(setName, keyword);
687 class TermNode
extends KWHitsNodeBase {
689 private final String setName;
690 private final String keyword;
692 private TermNode(String setName, String keyword) {
693 super(Children.create(createChildFactory(setName, keyword),
true), Lookups.singleton(keyword), keyword);
702 super.setName(setName +
"_" + keyword);
703 this.setName = setName;
704 this.keyword = keyword;
705 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
707 keywordResults.addObserver(
this);
711 int countTotalDescendants() {
712 return keywordResults.getKeywordInstances(setName, keyword).stream()
713 .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
718 public boolean isLeafTypeNode() {
724 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
725 return visitor.visit(
this);
729 @NbBundle.Messages({
"KeywordHits.createSheet.filesWithHits.name=Files with Hits",
730 "KeywordHits.createSheet.filesWithHits.displayName=Files with Hits",
731 "KeywordHits.createSheet.filesWithHits.desc=no description"})
732 protected Sheet createSheet() {
733 Sheet sheet = super.createSheet();
734 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
735 if (sheetSet == null) {
736 sheetSet = Sheet.createPropertiesSet();
739 sheetSet.put(
new NodeProperty<>(
740 KeywordHits_createSheet_listName_name(),
741 KeywordHits_createSheet_listName_displayName(),
742 KeywordHits_createSheet_listName_desc(),
745 sheetSet.put(
new NodeProperty<>(
746 KeywordHits_createSheet_filesWithHits_name(),
747 KeywordHits_createSheet_filesWithHits_displayName(),
748 KeywordHits_createSheet_filesWithHits_desc(),
749 countTotalDescendants()));
772 list.addAll(keywordResults.getKeywordInstances(setName, keyword));
778 return new RegExpInstanceNode(setName, keyword, key);
785 class RegExpInstanceNode
extends KWHitsNodeBase {
787 private final String setName;
788 private final String keyword;
789 private final String instance;
791 private RegExpInstanceNode(String setName, String keyword, String instance) {
792 super(Children.create(
new HitsFactory(setName, keyword, instance),
true), Lookups.singleton(instance), instance);
801 super.setName(setName +
"_" + keyword +
"_" + instance);
802 this.setName = setName;
803 this.keyword = keyword;
804 this.instance = instance;
805 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
807 keywordResults.addObserver(
this);
811 int countTotalDescendants() {
812 return keywordResults.getArtifactIds(setName, keyword, instance).size();
816 public boolean isLeafTypeNode() {
821 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
822 return visitor.visit(
this);
826 protected Sheet createSheet() {
827 Sheet sheet = super.createSheet();
828 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
829 if (sheetSet == null) {
830 sheetSet = Sheet.createPropertiesSet();
834 sheetSet.put(
new NodeProperty<>(
835 KeywordHits_createSheet_listName_name(),
836 KeywordHits_createSheet_listName_displayName(),
837 KeywordHits_createSheet_listName_desc(),
840 sheetSet.put(
new NodeProperty<>(
841 KeywordHits_createSheet_filesWithHits_name(),
842 KeywordHits_createSheet_filesWithHits_displayName(),
843 KeywordHits_createSheet_filesWithHits_desc(),
844 keywordResults.getArtifactIds(setName, keyword, instance).size()));
858 @NbBundle.Messages({
"KeywordHits.createNodeForKey.modTime.name=ModifiedTime",
859 "KeywordHits.createNodeForKey.modTime.displayName=Modified Time",
860 "KeywordHits.createNodeForKey.modTime.desc=Modified Time",
861 "KeywordHits.createNodeForKey.accessTime.name=AccessTime",
862 "KeywordHits.createNodeForKey.accessTime.displayName=Access Time",
863 "KeywordHits.createNodeForKey.accessTime.desc=Access Time",
864 "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
865 "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
866 "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
868 if (skCase == null) {
876 AbstractFile file = n.getLookup().lookup(AbstractFile.class);
879 file = skCase.getAbstractFileById(art.getObjectID());
880 }
catch (TskCoreException ex) {
881 logger.log(Level.SEVERE,
"TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex);
894 KeywordHits_createNodeForKey_modTime_name(),
895 KeywordHits_createNodeForKey_modTime_displayName(),
896 KeywordHits_createNodeForKey_modTime_desc(),
899 KeywordHits_createNodeForKey_accessTime_name(),
900 KeywordHits_createNodeForKey_accessTime_displayName(),
901 KeywordHits_createNodeForKey_accessTime_desc(),
904 KeywordHits_createNodeForKey_chgTime_name(),
905 KeywordHits_createNodeForKey_chgTime_displayName(),
906 KeywordHits_createNodeForKey_chgTime_desc(),
919 private final Map<Long, AnalysisResult>
artifactHits =
new HashMap<>();
921 private HitsFactory(String setName, String keyword, String instance) {
928 super(setName +
"_" + keyword + (DEFAULT_INSTANCE_NAME.equals(instance) ?
"" :
"_" +
instance));
936 if (skCase != null) {
937 keywordResults.getArtifactIds(setName, keyword, instance).forEach((
id) -> {
939 if (!artifactHits.containsKey(
id)) {
940 AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(
id);
944 artifactHits.put(
id, art);
946 }
catch (TskCoreException ex) {
947 logger.log(Level.SEVERE,
"TSK Exception occurred", ex);
951 return new ArrayList<>(artifactHits.values());
953 return Collections.emptyList();
963 keywordResults.addObserver(
this);
968 keywordResults.deleteObserver(
this);
972 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)
Node createNodeForKey(String key)
final long filteringDSObjId
static synchronized IngestManager getInstance()
void update(Observable o, Object arg)
final KeywordResults keywordResults
Node createNodeForKey(AnalysisResult art)
static String getFormattedTime(long epochTime)
static final String SIMPLE_REGEX_SEARCH
TermFactory(String setName)
final Map< String, Map< String, Map< String, Set< Long > > > > topLevelMap
final Map< Long, AnalysisResult > artifactHits
BlackboardArtifactNode createBlackboardArtifactNode(AnalysisResult art)
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
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)
final PropertyChangeListener weakPcl
T visit(DataSourceFilesNode in)
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
List< AnalysisResult > makeKeys()