diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/Accounts.java index d45b196331..eec9ac8930 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Accounts.java @@ -460,14 +460,15 @@ public class Accounts extends Observable implements AutopsyVisitableItem { @Override protected Node createNodeForKey(FileWithCCN key) { + //add all account artifacts for the file and the file itself to th elookup try { - List artifacts = new ArrayList<>(); + List lookupContents = new ArrayList<>(); for (long artId : key.artifactIDS) { - artifacts.add(skCase.getBlackboardArtifact(artId)); + lookupContents.add(skCase.getBlackboardArtifact(artId)); } AbstractFile abstractFileById = skCase.getAbstractFileById(key.getObjID()); - artifacts.add(abstractFileById); - return new FileWithCCNNode(key, abstractFileById, artifacts.toArray()); + lookupContents.add(abstractFileById); + return new FileWithCCNNode(key, abstractFileById, lookupContents.toArray()); } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error getting content for file with ccn hits.", ex); //NON-NLS return null; @@ -488,6 +489,15 @@ public class Accounts extends Observable implements AutopsyVisitableItem { private final FileWithCCN fileKey; private final String fileName; + /** + * Constructor + * + * @param key The FileWithCCN that backs this node. + * @param content The Content object the key represents. + * @param lookupContents The contents of this Node's lookup. It should + * contain the content object and the account + * artifacts. + */ @NbBundle.Messages({ "# {0} - raw file name", "# {1} - solr chunk id", diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 7d1c1cf5a9..fbc17420b2 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -140,7 +140,6 @@ public class BlackboardArtifactNode extends DisplayableItemNode { //if this artifact has associated content, add the action to view the content in the timeline AbstractFile file = getLookup().lookup(AbstractFile.class); if (null != file) { - actionsList.add(ViewFileInTimelineAction.createViewSourceFileAction(file)); } @@ -423,19 +422,18 @@ public class BlackboardArtifactNode extends DisplayableItemNode { List attributes = artifact.getAttributes(); String keyword = null; String regexp = null; - boolean isRegexp = false; for (BlackboardAttribute att : attributes) { final int attributeTypeID = att.getAttributeType().getTypeID(); if (attributeTypeID == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) { keyword = att.getValueString(); } else if (attributeTypeID == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()) { regexp = att.getValueString(); - isRegexp = StringUtils.isNotBlank(regexp); } else if (attributeTypeID == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) { objectId = att.getValueLong(); } } if (keyword != null) { + boolean isRegexp = StringUtils.isNotBlank(regexp); String origQuery = isRegexp ? regexp : keyword; return highlightFactory.createInstance(objectId, keyword, isRegexp, origQuery); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AccountsText.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AccountsText.java index 6b3b5c48c9..40b5e1430e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AccountsText.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AccountsText.java @@ -72,6 +72,9 @@ class AccountsText implements IndexedText, TextMarkupLookup { return displayName; } + @NbBundle.Messages({ + "AccountsText.creditCardNumber=Credit Card Number", + "AccountsText.creditCardNumbers=Credit Card Numbers"}) AccountsText(String objectId, Set keywords) { this.solrDocumentId = objectId; this.keywords.addAll(keywords); @@ -89,8 +92,8 @@ class AccountsText implements IndexedText, TextMarkupLookup { } displayName = keywords.size() == 1 - ? Bundle.ExtractedContentViewer_creditCardNumber() - : Bundle.ExtractedContentViewer_creditCardNumbers(); + ? Bundle.AccountsText_creditCardNumber() + : Bundle.AccountsText_creditCardNumbers(); } long getObjectId() { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java index f6dc52835c..443c25aa5e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java @@ -667,10 +667,10 @@ class ExtractedContentPanel extends javax.swing.JPanel { * thread and then set the panel text in the EDT Helps not to block the UI * while content from Solr is retrieved. */ - private final class SetMarkupWorker extends SwingWorker { + private final class SetMarkupWorker extends SwingWorker { + + private final IndexedText source; - private IndexedText source; - private String markup; private ProgressHandle progress; SetMarkupWorker(IndexedText source) { @@ -678,36 +678,38 @@ class ExtractedContentPanel extends javax.swing.JPanel { } @Override - protected Object doInBackground() throws Exception { + protected String doInBackground() throws Exception { progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "ExtractedContentPanel.SetMarkup.progress.loading")); progress.setDisplayName(NbBundle.getMessage(this.getClass(), "ExtractedContentPanel.SetMarkup.progress.displayName")); progress.start(); progress.switchToIndeterminate(); - markup = source.getText(); - return null; + return source.getText(); } + @NbBundle.Messages({ + "ExtractedContentPanel.SetMarkup.error=There was an error getting the text for the selected source."}) @Override protected void done() { - //super.done(); + super.done(); progress.finish(); // see if there are any errors - // @@@ BC: Display the errors to the user somehow try { - get(); + String markup = get(); + if (markup != null) { + setPanelText(markup, true); + } else { + setPanelText("", false); + } + } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, "Error getting marked up text", ex); //NON-NLS + setPanelText(Bundle.ExtractedContentPanel_SetMarkup_error(), true); } // catch and ignore if we were cancelled catch (java.util.concurrent.CancellationException ex) { } - if (markup != null) { - setPanelText(markup, true); - } else { - setPanelText("", false); - } updateControls(source); scrollToCurrentHit(source); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java index 99a84d1e48..2ffccf5ce3 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.keywordsearch; -import com.drew.lang.annotations.NotNull; import java.awt.Component; import java.awt.Cursor; import java.awt.event.ActionEvent; @@ -44,8 +43,6 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentVisitor; -import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.TskCoreException; /** @@ -56,11 +53,13 @@ import org.sleuthkit.datamodel.TskCoreException; public class ExtractedContentViewer implements DataContentViewer { private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName()); + private static final long INVALID_DOCUMENT_ID = 0L; + private static final BlackboardAttribute.Type TSK_ASSOCIATED_ARTIFACT_TYPE = new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT); + private ExtractedContentPanel panel; private volatile Node currentNode = null; private IndexedText currentSource = null; - List sources = new ArrayList<>(); /** * Constructs a content viewer that displays the indexed text associated @@ -77,9 +76,7 @@ public class ExtractedContentViewer implements DataContentViewer { */ @Override public void setNode(final Node node) { - /* - * Clear the viewer. - */ + // Clear the viewer. if (node == null) { currentNode = null; resetComponent(); @@ -98,13 +95,13 @@ public class ExtractedContentViewer implements DataContentViewer { Lookup nodeLookup = node.getLookup(); Content content = nodeLookup.lookup(Content.class); - Collection artifacts = nodeLookup.lookupAll(BlackboardArtifact.class); + /* * Assemble a collection of all of the indexed text "sources" associated * with the node. */ - sources.clear(); + List sources = new ArrayList<>(); IndexedText highlightedHitText = null; IndexedText rawContentText = null; IndexedText rawArtifactText = null; @@ -117,9 +114,59 @@ public class ExtractedContentViewer implements DataContentViewer { sources.addAll(nodeLookup.lookupAll(IndexedText.class)); if (!sources.isEmpty()) { + //if the look up had any sources use them and don't make a new one. highlightedHitText = sources.get(0); - } else if (null != content && solrHasContent(content.getId())) { - highlightedHitText = addAccountHighlightedText(artifacts, content); + } else if (null != content && solrHasContent(content.getId())) {//if the lookup didn't have any sources, and solr has indexed the content... + /* + * get all the credit card artifacts and make a AccountsText object + * that will highlight them. + */ + String solrDocumentID = String.valueOf(content.getId()); //grab the object id as the solrDocumentID + Set accountNumbers = new HashSet<>(); + try { + //if the node had artifacts in the lookup use them, other wise look up all credit card artifacts for the content. + Collection artifacts = nodeLookup.lookupAll(BlackboardArtifact.class); + artifacts = (artifacts == null || artifacts.isEmpty()) + ? content.getArtifacts(TSK_CREDIT_CARD_ACCOUNT) + : artifacts; + + /* + * For each artifact add the account number to the list of + * accountNumbers to highlight, and use the solrDocumentId + * attribute(in place of the content's object Id) if it exists + * + * NOTE: this assumes all the artifacts will be from the same + * solrDocumentId + */ + for (BlackboardArtifact artifact : artifacts) { + try { + BlackboardAttribute solrIDAttr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SOLR_DOCUMENT_ID)); + if (solrIDAttr != null) { + String valueString = solrIDAttr.getValueString(); + if (StringUtils.isNotBlank(valueString)) { + solrDocumentID = valueString; + } + } + + BlackboardAttribute keyWordAttr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_NUMBER)); + if (keyWordAttr != null) { + String valueString = keyWordAttr.getValueString(); + if (StringUtils.isNotBlank(valueString)) { + accountNumbers.add(valueString); + } + } + + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to retrieve Blackboard Attributes", ex); //NON-NLS + } + } + if (accountNumbers.isEmpty() == false) { + highlightedHitText = new AccountsText(solrDocumentID, accountNumbers); + sources.add(highlightedHitText); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to retrieve Blackboard Artifacts", ex); //NON-NLS + } } /* @@ -127,7 +174,8 @@ public class ExtractedContentViewer implements DataContentViewer { * associated with the node. */ if (null != content && solrHasContent(content.getId())) { - rawContentText = addRawContentText(content); + rawContentText = new RawText(content, content.getId()); + sources.add(rawContentText); } /* @@ -136,12 +184,30 @@ public class ExtractedContentViewer implements DataContentViewer { */ BlackboardArtifact artifact = nodeLookup.lookup(BlackboardArtifact.class); if (null != artifact) { - rawArtifactText = addRawArtifactText(artifact); + /** + * For keyword hit artifacts, add the text of the artifact that hit, + * not the hit artifact; otherwise add the text for the artifact. + */ + if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { + try { + BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_ARTIFACT_TYPE); + if (attribute != null) { + long artifactId = attribute.getValueLong(); + BlackboardArtifact associatedArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(artifactId); + rawArtifactText = new RawText(associatedArtifact, associatedArtifact.getArtifactID()); + sources.add(rawArtifactText); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting associated artifact attributes", ex); //NON-NLS + } + } else { + rawArtifactText = new RawText(artifact, artifact.getArtifactID()); + sources.add(rawArtifactText); + } + } - /* - * Now set the default source to be displayed. - */ + // Now set the default source to be displayed. if (null != highlightedHitText) { currentSource = highlightedHitText; } else if (null != rawContentText) { @@ -150,9 +216,7 @@ public class ExtractedContentViewer implements DataContentViewer { currentSource = rawArtifactText; } - /* - * Push the text sources into the panel. - */ + // Push the text sources into the panel. for (IndexedText source : sources) { int currentPage = source.getCurrentPage(); if (currentPage == 0 && source.hasNextPage()) { @@ -163,82 +227,6 @@ public class ExtractedContentViewer implements DataContentViewer { setPanel(sources); } - private IndexedText addRawContentText(Content content) { - RawText rawContentText = new RawText(content, content.getId()); - sources.add(rawContentText); - return rawContentText; - } - - /** - * For keyword hit artifacts, add the text of the artifact that hit, not the - * hit artifact; otherwise add the text for the artifact. - */ - private IndexedText addRawArtifactText(BlackboardArtifact artifact) { - RawText rawArtifactText = null; - if (artifact.getArtifactTypeID() != TSK_KEYWORD_HIT.getTypeID()) { - rawArtifactText = new RawText(artifact, artifact.getArtifactID()); - sources.add(rawArtifactText); - } else { - try { - BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_ARTIFACT_TYPE); - if (attribute != null) { - long artifactId = attribute.getValueLong(); - BlackboardArtifact associatedArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(artifactId); - rawArtifactText = new RawText(associatedArtifact, associatedArtifact.getArtifactID()); - sources.add(rawArtifactText); - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting associated artifact attributes", ex); //NON-NLS - } - } - return rawArtifactText; - } - - @NbBundle.Messages({ - "ExtractedContentViewer.creditCardNumber=Credit Card Number", - "ExtractedContentViewer.creditCardNumbers=Credit Card Numbers"}) - private AccountsText addAccountHighlightedText(Collection artifacts, @NotNull Content content) { - String objectId = String.valueOf(content.getId()); - Set keywords = new HashSet<>(); - try { - if (artifacts == null || artifacts.isEmpty()) { - artifacts = content.getArtifacts(TSK_CREDIT_CARD_ACCOUNT); - } - for (BlackboardArtifact artifact : artifacts) { - try { - BlackboardAttribute solrIDAttr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SOLR_DOCUMENT_ID)); - if (solrIDAttr != null) { - String valueString = solrIDAttr.getValueString(); - if (StringUtils.isNotBlank(valueString)) { - objectId = valueString; - } - } - - BlackboardAttribute keyWordAttr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_NUMBER)); - if (keyWordAttr != null) { - String valueString = keyWordAttr.getValueString(); - if (StringUtils.isNotBlank(valueString)) { - keywords.add(valueString); - } - } - - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Failed to retrieve Blackboard Attributes", ex); //NON-NLS - } - } - if (keywords.isEmpty() == false) { - AccountsText highlightedAccountText = new AccountsText(objectId,keywords); - - sources.add(highlightedAccountText); - return highlightedAccountText; - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Failed to retrieve Blackboard Artifacts", ex); //NON-NLS - } - return null; - } - private static final BlackboardAttribute.Type TSK_ASSOCIATED_ARTIFACT_TYPE = new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT); - private void scrollToCurrentHit() { final IndexedText source = panel.getSelectedSource(); if (source == null || !source.isSearchable()) { @@ -279,7 +267,7 @@ public class ExtractedContentViewer implements DataContentViewer { @Override public void resetComponent() { - setPanel(new ArrayList()); + setPanel(new ArrayList<>()); panel.resetDisplay(); currentNode = null; currentSource = null; @@ -302,11 +290,15 @@ public class ExtractedContentViewer implements DataContentViewer { return true; } + /* + * Is there a credit card artifact in the lookup + */ Collection artifacts = node.getLookup().lookupAll(BlackboardArtifact.class); - - for (BlackboardArtifact art : artifacts) { - if (art.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CREDIT_CARD_ACCOUNT.getTypeID()) { - return true; + if (artifacts != null) { + for (BlackboardArtifact art : artifacts) { + if (art.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CREDIT_CARD_ACCOUNT.getTypeID()) { + return true; + } } } @@ -348,19 +340,6 @@ public class ExtractedContentViewer implements DataContentViewer { } } - private class IsDirVisitor extends ContentVisitor.Default { - - @Override - protected Boolean defaultVisit(Content cntnt) { - return false; - } - - @Override - public Boolean visit(Directory d) { - return true; - } - } - /** * Check if Solr has extracted content for a given node * diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java index 6c04844d39..835fe539ee 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java @@ -315,7 +315,7 @@ class KeywordSearchResultFactory extends ChildFactory { //wrap in KeywordSearchFilterNode for the markup content, might need to override FilterNode for more customization // store the data in HighlightedMatchesSource so that it can be looked up (in content viewer) - HighlightedText highlights = new HighlightedText(key.solrObjectId, queryStr, !key.getQuery().isLiteral(), hits); + HighlightedText highlights = new HighlightedText(key.solrObjectId, queryStr, !key.getQuery().isLiteral(), false, hits); return new KeywordSearchFilterNode(highlights, kvNode); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index 26313f5776..97a67df061 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -313,7 +313,7 @@ class LuceneQuery implements KeywordSearchQuery { * * @return */ - static Set filterOneHitPerDocument(SolrDocumentList resultList) { + private Set filterOneHitPerDocument(SolrDocumentList resultList) { // sort the list so that we consistently pick the same chunk each time. // note this sort is doing a string comparison and not an integer comparison, so // chunk 10 will be smaller than chunk 9. @@ -481,7 +481,7 @@ class LuceneQuery implements KeywordSearchQuery { * Compares SolrDocuments based on their ID's. Two SolrDocuments with * different chunk numbers are considered equal. */ - static private class SolrDocumentComparatorIgnoresChunkId implements Comparator { + private class SolrDocumentComparatorIgnoresChunkId implements Comparator { @Override public int compare(SolrDocument left, SolrDocument right) {