From 19212bcdf1e69faacb1783a3da83cdda94bf8fc8 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 16 Mar 2017 13:41:23 -0400 Subject: [PATCH 1/7] SearchRunner and KeywordSearchPanel now use same logic to select proper KeywordSearchQuery for Keyword --- .../KeywordSearchQueryDelegator.java | 17 +---------- .../keywordsearch/KeywordSearchUtil.java | 20 +++++++++++++ .../autopsy/keywordsearch/SearchRunner.java | 30 +++++++------------ 3 files changed, 32 insertions(+), 35 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryDelegator.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryDelegator.java index af5381ba4b..f2dfb53622 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryDelegator.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryDelegator.java @@ -55,22 +55,7 @@ class KeywordSearchQueryDelegator { for (KeywordList keywordList : keywordLists) { for (Keyword keyword : keywordList.getKeywords()) { - KeywordSearchQuery query; - if (keyword.searchTermIsLiteral()) { - // literal, exact match - if (keyword.searchTermIsWholeWord()) { - query = new LuceneQuery(keywordList, keyword); - query.escape(); - } // literal, substring match - else { - query = new TermsComponentQuery(keywordList, keyword); - query.escape(); - query.setSubstringQuery(); - } - } // regexp - else { - query = new RegexQuery(keywordList, keyword); - } + KeywordSearchQuery query = KeywordSearchUtil.getQueryForKeyword(keyword, keywordList); queryDelegates.add(query); } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java index bb2a7a4111..cb272811b1 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java @@ -126,6 +126,26 @@ class KeywordSearchUtil { return false; } } + + static KeywordSearchQuery getQueryForKeyword(Keyword keyword, KeywordList keywordList) { + KeywordSearchQuery query = null; + if (keyword.searchTermIsLiteral()) { + // literal, exact match + if (keyword.searchTermIsWholeWord()) { + query = new LuceneQuery(keywordList, keyword); + query.escape(); + } // literal, substring match + else { + query = new TermsComponentQuery(keywordList, keyword); + query.escape(); + query.setSubstringQuery(); + } + } // regexp + else { + query = new RegexQuery(keywordList, keyword); + } + return query; + } /** * Is the Keyword Search list at absPath an XML list? diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java index ce0d07bc78..560a90d0db 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java @@ -425,14 +425,14 @@ public final class SearchRunner { int keywordsSearched = 0; - for (Keyword keywordQuery : keywords) { + for (Keyword keyword : keywords) { if (this.isCancelled()) { - logger.log(Level.INFO, "Cancel detected, bailing before new keyword processed: {0}", keywordQuery.getSearchTerm()); //NON-NLS + logger.log(Level.INFO, "Cancel detected, bailing before new keyword processed: {0}", keyword.getSearchTerm()); //NON-NLS return null; } - final String queryStr = keywordQuery.getSearchTerm(); - final KeywordList list = keywordToList.get(queryStr); + final String queryStr = keyword.getSearchTerm(); + final KeywordList keywordList = keywordToList.get(queryStr); //new subProgress will be active after the initial query //when we know number of hits to start() with @@ -440,15 +440,7 @@ public final class SearchRunner { subProgresses[keywordsSearched - 1].finish(); } - KeywordSearchQuery keywordSearchQuery = null; - - boolean isRegex = !keywordQuery.searchTermIsLiteral(); - if (isRegex) { - keywordSearchQuery = new RegexQuery(list, keywordQuery); - } else { - keywordSearchQuery = new LuceneQuery(list, keywordQuery); - keywordSearchQuery.escape(); - } + KeywordSearchQuery keywordSearchQuery = KeywordSearchUtil.getQueryForKeyword(keyword, keywordList); // Filtering //limit search to currently ingested data sources @@ -462,14 +454,14 @@ public final class SearchRunner { try { queryResults = keywordSearchQuery.performQuery(); } catch (KeywordSearchModuleException | NoOpenCoreException ex) { - logger.log(Level.SEVERE, "Error performing query: " + keywordQuery.getSearchTerm(), ex); //NON-NLS - MessageNotifyUtil.Notify.error(Bundle.SearchRunner_query_exception_msg() + keywordQuery.getSearchTerm(), ex.getCause().getMessage()); + logger.log(Level.SEVERE, "Error performing query: " + keyword.getSearchTerm(), ex); //NON-NLS + MessageNotifyUtil.Notify.error(Bundle.SearchRunner_query_exception_msg() + keyword.getSearchTerm(), ex.getCause().getMessage()); //no reason to continue with next query if recovery failed //or wait for recovery to kick in and run again later //likely case has closed and threads are being interrupted return null; } catch (CancellationException e) { - logger.log(Level.INFO, "Cancel detected, bailing during keyword query: {0}", keywordQuery.getSearchTerm()); //NON-NLS + logger.log(Level.INFO, "Cancel detected, bailing during keyword query: {0}", keyword.getSearchTerm()); //NON-NLS return null; } @@ -487,14 +479,14 @@ public final class SearchRunner { int totalUnits = newResults.getKeywords().size(); subProgresses[keywordsSearched].start(totalUnits); int unitProgress = 0; - String queryDisplayStr = keywordQuery.getSearchTerm(); + String queryDisplayStr = keyword.getSearchTerm(); if (queryDisplayStr.length() > 50) { queryDisplayStr = queryDisplayStr.substring(0, 49) + "..."; } - subProgresses[keywordsSearched].progress(list.getName() + ": " + queryDisplayStr, unitProgress); + subProgresses[keywordsSearched].progress(keywordList.getName() + ": " + queryDisplayStr, unitProgress); // Create blackboard artifacts - newArtifacts = newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched], this, list.getIngestMessages()); + newArtifacts = newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages()); } //if has results From 54e2c86c1dc89f14f5689e568f2daeb2b9af5e1a Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Wed, 22 Mar 2017 15:35:51 -0400 Subject: [PATCH 2/7] debugging --- .../autopsy/keywordsearch/TermsComponentQuery.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java index 7ecfdbe35a..387281c584 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java @@ -281,6 +281,7 @@ final class TermsComponentQuery implements KeywordSearchQuery { * Do a term query for each term that matched the regex. */ QueryResults results = new QueryResults(this); + Set termHits = new HashSet<>(); // ELDEBUG for (Term term : terms) { /* * If searching for credit card account numbers, do a Luhn check on @@ -309,12 +310,15 @@ final class TermsComponentQuery implements KeywordSearchQuery { LuceneQuery termQuery = new LuceneQuery(keywordList, new Keyword(escapedTerm, true)); filters.forEach(termQuery::addFilter); // This appears to be unused QueryResults termQueryResult = termQuery.performQuery(); - Set termHits = new HashSet<>(); + // ELDEBUG Set termHits = new HashSet<>(); for (Keyword word : termQueryResult.getKeywords()) { termHits.addAll(termQueryResult.getResults(word)); } - results.addResult(new Keyword(term.getTerm(), false), new ArrayList<>(termHits)); + // ELDEBUG +// results.addResult(new Keyword(term.getTerm(), false), new ArrayList<>(termHits)); } + // ELDEBUG + results.addResult(new Keyword(originalKeyword.getSearchTerm(), false), new ArrayList<>(termHits)); return results; } @@ -347,6 +351,9 @@ final class TermsComponentQuery implements KeywordSearchQuery { if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, originalKeyword.getSearchTerm())); + + // add original substring search keyword + //attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); try { newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); From 89bcf3dcb0099f2b2b15ab106e41a6e4d329016e Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Wed, 22 Mar 2017 17:45:56 -0400 Subject: [PATCH 3/7] Changes how KWS list substring search results are displayed --- .../autopsy/datamodel/KeywordHits.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index c7b8983be0..dc3cdc115a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -139,11 +139,21 @@ public class KeywordHits implements AutopsyVisitableItem { } Map> listMap = listsMap.get(listName); - if (listMap.containsKey(word) == false) { - listMap.put(word, new HashSet()); - } + if (reg == null) { + // litteral search result, use keyword as display name + if (listMap.containsKey(word) == false) { + listMap.put(word, new HashSet()); + } - listMap.get(word).add(id); + listMap.get(word).add(id); + } else { + // regular expression or substring search result, use regular expression as display name. + // this way all hits for the regular expression or substring will be grouped together. + if (listMap.containsKey(reg) == false) { + listMap.put(reg, new HashSet()); + } + listMap.get(reg).add(id); + } } // regular expression, single term else if (reg != null) { if (regexMap.containsKey(reg) == false) { From c91ddf8650f1b4f80afe2431208b001dcce29797 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Wed, 22 Mar 2017 17:46:40 -0400 Subject: [PATCH 4/7] Changes to TermsComponentQuery --- .../autopsy/keywordsearch/TermsComponentQuery.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java index 387281c584..c1149eee87 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java @@ -281,7 +281,6 @@ final class TermsComponentQuery implements KeywordSearchQuery { * Do a term query for each term that matched the regex. */ QueryResults results = new QueryResults(this); - Set termHits = new HashSet<>(); // ELDEBUG for (Term term : terms) { /* * If searching for credit card account numbers, do a Luhn check on @@ -310,15 +309,12 @@ final class TermsComponentQuery implements KeywordSearchQuery { LuceneQuery termQuery = new LuceneQuery(keywordList, new Keyword(escapedTerm, true)); filters.forEach(termQuery::addFilter); // This appears to be unused QueryResults termQueryResult = termQuery.performQuery(); - // ELDEBUG Set termHits = new HashSet<>(); + Set termHits = new HashSet<>(); for (Keyword word : termQueryResult.getKeywords()) { termHits.addAll(termQueryResult.getResults(word)); } - // ELDEBUG -// results.addResult(new Keyword(term.getTerm(), false), new ArrayList<>(termHits)); + results.addResult(new Keyword(term.getTerm(), false), new ArrayList<>(termHits)); } - // ELDEBUG - results.addResult(new Keyword(originalKeyword.getSearchTerm(), false), new ArrayList<>(termHits)); return results; } @@ -352,8 +348,6 @@ final class TermsComponentQuery implements KeywordSearchQuery { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, originalKeyword.getSearchTerm())); - // add original substring search keyword - //attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); try { newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); From aa14a594e215a572325b79bf7a4558056fb884b3 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Thu, 23 Mar 2017 18:18:30 -0400 Subject: [PATCH 5/7] initial attempt at changing the keyword search tree to handle lists with substrings and regexps --- .../datamodel/DisplayableItemNodeVisitor.java | 7 + .../autopsy/datamodel/KeywordHits.java | 287 +++++++++++++++--- 2 files changed, 259 insertions(+), 35 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 2b9fb9f2b8..5af34027b4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -90,6 +90,8 @@ public interface DisplayableItemNodeVisitor { T visit(KeywordHits.ListNode khsn); T visit(KeywordHits.TermNode khmln); + + T visit(KeywordHits.InstanceNode khmln); T visit(HashsetHits.RootNode hhrn); @@ -280,6 +282,11 @@ public interface DisplayableItemNodeVisitor { public T visit(KeywordHits.ListNode khsn) { return defaultVisit(khsn); } + + @Override + public T visit(KeywordHits.InstanceNode khsn) { + return defaultVisit(khsn); + } @Override public T visit(KeywordHits.TermNode khmln) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index c7b8983be0..6159d3a26d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -64,7 +64,8 @@ public class KeywordHits implements AutopsyVisitableItem { public static final String SIMPLE_REGEX_SEARCH = NbBundle .getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text"); private final KeywordResults keywordResults; - + private final String DUMMY_INSTANCE = "DUMMY_EXACT_MATCH_INSTANCE"; + public KeywordHits(SleuthkitCase skCase) { this.skCase = skCase; keywordResults = new KeywordResults(); @@ -72,9 +73,9 @@ public class KeywordHits implements AutopsyVisitableItem { private final class KeywordResults extends Observable { - // Map from listName/Type to Map of keyword to set of artifact Ids + // Map from listName/Type to Map of keywords/regexp to Map of instance terms to Set of artifact Ids // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized - private final Map>> topLevelMap = new LinkedHashMap<>(); + private final Map>>> topLevelMap = new LinkedHashMap<>(); KeywordResults() { update(); @@ -98,26 +99,66 @@ public class KeywordHits implements AutopsyVisitableItem { Collections.sort(keywords); return keywords; } - - Set getArtifactIds(String listName, String keyword) { + + List getKeywordInstances(String listName, String keyword) { + List instances; synchronized (topLevelMap) { - return topLevelMap.get(listName).get(keyword); + instances = new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet()); + } + Collections.sort(instances); + return instances; + } + + Set getArtifactIds(String listName, String keyword, String keywordInstance) { + synchronized (topLevelMap) { + return topLevelMap.get(listName).get(keyword).get(keywordInstance); } } + void addRegExpToList(Map>> listMap, String regExp, String word, Long id) { + if (listMap.containsKey(regExp) == false) { + listMap.put(regExp, new LinkedHashMap<>()); + } + Map> instanceMap = listMap.get(regExp); + + // get or create keyword instances entry. + if (instanceMap.containsKey(word) == false) { + instanceMap.put(word, new HashSet<>()); + } + + // add this ID to the instance + instanceMap.get(word).add(id); + } + + void addExactMatchToList(Map>> listMap, String word, Long id) { + if (listMap.containsKey(word) == false) { + listMap.put(word, new LinkedHashMap<>()); + } + Map> instanceMap = listMap.get(word); + + // get or create keyword instances entry. + // for exact match, use a dummy instance + if (instanceMap.containsKey(DUMMY_INSTANCE) == false) { + instanceMap.put(DUMMY_INSTANCE, new HashSet<>()); + } + + // add this ID to the instance + instanceMap.get(DUMMY_INSTANCE).add(id); + } + // populate maps based on artifactIds void populateMaps(Map> artifactIds) { synchronized (topLevelMap) { topLevelMap.clear(); // map of list name to keword to artifact IDs - Map>> listsMap = new LinkedHashMap<>(); + Map>>> listsMap = new LinkedHashMap<>(); - // Map from from literal keyword to artifact IDs - Map> literalMap = new LinkedHashMap<>(); + // Map from from literal keyword to instances (which will be empty) to artifact IDs + Map>> literalMap = new LinkedHashMap<>(); - // Map from regex keyword artifact IDs - Map> regexMap = new LinkedHashMap<>(); + // Map from regex keyword artifact to instances to artifact IDs + Map>> regexMap = new LinkedHashMap<>(); // top-level nodes topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap); @@ -134,31 +175,26 @@ public class KeywordHits implements AutopsyVisitableItem { // part of a list if (listName != null) { + // get or create list entry if (listsMap.containsKey(listName) == false) { - listsMap.put(listName, new LinkedHashMap>()); + listsMap.put(listName, new LinkedHashMap<>()); } - - Map> listMap = listsMap.get(listName); - if (listMap.containsKey(word) == false) { - listMap.put(word, new HashSet()); + Map>> listMap = listsMap.get(listName); + + if (reg != null) { + addRegExpToList(listMap, reg, word, id); + } else { + addExactMatchToList(listMap, word, id); } - - listMap.get(word).add(id); } // regular expression, single term else if (reg != null) { - if (regexMap.containsKey(reg) == false) { - regexMap.put(reg, new HashSet()); - } - regexMap.get(reg).add(id); + addRegExpToList(regexMap, reg, word, id); } // literal, single term else { - if (literalMap.containsKey(word) == false) { - literalMap.put(word, new HashSet()); - } - literalMap.get(word).add(id); - } - topLevelMap.putAll(listsMap); + addExactMatchToList(literalMap, word, id); + } } + topLevelMap.putAll(listsMap); } setChanged(); @@ -360,8 +396,10 @@ public class KeywordHits implements AutopsyVisitableItem { private void updateDisplayName() { int totalDescendants = 0; for (String word : keywordResults.getKeywords(listName)) { - Set ids = keywordResults.getArtifactIds(listName, word); - totalDescendants += ids.size(); + for (String instance : keywordResults.getKeywordInstances(listName, word)) { + Set ids = keywordResults.getArtifactIds(listName, word, instance); + totalDescendants += ids.size(); + } } super.setDisplayName(listName + " (" + totalDescendants + ")"); } @@ -451,7 +489,7 @@ public class KeywordHits implements AutopsyVisitableItem { private final String keyword; public TermNode(String setName, String keyword) { - super(Children.create(new HitsFactory(setName, keyword), true), Lookups.singleton(keyword)); + super(Children.create(new InstancesFactory(setName, keyword), true), Lookups.singleton(keyword)); super.setName(keyword); this.setName = setName; this.keyword = keyword; @@ -461,7 +499,183 @@ public class KeywordHits implements AutopsyVisitableItem { } private void updateDisplayName() { - super.setDisplayName(keyword + " (" + keywordResults.getArtifactIds(setName, keyword).size() + ")"); + int totalDescendants = 0; + + for (String instance : keywordResults.getKeywordInstances(setName, keyword)) { + Set ids = keywordResults.getArtifactIds(setName, keyword, instance); + totalDescendants += ids.size(); + } + + super.setDisplayName(keyword + " (" + totalDescendants + ")"); + } + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(); + } + + @Override + public boolean isLeafTypeNode() { + List instances = keywordResults.getKeywordInstances(setName, keyword); + // is this an exact match + // TODO: Enable this again, maybe? + /* + if (instances.size() == 1 && instances.get(0).equals(DUMMY_INSTANCE)) { + return true; + } + else { + return false; + } +*/ + + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set ss = s.get(Sheet.PROPERTIES); + if (ss == null) { + ss = Sheet.createPropertiesSet(); + s.put(ss); + } + + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.name"), + NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.displayName"), + NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.desc"), + getDisplayName())); + + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.name"), + NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.displayName"), + NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.desc"), + keywordResults.getKeywordInstances(setName, keyword).size())); + + return s; + } + + @Override + public String getItemType() { + return getClass().getName(); + } + } + + public class InstancesFactory extends ChildFactory.Detachable implements Observer { + private final String keyword; + private final String setName; + + public InstancesFactory(String setName, String keyword) { + super(); + this.setName = setName; + this.keyword = keyword; + } + + @Override + protected void addNotify() { + keywordResults.addObserver(this); + } + + @Override + protected void removeNotify() { + keywordResults.deleteObserver(this); + } + + @Override + protected boolean createKeys(List list) { + list.addAll(keywordResults.getKeywordInstances(setName, keyword)); + return true; + } + + @Override + protected Node createNodeForKey(String key) { + // + if (key.equals(DUMMY_INSTANCE)) { + // COPY AND PASTED from below, make single method in future + if (skCase == null) { + return null; + } + + Set artifactIds = keywordResults.getArtifactIds(setName, keyword, keyword); + Long artifactId = (Long)artifactIds.toArray()[0]; + try { + BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId); + BlackboardArtifactNode n = new BlackboardArtifactNode(art); + AbstractFile file; + try { + file = skCase.getAbstractFileById(art.getObjectID()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren"); //NON-NLS + return n; + } + + // It is possible to get a keyword hit on artifacts generated + // for the underlying image in which case MAC times are not + // available/applicable/useful. + if (file == null) { + return n; + } + + n.addNodeProperty(new NodeProperty<>( + NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.modTime.name"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.modTime.displayName"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.modTime.desc"), + ContentUtils.getStringTime(file.getMtime(), file))); + n.addNodeProperty(new NodeProperty<>( + NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.accessTime.name"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.accessTime.displayName"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.accessTime.desc"), + ContentUtils.getStringTime(file.getAtime(), file))); + n.addNodeProperty(new NodeProperty<>( + NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.chgTime.name"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.chgTime.displayName"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.chgTime.desc"), + ContentUtils.getStringTime(file.getCtime(), file))); + return n; + } catch (TskException ex) { + logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS + } + return null; + } + return new InstanceNode(setName, keyword, key); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); + } + + } + + public class InstanceNode extends DisplayableItemNode implements Observer { + + private final String setName; + private final String keyword; + private final String instance; + + public InstanceNode(String setName, String keyword, String instance) { + super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(keyword)); + super.setName(keyword); + this.setName = setName; + this.keyword = keyword; + this.instance = instance; + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS + updateDisplayName(); + keywordResults.addObserver(this); + } + + private void updateDisplayName() { + int totalDescendants = keywordResults.getArtifactIds(setName, keyword, instance).size(); + super.setDisplayName(keyword + " (" + totalDescendants + ")"); } @Override @@ -496,7 +710,7 @@ public class KeywordHits implements AutopsyVisitableItem { ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.name"), NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.displayName"), NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.desc"), - keywordResults.getArtifactIds(setName, keyword).size())); + keywordResults.getKeywordInstances(setName, keyword).size())); return s; } @@ -507,15 +721,18 @@ public class KeywordHits implements AutopsyVisitableItem { } } + public class HitsFactory extends ChildFactory.Detachable implements Observer { private final String keyword; private final String setName; + private final String instance; - public HitsFactory(String setName, String keyword) { + public HitsFactory(String setName, String keyword, String instance) { super(); this.setName = setName; this.keyword = keyword; + this.instance = instance; } @Override @@ -530,7 +747,7 @@ public class KeywordHits implements AutopsyVisitableItem { @Override protected boolean createKeys(List list) { - list.addAll(keywordResults.getArtifactIds(setName, keyword)); + list.addAll(keywordResults.getArtifactIds(setName, keyword, instance)); return true; } From 7fd6536182ba70e0f3141153fd93f198eea90967 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Thu, 23 Mar 2017 22:17:10 -0400 Subject: [PATCH 6/7] Update to previous commit to now properly show files --- .../autopsy/datamodel/KeywordHits.java | 67 ++++++++++++++----- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index 6159d3a26d..049310723f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -518,17 +518,12 @@ public class KeywordHits implements AutopsyVisitableItem { public boolean isLeafTypeNode() { List instances = keywordResults.getKeywordInstances(setName, keyword); // is this an exact match - // TODO: Enable this again, maybe? - /* if (instances.size() == 1 && instances.get(0).equals(DUMMY_INSTANCE)) { return true; } else { return false; } -*/ - - return false; } @Override @@ -563,8 +558,35 @@ public class KeywordHits implements AutopsyVisitableItem { return getClass().getName(); } } + + // Allows us to pass in either longs or strings + // as they keys for different types of nodes at the + // same level. Probably a better way to do this, but + // it works. + class InstanceKey { + private boolean isRegExp; + private String strKey; + private Long longKey; + public InstanceKey(String key) { + isRegExp = true; + strKey = key; + } + public InstanceKey(Long key) { + isRegExp = false; + longKey = key; + } + boolean isRegExp() { + return isRegExp; + } + Long getIdKey() { + return longKey; + } + String getRegExpKey() { + return strKey; + } + } - public class InstancesFactory extends ChildFactory.Detachable implements Observer { + public class InstancesFactory extends ChildFactory.Detachable implements Observer { private final String keyword; private final String setName; @@ -585,22 +607,35 @@ public class KeywordHits implements AutopsyVisitableItem { } @Override - protected boolean createKeys(List list) { - list.addAll(keywordResults.getKeywordInstances(setName, keyword)); + protected boolean createKeys(List list) { + List instances = keywordResults.getKeywordInstances(setName, keyword); + // The keys are different depending on what we are displaying. + // regexp get another layer to show instances. + // Exact matches don't. + if ((instances.size() == 1) && (instances.get(0).equals(DUMMY_INSTANCE))) { + for (Long id : keywordResults.getArtifactIds(setName, keyword, DUMMY_INSTANCE) ) { + list.add(new InstanceKey(id)); + } + } else { + for (String instance : instances) { + list.add(new InstanceKey(instance)); + } + + } return true; } @Override - protected Node createNodeForKey(String key) { + protected Node createNodeForKey(InstanceKey key) { // - if (key.equals(DUMMY_INSTANCE)) { - // COPY AND PASTED from below, make single method in future + if (key.isRegExp() == false) { + // COPY AND PASTED from below, + // TODO make single method if (skCase == null) { return null; } - Set artifactIds = keywordResults.getArtifactIds(setName, keyword, keyword); - Long artifactId = (Long)artifactIds.toArray()[0]; + Long artifactId = key.getIdKey(); try { BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId); BlackboardArtifactNode n = new BlackboardArtifactNode(art); @@ -646,7 +681,7 @@ public class KeywordHits implements AutopsyVisitableItem { } return null; } - return new InstanceNode(setName, keyword, key); + return new InstanceNode(setName, keyword, key.getRegExpKey()); } @Override @@ -675,7 +710,7 @@ public class KeywordHits implements AutopsyVisitableItem { private void updateDisplayName() { int totalDescendants = keywordResults.getArtifactIds(setName, keyword, instance).size(); - super.setDisplayName(keyword + " (" + totalDescendants + ")"); + super.setDisplayName(instance + " (" + totalDescendants + ")"); } @Override @@ -753,6 +788,8 @@ public class KeywordHits implements AutopsyVisitableItem { @Override protected Node createNodeForKey(Long artifactId) { + // TODO Move to private method that can be used + // with copy and pasted code above if (skCase == null) { return null; } From cb111c22a304d8c0f3595aa6a0619a32e94ae654 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Fri, 24 Mar 2017 09:49:11 -0400 Subject: [PATCH 7/7] desired tree now works, except for the extra layer at the predefined regexps. --- .../datamodel/DisplayableItemNodeVisitor.java | 4 +- .../autopsy/datamodel/KeywordHits.java | 256 +++++++++--------- .../autopsy/keywordsearch/KeywordSearch.java | 3 +- 3 files changed, 137 insertions(+), 126 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 5af34027b4..972951ea99 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -91,7 +91,7 @@ public interface DisplayableItemNodeVisitor { T visit(KeywordHits.TermNode khmln); - T visit(KeywordHits.InstanceNode khmln); + T visit(KeywordHits.RegExpInstanceNode khmln); T visit(HashsetHits.RootNode hhrn); @@ -284,7 +284,7 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(KeywordHits.InstanceNode khsn) { + public T visit(KeywordHits.RegExpInstanceNode khsn) { return defaultVisit(khsn); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index 049310723f..72746663e7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -70,6 +70,12 @@ public class KeywordHits implements AutopsyVisitableItem { this.skCase = skCase; keywordResults = new KeywordResults(); } + + /* All of these maps and code assume the following: + * Regexps will have an 'instance' layer that shows the specific words that matched the regexp + * Exact match and substring will not have the instance layer and instead will have the specific hits + * below their term. + */ private final class KeywordResults extends Observable { @@ -172,7 +178,9 @@ public class KeywordHits implements AutopsyVisitableItem { String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID())); String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID())); String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID())); - + // new in 4.4 + String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID())); + // part of a list if (listName != null) { // get or create list entry @@ -181,14 +189,30 @@ public class KeywordHits implements AutopsyVisitableItem { } Map>> listMap = listsMap.get(listName); - if (reg != null) { + // substring, treated same as exact match + // Enum for "1" is defined in KeywordSearch.java + if ((kwType != null) && (kwType.equals("1"))) { + // original term should be stored in reg + if (reg != null) { + addExactMatchToList(listMap, reg, id); + } else { + addExactMatchToList(listMap, word, id); + } + } + else if (reg != null) { addRegExpToList(listMap, reg, word, id); } else { addExactMatchToList(listMap, word, id); } } // regular expression, single term else if (reg != null) { - addRegExpToList(regexMap, reg, word, id); + // substring is treated same as exact + if ((kwType != null) && (kwType.equals("1"))) { + // original term should be stored in reg + addExactMatchToList(literalMap, reg, id); + } else { + addRegExpToList(regexMap, reg, word, id); + } } // literal, single term else { addExactMatchToList(literalMap, word, id); @@ -203,35 +227,43 @@ public class KeywordHits implements AutopsyVisitableItem { @SuppressWarnings("deprecation") public void update() { + // maps Artifact ID to map of attribute types to attribute values Map> artifactIds = new LinkedHashMap<>(); if (skCase == null) { return; } + // query attributes table for the ones that we need for the tree int setId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); int wordId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID(); int regexId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID(); int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(); - String query = "SELECT blackboard_attributes.value_text,blackboard_attributes.artifact_id," //NON-NLS + String query = "SELECT blackboard_attributes.value_text,blackboard_attributes.value_int32," + + "blackboard_attributes.artifact_id," //NON-NLS + "blackboard_attributes.attribute_type_id FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + "(blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id AND " //NON-NLS + "blackboard_artifacts.artifact_type_id=" + artId //NON-NLS + ") AND (attribute_type_id=" + setId + " OR " //NON-NLS + "attribute_type_id=" + wordId + " OR " //NON-NLS + + "attribute_type_id=" + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID() + " OR " //NON-NLS + "attribute_type_id=" + regexId + ")"; //NON-NLS try (CaseDbQuery dbQuery = skCase.executeQuery(query)) { ResultSet resultSet = dbQuery.getResultSet(); while (resultSet.next()) { - String value = resultSet.getString("value_text"); //NON-NLS + String valueStr = resultSet.getString("value_text"); //NON-NLS long artifactId = resultSet.getLong("artifact_id"); //NON-NLS long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS if (!artifactIds.containsKey(artifactId)) { artifactIds.put(artifactId, new LinkedHashMap()); } - if (!value.equals("")) { - artifactIds.get(artifactId).put(typeId, value); + if (valueStr != null && !valueStr.equals("")) { + artifactIds.get(artifactId).put(typeId, valueStr); + } else { + // Keyword Search Type is an int + Long valueLong = resultSet.getLong("value_int32"); + artifactIds.get(artifactId).put(typeId, valueLong.toString()); } } } catch (TskCoreException | SQLException ex) { @@ -290,6 +322,9 @@ public class KeywordHits implements AutopsyVisitableItem { } } + /** + * Creates the list nodes + */ private class ListFactory extends ChildFactory.Detachable implements Observer { private final PropertyChangeListener pcl = new PropertyChangeListener() { @@ -380,6 +415,9 @@ public class KeywordHits implements AutopsyVisitableItem { } } + /** + * Represents the keyword search lists (or default groupings if list was not given) + */ public class ListNode extends DisplayableItemNode implements Observer { private final String listName; @@ -447,6 +485,9 @@ public class KeywordHits implements AutopsyVisitableItem { } } + /** + * Creates the nodes that represent search terms + */ private class TermFactory extends ChildFactory.Detachable implements Observer { private final String setName; @@ -483,13 +524,16 @@ public class KeywordHits implements AutopsyVisitableItem { } } + /** + * Represents the search term or regexp that user searched for + */ public class TermNode extends DisplayableItemNode implements Observer { private final String setName; private final String keyword; public TermNode(String setName, String keyword) { - super(Children.create(new InstancesFactory(setName, keyword), true), Lookups.singleton(keyword)); + super(Children.create(new RegExpInstancesFactory(setName, keyword), true), Lookups.singleton(keyword)); super.setName(keyword); this.setName = setName; this.keyword = keyword; @@ -563,15 +607,15 @@ public class KeywordHits implements AutopsyVisitableItem { // as they keys for different types of nodes at the // same level. Probably a better way to do this, but // it works. - class InstanceKey { - private boolean isRegExp; + class RegExpInstanceKey { + private final boolean isRegExp; private String strKey; private Long longKey; - public InstanceKey(String key) { + public RegExpInstanceKey(String key) { isRegExp = true; strKey = key; } - public InstanceKey(Long key) { + public RegExpInstanceKey(Long key) { isRegExp = false; longKey = key; } @@ -586,11 +630,14 @@ public class KeywordHits implements AutopsyVisitableItem { } } - public class InstancesFactory extends ChildFactory.Detachable implements Observer { + /** + * Creates the nodes for a given regexp that represent the specific terms that were found + */ + public class RegExpInstancesFactory extends ChildFactory.Detachable implements Observer { private final String keyword; private final String setName; - public InstancesFactory(String setName, String keyword) { + public RegExpInstancesFactory(String setName, String keyword) { super(); this.setName = setName; this.keyword = keyword; @@ -607,18 +654,18 @@ public class KeywordHits implements AutopsyVisitableItem { } @Override - protected boolean createKeys(List list) { + protected boolean createKeys(List list) { List instances = keywordResults.getKeywordInstances(setName, keyword); // The keys are different depending on what we are displaying. // regexp get another layer to show instances. // Exact matches don't. if ((instances.size() == 1) && (instances.get(0).equals(DUMMY_INSTANCE))) { for (Long id : keywordResults.getArtifactIds(setName, keyword, DUMMY_INSTANCE) ) { - list.add(new InstanceKey(id)); + list.add(new RegExpInstanceKey(id)); } } else { for (String instance : instances) { - list.add(new InstanceKey(instance)); + list.add(new RegExpInstanceKey(instance)); } } @@ -626,62 +673,13 @@ public class KeywordHits implements AutopsyVisitableItem { } @Override - protected Node createNodeForKey(InstanceKey key) { - // + protected Node createNodeForKey(RegExpInstanceKey key) { + // if it isn't not a regexp, then skip the 'instance' layer of the tree if (key.isRegExp() == false) { - // COPY AND PASTED from below, - // TODO make single method - if (skCase == null) { - return null; - } - - Long artifactId = key.getIdKey(); - try { - BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId); - BlackboardArtifactNode n = new BlackboardArtifactNode(art); - AbstractFile file; - try { - file = skCase.getAbstractFileById(art.getObjectID()); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren"); //NON-NLS - return n; - } - - // It is possible to get a keyword hit on artifacts generated - // for the underlying image in which case MAC times are not - // available/applicable/useful. - if (file == null) { - return n; - } - - n.addNodeProperty(new NodeProperty<>( - NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.modTime.name"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.modTime.displayName"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.modTime.desc"), - ContentUtils.getStringTime(file.getMtime(), file))); - n.addNodeProperty(new NodeProperty<>( - NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.accessTime.name"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.accessTime.displayName"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.accessTime.desc"), - ContentUtils.getStringTime(file.getAtime(), file))); - n.addNodeProperty(new NodeProperty<>( - NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.chgTime.name"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.chgTime.displayName"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.chgTime.desc"), - ContentUtils.getStringTime(file.getCtime(), file))); - return n; - } catch (TskException ex) { - logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS - } - return null; + return createBlackboardArtifactNode(key.getIdKey()); + } else { + return new RegExpInstanceNode(setName, keyword, key.getRegExpKey()); } - return new InstanceNode(setName, keyword, key.getRegExpKey()); } @Override @@ -691,13 +689,16 @@ public class KeywordHits implements AutopsyVisitableItem { } - public class InstanceNode extends DisplayableItemNode implements Observer { + /** + * Represents a specific term that was found from a regexp + */ + public class RegExpInstanceNode extends DisplayableItemNode implements Observer { private final String setName; private final String keyword; private final String instance; - public InstanceNode(String setName, String keyword, String instance) { + public RegExpInstanceNode(String setName, String keyword, String instance) { super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(keyword)); super.setName(keyword); this.setName = setName; @@ -756,7 +757,65 @@ public class KeywordHits implements AutopsyVisitableItem { } } + /** + * Create a blackboard node for the given Keyword Hit artifact + * @param artifactId + * @return Node or null on error + */ + private BlackboardArtifactNode createBlackboardArtifactNode (Long artifactId) { + if (skCase == null) { + return null; + } + + try { + BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId); + BlackboardArtifactNode n = new BlackboardArtifactNode(art); + AbstractFile file; + try { + file = skCase.getAbstractFileById(art.getObjectID()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren"); //NON-NLS + return n; + } + + // It is possible to get a keyword hit on artifacts generated + // for the underlying image in which case MAC times are not + // available/applicable/useful. + if (file == null) { + return n; + } + + n.addNodeProperty(new NodeProperty<>( + NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.modTime.name"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.modTime.displayName"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.modTime.desc"), + ContentUtils.getStringTime(file.getMtime(), file))); + n.addNodeProperty(new NodeProperty<>( + NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.accessTime.name"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.accessTime.displayName"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.accessTime.desc"), + ContentUtils.getStringTime(file.getAtime(), file))); + n.addNodeProperty(new NodeProperty<>( + NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.chgTime.name"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.chgTime.displayName"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.chgTime.desc"), + ContentUtils.getStringTime(file.getCtime(), file))); + return n; + } catch (TskException ex) { + logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS + } + return null; + } + /** + * Creates nodes for individual files that had hits + */ public class HitsFactory extends ChildFactory.Detachable implements Observer { private final String keyword; @@ -788,56 +847,7 @@ public class KeywordHits implements AutopsyVisitableItem { @Override protected Node createNodeForKey(Long artifactId) { - // TODO Move to private method that can be used - // with copy and pasted code above - if (skCase == null) { - return null; - } - - try { - BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId); - BlackboardArtifactNode n = new BlackboardArtifactNode(art); - AbstractFile file; - try { - file = skCase.getAbstractFileById(art.getObjectID()); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren"); //NON-NLS - return n; - } - - // It is possible to get a keyword hit on artifacts generated - // for the underlying image in which case MAC times are not - // available/applicable/useful. - if (file == null) { - return n; - } - - n.addNodeProperty(new NodeProperty<>( - NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.modTime.name"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.modTime.displayName"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.modTime.desc"), - ContentUtils.getStringTime(file.getMtime(), file))); - n.addNodeProperty(new NodeProperty<>( - NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.accessTime.name"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.accessTime.displayName"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.accessTime.desc"), - ContentUtils.getStringTime(file.getAtime(), file))); - n.addNodeProperty(new NodeProperty<>( - NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.chgTime.name"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.chgTime.displayName"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.chgTime.desc"), - ContentUtils.getStringTime(file.getCtime(), file))); - return n; - } catch (TskException ex) { - logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS - } - return null; + return createBlackboardArtifactNode(artifactId); } @Override diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearch.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearch.java index 65fc900569..b09ad5430e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearch.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearch.java @@ -42,8 +42,9 @@ public class KeywordSearch { private static final Logger TIKA_LOGGER = Logger.getLogger("Tika"); //NON-NLS private static final org.sleuthkit.autopsy.coreutils.Logger logger = org.sleuthkit.autopsy.coreutils.Logger.getLogger(Case.class.getName()); + // @@@ We should move this into TskData (or somewhere) because we are using + // this value in the results tree to display substring differently from regexp (KeywordHit.java) public enum QueryType { - LITERAL, SUBSTRING, REGEX }; public static final String NUM_FILES_CHANGE_EVT = "NUM_FILES_CHANGE_EVT"; //NON-NLS