diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourcePastCasesSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourcePastCasesSummary.java index edd25d5833..f95cf30206 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourcePastCasesSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourcePastCasesSummary.java @@ -5,47 +5,58 @@ */ package org.sleuthkit.autopsy.datasourcesummary.datamodel; -import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; -import org.apache.commons.lang3.StringUtils; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory; 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.DataSource; +import org.sleuthkit.datamodel.TskCoreException; /** * * @author gregd */ public class DataSourcePastCasesSummary { - private static final String CENTRAL_REPO_INGEST_NAME = CentralRepoIngestModuleFactory.getModuleName(); - + + private static final String CENTRAL_REPO_INGEST_NAME = CentralRepoIngestModuleFactory.getModuleName().toUpperCase().trim(); + private static final String CASE_SEPARATOR = ","; private static final String PREFIX_END = ":"; private final SleuthkitCaseProvider caseProvider; private final java.util.logging.Logger logger; - - - - /** * Main constructor. */ public DataSourcePastCasesSummary() { this(SleuthkitCaseProvider.DEFAULT, org.sleuthkit.autopsy.coreutils.Logger.getLogger(DataSourceUserActivitySummary.class.getName())); - - + } private static final BlackboardAttribute.Type TYPE_COMMENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COMMENT); - /** + + + private static boolean isCentralRepoGenerated(List sources) { + if (sources == null) { + return false; + } + + return sources.stream().anyMatch((str) -> (str == null) ? false : CENTRAL_REPO_INGEST_NAME.equals(str.toUpperCase().trim())); + } + + + + /** * Gets a list of cases from the TSK_COMMENT of an artifact. The cases * string is expected to be of a form of "Previous Case: * case1,case2...caseN". @@ -54,23 +65,45 @@ public class DataSourcePastCasesSummary { * * @return The list of cases if found or empty list if not. */ - private static List getCases(BlackboardArtifact artifact) { - String casesString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_COMMENT); - if (StringUtils.isBlank(casesString)) { + + + + + + private static List getCasesFromArtifact(BlackboardArtifact artifact) { + if (artifact == null) { + return Collections.emptyList(); + } + + BlackboardAttribute commentAttr = null; + try { + commentAttr = artifact.getAttribute(TYPE_COMMENT); + } catch (TskCoreException ignored) { + // ignore if no attribute can be found + } + + if (commentAttr == null) { + return Collections.emptyList(); + } + + if (!isCentralRepoGenerated(commentAttr.getSources())) { + return Collections.emptyList(); + } + + String commentStr = commentAttr.getValueString(); + + int prefixCharIdx = commentStr.indexOf(PREFIX_END); + if (prefixCharIdx < 0 || prefixCharIdx >= commentStr.length() - 1) { return Collections.emptyList(); } - int prefixCharIdx = casesString.indexOf(PREFIX_END); - if (prefixCharIdx < 0 || prefixCharIdx >= casesString.length() - 1) { - return Collections.emptyList(); - } - - String justCasesStr = casesString.substring(prefixCharIdx + 1).trim(); - return Arrays.asList(justCasesStr.split(CASE_SEPARATOR)); + String justCasesStr = commentStr.substring(prefixCharIdx + 1).trim(); + return Stream.of(justCasesStr.split(CASE_SEPARATOR)) + .map(String::trim) + .collect(Collectors.toList()); + } - - /** * Main constructor with external dependencies specified. This constructor * is designed with unit testing in mind since mocked dependencies can be @@ -87,11 +120,36 @@ public class DataSourcePastCasesSummary { this.logger = logger; } - public List> getPastCasesWithNotableFile(DataSource dataSource) { + private List> getPastCases(DataSource dataSource, ARTIFACT_TYPE artifactType) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException { + // get a list of case names grouped by case insensitive grouping of names + + Collection> cases = this.caseProvider.get().getBlackboard().getArtifacts(artifactType.getTypeID(), dataSource.getId()) + .stream() + // convert to list of cases where there is a TSK_COMMENT from the central repo + .flatMap((art) -> getCasesFromArtifact(art).stream()) + // group by case insensitive compare of cases + .collect(Collectors.groupingBy((caseStr) -> caseStr.toUpperCase().trim())) + .values(); + return cases + .stream() + // get any cases where an actual case is found + .filter((lst) -> lst != null && lst.size() > 0) + // get non-normalized (i.e. not all caps) case name and number of items found + .map((lst) -> Pair.of(lst.get(0), (long) lst.size())) + // sorted descending + .sorted((a,b) -> -Long.compare(a.getValue(), b.getValue())) + .collect(Collectors.toList()); } - public List> getPastCasesWithSameId(DataSource dataSource) { + public List> getPastCasesWithNotableFile(DataSource dataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException { + return getPastCases(dataSource, ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); + } + public List> getPastCasesWithSameId(DataSource dataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException { + return getPastCases(dataSource, ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.form new file mode 100644 index 0000000000..4f9abb50dc --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.form @@ -0,0 +1,28 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java new file mode 100644 index 0000000000..75cd0b2d12 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java @@ -0,0 +1,54 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.datasourcesummary.ui; + +import org.sleuthkit.datamodel.DataSource; + +/** + * + * @author gregd + */ +public class PastCasesPanel extends BaseDataSourceSummaryPanel { + + /** + * Creates new form PastCasesPanel + */ + public PastCasesPanel() { + initComponents(); + } + + @Override + protected void onNewDataSource(DataSource dataSource) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * 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() { + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 400, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 300, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + + + + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables +}