diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index 8a3fbdae88..945d1018e7 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -34,12 +34,16 @@ import javax.swing.SwingWorker; import org.openide.nodes.Node; import org.openide.util.Lookup; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.contentviewers.Utilities; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.datamodel.ArtifactStringContent; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.datamodel.TskCoreException; /** * Instances of this class display the BlackboardArtifacts associated with the Content represented by a Node. @@ -471,7 +475,31 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat index = artifacts.indexOf(artifact); if (index == -1) { index = 0; - } + } else { + // if the artifact has an ASSOCIATED ARTIFACT, then we display the associated artifact instead + try { + for (BlackboardAttribute attr : artifact.getAttributes()) { + if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) { + long assocArtifactId = attr.getValueLong(); + int assocArtifactIndex = -1; + for (BlackboardArtifact art: artifacts) { + if (assocArtifactId == art.getArtifactID()) { + assocArtifactIndex = artifacts.indexOf(art); + break; + } + } + if (assocArtifactIndex >= 0) { + index = assocArtifactIndex; + } + break; + } + } + } + catch (TskCoreException ex) { + logger.log(Level.WARNING, "Couldn't get associated artifact to display in Content Viewer.", ex); + } + } + } if (isCancelled()) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index eaccbc35e2..87494f2e1f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -149,7 +149,12 @@ abstract class AbstractContentChildren extends Keys { public AbstractNode visit(HashsetHits hh) { return hh.new HashsetHitsRootNode(); } - + + @Override + public AbstractNode visit(InterestingHits ih) { + return ih.new InterestingHitsRootNode(); + } + @Override public AbstractNode visit(EmailExtracted ee) { return ee.new EmailExtractedRootNode(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java index f57388ae65..27d505b489 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java @@ -53,6 +53,8 @@ public interface AutopsyItemVisitor { T visit(EmailExtracted ee); T visit(TagsNodeKey tagsNodeKey); + + T visit(InterestingHits ih); T visit(DataSources i); @@ -128,7 +130,11 @@ public interface AutopsyItemVisitor { public T visit(HashsetHits hh) { return defaultVisit(hh); } - + + @Override + public T visit(InterestingHits ih) { + return defaultVisit(ih); + } @Override public T visit(EmailExtracted ee) { return defaultVisit(ee); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 6166fdd4d6..024546a808 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -55,6 +55,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), }; /** @@ -232,7 +233,8 @@ public class BlackboardArtifactNode extends DisplayableItemNode { final int attributeTypeID = attribute.getAttributeTypeID(); //skip some internal attributes that user shouldn't see if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID() - || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()) { + || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID() + || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) { continue; } else { switch (attribute.getValueType()) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index e2cef3846e..88921891fd 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -27,6 +27,8 @@ import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNod import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsRootNode; import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode; +import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsRootNode; +import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsSetNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode; @@ -84,7 +86,11 @@ public interface DisplayableItemNodeVisitor { T visit(EmailExtractedFolderNode eefn); T visit(TagsNode node); + + T visit(InterestingHitsRootNode ihrn); + T visit(InterestingHitsSetNode ihsn); + T visit(TagNameNode node); T visit(ContentTagTypeNode node); @@ -237,7 +243,17 @@ public interface DisplayableItemNodeVisitor { public T visit(HashsetHitsSetNode hhsn) { return defaultVisit(hhsn); } + + @Override + public T visit(InterestingHitsRootNode ihrn) { + return defaultVisit(ihrn); + } + @Override + public T visit(InterestingHitsSetNode ihsn) { + return defaultVisit(ihsn); + } + @Override public T visit(EmailExtractedRootNode eern) { return defaultVisit(eern); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContentChildren.java index 3559ad1395..41ae98a2d0 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContentChildren.java @@ -53,6 +53,8 @@ public class ExtractedContentChildren extends ChildFactory 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.datamodel; + + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.nodes.Sheet; +import org.openide.util.lookup.Lookups; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskException; + + +public class InterestingHits implements AutopsyVisitableItem { + + private static final String INTERESTING_ITEMS = "INTERESTING ITEMS"; + private static final String DISPLAY_NAME = "Interesting Items"; + private static final Logger logger = Logger.getLogger(InterestingHits.class.getName()); + private SleuthkitCase skCase; + private Map> interestingItemsMap; + + public InterestingHits(SleuthkitCase skCase) { + this.skCase = skCase; + interestingItemsMap = new LinkedHashMap<>(); + } + + @SuppressWarnings("deprecation") + private void initArtifacts() { + interestingItemsMap.clear(); + loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); + loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); + } + + /* + * Reads the artifacts of specified type, grouped by Set, and loads into the interestingItemsMap + */ + private void loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE artType) { + ResultSet rs = null; + try { + int setNameId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); + int artId = artType.getTypeID(); + String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " + + "FROM blackboard_attributes,blackboard_artifacts WHERE " + + "attribute_type_id=" + setNameId + + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" + + " AND blackboard_artifacts.artifact_type_id=" + artId; + rs = skCase.runQuery(query); + while (rs.next()) { + String value = rs.getString("value_text"); + long artifactId = rs.getLong("artifact_id"); + if (!interestingItemsMap.containsKey(value)) { + interestingItemsMap.put(value, new HashSet()); + } + interestingItemsMap.get(value).add(artifactId); + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "SQL Exception occurred: ", ex); + } + finally { + if (rs != null) { + try { + skCase.closeRunQuery(rs); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Error closing result set after getting artifacts", ex); + } + } + } + } + + @Override + public T accept(AutopsyItemVisitor v) { + return v.visit(this); + } + + /** + * Node for the interesting items + */ + public class InterestingHitsRootNode extends DisplayableItemNode { + + public InterestingHitsRootNode() { + super(Children.create(new InterestingHitsRootChildren(), true), Lookups.singleton(DISPLAY_NAME)); + super.setName(INTERESTING_ITEMS); + super.setDisplayName(DISPLAY_NAME); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); + initArtifacts(); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set ss = s.get(Sheet.PROPERTIES); + if (ss == null) { + ss = Sheet.createPropertiesSet(); + s.put(ss); + } + + ss.put(new NodeProperty("Name", + "Name", + "no description", + getName())); + + return s; + } + } + + private class InterestingHitsRootChildren extends ChildFactory { + + @Override + protected boolean createKeys(List list) { + list.addAll(interestingItemsMap.keySet()); + return true; + } + + @Override + protected Node createNodeForKey(String key) { + return new InterestingHitsSetNode(key, interestingItemsMap.get(key)); + } + } + + public class InterestingHitsSetNode extends DisplayableItemNode { + + public InterestingHitsSetNode(String name, Set children) { + super(Children.create(new InterestingHitsSetChildren(children), true), Lookups.singleton(name)); + super.setName(name); + super.setDisplayName(name + " (" + children.size() + ")"); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + + @Override + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set ss = s.get(Sheet.PROPERTIES); + if (ss == null) { + ss = Sheet.createPropertiesSet(); + s.put(ss); + } + + ss.put(new NodeProperty("Name", + "Name", + "no description", + getName())); + + return s; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + } + + private class InterestingHitsSetChildren extends ChildFactory { + + private Set children; + + private InterestingHitsSetChildren(Set children) { + super(); + this.children = children; + } + + @Override + protected boolean createKeys(List list) { + for (long l : children) { + try { + list.add(skCase.getBlackboardArtifact(l)); + } catch (TskException ex) { + logger.log(Level.WARNING, "TSK Exception occurred", ex); + } + } + return true; + } + + @Override + protected Node createNodeForKey(BlackboardArtifact artifact) { + return new BlackboardArtifactNode(artifact); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ResultsNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ResultsNode.java index d87f1f2670..cbd1d941ea 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ResultsNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ResultsNode.java @@ -35,6 +35,7 @@ public class ResultsNode extends DisplayableItemNode { new KeywordHits(sleuthkitCase), new HashsetHits(sleuthkitCase), new EmailExtracted(sleuthkitCase), + new InterestingHits(sleuthkitCase), new TagsNodeKey() )), Lookups.singleton(NAME)); setName(NAME); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java index 8b1e2ecc47..da6843c8d2 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java @@ -84,7 +84,12 @@ public class RootContentChildren extends AbstractContentChildren { case TSK_TAG_ARTIFACT: if (o instanceof TagsNodeKey) this.refreshKey(o); - break; + break; + case TSK_INTERESTING_FILE_HIT: + case TSK_INTERESTING_ARTIFACT_HIT: + if (o instanceof InterestingHits) + this.refreshKey(o); + break; default: if (o instanceof ExtractedContent) this.refreshKey(o); @@ -96,9 +101,11 @@ public class RootContentChildren extends AbstractContentChildren { this.refreshKey(o); else if (o instanceof KeywordHits) this.refreshKey(o); + else if (o instanceof TagsNodeKey) + this.refreshKey(o); else if (o instanceof EmailExtracted) this.refreshKey(o); - else if (o instanceof TagsNodeKey) + else if (o instanceof InterestingHits) this.refreshKey(o); else if (o instanceof ExtractedContent) this.refreshKey(o); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 6dfd351ad6..b4e52a0f3c 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -56,6 +56,8 @@ import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNod import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsRootNode; import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode; +import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsRootNode; +import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsSetNode; import org.sleuthkit.autopsy.datamodel.ImageNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; @@ -184,7 +186,7 @@ public class DataResultFilterNode extends FilterNode { final int artifactTypeID = ba.getArtifactTypeID(); if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() - || artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { + || artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() ) { actions.add(new ViewContextAction("View File in Directory", ban)); } else { // if the artifact links to another file, add an action to go to @@ -365,7 +367,17 @@ public class DataResultFilterNode extends FilterNode { public AbstractAction visit(HashsetHitsSetNode hhsn) { return openChild(hhsn); } + + @Override + public AbstractAction visit(InterestingHitsRootNode iarn) { + return openChild(iarn); + } + @Override + public AbstractAction visit(InterestingHitsSetNode iasn) { + return openChild(iasn); + } + @Override public AbstractAction visit(EmailExtractedRootNode eern) { return openChild(eern); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index f25e89e741..250bc12bca 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -969,6 +969,23 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } catch (TskException ex) { logger.log(Level.WARNING, "Error retrieving attributes", ex); } + } else if ( type.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT) || + type.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT) ) { + Node interestingItemsRootNode = resultsChilds.findChild(type.getLabel()); + Children interestingItemsRootChildren = interestingItemsRootNode.getChildren(); + try { + String setName = null; + List attributes = art.getAttributes(); + for (BlackboardAttribute att : attributes) { + int typeId = att.getAttributeTypeID(); + if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) { + setName = att.getValueString(); + } + } + treeNode = interestingItemsRootChildren.findChild(setName); + } catch (TskException ex) { + logger.log(Level.WARNING, "Error retrieving attributes", ex); + } } else { Node extractedContent = resultsChilds.findChild(ExtractedContentNode.NAME); Children extractedChilds = extractedContent.getChildren(); diff --git a/Core/src/org/sleuthkit/autopsy/images/interesting_item.png b/Core/src/org/sleuthkit/autopsy/images/interesting_item.png new file mode 100644 index 0000000000..f538804132 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/interesting_item.png differ diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index 5978c64bef..4942c41a14 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -302,7 +302,7 @@ public final class KeywordSearchIngestModule extends IngestModuleAbstractFile { commitTimer.stop(); searchTimer.stop(); commitTimer = null; - searchTimer = null; + //searchTimer = null; // do not collect, final searcher might still be running, in which case it throws an exception textExtractors.clear(); textExtractors = null;