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;
54 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
64 @NbBundle.Messages(
"KeywordHits.kwHits.text=Keyword Hits")
65 private static final String
KEYWORD_HITS = KeywordHits_kwHits_text();
66 @NbBundle.Messages(
"KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
68 @NbBundle.Messages(
"KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
71 public static final String
NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
87 +
"blackboard_attributes.value_int32, "
88 +
"blackboard_attributes.artifact_id, "
89 +
"blackboard_attributes.attribute_type_id "
90 +
"FROM blackboard_attributes, blackboard_artifacts "
91 +
"WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "
92 +
" AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
93 +
" AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
94 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()
95 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
96 +
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()
100 return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
118 private final Map<String, Map<String, Map<String, Set<Long>>>>
topLevelMap =
new LinkedHashMap<>();
129 List<String> getListNames() {
131 List<String> names =
new ArrayList<>(topLevelMap.keySet());
147 List<String> getKeywords(String listName) {
148 List<String> keywords;
150 keywords =
new ArrayList<>(topLevelMap.get(listName).keySet());
152 Collections.sort(keywords);
166 List<String> getKeywordInstances(String listName, String keyword) {
167 List<String> instances;
169 instances =
new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
171 Collections.sort(instances);
186 Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
188 return topLevelMap.get(listName).get(keyword).get(keywordInstance);
201 void addRegExpToList(Map<String, Map<String, Set<Long>>> listMap, String regExp, String keywordInstance, Long artifactId) {
202 Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(regExp, r ->
new LinkedHashMap<>());
204 instanceMap.computeIfAbsent(keywordInstance, ki ->
new HashSet<>()).add(artifactId);
215 void addNonRegExpMatchToList(Map<String, Map<String, Set<Long>>> listMap, String keyWord, Long artifactId) {
216 Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(keyWord, k ->
new LinkedHashMap<>());
219 instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN ->
new HashSet<>()).add(artifactId);
229 void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
234 Map<String, Map<String, Map<String, Set<Long>>>> listsMap =
new LinkedHashMap<>();
237 Map<String, Map<String, Set<Long>>> literalMap =
new LinkedHashMap<>();
240 Map<String, Map<String, Set<Long>>> regexMap =
new LinkedHashMap<>();
243 topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
244 topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
246 for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
247 long id = art.getKey();
248 Map<Long, String> attributes = art.getValue();
251 String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
252 String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
253 String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
254 String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()));
256 if (listName != null) {
258 Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln ->
new LinkedHashMap<>());
260 if (
"1".equals(kwType) || reg == null) {
267 word = (reg != null) ? reg : word;
268 addNonRegExpMatchToList(listMap, word,
id);
270 addRegExpToList(listMap, reg, word,
id);
273 if (
"1".equals(kwType) || reg == null) {
280 word = (reg != null) ? reg : word;
281 addNonRegExpMatchToList(literalMap, word,
id);
283 addRegExpToList(regexMap, reg, word,
id);
287 topLevelMap.putAll(listsMap);
296 Map<Long, Map<Long, String>> artifactIds =
new LinkedHashMap<>();
298 if (skCase == null) {
302 try (CaseDbQuery dbQuery = skCase.executeQuery(KEYWORD_HIT_ATTRIBUTES_QUERY)) {
303 ResultSet resultSet = dbQuery.getResultSet();
304 while (resultSet.next()) {
305 long artifactId = resultSet.getLong(
"artifact_id");
306 long typeId = resultSet.getLong(
"attribute_type_id");
307 String valueStr = resultSet.getString(
"value_text");
310 Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai ->
new LinkedHashMap<>());
311 if (StringUtils.isNotEmpty(valueStr)) {
312 attributesByTypeMap.put(typeId, valueStr);
315 Long valueLong = resultSet.getLong(
"value_int32");
316 attributesByTypeMap.put(typeId, valueLong.toString());
319 }
catch (TskCoreException | SQLException ex) {
320 logger.log(Level.WARNING,
"SQL Exception occurred: ", ex);
323 populateTreeMaps(artifactIds);
329 return v.
visit(
this);
336 super(Children.create(
new ListFactory(),
true), Lookups.singleton(KEYWORD_HITS));
338 super.setDisplayName(KEYWORD_HITS);
339 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
349 return v.
visit(
this);
353 @NbBundle.Messages({
"KeywordHits.createSheet.name.name=Name",
354 "KeywordHits.createSheet.name.displayName=Name",
355 "KeywordHits.createSheet.name.desc=no description"})
357 Sheet s = super.createSheet();
358 Sheet.Set ss = s.get(Sheet.PROPERTIES);
360 ss = Sheet.createPropertiesSet();
365 KeywordHits_createSheet_name_name(),
366 KeywordHits_createSheet_name_displayName(),
367 KeywordHits_createSheet_name_desc(),
375 return getClass().getName();
383 keywordResults.addObserver(
this);
388 keywordResults.deleteObserver(
this);
392 public void update(Observable o, Object arg) {
402 private final PropertyChangeListener
pcl =
new PropertyChangeListener() {
404 public void propertyChange(PropertyChangeEvent evt) {
405 String eventType = evt.getPropertyName();
422 if (null != eventData && eventData.
getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
425 }
catch (IllegalStateException notUsed) {
439 }
catch (IllegalStateException notUsed) {
443 && evt.getNewValue() == null) {
469 super.removeNotify();
474 list.addAll(keywordResults.getListNames());
480 return new ListNode(key);
487 super(children, lookup);
496 return getClass().getName();
500 public void update(Observable o, Object arg) {
504 final void updateDisplayName() {
505 super.setDisplayName(getName() +
" (" + countTotalDescendants() +
")");
508 abstract int countTotalDescendants();
515 class ListNode
extends KWHitsNodeBase {
517 private final String listName;
519 private ListNode(String listName) {
520 super(Children.create(
new TermFactory(listName),
true), Lookups.singleton(listName));
521 super.setName(listName);
522 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
523 this.listName = listName;
525 keywordResults.addObserver(
this);
529 public int countTotalDescendants() {
530 int totalDescendants = 0;
532 for (String word : keywordResults.getKeywords(listName)) {
533 for (String instance : keywordResults.getKeywordInstances(listName, word)) {
534 Set<Long> ids = keywordResults.getArtifactIds(listName, word, instance);
535 totalDescendants += ids.size();
538 return totalDescendants;
542 @NbBundle.Messages({
"KeywordHits.createSheet.listName.name=List Name",
543 "KeywordHits.createSheet.listName.displayName=List Name",
544 "KeywordHits.createSheet.listName.desc=no description",
545 "KeywordHits.createSheet.numChildren.name=Number of Children",
546 "KeywordHits.createSheet.numChildren.displayName=Number of Children",
547 "KeywordHits.createSheet.numChildren.desc=no description"})
548 protected Sheet createSheet() {
549 Sheet s = super.createSheet();
550 Sheet.Set ss = s.get(Sheet.PROPERTIES);
552 ss = Sheet.createPropertiesSet();
556 ss.put(
new NodeProperty<>(
557 KeywordHits_createSheet_listName_name(),
558 KeywordHits_createSheet_listName_displayName(),
559 KeywordHits_createSheet_listName_desc(),
562 ss.put(
new NodeProperty<>(
563 KeywordHits_createSheet_numChildren_name(),
564 KeywordHits_createSheet_numChildren_displayName(),
565 KeywordHits_createSheet_numChildren_desc(),
566 keywordResults.getKeywords(listName).size()));
572 public boolean isLeafTypeNode() {
577 public <T> T accept(DisplayableItemNodeVisitor<T> v) {
578 return v.visit(
this);
596 list.addAll(keywordResults.getKeywords(setName));
602 return new TermNode(setName, key);
609 class TermNode
extends KWHitsNodeBase {
611 private final String setName;
612 private final String keyword;
614 private TermNode(String setName, String keyword) {
615 super(Children.create(
new RegExpInstancesFactory(setName, keyword),
true), Lookups.singleton(keyword));
616 super.setName(keyword);
617 this.setName = setName;
618 this.keyword = keyword;
619 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
621 keywordResults.addObserver(
this);
625 int countTotalDescendants() {
626 return keywordResults.getKeywordInstances(setName, keyword).stream()
627 .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
632 public boolean isLeafTypeNode() {
638 public <T> T accept(DisplayableItemNodeVisitor<T> v) {
639 return v.visit(
this);
643 @NbBundle.Messages({
"KeywordHits.createSheet.filesWithHits.name=Files with Hits",
644 "KeywordHits.createSheet.filesWithHits.displayName=Files with Hits",
645 "KeywordHits.createSheet.filesWithHits.desc=no description"})
646 protected Sheet createSheet() {
647 Sheet s = super.createSheet();
648 Sheet.Set ss = s.get(Sheet.PROPERTIES);
650 ss = Sheet.createPropertiesSet();
653 ss.put(
new NodeProperty<>(
654 KeywordHits_createSheet_listName_name(),
655 KeywordHits_createSheet_listName_displayName(),
656 KeywordHits_createSheet_listName_desc(),
659 ss.put(
new NodeProperty<>(
660 KeywordHits_createSheet_filesWithHits_name(),
661 KeywordHits_createSheet_filesWithHits_displayName(),
662 KeywordHits_createSheet_filesWithHits_desc(),
663 countTotalDescendants()));
698 String getRegExpKey() {
720 List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
725 list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream()
726 .map(RegExpInstanceKey::new)
727 .collect(Collectors.toList()));
729 list.addAll(instances.stream()
730 .map(RegExpInstanceKey::new)
731 .collect(Collectors.toList()));
739 return new RegExpInstanceNode(setName, keyword, key.getRegExpKey());
751 class RegExpInstanceNode
extends KWHitsNodeBase {
753 private final String setName;
754 private final String keyword;
755 private final String instance;
757 private RegExpInstanceNode(String setName, String keyword, String instance) {
758 super(Children.create(
new HitsFactory(setName, keyword, instance),
true), Lookups.singleton(instance));
760 this.setName = setName;
761 this.keyword = keyword;
762 this.instance = instance;
763 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png");
765 keywordResults.addObserver(
this);
769 int countTotalDescendants() {
770 return keywordResults.getArtifactIds(setName, keyword, instance).size();
774 public boolean isLeafTypeNode() {
779 public <T> T accept(DisplayableItemNodeVisitor<T> v) {
780 return v.visit(
this);
784 protected Sheet createSheet() {
785 Sheet s = super.createSheet();
786 Sheet.Set ss = s.get(Sheet.PROPERTIES);
788 ss = Sheet.createPropertiesSet();
792 ss.put(
new NodeProperty<>(
793 KeywordHits_createSheet_listName_name(),
794 KeywordHits_createSheet_listName_displayName(),
795 KeywordHits_createSheet_listName_desc(),
798 ss.put(
new NodeProperty<>(
799 KeywordHits_createSheet_filesWithHits_name(),
800 KeywordHits_createSheet_filesWithHits_displayName(),
801 KeywordHits_createSheet_filesWithHits_desc(),
802 keywordResults.getArtifactIds(setName, keyword, instance).size()));
816 @NbBundle.Messages({
"KeywordHits.createNodeForKey.modTime.name=ModifiedTime",
817 "KeywordHits.createNodeForKey.modTime.displayName=Modified Time",
818 "KeywordHits.createNodeForKey.modTime.desc=Modified Time",
819 "KeywordHits.createNodeForKey.accessTime.name=AccessTime",
820 "KeywordHits.createNodeForKey.accessTime.displayName=Access Time",
821 "KeywordHits.createNodeForKey.accessTime.desc=Access Time",
822 "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
823 "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
824 "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
826 if (skCase == null) {
831 BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId);
835 file = skCase.getAbstractFileById(art.getObjectID());
836 }
catch (TskCoreException ex) {
837 logger.log(Level.SEVERE,
"TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex);
851 KeywordHits_createNodeForKey_modTime_name(),
852 KeywordHits_createNodeForKey_modTime_displayName(),
853 KeywordHits_createNodeForKey_modTime_desc(),
856 KeywordHits_createNodeForKey_accessTime_name(),
857 KeywordHits_createNodeForKey_accessTime_displayName(),
858 KeywordHits_createNodeForKey_accessTime_desc(),
861 KeywordHits_createNodeForKey_chgTime_name(),
862 KeywordHits_createNodeForKey_chgTime_displayName(),
863 KeywordHits_createNodeForKey_chgTime_desc(),
866 }
catch (TskCoreException ex) {
867 logger.log(Level.WARNING,
"TSK Exception occurred", ex);
881 private HitsFactory(String setName, String keyword, String instance) {
890 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)
static Case getCurrentCase()
synchronized static Logger getLogger(String name)
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