diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java index 6495e9d7fe..9e3555d438 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java @@ -32,7 +32,6 @@ import java.util.Observable; import java.util.Observer; import java.util.Set; import java.util.logging.Level; -import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -45,10 +44,12 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode; /** * Support for TSK_EMAIL_MSG nodes and displaying emails in the directory tree. @@ -59,7 +60,6 @@ import org.sleuthkit.datamodel.TskCoreException; public class EmailExtracted implements AutopsyVisitableItem { private static final String LABEL_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getLabel(); - private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getDisplayName(); private static final Logger logger = Logger.getLogger(EmailExtracted.class.getName()); private static final String MAIL_ACCOUNT = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailAccount.text"); private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text"); @@ -91,14 +91,6 @@ public class EmailExtracted implements AutopsyVisitableItem { private SleuthkitCase skCase; private final EmailResults emailResults; private final long filteringDSObjId; // 0 if not filtering/grouping by data source - - /** - * Returns the display name for this module. - * @return The display name for this module. - */ - static String getDisplayName() { - return DISPLAY_NAME; - } /** * Constructor @@ -213,12 +205,16 @@ public class EmailExtracted implements AutopsyVisitableItem { * Mail root node grouping all mail accounts, supports account-> folder * structure */ - public class RootNode extends DisplayableItemNode { + public class RootNode extends UpdatableTypeCountNode { public RootNode() { - super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME)); + super(Children.create(new AccountFactory(), true), + Lookups.singleton(TSK_EMAIL_MSG.getDisplayName()), + TSK_EMAIL_MSG.getDisplayName(), + filteringDSObjId, + new BlackboardArtifact.Type(TSK_EMAIL_MSG)); + //super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME)); super.setName(LABEL_NAME); - super.setDisplayName(DISPLAY_NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS emailResults.update(); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index 2204fcf64c..6893c546db 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -20,9 +20,11 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Level; @@ -32,6 +34,7 @@ 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; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.Lookups; @@ -185,12 +188,14 @@ public class ExtractedContent implements AutopsyVisitableItem { } private static class AnalysisResultsTypeFactory extends TypeFactory { + AnalysisResultsTypeFactory(long filteringDSObjId) { super(Category.ANALYSIS_RESULT, filteringDSObjId); } } private static class DataArtifactsTypeFactory extends TypeFactory { + DataArtifactsTypeFactory(long filteringDSObjId) { super(Category.DATA_ARTIFACT, filteringDSObjId); } @@ -274,7 +279,9 @@ public class ExtractedContent implements AutopsyVisitableItem { null, Sets.newHashSet(new BlackboardArtifact.Type(TSK_KEYWORD_HIT))); - } else if (TSK_EMAIL_MSG.getTypeID() == typeId) { + } else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId || + TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) { + InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, dsObjId).new RootNode(); return new TypeNodeRecord( interestingHitsNode, @@ -351,13 +358,11 @@ public class ExtractedContent implements AutopsyVisitableItem { protected boolean createKeys(List list) { try { - // Potentially can reuse - List types = (this.filteringDSObjId > 0) - ? Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getArtifactTypesInUse(this.filteringDSObjId) - : Case.getCurrentCaseThrows().getSleuthkitCase().getArtifactTypesInUse(); - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - + List types = (this.filteringDSObjId > 0) + ? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId) + : skCase.getArtifactTypesInUse(); + List allKeysSorted = types.stream() .filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp)) .map(tp -> { @@ -435,41 +440,84 @@ public class ExtractedContent implements AutopsyVisitableItem { } } + public static abstract class UpdatableTypeCountNode extends DisplayableItemNode { + + private static final Logger logger = Logger.getLogger(UpdatableTypeCountNode.class.getName()); + + private final Set types; + private final long filteringDSObjId; + private long childCount = 0; + private final String baseName; + + /** + * Constructs a node that is eligible for display in the tree view or + * results view. Capabilitites include accepting a + * DisplayableItemNodeVisitor, indicating whether or not the node is a + * leaf node, providing an item type string suitable for use as a key, + * and storing information about a child node that is to be selected if + * the node is selected in the tree view. + * + * @param children The Children object for the node. + * @param lookup The Lookup object for the node. + */ + public UpdatableTypeCountNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type... types) { + super(children, lookup); + this.types = Stream.of(types).collect(Collectors.toSet()); + this.filteringDSObjId = filteringDSObjId; + this.baseName = baseName; + updateDisplayName(); + } + + protected long getChildCount() { + return this.childCount; + } + + void updateDisplayName() { + try { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + + int count = 0; + for (BlackboardArtifact.Type type : this.types) { + if (filteringDSObjId > 0) { + count += skCase.getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId); + } else { + count += skCase.getBlackboardArtifactsTypeCount(type.getTypeID()); + } + } + + this.childCount = count; + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Error fetching data when case closed.", ex); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS + } + super.setDisplayName(this.baseName + " \u200E(\u200E" + this.childCount + ")\u200E"); + } + } + /** * Node encapsulating blackboard artifact type. This is used on the * left-hand navigation side of the Autopsy UI as the parent node for all of * the artifacts of a given type. Its children will be * BlackboardArtifactNode objects. */ - public static class TypeNode extends DisplayableItemNode { + public static class TypeNode extends UpdatableTypeCountNode { private static final Logger logger = Logger.getLogger(TypeNode.class.getName()); private final BlackboardArtifact.Type type; - private long childCount = 0; - private final long filteringDSObjId; TypeNode(BlackboardArtifact.Type type, long filteringDSObjId) { - super(Children.create(new ArtifactFactory(type, filteringDSObjId), true), Lookups.singleton(type.getDisplayName())); + super(Children.create(new ArtifactFactory(type, filteringDSObjId), true), + Lookups.singleton(type.getDisplayName()), + type.getDisplayName(), + filteringDSObjId, + type); + super.setName(type.getTypeName()); this.type = type; - this.filteringDSObjId = filteringDSObjId; String iconPath = IconsUtil.getIconFilePath(type.getTypeID()); setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); - updateDisplayName(); - } - - final void updateDisplayName() { - try { - this.childCount = (filteringDSObjId > 0) - ? Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId) - : Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifactsTypeCount(type.getTypeID()); - } catch (NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Error fetching data when case closed.", ex); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS - } - super.setDisplayName(type.getDisplayName() + " \u200E(\u200E" + childCount + ")\u200E"); } @Override @@ -489,7 +537,7 @@ public class ExtractedContent implements AutopsyVisitableItem { sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"), NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"), NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"), - childCount)); + getChildCount())); return sheet; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index e9eeb392d0..c063081699 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -47,10 +47,14 @@ import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode; + /** * Hash set hits node support. Inner classes have all of the nodes in the tree. @@ -66,13 +70,6 @@ public class HashsetHits implements AutopsyVisitableItem { private final HashsetResults hashsetResults; private final long filteringDSObjId; // 0 if not filtering/grouping by data source - /** - * Returns the display name for this module. - * @return The display name for this module. - */ - static String getDisplayName() { - return DISPLAY_NAME; - } /** * Constructor @@ -176,10 +173,15 @@ public class HashsetHits implements AutopsyVisitableItem { /** * Top-level node for all hash sets */ - public class RootNode extends DisplayableItemNode { + public class RootNode extends UpdatableTypeCountNode { public RootNode() { - super(Children.create(new HashsetNameFactory(), true), Lookups.singleton(DISPLAY_NAME)); + super(Children.create(new HashsetNameFactory(), true), + Lookups.singleton(DISPLAY_NAME), + DISPLAY_NAME, + filteringDSObjId, + new BlackboardArtifact.Type(TSK_HASHSET_HIT)); + super.setName(HASHSET_HITS); super.setDisplayName(DISPLAY_NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hashset_hits.png"); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index 8db8588338..93b6480c26 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -50,6 +50,9 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; + public class InterestingHits implements AutopsyVisitableItem { @@ -63,14 +66,6 @@ public class InterestingHits implements AutopsyVisitableItem { private final InterestingResults interestingResults = new InterestingResults(); private final long filteringDSObjId; // 0 if not filtering/grouping by data source - /** - * Returns the display name for this module. - * @return The display name for this module. - */ - static String getDisplayName() { - return DISPLAY_NAME; - } - /** * Constructor * @@ -173,12 +168,16 @@ public class InterestingHits implements AutopsyVisitableItem { /** * Node for the interesting items */ - public class RootNode extends DisplayableItemNode { + public class RootNode extends UpdatableTypeCountNode { public RootNode() { - super(Children.create(new SetNameFactory(), true), Lookups.singleton(DISPLAY_NAME)); + super(Children.create(new SetNameFactory(), true), + Lookups.singleton(DISPLAY_NAME), + DISPLAY_NAME, + filteringDSObjId, + new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT), + new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT)); super.setName(INTERESTING_ITEMS); - super.setDisplayName(DISPLAY_NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index 230e35ff65..82ea4263b1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -55,6 +55,8 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; +import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode; /** * Keyword hits node support @@ -104,15 +106,6 @@ public class KeywordHits implements AutopsyVisitableItem { return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME)); } - - /** - * Returns the display name for KeywordHits. - * @return The display name for KeywordHits. - */ - static String getDisplayName() { - return KEYWORD_HITS; - } - /** * Constructor * @@ -384,12 +377,16 @@ public class KeywordHits implements AutopsyVisitableItem { } // Created by CreateAutopsyNodeVisitor - public class RootNode extends DisplayableItemNode { + public class RootNode extends UpdatableTypeCountNode { public RootNode() { - super(Children.create(new ListFactory(), true), Lookups.singleton(KEYWORD_HITS)); + super(Children.create(new ListFactory(), true), + Lookups.singleton(KEYWORD_HITS), + KEYWORD_HITS, + filteringDSObjId, + new BlackboardArtifact.Type(TSK_KEYWORD_HIT)); + super.setName(NAME); - super.setDisplayName(KEYWORD_HITS); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index 5ca9ba08c5..2eb2c6e021 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -69,6 +69,8 @@ import org.sleuthkit.autopsy.datamodel.CreditCards; import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; +import org.sleuthkit.autopsy.datamodel.ExtractedContent; +import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -77,6 +79,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; @@ -115,14 +118,6 @@ final public class Accounts implements AutopsyVisitableItem { private final AccountTypeResults accountTypeResults; - /** - * Returns the display name for this module. - * @return The display name for this module. - */ - public static String getDisplayName() { - return DISPLAY_NAME; - } - /** * Constructor * @@ -241,12 +236,16 @@ final public class Accounts implements AutopsyVisitableItem { * Top-level node for the accounts tree */ @NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"}) - final public class AccountsRootNode extends DisplayableItemNode { + final public class AccountsRootNode extends UpdatableTypeCountNode { public AccountsRootNode() { - super(Children.create(new AccountTypeFactory(), true), Lookups.singleton(Accounts.this)); + super(Children.create(new AccountTypeFactory(), true), + Lookups.singleton(Accounts.this), + DISPLAY_NAME, + filteringDSObjId, + new BlackboardArtifact.Type(TSK_ACCOUNT)); + setName(Accounts.NAME); - setDisplayName(DISPLAY_NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS } @@ -303,7 +302,7 @@ final public class Accounts implements AutopsyVisitableItem { = "SELECT blackboard_attributes.value_text as account_type, COUNT(*) as count " + " FROM blackboard_artifacts " //NON-NLS + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS - + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS + + " WHERE blackboard_artifacts.artifact_type_id = " + TSK_ACCOUNT.getTypeID() //NON-NLS + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS + getFilterByDataSourceClause() + " GROUP BY blackboard_attributes.value_text ";