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.Observable;
33 import java.util.Observer;
35 import java.util.logging.Level;
36 import java.util.stream.Collectors;
37 import org.apache.commons.lang3.StringUtils;
38 import org.openide.nodes.ChildFactory;
39 import org.openide.nodes.Children;
40 import org.openide.nodes.Node;
41 import org.openide.nodes.Sheet;
42 import org.openide.util.Lookup;
43 import org.openide.util.NbBundle;
44 import org.openide.util.lookup.Lookups;
55 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
65 @NbBundle.Messages(
"KeywordHits.kwHits.text=Keyword Hits")
66 private static final String
KEYWORD_HITS = KeywordHits_kwHits_text();
67 @NbBundle.Messages(
"KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
69 @NbBundle.Messages(
"KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
72 public static final String
NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
88 +
"blackboard_attributes.value_int32, "
89 +
"blackboard_attributes.artifact_id, "
90 +
"blackboard_attributes.attribute_type_id "
91 +
"FROM blackboard_attributes, blackboard_artifacts "
92 +
"WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "
93 +
" AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
94 +
" AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
95 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()
96 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
97 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()
101 return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
119 private final Map<String, Map<String, Map<String, Set<Long>>>>
topLevelMap =
new LinkedHashMap<>();
130 List<String> getListNames() {
132 List<String> names =
new ArrayList<>(topLevelMap.keySet());
148 List<String> getKeywords(String listName) {
149 List<String> keywords;
151 keywords =
new ArrayList<>(topLevelMap.get(listName).keySet());
153 Collections.sort(keywords);
167 List<String> getKeywordInstances(String listName, String keyword) {
168 List<String> instances;
170 instances =
new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
172 Collections.sort(instances);
187 Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
189 return topLevelMap.get(listName).get(keyword).get(keywordInstance);
202 void addRegExpToList(Map<String, Map<String, Set<Long>>> listMap, String regExp, String keywordInstance, Long artifactId) {
203 Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(regExp, r ->
new LinkedHashMap<>());
205 instanceMap.computeIfAbsent(keywordInstance, ki ->
new HashSet<>()).add(artifactId);
216 void addNonRegExpMatchToList(Map<String, Map<String, Set<Long>>> listMap, String keyWord, Long artifactId) {
217 Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(keyWord, k ->
new LinkedHashMap<>());
220 instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN ->
new HashSet<>()).add(artifactId);
230 void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
235 Map<String, Map<String, Map<String, Set<Long>>>> listsMap =
new LinkedHashMap<>();
238 Map<String, Map<String, Set<Long>>> literalMap =
new LinkedHashMap<>();
241 Map<String, Map<String, Set<Long>>> regexMap =
new LinkedHashMap<>();
244 topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
245 topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
247 for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
248 long id = art.getKey();
249 Map<Long, String> attributes = art.getValue();
252 String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
253 String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
254 String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
255 String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()));
257 if (listName != null) {
259 Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln ->
new LinkedHashMap<>());
261 if (
"1".equals(kwType) || reg == null) {
268 word = (reg != null) ? reg : word;
269 addNonRegExpMatchToList(listMap, word,
id);
271 addRegExpToList(listMap, reg, word,
id);
274 if (
"1".equals(kwType) || reg == null) {
281 word = (reg != null) ? reg : word;
282 addNonRegExpMatchToList(literalMap, word,
id);
284 addRegExpToList(regexMap, reg, word,
id);
288 topLevelMap.putAll(listsMap);
297 Map<Long, Map<Long, String>> artifactIds =
new LinkedHashMap<>();
299 if (skCase == null) {
303 try (CaseDbQuery dbQuery = skCase.executeQuery(KEYWORD_HIT_ATTRIBUTES_QUERY)) {
304 ResultSet resultSet = dbQuery.getResultSet();
305 while (resultSet.next()) {
306 long artifactId = resultSet.getLong(
"artifact_id");
307 long typeId = resultSet.getLong(
"attribute_type_id");
308 String valueStr = resultSet.getString(
"value_text");
311 Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai ->
new LinkedHashMap<>());
312 if (StringUtils.isNotEmpty(valueStr)) {
313 attributesByTypeMap.put(typeId, valueStr);
316 Long valueLong = resultSet.getLong(
"value_int32");
317 attributesByTypeMap.put(typeId, valueLong.toString());
320 }
catch (TskCoreException | SQLException ex) {
321 logger.log(Level.WARNING,
"SQL Exception occurred: ", ex);
324 populateTreeMaps(artifactIds);
330 return visitor.
visit(
this);
337 super(Children.create(
new ListFactory(),
true), Lookups.singleton(KEYWORD_HITS));
339 super.setDisplayName(KEYWORD_HITS);
340 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
350 return visitor.
visit(
this);
354 @NbBundle.Messages({
"KeywordHits.createSheet.name.name=Name",
355 "KeywordHits.createSheet.name.displayName=Name",
356 "KeywordHits.createSheet.name.desc=no description"})
358 Sheet sheet = super.createSheet();
359 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
360 if (sheetSet == null) {
361 sheetSet = Sheet.createPropertiesSet();
366 KeywordHits_createSheet_name_name(),
367 KeywordHits_createSheet_name_displayName(),
368 KeywordHits_createSheet_name_desc(),
376 return getClass().getName();
384 keywordResults.addObserver(
this);
389 keywordResults.deleteObserver(
this);
393 public void update(Observable o, Object arg) {
403 private final PropertyChangeListener
pcl =
new PropertyChangeListener() {
405 public void propertyChange(PropertyChangeEvent evt) {
406 String eventType = evt.getPropertyName();
423 if (null != eventData && eventData.
getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
444 && evt.getNewValue() == null) {
470 super.removeNotify();
475 list.addAll(keywordResults.getListNames());
481 return new ListNode(key);
488 super(children, lookup);
497 return getClass().getName();
501 public void update(Observable o, Object arg) {
505 final void updateDisplayName() {
506 super.setDisplayName(getName() +
" (" + countTotalDescendants() +
")");
509 abstract int countTotalDescendants();
516 class ListNode
extends KWHitsNodeBase {
518 private final String listName;
520 private ListNode(String listName) {
521 super(Children.create(
new TermFactory(listName),
true), Lookups.singleton(listName));
522 super.setName(listName);
523 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
524 this.listName = listName;
526 keywordResults.addObserver(
this);
530 public int countTotalDescendants() {
531 int totalDescendants = 0;
533 for (String word : keywordResults.getKeywords(listName)) {
534 for (String instance : keywordResults.getKeywordInstances(listName, word)) {
535 Set<Long> ids = keywordResults.getArtifactIds(listName, word, instance);
536 totalDescendants += ids.size();
539 return totalDescendants;
543 @NbBundle.Messages({
"KeywordHits.createSheet.listName.name=List Name",
544 "KeywordHits.createSheet.listName.displayName=List Name",
545 "KeywordHits.createSheet.listName.desc=no description",
546 "KeywordHits.createSheet.numChildren.name=Number of Children",
547 "KeywordHits.createSheet.numChildren.displayName=Number of Children",
548 "KeywordHits.createSheet.numChildren.desc=no description"})
549 protected Sheet createSheet() {
550 Sheet sheet = super.createSheet();
551 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
552 if (sheetSet == null) {
553 sheetSet = Sheet.createPropertiesSet();
557 sheetSet.put(
new NodeProperty<>(
558 KeywordHits_createSheet_listName_name(),
559 KeywordHits_createSheet_listName_displayName(),
560 KeywordHits_createSheet_listName_desc(),
563 sheetSet.put(
new NodeProperty<>(
564 KeywordHits_createSheet_numChildren_name(),
565 KeywordHits_createSheet_numChildren_displayName(),
566 KeywordHits_createSheet_numChildren_desc(),
567 keywordResults.getKeywords(listName).size()));
573 public boolean isLeafTypeNode() {
578 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
579 return visitor.visit(
this);
597 list.addAll(keywordResults.getKeywords(setName));
603 return new TermNode(setName, key);
610 class TermNode
extends KWHitsNodeBase {
612 private final String setName;
613 private final String keyword;
615 private TermNode(String setName, String keyword) {
616 super(Children.create(
new RegExpInstancesFactory(setName, keyword),
true), Lookups.singleton(keyword));
617 super.setName(keyword);
618 this.setName = setName;
619 this.keyword = keyword;
620 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
622 keywordResults.addObserver(
this);
626 int countTotalDescendants() {
627 return keywordResults.getKeywordInstances(setName, keyword).stream()
628 .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
633 public boolean isLeafTypeNode() {
639 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
640 return visitor.visit(
this);
644 @NbBundle.Messages({
"KeywordHits.createSheet.filesWithHits.name=Files with Hits",
645 "KeywordHits.createSheet.filesWithHits.displayName=Files with Hits",
646 "KeywordHits.createSheet.filesWithHits.desc=no description"})
647 protected Sheet createSheet() {
648 Sheet sheet = super.createSheet();
649 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
650 if (sheetSet == null) {
651 sheetSet = Sheet.createPropertiesSet();
654 sheetSet.put(
new NodeProperty<>(
655 KeywordHits_createSheet_listName_name(),
656 KeywordHits_createSheet_listName_displayName(),
657 KeywordHits_createSheet_listName_desc(),
660 sheetSet.put(
new NodeProperty<>(
661 KeywordHits_createSheet_filesWithHits_name(),
662 KeywordHits_createSheet_filesWithHits_displayName(),
663 KeywordHits_createSheet_filesWithHits_desc(),
664 countTotalDescendants()));
699 String getRegExpKey() {
721 List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
726 list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream()
727 .map(RegExpInstanceKey::new)
728 .collect(Collectors.toList()));
730 list.addAll(instances.stream()
731 .map(RegExpInstanceKey::new)
732 .collect(Collectors.toList()));
740 return new RegExpInstanceNode(setName, keyword, key.getRegExpKey());
752 class RegExpInstanceNode
extends KWHitsNodeBase {
754 private final String setName;
755 private final String keyword;
756 private final String instance;
758 private RegExpInstanceNode(String setName, String keyword, String instance) {
759 super(Children.create(
new HitsFactory(setName, keyword, instance),
true), Lookups.singleton(instance));
761 this.setName = setName;
762 this.keyword = keyword;
763 this.instance = instance;
764 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
766 keywordResults.addObserver(
this);
770 int countTotalDescendants() {
771 return keywordResults.getArtifactIds(setName, keyword, instance).size();
775 public boolean isLeafTypeNode() {
780 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
781 return visitor.visit(
this);
785 protected Sheet createSheet() {
786 Sheet sheet = super.createSheet();
787 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
788 if (sheetSet == null) {
789 sheetSet = Sheet.createPropertiesSet();
793 sheetSet.put(
new NodeProperty<>(
794 KeywordHits_createSheet_listName_name(),
795 KeywordHits_createSheet_listName_displayName(),
796 KeywordHits_createSheet_listName_desc(),
799 sheetSet.put(
new NodeProperty<>(
800 KeywordHits_createSheet_filesWithHits_name(),
801 KeywordHits_createSheet_filesWithHits_displayName(),
802 KeywordHits_createSheet_filesWithHits_desc(),
803 keywordResults.getArtifactIds(setName, keyword, instance).size()));
817 @NbBundle.Messages({
"KeywordHits.createNodeForKey.modTime.name=ModifiedTime",
818 "KeywordHits.createNodeForKey.modTime.displayName=Modified Time",
819 "KeywordHits.createNodeForKey.modTime.desc=Modified Time",
820 "KeywordHits.createNodeForKey.accessTime.name=AccessTime",
821 "KeywordHits.createNodeForKey.accessTime.displayName=Access Time",
822 "KeywordHits.createNodeForKey.accessTime.desc=Access Time",
823 "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
824 "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
825 "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
827 if (skCase == null) {
832 BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId);
836 file = skCase.getAbstractFileById(art.getObjectID());
837 }
catch (TskCoreException ex) {
838 logger.log(Level.SEVERE,
"TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex);
852 KeywordHits_createNodeForKey_modTime_name(),
853 KeywordHits_createNodeForKey_modTime_displayName(),
854 KeywordHits_createNodeForKey_modTime_desc(),
857 KeywordHits_createNodeForKey_accessTime_name(),
858 KeywordHits_createNodeForKey_accessTime_displayName(),
859 KeywordHits_createNodeForKey_accessTime_desc(),
862 KeywordHits_createNodeForKey_chgTime_name(),
863 KeywordHits_createNodeForKey_chgTime_displayName(),
864 KeywordHits_createNodeForKey_chgTime_desc(),
867 }
catch (TskCoreException ex) {
868 logger.log(Level.WARNING,
"TSK Exception occurred", ex);
882 private HitsFactory(String setName, String keyword, String instance) {
891 list.addAll(keywordResults.getArtifactIds(setName, keyword, instance));
final PropertyChangeListener pcl
KeywordHits(SleuthkitCase skCase)
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 final Logger logger
static final String KEYWORD_HIT_ATTRIBUTES_QUERY
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
void update(Observable o, Object arg)
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