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.EnumSet;
28 import java.util.HashSet;
29 import java.util.LinkedHashMap;
30 import java.util.List;
32 import java.util.Objects;
33 import java.util.Observable;
34 import java.util.Observer;
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;
58 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
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")
72 @NbBundle.Messages(
"KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
75 public static final String
NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
93 +
"blackboard_attributes.value_int32, "
94 +
"blackboard_attributes.artifact_id, "
95 +
"blackboard_attributes.attribute_type_id "
96 +
"FROM blackboard_attributes, blackboard_artifacts "
97 +
"WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "
98 +
" AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
99 +
" AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
100 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()
101 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
102 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()
106 return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
127 this.datasourceObjId = objId;
141 private final Map<String, Map<String, Map<String, Set<Long>>>>
topLevelMap =
new LinkedHashMap<>();
152 List<String> getListNames() {
154 List<String> names =
new ArrayList<>(topLevelMap.keySet());
170 List<String> getKeywords(String listName) {
171 List<String> keywords;
173 keywords =
new ArrayList<>(topLevelMap.get(listName).keySet());
175 Collections.sort(keywords);
189 List<String> getKeywordInstances(String listName, String keyword) {
190 List<String> instances;
192 instances =
new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
194 Collections.sort(instances);
209 Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
211 return topLevelMap.get(listName).get(keyword).get(keywordInstance);
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<>());
227 instanceMap.computeIfAbsent(keywordInstance, ki ->
new HashSet<>()).add(artifactId);
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<>());
242 instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN ->
new HashSet<>()).add(artifactId);
252 void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
257 Map<String, Map<String, Map<String, Set<Long>>>> listsMap =
new LinkedHashMap<>();
260 Map<String, Map<String, Set<Long>>> literalMap =
new LinkedHashMap<>();
263 Map<String, Map<String, Set<Long>>> regexMap =
new LinkedHashMap<>();
266 topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
267 topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
269 for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
270 long id = art.getKey();
271 Map<Long, String> attributes = art.getValue();
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()));
279 if (listName != null) {
281 Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln ->
new LinkedHashMap<>());
283 if (
"1".equals(kwType) || reg == null) {
290 word = (reg != null) ? reg : word;
291 addNonRegExpMatchToList(listMap, word,
id);
293 addRegExpToList(listMap, reg, word,
id);
296 if (
"1".equals(kwType) || reg == null) {
303 word = (reg != null) ? reg : word;
304 addNonRegExpMatchToList(literalMap, word,
id);
306 addRegExpToList(regexMap, reg, word,
id);
310 topLevelMap.putAll(listsMap);
319 Map<Long, Map<Long, String>> artifactIds =
new LinkedHashMap<>();
321 if (skCase == null) {
327 queryStr +=
" AND blackboard_artifacts.data_source_obj_id = " +
datasourceObjId;
330 try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
331 ResultSet resultSet = dbQuery.getResultSet();
332 while (resultSet.next()) {
333 long artifactId = resultSet.getLong(
"artifact_id");
334 long typeId = resultSet.getLong(
"attribute_type_id");
335 String valueStr = resultSet.getString(
"value_text");
338 Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai ->
new LinkedHashMap<>());
339 if (StringUtils.isNotEmpty(valueStr)) {
340 attributesByTypeMap.put(typeId, valueStr);
343 Long valueLong = resultSet.getLong(
"value_int32");
344 attributesByTypeMap.put(typeId, valueLong.toString());
347 }
catch (TskCoreException | SQLException ex) {
348 logger.log(Level.WARNING,
"SQL Exception occurred: ", ex);
351 populateTreeMaps(artifactIds);
357 return visitor.
visit(
this);
364 super(Children.create(
new ListFactory(),
true), Lookups.singleton(KEYWORD_HITS));
366 super.setDisplayName(KEYWORD_HITS);
367 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
377 return visitor.
visit(
this);
381 @NbBundle.Messages({
"KeywordHits.createSheet.name.name=Name",
382 "KeywordHits.createSheet.name.displayName=Name",
383 "KeywordHits.createSheet.name.desc=no description"})
385 Sheet sheet = super.createSheet();
386 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
387 if (sheetSet == null) {
388 sheetSet = Sheet.createPropertiesSet();
393 KeywordHits_createSheet_name_name(),
394 KeywordHits_createSheet_name_displayName(),
395 KeywordHits_createSheet_name_desc(),
403 return getClass().getName();
411 keywordResults.addObserver(
this);
416 keywordResults.deleteObserver(
this);
420 public void update(Observable o, Object arg) {
430 private final PropertyChangeListener
pcl =
new PropertyChangeListener() {
432 public void propertyChange(PropertyChangeEvent evt) {
433 String eventType = evt.getPropertyName();
450 if (null != eventData && eventData.
getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
471 && evt.getNewValue() == null) {
497 super.removeNotify();
502 list.addAll(keywordResults.getListNames());
508 return new ListNode(key);
515 super(children, lookup);
524 return getClass().getName();
528 public void update(Observable o, Object arg) {
532 final void updateDisplayName() {
533 super.setDisplayName(getName() +
" (" + countTotalDescendants() +
")");
536 abstract int countTotalDescendants();
543 class ListNode
extends KWHitsNodeBase {
545 private final String listName;
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");
551 this.listName = listName;
553 keywordResults.addObserver(
this);
557 public int countTotalDescendants() {
558 int totalDescendants = 0;
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();
566 return totalDescendants;
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();
584 sheetSet.put(
new NodeProperty<>(
585 KeywordHits_createSheet_listName_name(),
586 KeywordHits_createSheet_listName_displayName(),
587 KeywordHits_createSheet_listName_desc(),
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()));
600 public boolean isLeafTypeNode() {
605 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
606 return visitor.visit(
this);
624 list.addAll(keywordResults.getKeywords(setName));
630 return new TermNode(setName, key);
637 class TermNode
extends KWHitsNodeBase {
639 private final String setName;
640 private final String keyword;
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");
649 keywordResults.addObserver(
this);
653 int countTotalDescendants() {
654 return keywordResults.getKeywordInstances(setName, keyword).stream()
655 .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
660 public boolean isLeafTypeNode() {
666 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
667 return visitor.visit(
this);
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();
681 sheetSet.put(
new NodeProperty<>(
682 KeywordHits_createSheet_listName_name(),
683 KeywordHits_createSheet_listName_displayName(),
684 KeywordHits_createSheet_listName_desc(),
687 sheetSet.put(
new NodeProperty<>(
688 KeywordHits_createSheet_filesWithHits_name(),
689 KeywordHits_createSheet_filesWithHits_displayName(),
690 KeywordHits_createSheet_filesWithHits_desc(),
691 countTotalDescendants()));
726 String getRegExpKey() {
748 List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
753 list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream()
754 .map(RegExpInstanceKey::new)
755 .collect(Collectors.toList()));
757 list.addAll(instances.stream()
758 .map(RegExpInstanceKey::new)
759 .collect(Collectors.toList()));
767 return new RegExpInstanceNode(setName, keyword, key.getRegExpKey());
779 class RegExpInstanceNode
extends KWHitsNodeBase {
781 private final String setName;
782 private final String keyword;
783 private final String instance;
785 private RegExpInstanceNode(String setName, String keyword, String instance) {
786 super(Children.create(
new HitsFactory(setName, keyword, instance),
true), Lookups.singleton(instance));
788 this.setName = setName;
789 this.keyword = keyword;
790 this.instance = instance;
791 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
793 keywordResults.addObserver(
this);
797 int countTotalDescendants() {
798 return keywordResults.getArtifactIds(setName, keyword, instance).size();
802 public boolean isLeafTypeNode() {
807 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
808 return visitor.visit(
this);
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();
820 sheetSet.put(
new NodeProperty<>(
821 KeywordHits_createSheet_listName_name(),
822 KeywordHits_createSheet_listName_displayName(),
823 KeywordHits_createSheet_listName_desc(),
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()));
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) {
859 BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId);
863 AbstractFile file = n.getLookup().lookup(AbstractFile.class);
866 file = skCase.getAbstractFileById(art.getObjectID());
867 }
catch (TskCoreException ex) {
868 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(),
897 }
catch (TskCoreException ex) {
898 logger.log(Level.WARNING,
"TSK Exception occurred", ex);
912 private HitsFactory(String setName, String keyword, String instance) {
921 list.addAll(keywordResults.getArtifactIds(setName, keyword, instance));
final PropertyChangeListener pcl
static boolean isOnlyDefaultInstance(List< String > instances)
static final String KEYWORD_HITS
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
KWHitsNodeBase(Children children, Lookup lookup)
Node createNodeForKey(RegExpInstanceKey key)
static String getStringTime(long epochSeconds, TimeZone tzone)
Node createNodeForKey(String key)
static synchronized IngestManager getInstance()
final KeywordResults keywordResults
void setName(String name)
static final String SIMPLE_REGEX_SEARCH
TermFactory(String setName)
BlackboardArtifactNode createBlackboardArtifactNode(Long artifactId)
final Map< String, Map< String, Map< String, Set< Long > > > > topLevelMap
T visit(DataSourcesNode in)
boolean createKeys(List< String > list)
void removeIngestJobEventListener(final PropertyChangeListener listener)
KWHitsNodeBase(Children children)
boolean createKeys(List< String > list)
void update(Observable o, Object arg)
boolean createKeys(List< RegExpInstanceKey > list)
RegExpInstancesFactory(String setName, String keyword)
void addIngestJobEventListener(final PropertyChangeListener listener)
static final String DEFAULT_INSTANCE_NAME
Node createNodeForKey(Long artifactId)
Node createNodeForKey(String key)
boolean createKeys(List< Long > list)
static Boolean getGroupItemsInTreeByDataSource()
static final Logger logger
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 datasourceObjId
HitsFactory(String setName, String keyword, String instance)
void addNodeProperty(NodeProperty<?> np)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
static final String SIMPLE_LITERAL_SEARCH