From b3e5ecb0904b3f212d5077bff47967c330c9ce9b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 11 Aug 2017 10:08:01 -0400 Subject: [PATCH 01/29] make hitsMultiMap local to performQuery() and remove unused getDocumentIds method. --- .../autopsy/keywordsearch/RegexQuery.java | 53 +++++-------------- 1 file changed, 14 insertions(+), 39 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index 45205b0ca7..ec4ee957ac 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -24,10 +24,8 @@ import com.google.common.collect.ListMultimap; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -52,11 +50,8 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; -import org.sleuthkit.datamodel.TskException; /** * The RegexQuery class supports issuing regular expression queries against a @@ -87,8 +82,6 @@ final class RegexQuery implements KeywordSearchQuery { private final int MIN_EMAIL_ADDR_LENGTH = 8; - private final ListMultimap hitsMultiMap = ArrayListMultimap.create(); - // Lucene regular expressions do not support the following Java predefined // and POSIX character classes. There are other valid Java character classes // that are not supported by Lucene but we do not check for all of them. @@ -191,9 +184,11 @@ final class RegexQuery implements KeywordSearchQuery { solrQuery.setSort(SortClause.asc(Server.Schema.ID.toString())); String cursorMark = CursorMarkParams.CURSOR_MARK_START; - SolrDocumentList resultList ; + SolrDocumentList resultList; boolean allResultsProcessed = false; + final ListMultimap hitsMultiMap = ArrayListMultimap.create(); + while (!allResultsProcessed) { try { solrQuery.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark); @@ -206,7 +201,7 @@ final class RegexQuery implements KeywordSearchQuery { for (KeywordHit hit : keywordHits) { hitsMultiMap.put(new Keyword(hit.getHit(), true, true, originalKeyword.getListName(), originalKeyword.getOriginalTerm()), hit); } - } catch (TskCoreException ex) { + } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS } } @@ -287,8 +282,8 @@ final class RegexQuery implements KeywordSearchQuery { } /* - * If searching for credit card account numbers, do a Luhn check - * on the term and discard it if it does not pass. + * If searching for credit card account numbers, do a Luhn + * check on the term and discard it if it does not pass. */ if (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { Matcher ccnMatcher = CREDIT_CARD_NUM_PATTERN.matcher(hit); @@ -319,10 +314,14 @@ final class RegexQuery implements KeywordSearchQuery { } catch (TskCoreException ex) { throw ex; } catch (Throwable error) { - /* NOTE: Matcher.find() is known to throw StackOverflowError in rare cases (see JIRA-2700). - StackOverflowError is an error, not an exception, and therefore needs to be caught - as a Throwable. When this occurs we should re-throw the error as TskCoreException so that it is - logged by the calling method and move on to the next Solr document. */ + /* + * NOTE: Matcher.find() is known to throw StackOverflowError in rare + * cases (see JIRA-2700). StackOverflowError is an error, not an + * exception, and therefore needs to be caught as a Throwable. When + * this occurs we should re-throw the error as TskCoreException so + * that it is logged by the calling method and move on to the next + * Solr document. + */ throw new TskCoreException("Failed to create keyword hits for Solr document id " + docId + " due to " + error.getMessage()); } return hits; @@ -373,30 +372,6 @@ final class RegexQuery implements KeywordSearchQuery { return escapedQuery; } - /** - * Get a unique, comma separated list of document ids that match the given - * hit for the same object. - * - * @param keyword The keyword object that resulted in one or more hits. - * @param hit The specific hit for which we want to identify all other - * chunks that match the keyword - * - * @return A comma separated list of unique document ids. - */ - private String getDocumentIds(Keyword keyword, KeywordHit hit) { - Set documentIds = new HashSet<>(); - - for (KeywordHit h : hitsMultiMap.get(keyword)) { - // Add the document id only if it is for the same object as the - // given hit and we haven't already seen it. - if (h.getSolrObjectId() == hit.getSolrObjectId() && !documentIds.contains(h.getSolrDocumentId())) { - documentIds.add(h.getSolrDocumentId()); - } - } - - return StringUtils.join(documentIds, ","); - } - /** * Converts the keyword hits for a given search term into artifacts. * From 15eb3c28ba70e9499187c636e3e90d2365f55aa7 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 14 Aug 2017 13:25:59 -0400 Subject: [PATCH 02/29] remove temporary hitsMultiMap altogether --- .../autopsy/keywordsearch/RegexQuery.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index ec4ee957ac..dca12ed5b4 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -19,8 +19,6 @@ package org.sleuthkit.autopsy.keywordsearch; import com.google.common.base.CharMatcher; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ListMultimap; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -186,8 +184,7 @@ final class RegexQuery implements KeywordSearchQuery { String cursorMark = CursorMarkParams.CURSOR_MARK_START; SolrDocumentList resultList; boolean allResultsProcessed = false; - - final ListMultimap hitsMultiMap = ArrayListMultimap.create(); + QueryResults results = new QueryResults(this); while (!allResultsProcessed) { try { @@ -199,7 +196,13 @@ final class RegexQuery implements KeywordSearchQuery { try { List keywordHits = createKeywordHits(resultDoc); for (KeywordHit hit : keywordHits) { - hitsMultiMap.put(new Keyword(hit.getHit(), true, true, originalKeyword.getListName(), originalKeyword.getOriginalTerm()), hit); + Keyword keywordInstance = new Keyword(hit.getHit(), true, true, originalKeyword.getListName(), originalKeyword.getOriginalTerm()); + List hitsForKeyword = results.getResults(keywordInstance); + if (hitsForKeyword == null) { + hitsForKeyword = new ArrayList<>(); + results.addResult(keywordInstance, hitsForKeyword); + } + hitsForKeyword.add(hit); } } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS @@ -216,10 +219,7 @@ final class RegexQuery implements KeywordSearchQuery { MessageNotifyUtil.Notify.error(NbBundle.getMessage(Server.class, "Server.query.exception.msg", keywordString), ex.getCause().getMessage()); } } - QueryResults results = new QueryResults(this); - for (Keyword k : hitsMultiMap.keySet()) { - results.addResult(k, hitsMultiMap.get(k)); - } + return results; } From 27afb595f23fc12f1aae3370fcbc11498640c4d1 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 14 Aug 2017 14:49:45 -0400 Subject: [PATCH 03/29] Use new Java garbage collector with string deduplication. --- build-windows-installer.xml | 2 +- build.xml | 2 +- nbproject/project.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build-windows-installer.xml b/build-windows-installer.xml index 1e608b1326..e3f0401632 100644 --- a/build-windows-installer.xml +++ b/build-windows-installer.xml @@ -170,7 +170,7 @@ - + diff --git a/build.xml b/build.xml index b0d244b119..685f285fda 100755 --- a/build.xml +++ b/build.xml @@ -92,7 +92,7 @@ - + diff --git a/nbproject/project.properties b/nbproject/project.properties index 03d618afe6..50957852e4 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -17,7 +17,7 @@ update_versions=false #custom JVM options #Note: can be higher on 64 bit systems, should be in sync with build.xml # for Japanese version add: -J-Duser.language=ja -run.args.extra=-J-Xms24m -J-XX:MaxPermSize=128M -J-Xverify:none +run.args.extra=-J-Xms24m -J-XX:MaxPermSize=128M -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication auxiliary.org-netbeans-modules-apisupport-installer.license-type=apache.v2 auxiliary.org-netbeans-modules-apisupport-installer.os-linux=false auxiliary.org-netbeans-modules-apisupport-installer.os-macosx=false From c7f8c32ca3fef23c6508ca22ed14a7f470323f50 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 14 Aug 2017 15:27:55 -0400 Subject: [PATCH 04/29] first pass at removing stored content in KeywordHit --- .../keywordsearch/KeywordCachedArtifact.java | 6 +- .../autopsy/keywordsearch/KeywordHit.java | 20 +++-- .../keywordsearch/KeywordSearchQuery.java | 4 +- .../KeywordSearchResultFactory.java | 30 +++++--- .../autopsy/keywordsearch/LuceneQuery.java | 6 +- .../autopsy/keywordsearch/QueryResults.java | 25 +++++-- .../autopsy/keywordsearch/RegexQuery.java | 73 +++++++------------ .../keywordsearch/TermsComponentQuery.java | 16 ++-- 8 files changed, 91 insertions(+), 89 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordCachedArtifact.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordCachedArtifact.java index 68f85dd29a..986a974caa 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordCachedArtifact.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordCachedArtifact.java @@ -31,12 +31,12 @@ import org.sleuthkit.datamodel.BlackboardAttribute; */ class KeywordCachedArtifact { - private BlackboardArtifact artifact; - private Map attributes; + private final BlackboardArtifact artifact; + private final Map attributes; KeywordCachedArtifact(BlackboardArtifact artifact) { this.artifact = artifact; - attributes = new HashMap(); + attributes = new HashMap<>(); } BlackboardArtifact getArtifact() { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java index f86b86b485..3a51c38488 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.keywordsearch; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -36,7 +35,7 @@ class KeywordHit implements Comparable { private final long solrObjectId; private final int chunkId; private final String snippet; - private final Content content; + private final long contentID; private final BlackboardArtifact artifact; private final String hit; @@ -73,20 +72,19 @@ class KeywordHit implements Comparable { } /** - * Look up the file associated with the keyword hit. If the high order - * bit of the object id is set, the hit was for an artifact. In this - * case, look up the artifact as well. + * Look up the artifact associated with the keyword hit if there is onw. + * If the high order bit of the object id is set, the hit was for an + * artifact. */ SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); - long fileId; + if (this.solrObjectId < 0) { this.artifact = caseDb.getBlackboardArtifact(this.solrObjectId); - fileId = artifact.getObjectID(); + contentID = artifact.getObjectID(); } else { this.artifact = null; - fileId = this.solrObjectId; + contentID = this.solrObjectId; } - this.content = caseDb.getContentById(fileId); /** * Store the text snippet. @@ -119,8 +117,8 @@ class KeywordHit implements Comparable { return this.snippet; } - Content getContent() { - return this.content; + long getContentID() { + return this.contentID; } /** diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java index cafd45b3e8..3ca2ab048a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.keywordsearch; +import org.sleuthkit.datamodel.Content; + /** * Interface for kewyord search queries. */ @@ -97,6 +99,6 @@ interface KeywordSearchQuery { */ String getEscapedQueryString(); - KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword keyword, KeywordHit hit, String snippet, String listName); + KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword keyword, KeywordHit hit, String snippet, String listName); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java index b82e7778e1..65f83551b0 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java @@ -18,13 +18,10 @@ */ package org.sleuthkit.autopsy.keywordsearch; -import com.google.common.collect.SetMultimap; -import com.google.common.collect.TreeMultimap; import java.awt.EventQueue; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -39,8 +36,10 @@ import org.netbeans.api.progress.ProgressHandle; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; @@ -56,6 +55,8 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEY import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; /** * Node factory that performs the keyword search and creates children nodes for @@ -69,16 +70,16 @@ class KeywordSearchResultFactory extends ChildFactory { private static final Logger logger = Logger.getLogger(KeywordSearchResultFactory.class.getName()); //common properties (superset of all Node properties) to be displayed as columns - static final List COMMON_PROPERTIES - = Stream.concat( + static final List COMMON_PROPERTIES = + Stream.concat( Stream.of( TSK_KEYWORD, TSK_KEYWORD_REGEXP, TSK_KEYWORD_PREVIEW) - .map(BlackboardAttribute.ATTRIBUTE_TYPE::getDisplayName), + .map(BlackboardAttribute.ATTRIBUTE_TYPE::getDisplayName), Arrays.stream(AbstractAbstractFileNode.AbstractFilePropertyType.values()) - .map(Object::toString)) - .collect(Collectors.toList()); + .map(Object::toString)) + .collect(Collectors.toList()); private final Collection queryRequests; @@ -144,6 +145,11 @@ class KeywordSearchResultFactory extends ChildFactory { MessageNotifyUtil.Notify.error(Bundle.KeywordSearchResultFactory_query_exception_msg() + queryRequest.getQueryString(), ex.getCause().getMessage()); return false; } + SleuthkitCase tskCase = null; + try { + tskCase = Case.getCurrentCase().getSleuthkitCase(); + } catch (IllegalStateException ex) { + } int hitNumber = 0; List tempList = new ArrayList<>(); @@ -153,7 +159,12 @@ class KeywordSearchResultFactory extends ChildFactory { * Get file properties. */ Map properties = new LinkedHashMap<>(); - Content content = hit.getContent(); + Content content = null; + try { + content = tskCase.getContentById(hit.getContentID()); + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + } String contentName = content.getName(); if (content instanceof AbstractFile) { AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content); @@ -244,6 +255,7 @@ class KeywordSearchResultFactory extends ChildFactory { * @param map Contains content metadata, snippets, etc. (property * map) * @param id User incremented ID + * @param solrObjectId * @param content File that had the hit. * @param query Query used in search * @param hits Full set of search results (for all files! @@@) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index 9f8686898f..388b471fcd 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.keywordsearch; import java.util.ArrayList; import java.util.Collection; -import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -40,6 +39,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; @@ -192,14 +192,14 @@ class LuceneQuery implements KeywordSearchQuery { } @Override - public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); Collection attributes = new ArrayList<>(); BlackboardArtifact bba; KeywordCachedArtifact writeResult; try { - bba = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); + bba = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); writeResult = new KeywordCachedArtifact(bba); } catch (TskCoreException e) { logger.log(Level.WARNING, "Error adding bb artifact for keyword hit", e); //NON-NLS diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index 410ec1a94c..e5562f9499 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -31,6 +31,7 @@ import org.apache.commons.lang.StringUtils; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.aggregate.ProgressContributor; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestMessage; @@ -40,6 +41,8 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; /** * Stores the results from running a Solr query (which could contain multiple @@ -60,8 +63,6 @@ class QueryResults { */ private final Map> results = new HashMap<>(); - - QueryResults(KeywordSearchQuery query) { this.keywordSearchQuery = query; } @@ -70,8 +71,6 @@ class QueryResults { results.put(keyword, hits); } - - KeywordSearchQuery getQuery() { return keywordSearchQuery; } @@ -145,14 +144,23 @@ class QueryResults { continue; } } - KeywordCachedArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName()); + Content content = null; + try { + SleuthkitCase tskCase = Case.getCurrentCase().getSleuthkitCase(); + content = tskCase.getContentById(hit.getContentID()); + } catch (TskCoreException tskCoreException) { + logger.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", tskCoreException); //NON-NLS + return null; + } catch (IllegalStateException ex) { + } + KeywordCachedArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(content, keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName()); if (writeResult != null) { newArtifacts.add(writeResult.getArtifact()); if (notifyInbox) { - writeSingleFileInboxMessage(writeResult, hit.getContent()); + writeSingleFileInboxMessage(writeResult, content); } } else { - logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{hit.getContent(), keyword.toString()}); //NON-NLS + logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{content, keyword.toString()}); //NON-NLS } } ++unitProgress; @@ -199,7 +207,7 @@ class QueryResults { * Generate an ingest inbox message for given keyword in given file * * @param written - * @param hitFile + * @param hitContent */ private void writeSingleFileInboxMessage(KeywordCachedArtifact written, Content hitContent) { StringBuilder subjectSb = new StringBuilder(); @@ -210,6 +218,7 @@ class QueryResults { } else { subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitLbl")); } + String uniqueKey = null; BlackboardAttribute attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()); if (attr != null) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index 45205b0ca7..b8875a9c23 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -24,10 +24,8 @@ import com.google.common.collect.ListMultimap; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -52,11 +50,9 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; -import org.sleuthkit.datamodel.TskException; /** * The RegexQuery class supports issuing regular expression queries against a @@ -191,7 +187,7 @@ final class RegexQuery implements KeywordSearchQuery { solrQuery.setSort(SortClause.asc(Server.Schema.ID.toString())); String cursorMark = CursorMarkParams.CURSOR_MARK_START; - SolrDocumentList resultList ; + SolrDocumentList resultList; boolean allResultsProcessed = false; while (!allResultsProcessed) { @@ -206,7 +202,7 @@ final class RegexQuery implements KeywordSearchQuery { for (KeywordHit hit : keywordHits) { hitsMultiMap.put(new Keyword(hit.getHit(), true, true, originalKeyword.getListName(), originalKeyword.getOriginalTerm()), hit); } - } catch (TskCoreException ex) { + } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS } } @@ -287,8 +283,8 @@ final class RegexQuery implements KeywordSearchQuery { } /* - * If searching for credit card account numbers, do a Luhn check - * on the term and discard it if it does not pass. + * If searching for credit card account numbers, do a Luhn + * check on the term and discard it if it does not pass. */ if (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { Matcher ccnMatcher = CREDIT_CARD_NUM_PATTERN.matcher(hit); @@ -319,10 +315,14 @@ final class RegexQuery implements KeywordSearchQuery { } catch (TskCoreException ex) { throw ex; } catch (Throwable error) { - /* NOTE: Matcher.find() is known to throw StackOverflowError in rare cases (see JIRA-2700). - StackOverflowError is an error, not an exception, and therefore needs to be caught - as a Throwable. When this occurs we should re-throw the error as TskCoreException so that it is - logged by the calling method and move on to the next Solr document. */ + /* + * NOTE: Matcher.find() is known to throw StackOverflowError in rare + * cases (see JIRA-2700). StackOverflowError is an error, not an + * exception, and therefore needs to be caught as a Throwable. When + * this occurs we should re-throw the error as TskCoreException so + * that it is logged by the calling method and move on to the next + * Solr document. + */ throw new TskCoreException("Failed to create keyword hits for Solr document id " + docId + " due to " + error.getMessage()); } return hits; @@ -373,33 +373,10 @@ final class RegexQuery implements KeywordSearchQuery { return escapedQuery; } - /** - * Get a unique, comma separated list of document ids that match the given - * hit for the same object. - * - * @param keyword The keyword object that resulted in one or more hits. - * @param hit The specific hit for which we want to identify all other - * chunks that match the keyword - * - * @return A comma separated list of unique document ids. - */ - private String getDocumentIds(Keyword keyword, KeywordHit hit) { - Set documentIds = new HashSet<>(); - - for (KeywordHit h : hitsMultiMap.get(keyword)) { - // Add the document id only if it is for the same object as the - // given hit and we haven't already seen it. - if (h.getSolrObjectId() == hit.getSolrObjectId() && !documentIds.contains(h.getSolrDocumentId())) { - documentIds.add(h.getSolrDocumentId()); - } - } - - return StringUtils.join(documentIds, ","); - } - /** * Converts the keyword hits for a given search term into artifacts. * + * @param content * @param foundKeyword The keyword that was found by the regex search. * @param hit The keyword hit. * @param snippet The document snippet that contains the hit @@ -411,12 +388,17 @@ final class RegexQuery implements KeywordSearchQuery { * @return An object that wraps an artifact and a mapping by id of its * attributes. */ - // TODO: Are we actually making meaningful use of the KeywordCachedArtifact - // class? @Override - public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + public KeywordCachedArtifact writeSingleFileHitsToBlackBoard( Content content,Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); + + + if (content == null) { + LOGGER.log(Level.WARNING, "Error adding artifact for keyword hit to blackboard"); //NON-NLS + return null; + } + /* * Create either a "plain vanilla" keyword hit artifact with keyword and * regex attributes, or a credit card account artifact with attributes @@ -429,8 +411,7 @@ final class RegexQuery implements KeywordSearchQuery { attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString())); try { - newArtifact = hit.getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT); - + newArtifact = content.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT); } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS return null; @@ -455,7 +436,7 @@ final class RegexQuery implements KeywordSearchQuery { if (hit.isArtifactHit()) { LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifact().getArtifactID())); //NON-NLS } else { - LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContent().getId())); //NON-NLS + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS } return null; } @@ -491,8 +472,8 @@ final class RegexQuery implements KeywordSearchQuery { * document id to support showing just the chunk that contained the * hit. */ - if (hit.getContent() instanceof AbstractFile) { - AbstractFile file = (AbstractFile) hit.getContent(); + if (content instanceof AbstractFile) { + AbstractFile file = (AbstractFile) content; if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) { attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId())); @@ -503,7 +484,7 @@ final class RegexQuery implements KeywordSearchQuery { * Create an account artifact. */ try { - newArtifact = hit.getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT); + newArtifact = content.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT); } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS return null; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java index f4413e3f2a..210229c060 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java @@ -42,6 +42,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -332,10 +333,9 @@ final class TermsComponentQuery implements KeywordSearchQuery { * @return An object that wraps an artifact and a mapping by id of its * attributes. */ - // TODO: Are we actually making meaningful use of the KeywordCachedArtifact - // class? + @Override - public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { /* * Create either a "plain vanilla" keyword hit artifact with keyword and * regex attributes, or a credit card account artifact with attributes @@ -349,7 +349,7 @@ final class TermsComponentQuery implements KeywordSearchQuery { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, originalKeyword.getSearchTerm())); try { - newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); + newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS @@ -375,7 +375,7 @@ final class TermsComponentQuery implements KeywordSearchQuery { if (hit.isArtifactHit()) { LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", searchTerm, hit.getSnippet(), hit.getArtifact().getArtifactID())); //NON-NLS } else { - LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), hit.getContent().getId())); //NON-NLS + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), hit.getContentID())); //NON-NLS } return null; } @@ -411,8 +411,8 @@ final class TermsComponentQuery implements KeywordSearchQuery { * document id to support showing just the chunk that contained the * hit. */ - if (hit.getContent() instanceof AbstractFile) { - AbstractFile file = (AbstractFile) hit.getContent(); + if (content instanceof AbstractFile) { + AbstractFile file = (AbstractFile)content; if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) { attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId())); @@ -423,7 +423,7 @@ final class TermsComponentQuery implements KeywordSearchQuery { * Create an account artifact. */ try { - newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_ACCOUNT); + newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_ACCOUNT); } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS return null; From 659d48f523b3438061c95a30d7a3d9bcb6c5b370 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 15 Aug 2017 14:46:04 -0400 Subject: [PATCH 05/29] cleanup Javadoc --- .../autopsy/keywordsearch/RegexQuery.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index b8875a9c23..1d568f6afd 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -389,11 +389,9 @@ final class RegexQuery implements KeywordSearchQuery { * attributes. */ @Override - public KeywordCachedArtifact writeSingleFileHitsToBlackBoard( Content content,Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); - - if (content == null) { LOGGER.log(Level.WARNING, "Error adding artifact for keyword hit to blackboard"); //NON-NLS return null; @@ -535,9 +533,9 @@ final class RegexQuery implements KeywordSearchQuery { * hit and turns them into artifact attributes. The track 1 data has the * same fields as the track two data, plus the account holder's name. * - * @param attributesMap A map of artifact attribute objects, used to avoid - * creating duplicate attributes. - * @param matcher A matcher for the snippet. + * @param attributeMap A map of artifact attribute objects, used to avoid + * creating duplicate attributes. + * @param matcher A matcher for the snippet. */ static private void parseTrack1Data(Map attributeMap, Matcher matcher) { parseTrack2Data(attributeMap, matcher); @@ -548,12 +546,12 @@ final class RegexQuery implements KeywordSearchQuery { * Creates an attribute of the the given type to the given artifact with a * value parsed from the snippet for a credit account number hit. * - * @param attributesMap A map of artifact attribute objects, used to avoid - * creating duplicate attributes. - * @param attrType The type of attribute to create. - * @param groupName The group name of the regular expression that was - * used to parse the attribute data. - * @param matcher A matcher for the snippet. + * @param attributeMap A map of artifact attribute objects, used to avoid + * creating duplicate attributes. + * @param attrType The type of attribute to create. + * @param groupName The group name of the regular expression that was + * used to parse the attribute data. + * @param matcher A matcher for the snippet. */ static private void addAttributeIfNotAlreadyCaptured(Map attributeMap, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String groupName, Matcher matcher) { BlackboardAttribute.Type type = new BlackboardAttribute.Type(attrType); From 976952b52ea8670613b1e341b948ade968954c88 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 16 Aug 2017 11:03:35 -0400 Subject: [PATCH 06/29] cleanup in KeywordHit --- .../autopsy/keywordsearch/KeywordHit.java | 59 +++++-------------- 1 file changed, 16 insertions(+), 43 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java index 3a51c38488..87ddac3d22 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.keywordsearch; +import java.util.Comparator; +import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.SleuthkitCase; @@ -26,8 +28,8 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Stores the fact that file or an artifact associated with a file had a keyword * hit. All instances make both the document id of the Solr document where the - * keyword was found and the file available to clients. Artifact keyword hits - * also make the artifact available to clients. + * keyword was found and the object Id available to clients. Artifact keyword + * hits also make the artifact available to clients. */ class KeywordHit implements Comparable { @@ -43,14 +45,9 @@ class KeywordHit implements Comparable { return hit; } - KeywordHit(String solrDocumentId, String snippet) throws TskCoreException { - this(solrDocumentId, snippet, null); - } - KeywordHit(String solrDocumentId, String snippet, String hit) throws TskCoreException { - /** - * Store the Solr document id. - */ + this.snippet = StringUtils.stripToEmpty(snippet); + this.hit = hit; this.solrDocumentId = solrDocumentId; /** @@ -71,26 +68,19 @@ class KeywordHit implements Comparable { this.chunkId = 0; } - /** - * Look up the artifact associated with the keyword hit if there is onw. - * If the high order bit of the object id is set, the hit was for an - * artifact. + /* + * If the high order bit of the object id is set (ie, it is negative), + * the hit was in an artifact, look up the artifact. */ - SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); - if (this.solrObjectId < 0) { + SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); this.artifact = caseDb.getBlackboardArtifact(this.solrObjectId); contentID = artifact.getObjectID(); } else { + //else the object id is for content. this.artifact = null; contentID = this.solrObjectId; } - - /** - * Store the text snippet. - */ - this.snippet = snippet; - this.hit = hit; } String getSolrDocumentId() { @@ -101,16 +91,12 @@ class KeywordHit implements Comparable { return this.solrObjectId; } - boolean hasChunkId() { - return this.chunkId != 0; - } - int getChunkId() { return this.chunkId; } boolean hasSnippet() { - return !this.snippet.isEmpty(); + return StringUtils.isNotBlank(this.snippet); } String getSnippet() { @@ -149,7 +135,7 @@ class KeywordHit implements Comparable { return false; } final KeywordHit other = (KeywordHit) obj; - return (this.solrObjectId == other.solrObjectId && this.chunkId == other.chunkId); + return this.compareTo(other) == 0; } @Override @@ -161,21 +147,8 @@ class KeywordHit implements Comparable { @Override public int compareTo(KeywordHit o) { - if (this.solrObjectId < o.solrObjectId) { - // Out object id is less than the other object id - return -1; - } else if (this.solrObjectId == o.solrObjectId) { - // Hits have same object id - if (this.chunkId < o.chunkId) { - // Our chunk id is lower than the other chunk id - return -1; - } else { - // Our chunk id is either greater than or equal to the other chunk id - return this.chunkId == o.chunkId ? 0 : 1; - } - } else { - // Our object id is greater than the other object id - return 1; - } + return Comparator.comparing(KeywordHit::getSolrObjectId) + .thenComparing(KeywordHit::getChunkId) + .compare(this, o); } } From 3639dcca2a2437903d53c35c64b02dbf70eb7bda Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Fri, 18 Aug 2017 15:14:24 -0400 Subject: [PATCH 07/29] Added timeout to Solr queries --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 3a55aad646..a004a89e70 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -1231,6 +1231,8 @@ public class Server { // the server to access a core needs to be built from a URL with the // core in it, and is only good for core-specific operations private final HttpSolrServer solrCore; + + private final int QUERY_TIMEOUT_MILLISECONDS = 86400000; // 24 Hours = 86,400,000 Milliseconds private Core(String name, CaseType caseType, Index index) { this.name = name; @@ -1240,7 +1242,8 @@ public class Server { this.solrCore = new HttpSolrServer(currentSolrServer.getBaseURL() + "/" + name); //NON-NLS //TODO test these settings - //solrCore.setSoTimeout(1000 * 60); // socket read timeout, make large enough so can index larger files + // socket read timeout, make large enough so can index larger files + solrCore.setSoTimeout(QUERY_TIMEOUT_MILLISECONDS); //solrCore.setConnectionTimeout(1000); solrCore.setDefaultMaxConnectionsPerHost(2); solrCore.setMaxTotalConnections(5); From 7d6dfaa7eac72fc43518aaff914a406f4d6567c1 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 16 Aug 2017 11:52:08 -0400 Subject: [PATCH 08/29] remove KeywordCachedArtifact --- .../keywordsearch/KeywordCachedArtifact.java | 63 ------------------- .../keywordsearch/KeywordSearchQuery.java | 48 +++++++++----- .../autopsy/keywordsearch/LuceneQuery.java | 8 +-- .../autopsy/keywordsearch/QueryResults.java | 47 ++++++++------ .../autopsy/keywordsearch/RegexQuery.java | 23 +------ .../keywordsearch/TermsComponentQuery.java | 33 +++------- 6 files changed, 74 insertions(+), 148 deletions(-) delete mode 100644 KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordCachedArtifact.java diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordCachedArtifact.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordCachedArtifact.java deleted file mode 100644 index 68f85dd29a..0000000000 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordCachedArtifact.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.keywordsearch; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; - -/** - * result of writing keyword search result to blackboard (cached artifact and - * attributes) This is mainly to cache the attributes, so that we don't query - * the DB to get them back again. - */ -class KeywordCachedArtifact { - - private BlackboardArtifact artifact; - private Map attributes; - - KeywordCachedArtifact(BlackboardArtifact artifact) { - this.artifact = artifact; - attributes = new HashMap(); - } - - BlackboardArtifact getArtifact() { - return artifact; - } - - Collection getAttributes() { - return attributes.values(); - } - - BlackboardAttribute getAttribute(Integer attrTypeID) { - return attributes.get(attrTypeID); - } - - void add(BlackboardAttribute attribute) { - attributes.put(attribute.getAttributeType().getTypeID(), attribute); - } - - void add(Collection attributes) { - for (BlackboardAttribute attr : attributes) { - this.attributes.put(attr.getAttributeType().getTypeID(), attr); - } - } -} diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java index cafd45b3e8..590b01d032 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java @@ -18,8 +18,9 @@ */ package org.sleuthkit.autopsy.keywordsearch; +import org.sleuthkit.datamodel.BlackboardArtifact; /** - * Interface for kewyord search queries. + * Interface for kewyord search queries. */ interface KeywordSearchQuery { @@ -30,18 +31,20 @@ interface KeywordSearchQuery { * * @return true if the query passed validation */ - boolean validate(); + boolean validate(); /** * execute query and return results without publishing them return results * for all matching terms * - * @throws KeywordSearchModuleException error while executing Solr term query - * @throws NoOpenCoreException if query failed due to server error, this - * could be a notification to stop processing + * @throws KeywordSearchModuleException error while executing Solr term + * query + * @throws NoOpenCoreException if query failed due to server error, + * this could be a notification to stop + * processing * @return */ - QueryResults performQuery() throws KeywordSearchModuleException, NoOpenCoreException; + QueryResults performQuery() throws KeywordSearchModuleException, NoOpenCoreException; /** * Set an optional filter to narrow down the search Adding multiple filters @@ -49,14 +52,14 @@ interface KeywordSearchQuery { * * @param filter filter to set on the query */ - void addFilter(KeywordQueryFilter filter); + void addFilter(KeywordQueryFilter filter); /** * Set an optional SOLR field to narrow down the search * * @param field field to set on the query */ - void setField(String field); + void setField(String field); /** * Modify the query string to be searched as a substring instead of a whole @@ -64,39 +67,54 @@ interface KeywordSearchQuery { * * @param isSubstring */ - void setSubstringQuery(); + void setSubstringQuery(); /** * escape the query string and use the escaped string in the query */ - void escape(); + void escape(); /** * * @return true if query was escaped */ - boolean isEscaped(); + boolean isEscaped(); /** * * @return true if query is a literal query (non regex) */ - boolean isLiteral(); + boolean isLiteral(); /** * return original keyword/query string * * @return the query String supplied originally */ - String getQueryString(); + String getQueryString(); /** * return escaped keyword/query string if escaping was done * * @return the escaped query string, or original string if no escaping done */ - String getEscapedQueryString(); + String getEscapedQueryString(); - KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword keyword, KeywordHit hit, String snippet, String listName); + /** + * Converts the keyword hits for a given search term into artifacts. + * + * @param foundKeyword The keyword that was found by the search, this may be + * different than the Keyword that was searched if, for + * example, it was a RegexQuery.. + * @param hit The keyword hit. + * @param snippet The document snippet that contains the hit + * @param listName The name of the keyword list that contained the + * keyword for which the hit was found. + * + * + * @return The newly created artifact or Null if there was a problem + * creating it. + */ + BlackboardArtifact writeSingleFileHitsToBlackBoard( Keyword foundKeyword, KeywordHit hit, String snippet, String listName); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index 9f8686898f..388a17d00f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.keywordsearch; import java.util.ArrayList; import java.util.Collection; -import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -192,15 +191,13 @@ class LuceneQuery implements KeywordSearchQuery { } @Override - public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + public BlackboardArtifact writeSingleFileHitsToBlackBoard( Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); Collection attributes = new ArrayList<>(); BlackboardArtifact bba; - KeywordCachedArtifact writeResult; try { bba = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); - writeResult = new KeywordCachedArtifact(bba); } catch (TskCoreException e) { logger.log(Level.WARNING, "Error adding bb artifact for keyword hit", e); //NON-NLS return null; @@ -233,8 +230,7 @@ class LuceneQuery implements KeywordSearchQuery { try { bba.addAttributes(attributes); //write out to bb - writeResult.add(attributes); - return writeResult; + return bba; } catch (TskCoreException e) { logger.log(Level.WARNING, "Error adding bb attributes to artifact", e); //NON-NLS return null; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index 410ec1a94c..ac74abc115 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -40,6 +40,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; /** * Stores the results from running a Solr query (which could contain multiple @@ -145,11 +146,15 @@ class QueryResults { continue; } } - KeywordCachedArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName()); + BlackboardArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard( keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName()); if (writeResult != null) { - newArtifacts.add(writeResult.getArtifact()); + newArtifacts.add(writeResult); if (notifyInbox) { - writeSingleFileInboxMessage(writeResult, hit.getContent()); + try { + writeSingleFileInboxMessage(writeResult, hit.getContent()); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error posting ,message to Ingest Inbox", ex); //NON-NLS + } } } else { logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{hit.getContent(), keyword.toString()}); //NON-NLS @@ -198,10 +203,10 @@ class QueryResults { /** * Generate an ingest inbox message for given keyword in given file * - * @param written + * @param artifact * @param hitFile */ - private void writeSingleFileInboxMessage(KeywordCachedArtifact written, Content hitContent) { + private void writeSingleFileInboxMessage(BlackboardArtifact artifact, Content hitContent) throws TskCoreException { StringBuilder subjectSb = new StringBuilder(); StringBuilder detailsSb = new StringBuilder(); @@ -211,30 +216,30 @@ class QueryResults { subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitLbl")); } String uniqueKey = null; - BlackboardAttribute attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()); + BlackboardAttribute attr; + + attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD)); if (attr != null) { final String keyword = attr.getValueString(); subjectSb.append(keyword); uniqueKey = keyword.toLowerCase(); + //details + detailsSb.append(""); //NON-NLS + //hit + detailsSb.append(""); //NON-NLS + detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitThLbl")); + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS } - //details - detailsSb.append("
").append(EscapeUtil.escapeHtml(keyword)).append("
"); //NON-NLS - //hit - detailsSb.append(""); //NON-NLS - detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitThLbl")); - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - //preview - attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW.getTypeID()); + attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW)); if (attr != null) { detailsSb.append(""); //NON-NLS detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.previewThLbl")); detailsSb.append(""); //NON-NLS detailsSb.append(""); //NON-NLS } - //file detailsSb.append(""); //NON-NLS detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.fileThLbl")); @@ -247,26 +252,30 @@ class QueryResults { detailsSb.append(""); //NON-NLS //list - attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); + attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); if (attr != null) { detailsSb.append(""); //NON-NLS detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.listThLbl")); detailsSb.append(""); //NON-NLS detailsSb.append(""); //NON-NLS } + //regex if (!keywordSearchQuery.isLiteral()) { - attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()); + + attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP)); if (attr != null) { detailsSb.append(""); //NON-NLS detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.regExThLbl")); detailsSb.append(""); //NON-NLS detailsSb.append(""); //NON-NLS } + } + detailsSb.append("
").append(EscapeUtil.escapeHtml(attr.getValueString())).append("
").append(EscapeUtil.escapeHtml(attr.getValueString())).append("
").append(attr.getValueString()).append("
").append(attr.getValueString()).append("
"); //NON-NLS - IngestServices.getInstance().postMessage(IngestMessage.createDataMessage(MODULE_NAME, subjectSb.toString(), detailsSb.toString(), uniqueKey, written.getArtifact())); + IngestServices.getInstance().postMessage(IngestMessage.createDataMessage(MODULE_NAME, subjectSb.toString(), detailsSb.toString(), uniqueKey, artifact)); } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index dca12ed5b4..bcd68d37c1 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -372,24 +372,9 @@ final class RegexQuery implements KeywordSearchQuery { return escapedQuery; } - /** - * Converts the keyword hits for a given search term into artifacts. - * - * @param foundKeyword The keyword that was found by the regex search. - * @param hit The keyword hit. - * @param snippet The document snippet that contains the hit - * @param listName The name of the keyword list that contained the - * keyword for which the hit was found. - * - * - * - * @return An object that wraps an artifact and a mapping by id of its - * attributes. - */ - // TODO: Are we actually making meaningful use of the KeywordCachedArtifact - // class? + @Override - public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + public BlackboardArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); /* @@ -499,9 +484,7 @@ final class RegexQuery implements KeywordSearchQuery { try { newArtifact.addAttributes(attributes); - KeywordCachedArtifact writeResult = new KeywordCachedArtifact(newArtifact); - writeResult.add(attributes); - return writeResult; + return newArtifact; } catch (TskCoreException e) { LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS return null; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java index f4413e3f2a..a52a212777 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java @@ -96,7 +96,7 @@ final class TermsComponentQuery implements KeywordSearchQuery { + "(?:\\?" // end sentinal: ? //NON-NLS + "(?.)" //longitudinal redundancy check //NON-NLS + "?)?)?)?)?)?");//close nested optional groups //NON-NLS - static final Pattern CREDIT_CARD_TRACK2_PATTERN = Pattern.compile( + static final Pattern CREDIT_CARD_TRACK2_PATTERN = Pattern.compile( /* * Track 2 is numeric plus six punctuation symbolls :;<=>? * @@ -115,7 +115,7 @@ final class TermsComponentQuery implements KeywordSearchQuery { + "(?:[:;<=>?]" //end sentinel //NON-NLS + "(?.)" //longitudinal redundancy check //NON-NLS + "?)?)?)?)?)?"); //close nested optional groups //NON-NLS - static final BlackboardAttribute.Type KEYWORD_SEARCH_DOCUMENT_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID); + static final BlackboardAttribute.Type KEYWORD_SEARCH_DOCUMENT_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID); /** * Constructs an object that implements a regex query that will be performed @@ -315,27 +315,12 @@ final class TermsComponentQuery implements KeywordSearchQuery { } results.addResult(new Keyword(term.getTerm(), false, true, originalKeyword.getListName(), originalKeyword.getOriginalTerm()), new ArrayList<>(termHits)); } - return results; + return results; } - /** - * Converts the keyword hits for a given search term into artifacts. - * - * @param foundKeyword The keyword that was found by the search. - * @param hit The keyword hit. - * @param snippet The document snippet that contains the hit - * @param listName The name of the keyword list that contained the keyword - * for which the hit was found. - * - * - * - * @return An object that wraps an artifact and a mapping by id of its - * attributes. - */ - // TODO: Are we actually making meaningful use of the KeywordCachedArtifact - // class? + @Override - public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + public BlackboardArtifact writeSingleFileHitsToBlackBoard( Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { /* * Create either a "plain vanilla" keyword hit artifact with keyword and * regex attributes, or a credit card account artifact with attributes @@ -347,7 +332,7 @@ 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())); - + try { newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); @@ -442,12 +427,10 @@ final class TermsComponentQuery implements KeywordSearchQuery { // TermsComponentQuery is now being used exclusively for substring searches. attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal())); - + try { newArtifact.addAttributes(attributes); - KeywordCachedArtifact writeResult = new KeywordCachedArtifact(newArtifact); - writeResult.add(attributes); - return writeResult; + return newArtifact; } catch (TskCoreException e) { LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS return null; From 77783d1537aa1e8c6046b8ed12cbffea888671e4 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 22 Aug 2017 03:02:35 -0400 Subject: [PATCH 09/29] minor cleanup --- .../keywordsearch/KeywordSearchQuery.java | 10 +++----- .../autopsy/keywordsearch/QueryResults.java | 25 +++++++------------ .../autopsy/keywordsearch/RegexQuery.java | 5 ++-- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java index 590b01d032..12d31db7e6 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.keywordsearch; import org.sleuthkit.datamodel.BlackboardArtifact; + /** * Interface for kewyord search queries. */ @@ -64,8 +65,6 @@ interface KeywordSearchQuery { /** * Modify the query string to be searched as a substring instead of a whole * word - * - * @param isSubstring */ void setSubstringQuery(); @@ -105,9 +104,9 @@ interface KeywordSearchQuery { * * @param foundKeyword The keyword that was found by the search, this may be * different than the Keyword that was searched if, for - * example, it was a RegexQuery.. + * example, it was a RegexQuery. * @param hit The keyword hit. - * @param snippet The document snippet that contains the hit + * @param snippet The document snippet that contains the hit. * @param listName The name of the keyword list that contained the * keyword for which the hit was found. * @@ -115,6 +114,5 @@ interface KeywordSearchQuery { * @return The newly created artifact or Null if there was a problem * creating it. */ - BlackboardArtifact writeSingleFileHitsToBlackBoard( Keyword foundKeyword, KeywordHit hit, String snippet, String listName); - + BlackboardArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index ac74abc115..200312c582 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -61,8 +61,6 @@ class QueryResults { */ private final Map> results = new HashMap<>(); - - QueryResults(KeywordSearchQuery query) { this.keywordSearchQuery = query; } @@ -71,8 +69,6 @@ class QueryResults { results.put(keyword, hits); } - - KeywordSearchQuery getQuery() { return keywordSearchQuery; } @@ -146,14 +142,14 @@ class QueryResults { continue; } } - BlackboardArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard( keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName()); + BlackboardArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName()); if (writeResult != null) { newArtifacts.add(writeResult); if (notifyInbox) { try { writeSingleFileInboxMessage(writeResult, hit.getContent()); } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error posting ,message to Ingest Inbox", ex); //NON-NLS + logger.log(Level.WARNING, "Error posting message to Ingest Inbox", ex); //NON-NLS } } } else { @@ -186,7 +182,6 @@ class QueryResults { * SolrObjectID-ChunkID pairs. */ private Collection getOneHitPerObject(Keyword keyword) { - HashMap hits = new HashMap<>(); // create a list of KeywordHits. KeywordHits with lowest chunkID is added the the list. @@ -201,10 +196,12 @@ class QueryResults { } /** - * Generate an ingest inbox message for given keyword in given file + * Generate and post an ingest inbox message for the given keyword in the given content. * - * @param artifact - * @param hitFile + * @param artifact The keyword hit artifact. + * @param hitContent The content that the hit is in. + * + * @throws TskCoreException If there is a problem generating or posting the inbox message. */ private void writeSingleFileInboxMessage(BlackboardArtifact artifact, Content hitContent) throws TskCoreException { StringBuilder subjectSb = new StringBuilder(); @@ -216,9 +213,7 @@ class QueryResults { subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitLbl")); } String uniqueKey = null; - BlackboardAttribute attr; - - attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD)); + BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD)); if (attr != null) { final String keyword = attr.getValueString(); subjectSb.append(keyword); @@ -240,6 +235,7 @@ class QueryResults { detailsSb.append("").append(EscapeUtil.escapeHtml(attr.getValueString())).append(""); //NON-NLS detailsSb.append(""); //NON-NLS } + //file detailsSb.append(""); //NON-NLS detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.fileThLbl")); @@ -262,7 +258,6 @@ class QueryResults { //regex if (!keywordSearchQuery.isLiteral()) { - attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP)); if (attr != null) { detailsSb.append(""); //NON-NLS @@ -270,12 +265,10 @@ class QueryResults { detailsSb.append("").append(attr.getValueString()).append(""); //NON-NLS detailsSb.append(""); //NON-NLS } - } detailsSb.append(""); //NON-NLS IngestServices.getInstance().postMessage(IngestMessage.createDataMessage(MODULE_NAME, subjectSb.toString(), detailsSb.toString(), uniqueKey, artifact)); } - } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index bcd68d37c1..9d4a7b419b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -512,7 +512,7 @@ final class RegexQuery implements KeywordSearchQuery { * hit and turns them into artifact attributes. The track 1 data has the * same fields as the track two data, plus the account holder's name. * - * @param attributesMap A map of artifact attribute objects, used to avoid + * @param attributeMap A map of artifact attribute objects, used to avoid * creating duplicate attributes. * @param matcher A matcher for the snippet. */ @@ -525,7 +525,7 @@ final class RegexQuery implements KeywordSearchQuery { * Creates an attribute of the the given type to the given artifact with a * value parsed from the snippet for a credit account number hit. * - * @param attributesMap A map of artifact attribute objects, used to avoid + * @param attributeMap A map of artifact attribute objects, used to avoid * creating duplicate attributes. * @param attrType The type of attribute to create. * @param groupName The group name of the regular expression that was @@ -547,5 +547,4 @@ final class RegexQuery implements KeywordSearchQuery { return null; }); } - } From 79b17fe17a025ca2e08d4dffeeceefaeebd21005 Mon Sep 17 00:00:00 2001 From: esaunders Date: Tue, 22 Aug 2017 10:28:43 -0400 Subject: [PATCH 10/29] Pass weak listener references to event publisher to allow blackboard artifact nodes to be garbage collected. --- .../datamodel/BlackboardArtifactNode.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 7317b5781f..47870bb9bf 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -25,9 +25,11 @@ import java.beans.PropertyChangeListener; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -37,6 +39,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.Lookup; import org.openide.util.NbBundle; +import org.openide.util.WeakListeners; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; @@ -64,6 +67,13 @@ import org.sleuthkit.datamodel.TskCoreException; public class BlackboardArtifactNode extends DisplayableItemNode { private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName()); + private static final Set CASE_EVENTS_OF_INTEREST = new HashSet<>(Arrays.asList(new String[]{ + Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString(), + Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString(), + Case.Events.CONTENT_TAG_ADDED.toString(), + Case.Events.CONTENT_TAG_DELETED.toString(), + Case.Events.CURRENT_CASE.toString() + })); private static Cache contentCache = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.MINUTES). @@ -138,7 +148,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { this.setName(Long.toString(artifact.getArtifactID())); this.setDisplayName(); this.setIconBaseWithExtension(iconPath); - Case.addPropertyChangeListener(pcl); + Case.addEventSubscriber(CASE_EVENTS_OF_INTEREST, WeakListeners.propertyChange(pcl, null)); } /** @@ -155,11 +165,11 @@ public class BlackboardArtifactNode extends DisplayableItemNode { this.setName(Long.toString(artifact.getArtifactID())); this.setDisplayName(); this.setIconBaseWithExtension(ExtractedContent.getIconFilePath(artifact.getArtifactTypeID())); //NON-NLS - Case.addPropertyChangeListener(pcl); + Case.addEventSubscriber(CASE_EVENTS_OF_INTEREST, WeakListeners.propertyChange(pcl, null)); } private void removeListeners() { - Case.removePropertyChangeListener(pcl); + Case.removeEventSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } @Override From bcefce90802af0c48b845d50f03fabfbbb779614 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 22 Aug 2017 16:25:42 -0400 Subject: [PATCH 11/29] Added re-tries for opening Solr cores --- .../autopsy/keywordsearch/Server.java | 79 ++++++++++++++----- 1 file changed, 59 insertions(+), 20 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index a004a89e70..94a3cc82d0 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -189,6 +189,8 @@ public class Server { private static final boolean DEBUG = false;//(Version.getBuildType() == Version.Type.DEVELOPMENT); private static final String SOLR = "solr"; private static final String CORE_PROPERTIES = "core.properties"; + private static final int MAX_NUM_CORE_OPEN_RETRIES = 3; // number of time to re-try loading a Solr core + private static final int TIME_TO_SLEEP_BETWEEN_RETIES_MILLISECODS = 10000; // wait 10 seconds before re-trying public enum CORE_EVT_STATES { @@ -644,7 +646,36 @@ public class Server { void openCoreForCase(Case theCase, Index index) throws KeywordSearchModuleException { currentCoreLock.writeLock().lock(); try { - currentCore = openCore(theCase, index); + // establish connection to Solr server + connectToServer(theCase.getCaseType()); + + int tryNum = 0; + while (tryNum < MAX_NUM_CORE_OPEN_RETRIES) { + tryNum++; + try { + // open Solr core + currentCore = openCore(theCase.getCaseType(), index); + break; + } catch (KeywordSearchModuleException ex) { + if (tryNum == MAX_NUM_CORE_OPEN_RETRIES) { + throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex); + } else { + try { + /* + NOTE: It has been noticed that sometimes Solr throws an exception + when trying to open a core on a network drive because it thinks that + some files are missing in the index folder. Same core is observed to + be successfully opened just seconds later by other Autopsy instances. + Therefore this re-try is being added to hopefully mitigate that (see JIRA-2941) + */ + logger.log(Level.WARNING, "Unable to open Solr core in " + index.getIndexPath() + ", re-trying", ex); + Thread.sleep(TIME_TO_SLEEP_BETWEEN_RETIES_MILLISECODS); + } catch (InterruptedException ex2) { + logger.log(Level.SEVERE, "Unexpected interrupt while waiting betwen Solr core re-tries", ex2); + } + } + } + } try { // execute a test query. if it fails, an exception will be thrown @@ -753,7 +784,7 @@ public class Server { /** * Creates/opens a Solr core (index) for a case. * - * @param theCase The case for which the core is to be created/opened. + * @param theCase Type of the current case * @param index The text index that the Solr core should be using. * * @return An object representing the created/opened core. @@ -761,21 +792,7 @@ public class Server { * @throws KeywordSearchModuleException If an error occurs while * creating/opening the core. */ - private Core openCore(Case theCase, Index index) throws KeywordSearchModuleException { - - try { - if (theCase.getCaseType() == CaseType.SINGLE_USER_CASE) { - currentSolrServer = this.localSolrServer; - } else { - String host = UserPreferences.getIndexingServerHost(); - String port = UserPreferences.getIndexingServerPort(); - currentSolrServer = new HttpSolrServer("http://" + host + ":" + port + "/solr"); //NON-NLS - } - connectToSolrServer(currentSolrServer); - - } catch (SolrServerException | IOException ex) { - throw new KeywordSearchModuleException(NbBundle.getMessage(Server.class, "Server.connect.exception.msg", ex.getLocalizedMessage()), ex); - } + private Core openCore(CaseType caseType, Index index) throws KeywordSearchModuleException { try { File dataDir = new File(new File(index.getIndexPath()).getParent()); // "data dir" is the parent of the index directory @@ -798,7 +815,7 @@ public class Server { // In single user mode, if there is a core.properties file already, // we've hit a solr bug. Compensate by deleting it. - if (theCase.getCaseType() == CaseType.SINGLE_USER_CASE) { + if (caseType == CaseType.SINGLE_USER_CASE) { Path corePropertiesFile = Paths.get(solrFolder.toString(), SOLR, coreName, CORE_PROPERTIES); if (corePropertiesFile.toFile().exists()) { try { @@ -822,12 +839,34 @@ public class Server { throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.noIndexDir.msg")); } - return new Core(coreName, theCase.getCaseType(), index); + return new Core(coreName, caseType, index); - } catch (SolrServerException | SolrException | IOException ex) { + } catch (Exception ex) { throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex); } } + + /** + * Connect to Solr server. + * + * @param caseType Type of the current case + * @throws KeywordSearchModuleException If unable to connect to Solr server + */ + void connectToServer(CaseType caseType) throws KeywordSearchModuleException { + try { + if (caseType == CaseType.SINGLE_USER_CASE) { + currentSolrServer = this.localSolrServer; + } else { + String host = UserPreferences.getIndexingServerHost(); + String port = UserPreferences.getIndexingServerPort(); + currentSolrServer = new HttpSolrServer("http://" + host + ":" + port + "/solr"); //NON-NLS + } + connectToSolrServer(currentSolrServer); + + } catch (SolrServerException | IOException ex) { + throw new KeywordSearchModuleException(NbBundle.getMessage(Server.class, "Server.connect.exception.msg", ex.getLocalizedMessage()), ex); + } + } /** * Commits current core if it exists From b7845f9af04d8966ea973687979633e73c7ac92e Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 22 Aug 2017 17:12:56 -0400 Subject: [PATCH 12/29] Minor --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 94a3cc82d0..f8e296722c 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -662,11 +662,11 @@ public class Server { } else { try { /* - NOTE: It has been noticed that sometimes Solr throws an exception - when trying to open a core on a network drive because it thinks that - some files are missing in the index folder. Same core is observed to + NOTE: It has been noticed that in rare cases Solr throws an exception + when trying to open a core that is stored on a network drive because + it thinks that some files are missing in the index folder. Same core is observed to be successfully opened just seconds later by other Autopsy instances. - Therefore this re-try is being added to hopefully mitigate that (see JIRA-2941) + Therefore a re-try is being added to hopefully mitigate this (see JIRA-2941) */ logger.log(Level.WARNING, "Unable to open Solr core in " + index.getIndexPath() + ", re-trying", ex); Thread.sleep(TIME_TO_SLEEP_BETWEEN_RETIES_MILLISECODS); From 909fda73fbddfec5fde95c340204ad22102ea8c4 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 22 Aug 2017 17:20:49 -0400 Subject: [PATCH 13/29] Minor --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index f8e296722c..a3ab026a9a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -189,7 +189,7 @@ public class Server { private static final boolean DEBUG = false;//(Version.getBuildType() == Version.Type.DEVELOPMENT); private static final String SOLR = "solr"; private static final String CORE_PROPERTIES = "core.properties"; - private static final int MAX_NUM_CORE_OPEN_RETRIES = 3; // number of time to re-try loading a Solr core + private static final int MAX_NUM_CORE_OPEN_ATTEMPTS = 3; // number of time to attempt loading a Solr core private static final int TIME_TO_SLEEP_BETWEEN_RETIES_MILLISECODS = 10000; // wait 10 seconds before re-trying public enum CORE_EVT_STATES { @@ -650,14 +650,14 @@ public class Server { connectToServer(theCase.getCaseType()); int tryNum = 0; - while (tryNum < MAX_NUM_CORE_OPEN_RETRIES) { + while (tryNum < MAX_NUM_CORE_OPEN_ATTEMPTS) { tryNum++; try { // open Solr core currentCore = openCore(theCase.getCaseType(), index); break; } catch (KeywordSearchModuleException ex) { - if (tryNum == MAX_NUM_CORE_OPEN_RETRIES) { + if (tryNum == MAX_NUM_CORE_OPEN_ATTEMPTS) { throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex); } else { try { From 9bdf496abe0ee472296f4f4967a3d4ffe7d8e509 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 23 Aug 2017 04:44:37 -0400 Subject: [PATCH 14/29] cleanup --- .../KeywordSearchResultFactory.java | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java index 65f83551b0..5583cfe96f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java @@ -36,7 +36,6 @@ import org.netbeans.api.progress.ProgressHandle; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; @@ -92,7 +91,7 @@ class KeywordSearchResultFactory extends ChildFactory { * properties are displayed as columns (since we are doing lazy child Node * load we need to preinitialize properties when sending parent Node) * - * @param toSet property set map for a Node + * @param toPopulate property set map for a Node */ @Override protected boolean createKeys(List toPopulate) { @@ -149,6 +148,8 @@ class KeywordSearchResultFactory extends ChildFactory { try { tskCase = Case.getCurrentCase().getSleuthkitCase(); } catch (IllegalStateException ex) { + logger.log(Level.SEVERE, "There was no case open.", ex); //NON-NLS + return false; } int hitNumber = 0; @@ -160,16 +161,20 @@ class KeywordSearchResultFactory extends ChildFactory { */ Map properties = new LinkedHashMap<>(); Content content = null; + String contentName = ""; try { content = tskCase.getContentById(hit.getContentID()); } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); + logger.log(Level.SEVERE, "There was a error getting content by id.", ex); //NON-NLS + return false; } - String contentName = content.getName(); - if (content instanceof AbstractFile) { - AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content); - } else { - properties.put(LOCATION.toString(), contentName); + if (content != null) { + contentName = content.getName(); + if (content instanceof AbstractFile) { + AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content); + } else { + properties.put(LOCATION.toString(), contentName); + } } /** @@ -251,14 +256,14 @@ class KeywordSearchResultFactory extends ChildFactory { * NOTE Parameters are defined based on how they are currently used in * practice * - * @param name File name that has hit. - * @param map Contains content metadata, snippets, etc. (property - * map) - * @param id User incremented ID + * @param name File name that has hit. + * @param map Contains content metadata, snippets, etc. + * (property map) + * @param id User incremented ID * @param solrObjectId - * @param content File that had the hit. - * @param query Query used in search - * @param hits Full set of search results (for all files! @@@) + * @param content File that had the hit. + * @param query Query used in search + * @param hits Full set of search results (for all files! @@@) */ KeyValueQueryContent(String name, Map map, int id, long solrObjectId, Content content, KeywordSearchQuery query, QueryResults hits) { super(name, map, id); From 8a7da368ad443d64617a529d2b78e2ca704361e8 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 23 Aug 2017 04:53:14 -0400 Subject: [PATCH 15/29] more cleanup --- .../KeywordSearchResultFactory.java | 26 ++++++++++--------- .../autopsy/keywordsearch/QueryResults.java | 15 ++++++----- .../autopsy/keywordsearch/SearchRunner.java | 8 ++---- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java index 5583cfe96f..b603f7b68c 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java @@ -48,7 +48,6 @@ import org.sleuthkit.autopsy.datamodel.KeyValue; import org.sleuthkit.autopsy.datamodel.KeyValueNode; import org.sleuthkit.autopsy.keywordsearch.KeywordSearchResultFactory.KeyValueQueryContent; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW; @@ -164,17 +163,20 @@ class KeywordSearchResultFactory extends ChildFactory { String contentName = ""; try { content = tskCase.getContentById(hit.getContentID()); + if (content == null) { + logger.log(Level.SEVERE, "There was a error getting content by id."); //NON-NLS + return false; + } } catch (TskCoreException ex) { logger.log(Level.SEVERE, "There was a error getting content by id.", ex); //NON-NLS return false; } - if (content != null) { - contentName = content.getName(); - if (content instanceof AbstractFile) { - AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content); - } else { - properties.put(LOCATION.toString(), contentName); - } + + contentName = content.getName(); + if (content instanceof AbstractFile) { + AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content); + } else { + properties.put(LOCATION.toString(), contentName); } /** @@ -238,6 +240,7 @@ class KeywordSearchResultFactory extends ChildFactory { //wrap in KeywordSearchFilterNode for the markup content, might need to override FilterNode for more customization return new KeywordSearchFilterNode(hits, kvNode); + } /** @@ -295,13 +298,12 @@ class KeywordSearchResultFactory extends ChildFactory { * worker for writing results to bb, with progress bar, cancellation, and * central registry of workers to be stopped when case is closed */ - static class BlackboardResultWriter extends SwingWorker { + static class BlackboardResultWriter extends SwingWorker { private static final List writers = new ArrayList<>(); private ProgressHandle progress; private final KeywordSearchQuery query; private final QueryResults hits; - private Collection newArtifacts = new ArrayList<>(); private static final int QUERY_DISPLAY_LEN = 40; BlackboardResultWriter(QueryResults hits, String listName) { @@ -315,13 +317,13 @@ class KeywordSearchResultFactory extends ChildFactory { } @Override - protected Object doInBackground() throws Exception { + protected Void doInBackground() throws Exception { registerWriter(this); //register (synchronized on class) outside of writerLock to prevent deadlock final String queryStr = query.getQueryString(); final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) + " ..." : queryStr; try { progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(true)); - newArtifacts = hits.writeAllHitsToBlackBoard(progress, null, this, false); + hits.writeAllHitsToBlackBoard(progress, null, this, false); } finally { finalizeWorker(); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index 8058c1d917..48fdb38f10 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -98,7 +98,7 @@ class QueryResults { * * @return The artifacts that were created. */ - Collection writeAllHitsToBlackBoard(ProgressHandle progress, ProgressContributor subProgress, SwingWorker worker, boolean notifyInbox) { + Collection writeAllHitsToBlackBoard(ProgressHandle progress, ProgressContributor subProgress, SwingWorker worker, boolean notifyInbox) { final Collection newArtifacts = new ArrayList<>(); if (progress != null) { progress.start(getKeywords().size()); @@ -148,10 +148,9 @@ class QueryResults { try { SleuthkitCase tskCase = Case.getCurrentCase().getSleuthkitCase(); content = tskCase.getContentById(hit.getContentID()); - } catch (TskCoreException tskCoreException) { + } catch (TskCoreException | IllegalStateException tskCoreException) { logger.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", tskCoreException); //NON-NLS return null; - } catch (IllegalStateException ex) { } BlackboardArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(content, keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName()); if (writeResult != null) { @@ -207,12 +206,14 @@ class QueryResults { } /** - * Generate and post an ingest inbox message for the given keyword in the given content. + * Generate and post an ingest inbox message for the given keyword in the + * given content. * - * @param artifact The keyword hit artifact. + * @param artifact The keyword hit artifact. * @param hitContent The content that the hit is in. * - * @throws TskCoreException If there is a problem generating or posting the inbox message. + * @throws TskCoreException If there is a problem generating or posting the + * inbox message. */ private void writeSingleFileInboxMessage(BlackboardArtifact artifact, Content hitContent) throws TskCoreException { StringBuilder subjectSb = new StringBuilder(); @@ -247,7 +248,7 @@ class QueryResults { detailsSb.append("").append(EscapeUtil.escapeHtml(attr.getValueString())).append(""); //NON-NLS detailsSb.append(""); //NON-NLS } - + //file detailsSb.append(""); //NON-NLS detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.fileThLbl")); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java index 1a2ffd1200..c9b95a770b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.keywordsearch; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -46,7 +45,6 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.StopWatch; import org.sleuthkit.autopsy.ingest.IngestMessage; import org.sleuthkit.autopsy.ingest.IngestServices; -import org.sleuthkit.datamodel.BlackboardArtifact; /** * Singleton keyword search manager: Launches search threads for each job and @@ -482,9 +480,7 @@ public final class SearchRunner { if (!newResults.getKeywords().isEmpty()) { // Write results to BB - //new artifacts created, to report to listeners - Collection newArtifacts = new ArrayList<>(); - + //scale progress bar more more granular, per result sub-progress, within per keyword int totalUnits = newResults.getKeywords().size(); subProgresses[keywordsSearched].start(totalUnits); @@ -496,7 +492,7 @@ public final class SearchRunner { subProgresses[keywordsSearched].progress(keywordList.getName() + ": " + queryDisplayStr, unitProgress); // Create blackboard artifacts - newArtifacts = newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages()); + newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages()); } //if has results From bcdf7880c28071b114de7f099283e8ffec141210 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Wed, 23 Aug 2017 09:05:28 -0400 Subject: [PATCH 16/29] Fixed typos --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index a3ab026a9a..0648c53310 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -190,7 +190,7 @@ public class Server { private static final String SOLR = "solr"; private static final String CORE_PROPERTIES = "core.properties"; private static final int MAX_NUM_CORE_OPEN_ATTEMPTS = 3; // number of time to attempt loading a Solr core - private static final int TIME_TO_SLEEP_BETWEEN_RETIES_MILLISECODS = 10000; // wait 10 seconds before re-trying + private static final int TIME_TO_SLEEP_BETWEEN_RETRIES_MILLISECONDS = 10000; // wait 10 seconds before re-trying public enum CORE_EVT_STATES { @@ -669,7 +669,7 @@ public class Server { Therefore a re-try is being added to hopefully mitigate this (see JIRA-2941) */ logger.log(Level.WARNING, "Unable to open Solr core in " + index.getIndexPath() + ", re-trying", ex); - Thread.sleep(TIME_TO_SLEEP_BETWEEN_RETIES_MILLISECODS); + Thread.sleep(TIME_TO_SLEEP_BETWEEN_RETRIES_MILLISECONDS); } catch (InterruptedException ex2) { logger.log(Level.SEVERE, "Unexpected interrupt while waiting betwen Solr core re-tries", ex2); } From b77fcb74c0eefa640589c4ba1fd0cc737f246bd9 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Wed, 23 Aug 2017 09:06:51 -0400 Subject: [PATCH 17/29] Fixed typos --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 0648c53310..95f44161c6 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -189,7 +189,7 @@ public class Server { private static final boolean DEBUG = false;//(Version.getBuildType() == Version.Type.DEVELOPMENT); private static final String SOLR = "solr"; private static final String CORE_PROPERTIES = "core.properties"; - private static final int MAX_NUM_CORE_OPEN_ATTEMPTS = 3; // number of time to attempt loading a Solr core + private static final int MAX_NUM_CORE_OPEN_ATTEMPTS = 3; // number of times to attempt loading a Solr core private static final int TIME_TO_SLEEP_BETWEEN_RETRIES_MILLISECONDS = 10000; // wait 10 seconds before re-trying public enum CORE_EVT_STATES { From 160a241780429c70e3a1d25bfbb0014306351acb Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 24 Aug 2017 14:19:37 -0400 Subject: [PATCH 18/29] Pulling commons-io version 2.5 instead of 2.4 --- CoreLibs/ivy.xml | 2 +- CoreLibs/nbproject/project.properties | 3 ++ CoreLibs/nbproject/project.xml | 72 +++++++++++++-------------- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/CoreLibs/ivy.xml b/CoreLibs/ivy.xml index 8043007b52..bfd9112f9e 100644 --- a/CoreLibs/ivy.xml +++ b/CoreLibs/ivy.xml @@ -24,7 +24,7 @@ Note there is no namespace collision with ver 3 --> - + diff --git a/CoreLibs/nbproject/project.properties b/CoreLibs/nbproject/project.properties index 62c4d535e6..13a62b62ee 100644 --- a/CoreLibs/nbproject/project.properties +++ b/CoreLibs/nbproject/project.properties @@ -10,6 +10,7 @@ file.reference.commons-codec-1.10.jar=release/modules/ext/commons-codec-1.10.jar file.reference.commons-collections4-4.1.jar=release/modules/ext/commons-collections4-4.1.jar file.reference.commons-csv-1.4.jar=release/modules/ext/commons-csv-1.4.jar file.reference.commons-io-2.4.jar=release/modules/ext/commons-io-2.4.jar +file.reference.commons-io-2.5.jar=release/modules/ext/commons-io-2.5.jar file.reference.commons-lang-2.6.jar=release/modules/ext/commons-lang-2.6.jar file.reference.commons-lang3-3.0-javadoc.jar=release/modules/ext/commons-lang3-3.0-javadoc.jar file.reference.commons-lang3-3.0-sources.jar=release/modules/ext/commons-lang3-3.0-sources.jar @@ -74,6 +75,7 @@ file.reference.xmlbeans-2.6.0.jar=release/modules/ext/xmlbeans-2.6.0.jar javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial javadoc.reference.commons-csv-1.4.jar=release/modules/ext/commons-csv-1.4-javadoc.jar +javadoc.reference.commons-io-2.5.jar=release/modules/ext/commons-io-2.5-javadoc.jar javadoc.reference.compiler-0.9.1.jar=release/modules/ext/compiler-0.9.1-javadoc.jar javadoc.reference.controlsfx-8.40.11.jar=release/modules/ext/controlsfx-8.40.11-javadoc.jar javadoc.reference.guava-19.0.jar=release/modules/ext/guava-19.0-javadoc.jar @@ -82,6 +84,7 @@ javadoc.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-contro javadoc.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4-javadoc.jar nbm.needs.restart=true source.reference.commons-csv-1.4.jar=release/modules/ext/commons-csv-1.4-sources.jar +source.reference.commons-io-2.5.jar=release/modules/ext/commons-io-2.5-sources.jar source.reference.compiler-0.9.1.jar=release/modules/ext/compiler-0.9.1-sources.jar source.reference.controlsfx-8.40.11.jar=release/modules/ext/controlsfx-8.40.11-sources.jar source.reference.guava-19.0.jar=release/modules/ext/guava-19.0-sources.jar diff --git a/CoreLibs/nbproject/project.xml b/CoreLibs/nbproject/project.xml index 23a41edaf1..6fcef46ccd 100644 --- a/CoreLibs/nbproject/project.xml +++ b/CoreLibs/nbproject/project.xml @@ -699,18 +699,10 @@ ext/sigar-1.6.4.jar release/modules/ext/sigar-1.6.4.jar - - ext/xmlbeans-2.6.0.jar - release/modules/ext/xmlbeans-2.6.0.jar - ext/jna-3.4.0.jar release/modules/ext/jna-3.4.0.jar - - ext/poi-ooxml-schemas-3.15.jar - release/modules/ext/poi-ooxml-schemas-3.15.jar - ext/gson-1.4.jar release/modules/ext/gson-1.4.jar @@ -731,6 +723,10 @@ ext/imgscalr-lib-4.2.jar release/modules/ext/imgscalr-lib-4.2.jar + + ext/xmlbeans-2.6.0.jar + release/modules/ext/xmlbeans-2.6.0.jar + ext/common-io-3.2.jar release/modules/ext/common-io-3.2.jar @@ -764,12 +760,12 @@ release/modules/ext/joda-time-2.4-javadoc.jar - ext/jcalendarbutton-1.4.6.jar - release/modules/ext/jcalendarbutton-1.4.6.jar + ext/poi-excelant-3.15.jar + release/modules/ext/poi-excelant-3.15.jar - ext/poi-ooxml-3.15.jar - release/modules/ext/poi-ooxml-3.15.jar + ext/jcalendarbutton-1.4.6.jar + release/modules/ext/jcalendarbutton-1.4.6.jar ext/imageio-psd-3.2.jar @@ -779,18 +775,10 @@ ext/stax-api-1.0.1.jar release/modules/ext/stax-api-1.0.1.jar - - ext/commons-collections4-4.1.jar - release/modules/ext/commons-collections4-4.1.jar - ext/servlet-api-2.5.jar release/modules/ext/servlet-api-2.5.jar - - ext/poi-excelant-3.15.jar - release/modules/ext/poi-excelant-3.15.jar - ext/imageio-pcx-3.2.jar release/modules/ext/imageio-pcx-3.2.jar @@ -827,10 +815,6 @@ ext/geronimo-jms_1.1_spec-1.0.jar release/modules/ext/geronimo-jms_1.1_spec-1.0.jar - - ext/poi-scratchpad-3.15.jar - release/modules/ext/poi-scratchpad-3.15.jar - ext/joda-time-2.4-sources.jar release/modules/ext/joda-time-2.4-sources.jar @@ -839,14 +823,26 @@ ext/jfxtras-fxml-8.0-r4.jar release/modules/ext/jfxtras-fxml-8.0-r4.jar + + ext/poi-ooxml-3.15.jar + release/modules/ext/poi-ooxml-3.15.jar + ext/joda-time-2.4.jar release/modules/ext/joda-time-2.4.jar + + ext/commons-collections4-4.1.jar + release/modules/ext/commons-collections4-4.1.jar + ext/commons-logging-1.1.2-javadoc.jar release/modules/ext/commons-logging-1.1.2-javadoc.jar + + ext/commons-codec-1.10.jar + release/modules/ext/commons-codec-1.10.jar + ext/slf4j-simple-1.6.1.jar release/modules/ext/slf4j-simple-1.6.1.jar @@ -855,6 +851,18 @@ ext/guava-19.0.jar release/modules/ext/guava-19.0.jar + + ext/commons-io-2.5.jar + release/modules/ext/commons-io-2.5.jar + + + ext/poi-ooxml-schemas-3.15.jar + release/modules/ext/poi-ooxml-schemas-3.15.jar + + + ext/poi-scratchpad-3.15.jar + release/modules/ext/poi-scratchpad-3.15.jar + ext/imageio-bmp-3.2.jar release/modules/ext/imageio-bmp-3.2.jar @@ -879,10 +887,6 @@ ext/ant-1.8.2.jar release/modules/ext/ant-1.8.2.jar - - ext/commons-codec-1.10.jar - release/modules/ext/commons-codec-1.10.jar - ext/javassist-3.12.1.GA.jar release/modules/ext/javassist-3.12.1.GA.jar @@ -895,14 +899,6 @@ ext/commons-logging-1.1.2.jar release/modules/ext/commons-logging-1.1.2.jar - - ext/commons-io-2.4.jar - release/modules/ext/commons-io-2.4.jar - - - ext/poi-3.15.jar - release/modules/ext/poi-3.15.jar - ext/controlsfx-8.40.11.jar release/modules/ext/controlsfx-8.40.11.jar @@ -915,6 +911,10 @@ ext/javaee-api-5.0-2.jar release/modules/ext/javaee-api-5.0-2.jar + + ext/poi-3.15.jar + release/modules/ext/poi-3.15.jar + ext/common-image-3.2.jar release/modules/ext/common-image-3.2.jar From 12f0ca2695a3b5505ab9e8f53878e032ea26b671 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 24 Aug 2017 15:55:29 -0400 Subject: [PATCH 19/29] Reverted code to retry solr core open --- .../autopsy/keywordsearch/Server.java | 81 +++++-------------- 1 file changed, 21 insertions(+), 60 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 95f44161c6..0ddf15bd67 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -189,8 +189,6 @@ public class Server { private static final boolean DEBUG = false;//(Version.getBuildType() == Version.Type.DEVELOPMENT); private static final String SOLR = "solr"; private static final String CORE_PROPERTIES = "core.properties"; - private static final int MAX_NUM_CORE_OPEN_ATTEMPTS = 3; // number of times to attempt loading a Solr core - private static final int TIME_TO_SLEEP_BETWEEN_RETRIES_MILLISECONDS = 10000; // wait 10 seconds before re-trying public enum CORE_EVT_STATES { @@ -646,36 +644,7 @@ public class Server { void openCoreForCase(Case theCase, Index index) throws KeywordSearchModuleException { currentCoreLock.writeLock().lock(); try { - // establish connection to Solr server - connectToServer(theCase.getCaseType()); - - int tryNum = 0; - while (tryNum < MAX_NUM_CORE_OPEN_ATTEMPTS) { - tryNum++; - try { - // open Solr core - currentCore = openCore(theCase.getCaseType(), index); - break; - } catch (KeywordSearchModuleException ex) { - if (tryNum == MAX_NUM_CORE_OPEN_ATTEMPTS) { - throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex); - } else { - try { - /* - NOTE: It has been noticed that in rare cases Solr throws an exception - when trying to open a core that is stored on a network drive because - it thinks that some files are missing in the index folder. Same core is observed to - be successfully opened just seconds later by other Autopsy instances. - Therefore a re-try is being added to hopefully mitigate this (see JIRA-2941) - */ - logger.log(Level.WARNING, "Unable to open Solr core in " + index.getIndexPath() + ", re-trying", ex); - Thread.sleep(TIME_TO_SLEEP_BETWEEN_RETRIES_MILLISECONDS); - } catch (InterruptedException ex2) { - logger.log(Level.SEVERE, "Unexpected interrupt while waiting betwen Solr core re-tries", ex2); - } - } - } - } + currentCore = openCore(theCase, index); try { // execute a test query. if it fails, an exception will be thrown @@ -784,7 +753,7 @@ public class Server { /** * Creates/opens a Solr core (index) for a case. * - * @param theCase Type of the current case + * @param theCase The case for which the core is to be created/opened. * @param index The text index that the Solr core should be using. * * @return An object representing the created/opened core. @@ -792,7 +761,21 @@ public class Server { * @throws KeywordSearchModuleException If an error occurs while * creating/opening the core. */ - private Core openCore(CaseType caseType, Index index) throws KeywordSearchModuleException { + private Core openCore(Case theCase, Index index) throws KeywordSearchModuleException { + + try { + if (theCase.getCaseType() == CaseType.SINGLE_USER_CASE) { + currentSolrServer = this.localSolrServer; + } else { + String host = UserPreferences.getIndexingServerHost(); + String port = UserPreferences.getIndexingServerPort(); + currentSolrServer = new HttpSolrServer("http://" + host + ":" + port + "/solr"); //NON-NLS + } + connectToSolrServer(currentSolrServer); + + } catch (SolrServerException | IOException ex) { + throw new KeywordSearchModuleException(NbBundle.getMessage(Server.class, "Server.connect.exception.msg", ex.getLocalizedMessage()), ex); + } try { File dataDir = new File(new File(index.getIndexPath()).getParent()); // "data dir" is the parent of the index directory @@ -815,7 +798,7 @@ public class Server { // In single user mode, if there is a core.properties file already, // we've hit a solr bug. Compensate by deleting it. - if (caseType == CaseType.SINGLE_USER_CASE) { + if (theCase.getCaseType() == CaseType.SINGLE_USER_CASE) { Path corePropertiesFile = Paths.get(solrFolder.toString(), SOLR, coreName, CORE_PROPERTIES); if (corePropertiesFile.toFile().exists()) { try { @@ -839,34 +822,12 @@ public class Server { throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.noIndexDir.msg")); } - return new Core(coreName, caseType, index); + return new Core(coreName, theCase.getCaseType(), index); - } catch (Exception ex) { + } catch (SolrServerException | SolrException | IOException ex) { throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex); } } - - /** - * Connect to Solr server. - * - * @param caseType Type of the current case - * @throws KeywordSearchModuleException If unable to connect to Solr server - */ - void connectToServer(CaseType caseType) throws KeywordSearchModuleException { - try { - if (caseType == CaseType.SINGLE_USER_CASE) { - currentSolrServer = this.localSolrServer; - } else { - String host = UserPreferences.getIndexingServerHost(); - String port = UserPreferences.getIndexingServerPort(); - currentSolrServer = new HttpSolrServer("http://" + host + ":" + port + "/solr"); //NON-NLS - } - connectToSolrServer(currentSolrServer); - - } catch (SolrServerException | IOException ex) { - throw new KeywordSearchModuleException(NbBundle.getMessage(Server.class, "Server.connect.exception.msg", ex.getLocalizedMessage()), ex); - } - } /** * Commits current core if it exists @@ -1530,4 +1491,4 @@ public class Server { return port; } } -} +} \ No newline at end of file From 8c6bb9bd949ecb17426c73f715c77f94b6a1b1d2 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 24 Aug 2017 16:13:54 -0400 Subject: [PATCH 20/29] Minor --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 0ddf15bd67..458f4e6f24 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -824,7 +824,7 @@ public class Server { return new Core(coreName, theCase.getCaseType(), index); - } catch (SolrServerException | SolrException | IOException ex) { + } catch (Exception ex) { throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex); } } From 4585ee3ac3d090c5071ad2bd8f951335d7618ded Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 24 Aug 2017 16:14:30 -0400 Subject: [PATCH 21/29] Minor --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 458f4e6f24..c2072cfa54 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -1491,4 +1491,4 @@ public class Server { return port; } } -} \ No newline at end of file +} From 04fcee7dbf6f1541470ce277843f5acbd53a13fb Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 28 Aug 2017 14:09:13 -0400 Subject: [PATCH 22/29] Listeners now only register for event types they are interested in. --- .../sleuthkit/autopsy/casemodule/Case.java | 32 +++++- .../autopsy/casemodule/CaseDeleteAction.java | 3 +- .../casemodule/CasePropertiesAction.java | 3 +- .../casemodule/CollaborationMonitor.java | 10 +- .../autopsy/coreutils/ImageUtils.java | 3 +- .../datamodel/AbstractAbstractFileNode.java | 18 +++- .../datamodel/BlackboardArtifactNode.java | 18 ++-- .../autopsy/datamodel/DataSourcesNode.java | 5 +- .../autopsy/datamodel/DeletedContent.java | 100 ++++++++++-------- .../autopsy/datamodel/EmailExtracted.java | 5 +- .../autopsy/datamodel/ExtractedContent.java | 8 +- .../sleuthkit/autopsy/datamodel/FileSize.java | 85 +++++++-------- .../datamodel/FileTypesByExtension.java | 12 +-- .../datamodel/FileTypesByMimeType.java | 9 +- .../autopsy/datamodel/HashsetHits.java | 5 +- .../autopsy/datamodel/ImageNode.java | 5 +- .../autopsy/datamodel/InterestingHits.java | 96 ++++++++--------- .../autopsy/datamodel/KeywordHits.java | 5 +- .../sleuthkit/autopsy/datamodel/Reports.java | 6 +- .../org/sleuthkit/autopsy/datamodel/Tags.java | 11 +- .../autopsy/datamodel/VolumeNode.java | 5 +- .../autopsy/datamodel/accounts/Accounts.java | 5 +- .../DirectoryTreeTopComponent.java | 3 +- .../autopsy/filesearch/DateSearchFilter.java | 6 +- .../autopsy/filesearch/FileSearchAction.java | 12 +-- .../autopsy/ingest/IngestManager.java | 3 +- .../autopsy/ingest/IngestMessagesToolbar.java | 3 +- .../autopsy/ingest/IngestMonitor.java | 3 +- .../hashdatabase/HashDbPanelSearchAction.java | 12 +-- .../autopsy/report/ReportWizardAction.java | 3 +- .../keywordsearch/DropdownToolbar.java | 3 +- 31 files changed, 284 insertions(+), 213 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 340d847343..c05256dcce 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -35,6 +35,7 @@ import java.sql.Statement; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -50,6 +51,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -377,7 +379,9 @@ public class Case { * * @param eventNames The events the subscriber is interested in. * @param subscriber The subscriber (PropertyChangeListener) to add. + * @deprecated Use addEventTypeSubscriber instead. */ + @Deprecated public static void addEventSubscriber(Set eventNames, PropertyChangeListener subscriber) { eventPublisher.addSubscriber(eventNames, subscriber); } @@ -385,9 +389,23 @@ public class Case { /** * Adds a subscriber to specific case events. * - * @param eventName The event the subscriber is interested in. + * @param eventTypes The events the subscriber is interested in. * @param subscriber The subscriber (PropertyChangeListener) to add. */ + public static void addEventTypeSubscriber(Set eventTypes, PropertyChangeListener subscriber) { + eventTypes.forEach((Events event) -> { + eventPublisher.addSubscriber(event.toString(), subscriber); + }); + } + + /** + * Adds a subscriber to specific case events. + * + * @param eventName The event the subscriber is interested in. + * @param subscriber The subscriber (PropertyChangeListener) to add. + * @deprecated Use addEventTypeSubscriber instead. + */ + @Deprecated public static void addEventSubscriber(String eventName, PropertyChangeListener subscriber) { eventPublisher.addSubscriber(eventName, subscriber); } @@ -412,6 +430,18 @@ public class Case { eventPublisher.removeSubscriber(eventNames, subscriber); } + /** + * Removes a subscriber to specific case events. + * + * @param eventTypes The events the subscriber is no longer interested in. + * @param subscriber The subscriber (PropertyChangeListener) to remove. + */ + public static void removeEventTypeSubscriber(Set eventTypes, PropertyChangeListener subscriber) { + eventTypes.forEach((Events event) -> { + eventPublisher.removeSubscriber(event.toString(), subscriber); + }); + } + /** * Checks if a case display name is valid, i.e., does not include any * characters that cannot be used in file names. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java index 2df3bfcc33..3ef30f2594 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.casemodule; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; +import java.util.EnumSet; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.Action; @@ -49,7 +50,7 @@ final class CaseDeleteAction extends CallableSystemAction { CaseDeleteAction() { putValue(Action.NAME, NbBundle.getMessage(CaseDeleteAction.class, "CTL_CaseDeleteAction")); this.setEnabled(false); - Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), (PropertyChangeEvent evt) -> { + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> { setEnabled(null != evt.getNewValue() && UserPreferences.getMode() != UserPreferences.SelectedMode.REVIEW); }); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesAction.java index 7cd6a93b10..c50c561564 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesAction.java @@ -22,6 +22,7 @@ import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; +import java.util.EnumSet; import javax.swing.Action; import javax.swing.JDialog; import javax.swing.SwingUtilities; @@ -42,7 +43,7 @@ final class CasePropertiesAction extends CallableSystemAction { CasePropertiesAction() { putValue(Action.NAME, NbBundle.getMessage(CasePropertiesAction.class, "CTL_CasePropertiesAction")); this.setEnabled(false); - Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), (PropertyChangeEvent evt) -> { + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> { setEnabled(null != evt.getNewValue()); }); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CollaborationMonitor.java b/Core/src/org/sleuthkit/autopsy/casemodule/CollaborationMonitor.java index 6042c24d12..ab55b67c5b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CollaborationMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CollaborationMonitor.java @@ -24,9 +24,8 @@ import java.beans.PropertyChangeListener; import java.io.Serializable; import java.time.Duration; import java.time.Instant; -import java.util.Arrays; +import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -57,7 +56,8 @@ final class CollaborationMonitor { private static final String EVENT_CHANNEL_NAME = "%s-Collaboration-Monitor-Events"; //NON-NLS private static final String COLLABORATION_MONITOR_EVENT = "COLLABORATION_MONITOR_EVENT"; //NON-NLS - private static final Set CASE_EVENTS_OF_INTEREST = new HashSet<>(Arrays.asList(new String[]{Case.Events.ADDING_DATA_SOURCE.toString(), Case.Events.DATA_SOURCE_ADDED.toString(), Case.Events.ADDING_DATA_SOURCE_FAILED.toString()})); + private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.ADDING_DATA_SOURCE, + Case.Events.DATA_SOURCE_ADDED, Case.Events.ADDING_DATA_SOURCE_FAILED); private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 2; private static final String PERIODIC_TASK_THREAD_NAME = "collab-monitor-periodic-tasks-%d"; //NON-NLS private static final long HEARTBEAT_INTERVAL_MINUTES = 1; @@ -113,7 +113,7 @@ final class CollaborationMonitor { */ localTasksManager = new LocalTasksManager(); IngestManager.getInstance().addIngestJobEventListener(localTasksManager); - Case.addEventSubscriber(CASE_EVENTS_OF_INTEREST, localTasksManager); + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, localTasksManager); /** * Start periodic tasks that: @@ -141,7 +141,7 @@ final class CollaborationMonitor { } } - Case.removeEventSubscriber(CASE_EVENTS_OF_INTEREST, localTasksManager); + Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, localTasksManager); IngestManager.getInstance().removeIngestJobEventListener(localTasksManager); if (null != eventPublisher) { diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java index eb6a16e6e0..eabc56e3bd 100755 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java @@ -35,6 +35,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.Iterator; import java.util.List; import static java.util.Objects.nonNull; @@ -153,7 +154,7 @@ public class ImageUtils { SUPPORTED_IMAGE_MIME_TYPES.removeIf("application/octet-stream"::equals); //NON-NLS //Clear the file map when the case changes, so we don't accidentaly get images from the old case. - Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), evt -> cacheFileMap.clear()); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> cacheFileMap.clear()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index e3c750826d..f3e08622d5 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -21,9 +21,11 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import org.openide.nodes.Children; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -49,6 +51,9 @@ public abstract class AbstractAbstractFileNode extends A private static final Logger LOGGER = Logger.getLogger(AbstractAbstractFileNode.class.getName()); + private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE, + Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED); + /** * @param type of the AbstractFile data to encapsulate * @param abstractFile file to encapsulate @@ -68,13 +73,14 @@ public abstract class AbstractAbstractFileNode extends A } } } - // Listen for case events so that we can detect when case is closed - Case.addPropertyChangeListener(pcl); + // Listen for case events so that we can detect when the case is closed + // or when tags are added. + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } private void removeListeners() { IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { @@ -96,7 +102,11 @@ public abstract class AbstractAbstractFileNode extends A // If so, refresh our children. try { Children parentsChildren = getParentNode().getChildren(); - if (parentsChildren != null) { + // We only want to refresh our parents children if we are in the + // data sources branch of the tree. The parent nodes in other + // branches of the tree (e.g. File Types and Deleted Files) do + // not need to be refreshed. + if (parentsChildren instanceof ContentChildren) { ((ContentChildren) parentsChildren).refreshChildren(); parentsChildren.getNodesCount(); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 47870bb9bf..2bda34f1c1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -25,6 +25,7 @@ import java.beans.PropertyChangeListener; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -67,13 +68,10 @@ import org.sleuthkit.datamodel.TskCoreException; public class BlackboardArtifactNode extends DisplayableItemNode { private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName()); - private static final Set CASE_EVENTS_OF_INTEREST = new HashSet<>(Arrays.asList(new String[]{ - Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString(), - Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString(), - Case.Events.CONTENT_TAG_ADDED.toString(), - Case.Events.CONTENT_TAG_DELETED.toString(), - Case.Events.CURRENT_CASE.toString() - })); + private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, + Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, + Case.Events.CONTENT_TAG_ADDED, + Case.Events.CONTENT_TAG_DELETED); private static Cache contentCache = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.MINUTES). @@ -148,7 +146,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { this.setName(Long.toString(artifact.getArtifactID())); this.setDisplayName(); this.setIconBaseWithExtension(iconPath); - Case.addEventSubscriber(CASE_EVENTS_OF_INTEREST, WeakListeners.propertyChange(pcl, null)); + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, WeakListeners.propertyChange(pcl, null)); } /** @@ -165,11 +163,11 @@ public class BlackboardArtifactNode extends DisplayableItemNode { this.setName(Long.toString(artifact.getArtifactID())); this.setDisplayName(); this.setIconBaseWithExtension(ExtractedContent.getIconFilePath(artifact.getArtifactTypeID())); //NON-NLS - Case.addEventSubscriber(CASE_EVENTS_OF_INTEREST, WeakListeners.propertyChange(pcl, null)); + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, WeakListeners.propertyChange(pcl, null)); } private void removeListeners() { - Case.removeEventSubscriber(CASE_EVENTS_OF_INTEREST, pcl); + Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java index 7ac9e37398..43ddd069a3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java @@ -22,6 +22,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.List; import java.util.logging.Level; import org.openide.nodes.Sheet; @@ -88,13 +89,13 @@ public class DataSourcesNode extends DisplayableItemNode { @Override protected void addNotify() { - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); reloadKeys(); } @Override protected void removeNotify() { - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); currentKeys.clear(); setKeys(Collections.emptySet()); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java index 9c738fb3ed..335a04930d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java @@ -22,10 +22,15 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashSet; import java.util.List; import java.util.Observable; import java.util.Observer; +import java.util.Set; import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.openide.nodes.AbstractNode; @@ -172,68 +177,69 @@ public class DeletedContent implements AutopsyVisitableItem { * Listens for case and ingest invest. Updates observers when events are * fired. Other nodes are listening to this for changes. */ - private final class DeletedContentsChildrenObservable extends Observable { + private static final class DeletedContentsChildrenObservable extends Observable { + private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of( + Case.Events.DATA_SOURCE_ADDED, + Case.Events.CURRENT_CASE + ); DeletedContentsChildrenObservable() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } private void removeListeners() { deleteObservers(); IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } - private final PropertyChangeListener pcl = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { + /** + * + // @@@ COULD CHECK If the new file is deleted + * before notifying... Checking for a current case is a + * stop gap measure + update(); until a different way of + * handling the closing of cases is worked out. + * Currently, remote events may be received for a case + * that is already closed. + */ + try { + Case.getCurrentCase(); + // new file was added + // @@@ COULD CHECK If the new file is deleted before notifying... + update(); + } catch (IllegalStateException notUsed) { /** - * + // @@@ COULD CHECK If the new file is deleted - * before notifying... Checking for a current case is a - * stop gap measure + update(); until a different way of - * handling the closing of cases is worked out. - * Currently, remote events may be received for a case - * that is already closed. + * Case is closed, do nothing. */ - try { - Case.getCurrentCase(); - // new file was added - // @@@ COULD CHECK If the new file is deleted before notifying... - update(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) - || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { - /** - * Checking for a current case is a stop gap measure - * until a different way of handling the closing of - * cases is worked out. Currently, remote events may be - * received for a case that is already closed. - */ - try { - Case.getCurrentCase(); - update(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - // case was closed. Remove listeners so that we don't get called with a stale case handle - if (evt.getNewValue() == null) { - removeListeners(); - } - maxFilesDialogShown = false; } + } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) + || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + /** + * Checking for a current case is a stop gap measure + * until a different way of handling the closing of + * cases is worked out. Currently, remote events may be + * received for a case that is already closed. + */ + try { + Case.getCurrentCase(); + update(); + } catch (IllegalStateException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeListeners(); + } + maxFilesDialogShown = false; } }; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java index ca4602e30a..4f1e190f4b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java @@ -23,6 +23,7 @@ import java.beans.PropertyChangeListener; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -285,7 +286,7 @@ public class EmailExtracted implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); emailResults.update(); emailResults.addObserver(this); } @@ -294,7 +295,7 @@ public class EmailExtracted implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); emailResults.deleteObserver(this); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index 896cec5322..34dc9bf517 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -23,8 +23,10 @@ import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; +import java.util.Set; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; @@ -251,14 +253,14 @@ public class ExtractedContent implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); } @Override protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); typeNodeList.clear(); } @@ -309,7 +311,7 @@ public class ExtractedContent implements AutopsyVisitableItem { */ public class TypeNode extends DisplayableItemNode { - private BlackboardArtifact.Type type; + private final BlackboardArtifact.Type type; private long childCount = 0; TypeNode(BlackboardArtifact.Type type) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java index 19fd4659e8..bc515b6d29 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java @@ -22,9 +22,11 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.List; import java.util.Observable; import java.util.Observer; +import java.util.Set; import java.util.logging.Level; import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; @@ -168,65 +170,64 @@ public class FileSize implements AutopsyVisitableItem { * Listens for case and ingest invest. Updates observers when events are * fired. Size-based nodes are listening to this for changes. */ - private final class FileSizeRootChildrenObservable extends Observable { + private static final class FileSizeRootChildrenObservable extends Observable { + + private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE); FileSizeRootChildrenObservable() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } private void removeListeners() { deleteObservers(); IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } - private final PropertyChangeListener pcl = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String eventType = evt.getPropertyName(); + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { + if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked + * out. Currently, remote events may be received for a case + * that is already closed. + */ + try { + // new file was added + // @@@ could check the size here and only fire off updates if we know the file meets the min size criteria + Case.getCurrentCase(); + update(); + } catch (IllegalStateException notUsed) { /** - * Checking for a current case is a stop gap measure - * until a different way of handling the closing of - * cases is worked out. Currently, remote events may be - * received for a case that is already closed. + * Case is closed, do nothing. */ - try { - // new file was added - // @@@ could check the size here and only fire off updates if we know the file meets the min size criteria - Case.getCurrentCase(); - update(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) - || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + } + } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) + || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked + * out. Currently, remote events may be received for a case + * that is already closed. + */ + try { + Case.getCurrentCase(); + update(); + } catch (IllegalStateException notUsed) { /** - * Checking for a current case is a stop gap measure - * until a different way of handling the closing of - * cases is worked out. Currently, remote events may be - * received for a case that is already closed. + * Case is closed, do nothing. */ - try { - Case.getCurrentCase(); - update(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - // case was closed. Remove listeners so that we don't get called with a stale case handle - if (evt.getNewValue() == null) { - removeListeners(); - } + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeListeners(); } } }; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 2e1fba50cc..1f109bd8d5 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -21,10 +21,11 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Arrays; +import java.util.EnumSet; import java.util.List; import java.util.Observable; import java.util.Observer; -import java.util.function.Function; +import java.util.Set; import java.util.logging.Level; import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; @@ -39,8 +40,6 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesKey; import org.sleuthkit.autopsy.ingest.IngestManager; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -75,9 +74,11 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { private class FileTypesByExtObservable extends Observable { private final PropertyChangeListener pcl; + private final Set CASE_EVENTS_OF_INTEREST; private FileTypesByExtObservable() { super(); + this.CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE); this.pcl = (PropertyChangeEvent evt) -> { String eventType = evt.getPropertyName(); if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) @@ -109,15 +110,14 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); - + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } private void removeListeners() { deleteObservers(); IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } private void update() { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index e5bd41b6e8..9fe183a8a8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -24,11 +24,13 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Observer; +import java.util.Set; import java.util.logging.Level; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -44,7 +46,6 @@ import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTr import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesKey; import org.sleuthkit.autopsy.ingest.IngestManager; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -79,6 +80,8 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi */ private final PropertyChangeListener pcl; + private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE); + /** * Create the base expression used as the where clause in the queries for * files by mime type. Filters out certain kinds of files and directories, @@ -102,7 +105,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi private void removeListeners() { deleteObservers(); IngestManager.getInstance().removeIngestJobEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } /** @@ -175,7 +178,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } }; IngestManager.getInstance().addIngestJobEventListener(pcl); - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); populateHashMap(); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index 2665894481..df3d239aef 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -24,6 +24,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -252,7 +253,7 @@ public class HashsetHits implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); hashsetResults.update(); hashsetResults.addObserver(this); } @@ -261,7 +262,7 @@ public class HashsetHits implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); hashsetResults.deleteObserver(this); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 682a3fa225..4efe1db364 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -24,6 +24,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.List; import java.util.logging.Level; import javax.swing.Action; @@ -79,12 +80,12 @@ public class ImageNode extends AbstractContentNode { // Listen for ingest events so that we can detect new added files (e.g. carved) IngestManager.getInstance().addIngestModuleEventListener(pcl); // Listen for case events so that we can detect when case is closed - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); } private void removeListeners() { IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index d9fecdcdb2..7a0b6b9ad9 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -24,6 +24,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -188,57 +189,54 @@ public class InterestingHits implements AutopsyVisitableItem { * nice methods for its startup and shutdown, so it seemed like a * cleaner place to register the property change listener. */ - private final PropertyChangeListener pcl = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked + * out. Currently, remote events may be received for a case + * that is already closed. + */ + try { + Case.getCurrentCase(); /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked - * out. Currently, remote events may be received for a case - * that is already closed. + * Even with the check above, it is still possible that + * the case will be closed in a different thread before + * this code executes. If that happens, it is possible + * for the event to have a null oldValue. */ - try { - Case.getCurrentCase(); - /** - * Even with the check above, it is still possible that - * the case will be closed in a different thread before - * this code executes. If that happens, it is possible - * for the event to have a null oldValue. - */ - ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); - if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() - || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())) { - interestingResults.update(); - } - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { - /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked - * out. Currently, remote events may be received for a case - * that is already closed. - */ - try { - Case.getCurrentCase(); + ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); + if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() + || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())) { interestingResults.update(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - // case was closed. Remove listeners so that we don't get called with a stale case handle - if (evt.getNewValue() == null) { - removeNotify(); - skCase = null; } + } catch (IllegalStateException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked + * out. Currently, remote events may be received for a case + * that is already closed. + */ + try { + Case.getCurrentCase(); + interestingResults.update(); + } catch (IllegalStateException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeNotify(); + skCase = null; } } }; @@ -247,7 +245,7 @@ public class InterestingHits implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); interestingResults.update(); interestingResults.addObserver(this); } @@ -256,7 +254,7 @@ public class InterestingHits implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); interestingResults.deleteObserver(this); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index 343074ddc9..75873d135e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -24,6 +24,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -455,7 +456,7 @@ public class KeywordHits implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); keywordResults.update(); super.addNotify(); } @@ -464,7 +465,7 @@ public class KeywordHits implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); super.removeNotify(); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java b/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java index c606984056..14cf3bfbd4 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java @@ -28,7 +28,9 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.EnumSet; import java.util.List; +import java.util.Set; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.Action; @@ -103,8 +105,10 @@ public final class Reports implements AutopsyVisitableItem { */ private static final class ReportNodeFactory extends ChildFactory { + private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.REPORT_ADDED, Case.Events.REPORT_DELETED); + ReportNodeFactory() { - Case.addPropertyChangeListener((PropertyChangeEvent evt) -> { + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, (PropertyChangeEvent evt) -> { String eventType = evt.getPropertyName(); if (eventType.equals(Case.Events.REPORT_ADDED.toString()) || eventType.equals(Case.Events.REPORT_DELETED.toString())) { /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java index b8127fe5a7..53722568c2 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java @@ -21,9 +21,11 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Collections; +import java.util.EnumSet; import java.util.List; import java.util.Observable; import java.util.Observer; +import java.util.Set; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; @@ -118,6 +120,11 @@ public class Tags implements AutopsyVisitableItem { private class TagNameNodeFactory extends ChildFactory.Detachable implements Observer { + private final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, + Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, + Case.Events.CONTENT_TAG_ADDED, + Case.Events.CONTENT_TAG_DELETED); + private final PropertyChangeListener pcl = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { @@ -171,7 +178,7 @@ public class Tags implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); tagResults.update(); tagResults.addObserver(this); } @@ -180,7 +187,7 @@ public class Tags implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); tagResults.deleteObserver(this); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java index ba37cd4f09..ee5a5713a3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import javax.swing.Action; import org.openide.nodes.Children; @@ -72,12 +73,12 @@ public class VolumeNode extends AbstractContentNode { // Listen for ingest events so that we can detect new added files (e.g. carved) IngestManager.getInstance().addIngestModuleEventListener(pcl); // Listen for case events so that we can detect when case is closed - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); } private void removeListeners() { IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); } /* diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index 6545ac0bef..e1ba5a2ca8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -311,7 +312,7 @@ final public class Accounts implements AutopsyVisitableItem { protected void removeNotify() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); super.removeNotify(); } @@ -319,7 +320,7 @@ final public class Accounts implements AutopsyVisitableItem { protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); super.addNotify(); refreshKeys(); } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index bfb98e5220..03c265daee 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -26,6 +26,7 @@ import java.beans.PropertyVetoException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -151,7 +152,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } } }); - Case.addEventSubscriber(new HashSet<>(Arrays.asList(Case.Events.CURRENT_CASE.toString(), Case.Events.DATA_SOURCE_ADDED.toString())), this); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE, Case.Events.DATA_SOURCE_ADDED), this); this.em.addPropertyChangeListener(this); IngestManager.getInstance().addIngestJobEventListener(this); IngestManager.getInstance().addIngestModuleEventListener(this); diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java index f7ae47cc73..205db56998 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java @@ -28,6 +28,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; +import java.util.EnumSet; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -53,6 +54,9 @@ class DateSearchFilter extends AbstractFileSearchFilter { private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy"); private static final String SEPARATOR = "SEPARATOR"; //NON-NLS + private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE, + Case.Events.DATA_SOURCE_ADDED, Case.Events.DATA_SOURCE_DELETED); + /** * New DateSearchFilter with the default panel */ @@ -62,7 +66,7 @@ class DateSearchFilter extends AbstractFileSearchFilter { private DateSearchFilter(DateSearchPanel panel) { super(panel); - Case.addPropertyChangeListener(this.new CasePropertyChangeListener()); + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, this.new CasePropertyChangeListener()); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchAction.java b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchAction.java index 603d9d1623..fb66a4b6d7 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchAction.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchAction.java @@ -20,7 +20,7 @@ package org.sleuthkit.autopsy.filesearch; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; +import java.util.EnumSet; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; @@ -35,14 +35,10 @@ final class FileSearchAction extends CallableSystemAction implements FileSearchP FileSearchAction() { super(); setEnabled(Case.isCaseOpen()); - Case.addPropertyChangeListener(new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { - setEnabled(evt.getNewValue() != null); - } + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { + setEnabled(evt.getNewValue() != null); } - }); } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java index 99f8e09fd8..e61f8d61f7 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -229,7 +230,7 @@ public class IngestManager { * opened/closed) events. */ private void subscribeToCaseEvents() { - Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), (PropertyChangeEvent event) -> { + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent event) -> { if (event.getNewValue() != null) { handleCaseOpened(); } else { diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagesToolbar.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagesToolbar.java index 20b791c64d..432cb48810 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagesToolbar.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagesToolbar.java @@ -25,6 +25,7 @@ import java.awt.Font; import java.awt.Graphics; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.EnumSet; import javax.swing.JButton; import org.openide.util.NbBundle; import org.openide.windows.Mode; @@ -128,7 +129,7 @@ class IngestMessagesToolbar extends javax.swing.JPanel { } }); - Case.addPropertyChangeListener((PropertyChangeEvent evt) -> { + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> { if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { setEnabled(evt.getNewValue() != null && RuntimeProperties.runningWithGUI()); } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java index 771380c415..f6580a3cbc 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java @@ -23,6 +23,7 @@ import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.io.File; import java.io.IOException; +import java.util.EnumSet; import java.util.logging.FileHandler; import java.util.logging.Level; import java.util.logging.SimpleFormatter; @@ -121,7 +122,7 @@ public final class IngestMonitor { MonitorTimerAction() { findRootDirectoryForCurrentCase(); - Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), (PropertyChangeEvent evt) -> { + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> { if (evt instanceof AutopsyEvent) { AutopsyEvent event = (AutopsyEvent) evt; if (AutopsyEvent.SourceType.LOCAL == event.getSourceType() && event.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbPanelSearchAction.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbPanelSearchAction.java index d7471df562..268f828fc7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbPanelSearchAction.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbPanelSearchAction.java @@ -23,7 +23,7 @@ import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; +import java.util.EnumSet; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; @@ -43,13 +43,9 @@ class HashDbPanelSearchAction extends CallableSystemAction { HashDbPanelSearchAction() { super(); setEnabled(Case.isCaseOpen()); - Case.addPropertyChangeListener(new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { - setEnabled(evt.getNewValue() != null); - } + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { + setEnabled(evt.getNewValue() != null); } }); } diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java b/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java index 75c8f7c205..49f37008be 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java @@ -27,6 +27,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.text.MessageFormat; +import java.util.EnumSet; import java.util.Map; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -81,7 +82,7 @@ public final class ReportWizardAction extends CallableSystemAction implements Pr public ReportWizardAction() { setEnabled(false); - Case.addPropertyChangeListener((PropertyChangeEvent evt) -> { + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> { if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { Case newCase = (Case) evt.getNewValue(); setEnabled(newCase != null && RuntimeProperties.runningWithGUI()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java index 3272b9dad1..1ad473dd01 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java @@ -23,6 +23,7 @@ import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.EnumSet; import java.util.logging.Level; import javax.swing.SwingUtilities; import javax.swing.event.PopupMenuEvent; @@ -76,7 +77,7 @@ class DropdownToolbar extends javax.swing.JPanel { private void customizeComponents() { searchSettingsChangeListener = new SearchSettingsChangeListener(); KeywordSearch.getServer().addServerActionListener(searchSettingsChangeListener); - Case.addPropertyChangeListener(searchSettingsChangeListener); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), searchSettingsChangeListener); DropdownListSearchPanel listsPanel = DropdownListSearchPanel.getDefault(); listsPanel.addSearchButtonActionListener((ActionEvent e) -> { From 9f95239cc4afaa5280daa664340a3f1a23279a61 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 29 Aug 2017 12:51:18 -0400 Subject: [PATCH 23/29] Added ability to search for MD5 --- .../autopsy/filesearch/Bundle.properties | 2 + .../autopsy/filesearch/FileSearchPanel.java | 2 + .../autopsy/filesearch/HashSearchFilter.java | 66 +++++++ .../autopsy/filesearch/HashSearchPanel.form | 104 +++++++++++ .../autopsy/filesearch/HashSearchPanel.java | 176 ++++++++++++++++++ 5 files changed, 350 insertions(+) create mode 100755 Core/src/org/sleuthkit/autopsy/filesearch/HashSearchFilter.java create mode 100755 Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.form create mode 100755 Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties index c5fd4d48e4..68cfc25429 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties @@ -56,3 +56,5 @@ SizeSearchPanel.sizeCompareComboBox.lessThan=less than MimeTypePanel.jLabel1.text=*Note: Multiple MIME types can be selected FileSearchPanel.searchButton.text=Search MimeTypePanel.mimeTypeCheckBox.text=MIME Type: +HashSearchPanel.md5CheckBox.text=MD5: +HashSearchPanel.emptyHashMsg.text=Must enter something for hash search. \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java index 23730c99c1..d7efb0b46f 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java @@ -94,6 +94,8 @@ class FileSearchPanel extends javax.swing.JPanel { this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.metadata"), metadataFilters)); this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.knownStatus"), new KnownStatusSearchFilter())); + + this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "HashSearchPanel.md5CheckBox.text"), new HashSearchFilter())); for (FilterArea fa : this.filterAreas) { fa.setMaximumSize(new Dimension(Integer.MAX_VALUE, fa.getMinimumSize().height)); diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchFilter.java new file mode 100755 index 0000000000..951f4b206e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchFilter.java @@ -0,0 +1,66 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.filesearch; + +import java.awt.event.ActionListener; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException; + +/** + * + */ +class HashSearchFilter extends AbstractFileSearchFilter { + + private static final String EMPTY_HASH_MESSAGE = NbBundle + .getMessage(HashSearchFilter.class, "HashSearchPanel.emptyHashMsg.text"); + + public HashSearchFilter() { + this(new HashSearchPanel()); + } + + public HashSearchFilter(HashSearchPanel component) { + super(component); + } + + @Override + public boolean isEnabled() { + return this.getComponent().getHashCheckBox().isSelected(); + } + + @Override + public String getPredicate() throws FilterValidationException { + String md5Hash = this.getComponent().getSearchTextField().getText(); + + if (md5Hash.isEmpty()) { + throw new FilterValidationException(EMPTY_HASH_MESSAGE); + } + + return "md5 = '" + md5Hash.toLowerCase() + "'"; //NON-NLS + } + + @Override + public void addActionListener(ActionListener l) { + getComponent().addActionListener(l); + } + + @Override + public boolean isValid() { + return !this.getComponent().getSearchTextField().getText().isEmpty(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.form new file mode 100755 index 0000000000..dabb115e57 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.form @@ -0,0 +1,104 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java new file mode 100755 index 0000000000..827cc31141 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java @@ -0,0 +1,176 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.filesearch; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JCheckBox; +import javax.swing.JMenuItem; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +/** + * + */ +class HashSearchPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + + /** + * Creates new form HashSearchPanel + */ + HashSearchPanel() { + initComponents(); + customizeComponents(); + setComponentsEnabled(); + } + + private void customizeComponents() { + + searchTextField.setComponentPopupMenu(rightClickMenu); + ActionListener actList = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JMenuItem jmi = (JMenuItem) e.getSource(); + if (jmi.equals(cutMenuItem)) { + searchTextField.cut(); + } else if (jmi.equals(copyMenuItem)) { + searchTextField.copy(); + } else if (jmi.equals(pasteMenuItem)) { + searchTextField.paste(); + } else if (jmi.equals(selectAllMenuItem)) { + searchTextField.selectAll(); + } + } + }; + cutMenuItem.addActionListener(actList); + copyMenuItem.addActionListener(actList); + pasteMenuItem.addActionListener(actList); + selectAllMenuItem.addActionListener(actList); + this.searchTextField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + }); + + } + + JCheckBox getHashCheckBox() { + return hashCheckBox; + } + + JTextField getSearchTextField() { + return searchTextField; + } + + void setComponentsEnabled() { + boolean enabled = hashCheckBox.isSelected(); + this.searchTextField.setEnabled(enabled); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + rightClickMenu = new javax.swing.JPopupMenu(); + cutMenuItem = new javax.swing.JMenuItem(); + copyMenuItem = new javax.swing.JMenuItem(); + pasteMenuItem = new javax.swing.JMenuItem(); + selectAllMenuItem = new javax.swing.JMenuItem(); + hashCheckBox = new javax.swing.JCheckBox(); + searchTextField = new javax.swing.JTextField(); + + cutMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.cutMenuItem.text")); // NOI18N + rightClickMenu.add(cutMenuItem); + + copyMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.copyMenuItem.text")); // NOI18N + rightClickMenu.add(copyMenuItem); + + pasteMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.pasteMenuItem.text")); // NOI18N + rightClickMenu.add(pasteMenuItem); + + selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.selectAllMenuItem.text")); // NOI18N + rightClickMenu.add(selectAllMenuItem); + + hashCheckBox.setFont(hashCheckBox.getFont().deriveFont(hashCheckBox.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + hashCheckBox.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.md5CheckBox.text")); // NOI18N + hashCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + hashCheckBoxActionPerformed(evt); + } + }); + + searchTextField.setFont(searchTextField.getFont().deriveFont(searchTextField.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + searchTextField.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.searchTextField.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(0, 0, 0) + .addComponent(hashCheckBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 247, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, 0)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(hashCheckBox) + .addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); + }// //GEN-END:initComponents + + private void hashCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hashCheckBoxActionPerformed + setComponentsEnabled(); + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + }//GEN-LAST:event_hashCheckBoxActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JMenuItem copyMenuItem; + private javax.swing.JMenuItem cutMenuItem; + private javax.swing.JCheckBox hashCheckBox; + private javax.swing.JMenuItem pasteMenuItem; + private javax.swing.JPopupMenu rightClickMenu; + private javax.swing.JTextField searchTextField; + private javax.swing.JMenuItem selectAllMenuItem; + // End of variables declaration//GEN-END:variables + + void addActionListener(ActionListener l) { + searchTextField.addActionListener(l); + } +} From ab6475010879db579f4406a9dab09546f90c6b27 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 29 Aug 2017 15:19:38 -0400 Subject: [PATCH 24/29] Minor changes to panel --- Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.form | 3 --- Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java | 1 - 2 files changed, 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.form index dabb115e57..bb410a2aaa 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.form @@ -95,9 +95,6 @@
- - - diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java index 827cc31141..22b8f74314 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java @@ -134,7 +134,6 @@ class HashSearchPanel extends javax.swing.JPanel { }); searchTextField.setFont(searchTextField.getFont().deriveFont(searchTextField.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - searchTextField.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.searchTextField.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); From 82abde2dda6419a7e4d482eaba751c566c6e386d Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Wed, 30 Aug 2017 16:08:21 -0400 Subject: [PATCH 25/29] AIm system log changes --- .../autopsy/experimental/autoingest/AutoIngestManager.java | 2 -- .../autopsy/experimental/autoingest/AutoIngestSystemLogger.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index afae397553..0f36d63aea 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -2646,8 +2646,6 @@ public final class AutoIngestManager extends Observable implements PropertyChang * back into hostNamesToRunningJobs as a result of * processing the job status update. */ - SYS_LOGGER.log(Level.WARNING, "Auto ingest node {0} timed out while processing folder {1}", - new Object[]{job.getNodeName(), job.getManifest().getFilePath().toString()}); hostNamesToRunningJobs.remove(job.getNodeName()); setChanged(); notifyObservers(Event.JOB_COMPLETED); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestSystemLogger.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestSystemLogger.java index 08b3016eb5..184d5d350a 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestSystemLogger.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestSystemLogger.java @@ -35,7 +35,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil; */ final class AutoIngestSystemLogger { - private static final int LOG_SIZE = 10000000; // In bytes, zero is unlimited, set to roughly 10mb currently + private static final int LOG_SIZE = 50000000; // In bytes, zero is unlimited, set to roughly 10mb currently private static final int LOG_FILE_COUNT = 10; private static final Logger LOGGER = Logger.getLogger("AutoIngest"); //NON-NLS private static final String NEWLINE = System.lineSeparator(); From bdbb42f1b7edc189891837a77c596aed8a10a105 Mon Sep 17 00:00:00 2001 From: esaunders Date: Thu, 31 Aug 2017 11:15:44 -0400 Subject: [PATCH 26/29] Missed CURRENT_CASE in my first pass for Tags and BlackboardArtifactNode. --- .../autopsy/datamodel/BlackboardArtifactNode.java | 7 ++++--- Core/src/org/sleuthkit/autopsy/datamodel/Tags.java | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 2bda34f1c1..ad96345202 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -69,9 +69,10 @@ public class BlackboardArtifactNode extends DisplayableItemNode { private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName()); private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, - Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, - Case.Events.CONTENT_TAG_ADDED, - Case.Events.CONTENT_TAG_DELETED); + Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, + Case.Events.CONTENT_TAG_ADDED, + Case.Events.CONTENT_TAG_DELETED, + Case.Events.CURRENT_CASE); private static Cache contentCache = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.MINUTES). diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java index 53722568c2..64b77ac245 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java @@ -123,7 +123,8 @@ public class Tags implements AutopsyVisitableItem { private final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, Case.Events.CONTENT_TAG_ADDED, - Case.Events.CONTENT_TAG_DELETED); + Case.Events.CONTENT_TAG_DELETED, + Case.Events.CURRENT_CASE); private final PropertyChangeListener pcl = new PropertyChangeListener() { @Override From 65038d90e234265fa2e3b054b6a43e1431fc789d Mon Sep 17 00:00:00 2001 From: esaunders Date: Thu, 31 Aug 2017 11:21:47 -0400 Subject: [PATCH 27/29] Remove unused import. --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index c05256dcce..d6226a06d8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -35,7 +35,6 @@ import java.sql.Statement; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; -import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -51,7 +50,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; From 36eb41780283dccd9760904ab9f0a1182a203224 Mon Sep 17 00:00:00 2001 From: esaunders Date: Thu, 31 Aug 2017 11:31:39 -0400 Subject: [PATCH 28/29] Remove more unused import. --- Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java | 3 --- Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java | 1 - 2 files changed, 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java index 335a04930d..a8873decde 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java @@ -23,14 +23,11 @@ import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; -import java.util.HashSet; import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.Set; import java.util.logging.Level; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.openide.nodes.AbstractNode; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index 34dc9bf517..317cb42d10 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -26,7 +26,6 @@ import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.List; -import java.util.Set; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; From 0f87862d66b4684b01ea8daaa96f6b36969951a3 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 1 Sep 2017 18:12:33 -0400 Subject: [PATCH 29/29] Update version number in project.properties for a beta release --- nbproject/project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nbproject/project.properties b/nbproject/project.properties index 50957852e4..81a4c5a0c4 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -4,7 +4,7 @@ app.title=Autopsy ### lowercase version of above app.name=${branding.token} ### if left unset, version will default to today's date -app.version=4.4.1 +app.version=4.4.2 ### build.type must be one of: DEVELOPMENT, RELEASE #build.type=RELEASE build.type=DEVELOPMENT