diff --git a/Core/build.xml b/Core/build.xml index 0135ad0b1d..d246edaa54 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -86,7 +86,7 @@ - + diff --git a/Core/ivy.xml b/Core/ivy.xml index 601077eb91..1ed71d69cf 100644 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -29,6 +29,8 @@ + + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 80bb473653..90ce31dab9 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -15,7 +15,7 @@ file.reference.postgresql-9.4.1211.jre7.jar=release/modules/ext/postgresql-9.4.1 file.reference.Rejistry-1.0-SNAPSHOT.jar=release/modules/ext/Rejistry-1.0-SNAPSHOT.jar file.reference.sevenzipjbinding-AllPlatforms.jar=release/modules/ext/sevenzipjbinding-AllPlatforms.jar file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar -file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar +file.reference.sqlite-jdbc-3.8.11.jar=release\\modules\\ext\\sqlite-jdbc-3.8.11.jar file.reference.StixLib.jar=release/modules/ext/StixLib.jar file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar file.reference.jackcess-2.1.8.jar=release/modules/ext/jackcess-2.1.8.jar @@ -35,6 +35,7 @@ file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar +file.reference.xlsx-streamer-1.2.1.jar=release/modules/ext/xlsx-streamer-1.2.1.jar file.reference.xmpcore-5.1.3.jar=release/modules/ext/xmpcore-5.1.3.jar file.reference.xz-1.6.jar=release/modules/ext/xz-1.6.jar file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 859db9f0d2..303d14422c 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -337,6 +337,7 @@ org.sleuthkit.autopsy.modules.vmextractor org.sleuthkit.autopsy.progress org.sleuthkit.autopsy.report + org.sleuthkit.autopsy.tabulardatareader org.sleuthkit.datamodel @@ -387,6 +388,10 @@ ext/sevenzipjbinding.jar release/modules/ext/sevenzipjbinding.jar + + ext/sleuthkit-postgresql-4.6.2.jar + release/modules/ext/sleuthkit-postgresql-4.6.2.jar + ext/mchange-commons-java-0.2.9.jar release/modules/ext/mchange-commons-java-0.2.9.jar @@ -411,10 +416,6 @@ ext/metadata-extractor-2.10.1.jar release/modules/ext/metadata-extractor-2.10.1.jar - - ext/sleuthkit-postgresql-4.6.2.jar - release/modules/ext/sleuthkit-postgresql-4.6.2.jar - ext/tika-core-1.17.jar release/modules/ext/tika-core-1.17.jar @@ -441,7 +442,7 @@ ext/sqlite-jdbc-3.8.11.jar - release/modules/ext/sqlite-jdbc-3.8.11.jar + release\modules\ext\sqlite-jdbc-3.8.11.jar ext/activemq-all-5.11.1.jar @@ -487,6 +488,14 @@ ext/jdom-2.0.5-contrib.jar release/modules/ext/jdom-2.0.5-contrib.jar + + ext/SparseBitSet-1.1.jar + release/modules/ext/SparseBitSet-1.1.jar + + + ext/xlsx-streamer-1.2.1.jar + release/modules/ext/xlsx-streamer-1.2.1.jar + ext/pdfbox-2.0.8.jar release/modules/ext/pdfbox-2.0.8.jar @@ -499,10 +508,6 @@ ext/xmpcore-5.1.3.jar release/modules/ext/xmpcore-5.1.3.jar - - ext/SparseBitSet-1.1.jar - release/modules/ext/SparseBitSet-1.1.jar - diff --git a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java index ef7e853d68..cf0c27fb27 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java +++ b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java @@ -24,10 +24,8 @@ import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.logging.Level; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.TreeMap; import javax.swing.AbstractAction; import javax.swing.ActionMap; @@ -57,7 +55,7 @@ public class GetTagNameAndCommentDialog extends JDialog { private final List tagNamesList = new ArrayList<>(); private final List standardTagNamesList = new ArrayList<>(); private TagNameAndComment tagNameAndComment = null; - + public static class TagNameAndComment { private final TagName tagName; @@ -105,7 +103,16 @@ public class GetTagNameAndCommentDialog extends JDialog { public static TagNameAndComment doDialog(Window owner) { GetTagNameAndCommentDialog dialog = new GetTagNameAndCommentDialog(owner); dialog.display(); - return dialog.tagNameAndComment; + return dialog.getTagNameAndComment(); + } + + /** + * Get the TagNameAndComment. + * + * @return the tagNameAndComment + */ + private TagNameAndComment getTagNameAndComment() { + return tagNameAndComment; } private GetTagNameAndCommentDialog(Window owner) { @@ -114,14 +121,14 @@ public class GetTagNameAndCommentDialog extends JDialog { ModalityType.APPLICATION_MODAL); } - private void display() { initComponents(); tagCombo.setRenderer(new DefaultListCellRenderer() { private static final long serialVersionUID = 1L; + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - String status = ((TagName) value).getKnownStatus() == TskData.FileKnown.BAD ?TagsManager.getNotableTagLabel() : ""; + String status = ((TagName) value).getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : ""; String newValue = ((TagName) value).getDisplayName() + status; return super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus); } @@ -151,7 +158,7 @@ public class GetTagNameAndCommentDialog extends JDialog { TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager(); List standardTagNames = TagsManager.getStandardTagNames(); Map tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap()); - + tagNamesMap.entrySet().stream().map((entry) -> entry.getValue()).forEachOrdered((tagName) -> { if (standardTagNames.contains(tagName.getDisplayName())) { standardTagNamesList.add(tagName); @@ -159,7 +166,6 @@ public class GetTagNameAndCommentDialog extends JDialog { tagNamesList.add(tagName); } }); - } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(GetTagNameAndCommentDialog.class @@ -320,4 +326,5 @@ public class GetTagNameAndCommentDialog extends JDialog { private javax.swing.JComboBox tagCombo; private javax.swing.JLabel tagLabel; // End of variables declaration//GEN-END:variables + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 9494b3288a..4154a913b9 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -146,6 +146,8 @@ UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive +NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system +NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive. CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index 1d2b1ce1e2..a095341c37 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -319,7 +319,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener { // Display warning if there is one (but don't disable "next" button) try { - if (false == PathValidator.isValid(path, Case.getCurrentCaseThrows().getCaseType())) { + if (false == PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) { pathErrorLabel.setVisible(true); pathErrorLabel.setText(Bundle.ImageFilePanel_pathValidation_dataSourceOnCDriveError()); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java index ef985db2dd..6703b16dc5 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java @@ -290,7 +290,7 @@ final class LocalFilesPanel extends javax.swing.JPanel { final Case.CaseType currentCaseType = Case.getCurrentCaseThrows().getCaseType(); for (String currentPath : pathsList) { - if (!PathValidator.isValid(currentPath, currentCaseType)) { + if (!PathValidator.isValidForMultiUserCase(currentPath, currentCaseType)) { errorLabel.setVisible(true); errorLabel.setText(Bundle.LocalFilesPanel_pathValidation_dataSourceOnCDriveError()); return; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java index 5e11d5dfa0..103fd8fd6c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java @@ -191,7 +191,7 @@ final class LogicalEvidenceFilePanel extends javax.swing.JPanel implements Docum } // display warning if there is one (but don't disable "next" button) try { - if (!PathValidator.isValid(path, Case.getCurrentCaseThrows().getCaseType())) { + if (!PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) { errorLabel.setVisible(true); errorLabel.setText(Bundle.LogicalEvidenceFilePanel_pathValidation_dataSourceOnCDriveError()); return false; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index 6f365af86e..8b551ce4ec 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -29,6 +29,7 @@ import javax.swing.event.DocumentListener; import org.sleuthkit.autopsy.casemodule.Case.CaseType; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.PathValidator; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; /** * The JPanel for the first page of the new case wizard. @@ -151,10 +152,23 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { */ caseParentDirWarningLabel.setVisible(false); String parentDir = getCaseParentDir(); - if (!PathValidator.isValid(parentDir, getCaseType())) { + if (!PathValidator.isValidForMultiUserCase(parentDir, getCaseType())) { caseParentDirWarningLabel.setVisible(true); caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnCDriveError.text")); } + + /** + * Check the base case directory if it can persist data and show a + * warning if it is a wrong choice + */ + if(!PathValidator.isValidForRunningOnTarget(parentDir)){ + caseParentDirWarningLabel.setVisible(true); + if(PlatformUtil.isWindowsOS()){ + caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text" )); + } else if(System.getProperty("os.name").toLowerCase().contains("nux")) { + caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text")); + } + } /** * Enable the "Next" button for the wizard if there is text entered for diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java index d80feed87a..896298c2bd 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java @@ -47,7 +47,6 @@ import org.sleuthkit.datamodel.TskData; public class TagsManager implements Closeable { private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName()); - private final SleuthkitCase caseDb; /** @@ -71,13 +70,14 @@ public class TagsManager implements Closeable { || tagDisplayName.contains(";")); } + @NbBundle.Messages({"TagsManager.notableTagEnding.text= (Notable)"}) /** - * Get String of text which is used to label tags as notable to the user. - * + * Get String of text which is used to label tags as notable to the user. + * * @return Bundle message TagsManager.notableTagEnding.text */ - public static String getNotableTagLabel(){ + public static String getNotableTagLabel() { return Bundle.TagsManager_notableTagEnding_text(); } @@ -123,13 +123,13 @@ public class TagsManager implements Closeable { /** * Returns a list of names of standard/predefined tags - * + * * @return list of predefined tag names */ public static List getStandardTagNames() { return TagNameDefinition.getStandardTagNames(); } - + /** * Constructs a per case Autopsy service that manages the addition of * content and artifact tags to the case database. @@ -166,21 +166,79 @@ public class TagsManager implements Closeable { return caseDb.getTagNamesInUse(); } + /** + * Gets a list of all tag names currently in use in the case database for + * tagging content or artifacts by the specified user. + * + * @param userName - the user name that you want to get tags for + * + * @return A list, possibly empty, of TagName objects. + * + * @throws TskCoreException If there is an error querying the case database. + */ + public List getTagNamesInUseForUser(String userName) throws TskCoreException { + Set tagNameSet = new HashSet<>(); + List artifactTags = caseDb.getAllBlackboardArtifactTags(); + for (BlackboardArtifactTag tag : artifactTags) { + if (tag.getUserName().equals(userName)) { + tagNameSet.add(tag.getName()); + } + } + List contentTags = caseDb.getAllContentTags(); + for (ContentTag tag : contentTags) { + if (tag.getUserName().equals(userName)) { + tagNameSet.add(tag.getName()); + } + } + return new ArrayList<>(tagNameSet); + } + /** * Selects all of the rows from the tag_names table in the case database for * which there is at least one matching row in the content_tags or * blackboard_artifact_tags tables, for the given data source object id. * * @param dsObjId data source object id - * + * * @return A list, possibly empty, of TagName data transfer objects (DTOs) - * for the rows. + * for the rows. * * @throws TskCoreException */ public List getTagNamesInUse(long dsObjId) throws TskCoreException { return caseDb.getTagNamesInUse(dsObjId); } + + /** + * Selects all of the rows from the tag_names table in the case database for + * which there is at least one matching row in the content_tags or + * blackboard_artifact_tags tables, for the given data source object id and user. + * + * @param dsObjId data source object id + * @param userName - the user name that you want to get tags for + * + * @return A list, possibly empty, of TagName data transfer objects (DTOs) + * for the rows. + * + * @throws TskCoreException + */ + public List getTagNamesInUseForUser(long dsObjId, String userName) throws TskCoreException { + Set tagNameSet = new HashSet<>(); + List artifactTags = caseDb.getAllBlackboardArtifactTags(); + for (BlackboardArtifactTag tag : artifactTags) { + if (tag.getUserName().equals(userName) && tag.getArtifact().getDataSource().getId() == dsObjId) { + tagNameSet.add(tag.getName()); + } + } + List contentTags = caseDb.getAllContentTags(); + for (ContentTag tag : contentTags) { + if (tag.getUserName().equals(userName) && tag.getContent().getDataSource().getId() == dsObjId) { + tagNameSet.add(tag.getName()); + } + } + return new ArrayList<>(tagNameSet); + } + /** * Gets a map of tag display names to tag name entries in the case database. * It has keys for the display names of the standard tag types, the current @@ -416,24 +474,77 @@ public class TagsManager implements Closeable { return caseDb.getContentTagsCountByTagName(tagName); } + /** + * Gets content tags count by tag name for the specified user. + * + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. + * @param userName - the user name that you want to get tags for + * + * @return A count of the content tags with the specified tag name for the + * specified user. + * + * @throws TskCoreException If there is an error getting the tags count from + * the case database. + */ + public long getContentTagsCountByTagNameForUser(TagName tagName, String userName) throws TskCoreException { + long count = 0; + List contentTags = getContentTagsByTagName(tagName); + for (ContentTag tag : contentTags) { + if (userName.equals(tag.getUserName())) { + count++; + } + } + return count; + } + /** * Gets content tags count by tag name, for the given data source * * @param tagName The representation of the desired tag type in the case - * database, which can be obtained by calling getTagNames and/or addTagName. - * + * database, which can be obtained by calling getTagNames + * and/or addTagName. + * * @param dsObjId data source object id * * @return A count of the content tags with the specified tag name, and for - * the given data source + * the given data source * * @throws TskCoreException If there is an error getting the tags count from - * the case database. + * the case database. */ public long getContentTagsCountByTagName(TagName tagName, long dsObjId) throws TskCoreException { return caseDb.getContentTagsCountByTagName(tagName, dsObjId); } - + + /** + * Gets content tags count by tag name, for the given data source and user + * + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. + * + * @param dsObjId data source object id + * @param userName - the user name that you want to get tags for + * + * @return A count of the content tags with the specified tag name, and for + * the given data source and user + * + * @throws TskCoreException If there is an error getting the tags count from + * the case database. + */ + public long getContentTagsCountByTagNameForUser(TagName tagName, long dsObjId, String userName) throws TskCoreException { + long count = 0; + List contentTags = getContentTagsByTagName(tagName, dsObjId); + for (ContentTag tag : contentTags) { + if (userName.equals(tag.getUserName())) { + count++; + } + } + return count; + } + /** * Gets a content tag by tag id. * @@ -463,11 +574,11 @@ public class TagsManager implements Closeable { return caseDb.getContentTagsByTagName(tagName); } - /** + /** * Gets content tags by tag name, for the given data source. * * @param tagName The tag name of interest. - * + * * @param dsObjId data source object id * * @return A list, possibly empty, of the content tags with the specified @@ -479,7 +590,7 @@ public class TagsManager implements Closeable { public List getContentTagsByTagName(TagName tagName, long dsObjId) throws TskCoreException { return caseDb.getContentTagsByTagName(tagName, dsObjId); } - + /** * Gets content tags count by content. * @@ -581,6 +692,31 @@ public class TagsManager implements Closeable { return caseDb.getBlackboardArtifactTagsCountByTagName(tagName); } + /** + * Gets an artifact tags count by tag name for a specific user. + * + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. + * @param userName - the user name that you want to get tags for + * + * @return A count of the artifact tags with the specified tag name for the + * specified user. + * + * @throws TskCoreException If there is an error getting the tags count from + * the case database. + */ + public long getBlackboardArtifactTagsCountByTagNameForUser(TagName tagName, String userName) throws TskCoreException { + long count = 0; + List artifactTags = getBlackboardArtifactTagsByTagName(tagName); + for (BlackboardArtifactTag tag : artifactTags) { + if (userName.equals(tag.getUserName())) { + count++; + } + } + return count; + } + /** * Gets an artifact tags count by tag name, for the given data source. * @@ -589,8 +725,8 @@ public class TagsManager implements Closeable { * and/or addTagName. * @param dsObjId data source object id * - * @return A count of the artifact tags with the specified tag name, - * for the given data source. + * @return A count of the artifact tags with the specified tag name, for the + * given data source. * * @throws TskCoreException If there is an error getting the tags count from * the case database. @@ -598,7 +734,34 @@ public class TagsManager implements Closeable { public long getBlackboardArtifactTagsCountByTagName(TagName tagName, long dsObjId) throws TskCoreException { return caseDb.getBlackboardArtifactTagsCountByTagName(tagName, dsObjId); } - + + /** + * Gets an artifact tags count by tag name, for the given data source and + * user. + * + * @param tagName The representation of the desired tag type in the case + * database, which can be obtained by calling getTagNames + * and/or addTagName. + * @param dsObjId data source object id + * @param userName - the user name that you want to get tags for + * + * @return A count of the artifact tags with the specified tag name, for the + * given data source and user. + * + * @throws TskCoreException If there is an error getting the tags count from + * the case database. + */ + public long getBlackboardArtifactTagsCountByTagNameForUser(TagName tagName, long dsObjId, String userName) throws TskCoreException { + long count = 0; + List artifactTags = getBlackboardArtifactTagsByTagName(tagName, dsObjId); + for (BlackboardArtifactTag tag : artifactTags) { + if (userName.equals(tag.getUserName())) { + count++; + } + } + return count; + } + /** * Gets an artifact tag by tag id. * @@ -647,7 +810,7 @@ public class TagsManager implements Closeable { public List getBlackboardArtifactTagsByTagName(TagName tagName, long dsObjId) throws TskCoreException { return caseDb.getBlackboardArtifactTagsByTagName(tagName, dsObjId); } - + /** * Gets artifact tags for a particular artifact. * diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java b/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java index df98197428..bdbf6a60b0 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java @@ -23,9 +23,8 @@ import java.util.logging.Level; import javax.swing.AbstractAction; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; -import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; @@ -42,7 +41,7 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction { private static final Logger logger = Logger.getLogger(AddEditCentralRepoCommentAction.class.getName()); private boolean addToDatabase; - private CorrelationAttribute correlationAttribute; + private CorrelationAttributeInstance correlationAttributeInstance; private String comment; /** @@ -50,9 +49,9 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction { * * @param correlationAttribute The correlation attribute to modify. */ - public AddEditCentralRepoCommentAction(CorrelationAttribute correlationAttribute) { + public AddEditCentralRepoCommentAction(CorrelationAttributeInstance correlationAttribute) { super(Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment()); - this.correlationAttribute = correlationAttribute; + this.correlationAttributeInstance = correlationAttribute; } /** @@ -63,10 +62,10 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction { */ public AddEditCentralRepoCommentAction(AbstractFile file) { super(Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment()); - correlationAttribute = EamArtifactUtil.getCorrelationAttributeFromContent(file); - if (correlationAttribute == null) { + correlationAttributeInstance = EamArtifactUtil.getInstanceFromContent(file); + if (correlationAttributeInstance == null) { addToDatabase = true; - correlationAttribute = EamArtifactUtil.makeCorrelationAttributeFromContent(file); + correlationAttributeInstance = EamArtifactUtil.makeInstanceFromContent(file); } } @@ -83,7 +82,7 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction { */ @Override public void actionPerformed(ActionEvent event) { - CentralRepoCommentDialog centralRepoCommentDialog = new CentralRepoCommentDialog(correlationAttribute); + CentralRepoCommentDialog centralRepoCommentDialog = new CentralRepoCommentDialog(correlationAttributeInstance); centralRepoCommentDialog.display(); comment = null; @@ -95,9 +94,9 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction { dbManager = EamDb.getInstance(); if (addToDatabase) { - dbManager.addArtifact(correlationAttribute); + dbManager.addArtifactInstance(correlationAttributeInstance); } else { - dbManager.updateAttributeInstanceComment(correlationAttribute); + dbManager.updateAttributeInstanceComment(correlationAttributeInstance); } comment = centralRepoCommentDialog.getComment(); @@ -127,7 +126,7 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction { * * @return The correlation attribute. */ - public CorrelationAttribute getCorrelationAttribute() { - return correlationAttribute; + public CorrelationAttributeInstance getCorrelationAttribute() { + return correlationAttributeInstance; } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties index 3223583037..1c7e6c2d7e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties @@ -5,8 +5,6 @@ OpenIDE-Module-Long-Description=\ Correlation Engine ingest module and central database. \n\n\ The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\ Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. -CentralRepoCommentDialog.fileLabel.text=File: CentralRepoCommentDialog.commentLabel.text=Comment: -CentralRepoCommentDialog.pathLabel.text= CentralRepoCommentDialog.okButton.text=&OK CentralRepoCommentDialog.cancelButton.text=C&ancel diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.form index 5a9882d4cf..8f471230d0 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.form @@ -31,14 +31,7 @@ - - - - - - - - + @@ -55,21 +48,16 @@ - - - - - - + - + - - - - + + + + - + @@ -113,20 +101,6 @@ - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.java index 529ffb8529..7b6778dffe 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.centralrepository; import org.openide.util.NbBundle.Messages; import org.openide.windows.WindowManager; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; /** @@ -31,31 +30,30 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class CentralRepoCommentDialog extends javax.swing.JDialog { - private final CorrelationAttribute correlationAttribute; + private final CorrelationAttributeInstance correlationAttributeInstance; private boolean commentUpdated = false; private String currentComment = ""; /** * Create an instance. * - * @param correlationAttribute The correlation attribute to be modified. + * @param correlationAttributeInstance The correlation attribute to be modified. */ - CentralRepoCommentDialog(CorrelationAttribute correlationAttribute) { + CentralRepoCommentDialog(CorrelationAttributeInstance correlationAttributeInstance) { super(WindowManager.getDefault().getMainWindow(), Bundle.CentralRepoCommentDialog_title_addEditCentralRepoComment()); initComponents(); - CorrelationAttributeInstance instance = correlationAttribute.getInstances().get(0); + CorrelationAttributeInstance instance = correlationAttributeInstance; // Store the original comment if (instance.getComment() != null) { currentComment = instance.getComment(); } - pathLabel.setText(instance.getFilePath()); commentTextArea.setText(instance.getComment()); - this.correlationAttribute = correlationAttribute; + this.correlationAttributeInstance = correlationAttributeInstance; } /** @@ -103,8 +101,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog { commentTextArea = new javax.swing.JTextArea(); okButton = new javax.swing.JButton(); cancelButton = new javax.swing.JButton(); - fileLabel = new javax.swing.JLabel(); - pathLabel = new javax.swing.JLabel(); commentLabel = new javax.swing.JLabel(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); @@ -131,10 +127,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog { } }); - org.openide.awt.Mnemonics.setLocalizedText(fileLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.fileLabel.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.pathLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.commentLabel.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); @@ -146,12 +138,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(fileLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pathLabel)) - .addComponent(commentLabel)) + .addComponent(commentLabel) .addGap(0, 451, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) @@ -164,17 +151,13 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(fileLabel) - .addComponent(pathLabel)) - .addGap(19, 19, 19) .addComponent(commentLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jScrollPane1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(okButton) - .addComponent(cancelButton)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cancelButton) + .addComponent(okButton)) .addContainerGap()) ); @@ -187,7 +170,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog { private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed currentComment = commentTextArea.getText(); - correlationAttribute.getInstances().get(0).setComment(currentComment); + correlationAttributeInstance.setComment(currentComment); commentUpdated = true; dispose(); @@ -197,9 +180,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog { private javax.swing.JButton cancelButton; private javax.swing.JLabel commentLabel; private javax.swing.JTextArea commentTextArea; - private javax.swing.JLabel fileLabel; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JButton okButton; - private javax.swing.JLabel pathLabel; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form index 60667bae46..9c42be16a8 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form @@ -80,7 +80,7 @@ - + @@ -106,7 +106,7 @@ - + @@ -133,23 +133,18 @@ - - - + - - - - - - - - + + + + + @@ -230,13 +225,6 @@ - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 26ceed5500..9da0b577b3 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.centralrepository.contentviewer; import java.awt.Component; +import java.awt.FontMetrics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedWriter; @@ -57,10 +58,10 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -85,10 +86,13 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi private static final long serialVersionUID = -1L; - private final static Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName()); + private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName()); + + private static final int DEFAULT_MIN_CELL_WIDTH = 15; + private static final int CELL_TEXT_WIDTH_PADDING = 5; private final DataContentViewerOtherCasesTableModel tableModel; - private final Collection correlationAttributes; + private final Collection correlationAttributes; /** * Could be null. */ @@ -125,8 +129,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi showCommonalityDetails(); } else if (jmi.equals(addCommentMenuItem)) { try { - OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(otherCasesTable.getSelectedRow()); - AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute()); + OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow()); + AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.getCorrelationAttribute()); action.actionPerformed(null); String currentComment = action.getComment(); if (currentComment != null) { @@ -149,7 +153,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi // Set background of every nth row as light grey. TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer(); otherCasesTable.setDefaultRenderer(Object.class, renderer); - tableStatusPanelLabel.setVisible(false); } @@ -175,7 +178,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi int percentage; try { EamDb dbManager = EamDb.getInstance(); - for (CorrelationAttribute eamArtifact : correlationAttributes) { + for (CorrelationAttributeInstance eamArtifact : correlationAttributes) { percentage = dbManager.getFrequencyPercentage(eamArtifact); msg.append(Bundle.DataContentViewerOtherCases_correlatedArtifacts_byType(percentage, eamArtifact.getCorrelationType().getDisplayName(), @@ -207,7 +210,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi if (-1 != selectedRowViewIdx) { EamDb dbManager = EamDb.getInstance(); int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx); - OtherOccurrenceNodeData nodeData = (OtherOccurrenceNodeData) tableModel.getRow(selectedRowModelIdx); + OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) tableModel.getRow(selectedRowModelIdx); CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase(); if (eamCasePartial == null) { JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, @@ -416,32 +419,40 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * * @return A list of attributes that can be used for correlation */ - private Collection getCorrelationAttributesFromNode(Node node) { - Collection ret = new ArrayList<>(); + private Collection getCorrelationAttributesFromNode(Node node) { + Collection ret = new ArrayList<>(); // correlate on blackboard artifact attributes if they exist and supported BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node); if (bbArtifact != null && EamDb.isEnabled()) { - ret.addAll(EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbArtifact, false, false)); + ret.addAll(EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, false)); } - + // we can correlate based on the MD5 if it is enabled - if (this.file != null && EamDb.isEnabled()) { + if (this.file != null && EamDb.isEnabled()) { try { - List artifactTypes = EamDb.getInstance().getDefinedCorrelationTypes(); + List artifactTypes = EamDb.getInstance().getDefinedCorrelationTypes(); String md5 = this.file.getMd5Hash(); if (md5 != null && !md5.isEmpty() && null != artifactTypes && !artifactTypes.isEmpty()) { - for (CorrelationAttribute.Type aType : artifactTypes) { - if (aType.getId() == CorrelationAttribute.FILES_TYPE_ID) { - ret.add(new CorrelationAttribute(aType, md5)); + for (CorrelationAttributeInstance.Type aType : artifactTypes) { + if (aType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { + CorrelationCase corCase = EamDb.getInstance().getCase(Case.getCurrentCase()); + ret.add(new CorrelationAttributeInstance( + md5, + aType, + corCase, + CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()), + file.getParentPath() + file.getName(), + "", + file.getKnown())); break; } } } - } catch (EamDbException ex) { + } catch (EamDbException | TskCoreException ex) { logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS - } + } } else { try { @@ -449,7 +460,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi if (this.file != null) { String md5 = this.file.getMd5Hash(); if (md5 != null && !md5.isEmpty()) { - ret.add(new CorrelationAttribute(CorrelationAttribute.getDefaultCorrelationTypes().get(0), md5)); + ret.add(new CorrelationAttributeInstance(CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0), md5)); } } } catch (EamDbException ex) { @@ -462,12 +473,12 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi @Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."}) /** - * Gets the list of Eam Cases and determines the earliest case creation date. - * Sets the label to display the earliest date string to the user. + * Gets the list of Eam Cases and determines the earliest case creation + * date. Sets the label to display the earliest date string to the user. */ - private void setEarliestCaseDate() { - String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable(); - + private void setEarliestCaseDate() { + String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable(); + if (EamDb.isEnabled()) { LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC); DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US); @@ -475,15 +486,15 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi EamDb dbManager = EamDb.getInstance(); List cases = dbManager.getCases(); for (CorrelationCase aCase : cases) { - LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate())); - - if (caseDate.isBefore(earliestDate)) { + LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate())); + + if (caseDate.isBefore(earliestDate)) { earliestDate = caseDate; dateStringDisplay = aCase.getCreationDate(); - } + } } - + } catch (EamDbException ex) { logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS } catch (ParseException ex) { @@ -495,10 +506,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } /** - * Query the central repo database (if enabled) and the case database to find all - * artifact instances correlated to the given central repository artifact. If the - * central repo is not enabled, this will only return files from the current case - * with matching MD5 hashes. + * Query the central repo database (if enabled) and the case database to + * find all artifact instances correlated to the given central repository + * artifact. If the central repo is not enabled, this will only return files + * from the current case with matching MD5 hashes. * * @param corAttr CorrelationAttribute to query for * @param dataSourceName Data source to filter results @@ -506,19 +517,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * * @return A collection of correlated artifact instances */ - private Map getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) { + private Map getCorrelatedInstances(CorrelationAttributeInstance corAttr, String dataSourceName, String deviceId) { // @@@ Check exception try { final Case openCase = Case.getCurrentCase(); String caseUUID = openCase.getName(); - HashMap nodeDataMap = new HashMap<>(); + HashMap nodeDataMap = new HashMap<>(); if (EamDb.isEnabled()) { List instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()); - for (CorrelationAttributeInstance artifactInstance:instances) { - + for (CorrelationAttributeInstance artifactInstance : instances) { + // Only add the attribute if it isn't the object the user selected. // We consider it to be a different object if at least one of the following is true: // - the case UUID is different @@ -530,14 +541,14 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi || !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId) || !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) { - OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); + OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); UniquePathKey uniquePathKey = new UniquePathKey(newNode); nodeDataMap.put(uniquePathKey, newNode); } } } - if (corAttr.getCorrelationType().getDisplayName().equals("Files")) { + if (corAttr.getCorrelationType().getDisplayName().equals("Files")) { List caseDbFiles = getCaseDbMatches(corAttr, openCase); for (AbstractFile caseDbFile : caseDbFiles) { @@ -560,15 +571,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } /** - * Get all other abstract files in the current case with the same MD5 as the selected node. + * Get all other abstract files in the current case with the same MD5 as the + * selected node. + * * @param corAttr The CorrelationAttribute containing the MD5 to search for * @param openCase The current case + * * @return List of matching AbstractFile objects + * * @throws NoCurrentCaseException * @throws TskCoreException - * @throws EamDbException + * @throws EamDbException */ - private List getCaseDbMatches(CorrelationAttribute corAttr, Case openCase) throws NoCurrentCaseException, TskCoreException, EamDbException { + private List getCaseDbMatches(CorrelationAttributeInstance corAttr, Case openCase) throws NoCurrentCaseException, TskCoreException, EamDbException { String md5 = corAttr.getCorrelationValue(); SleuthkitCase tsk = openCase.getSleuthkitCase(); List matches = tsk.findAllFilesWhere(String.format("md5 = '%s'", new Object[]{md5})); @@ -586,18 +601,18 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi /** * Adds the file to the nodeDataMap map if it does not already exist - * - * @param autopsyCase + * + * @param autopsyCase * @param nodeDataMap * @param newFile * * @throws TskCoreException * @throws EamDbException */ - private void addOrUpdateNodeData(final Case autopsyCase, Map nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException { - - OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(newFile, autopsyCase); - + private void addOrUpdateNodeData(final Case autopsyCase, Map nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException { + + OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase); + // If the caseDB object has a notable tag associated with it, update // the known status to BAD if (newNode.getKnown() != TskData.FileKnown.BAD) { @@ -613,13 +628,13 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi // Make a key to see if the file is already in the map UniquePathKey uniquePathKey = new UniquePathKey(newNode); - + // If this node is already in the list, the only thing we need to do is // update the known status to BAD if the caseDB version had known status BAD. // Otherwise this is a new node so add the new node to the map. if (nodeDataMap.containsKey(uniquePathKey)) { if (newNode.getKnown() == TskData.FileKnown.BAD) { - OtherOccurrenceNodeData prevInstance = nodeDataMap.get(uniquePathKey); + OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey); prevInstance.updateKnown(newNode.getKnown()); } } else { @@ -642,7 +657,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } else { return this.file != null && this.file.getSize() > 0 - && ((this.file.getMd5Hash() != null) && ( ! this.file.getMd5Hash().isEmpty())); + && ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty())); } } @@ -665,8 +680,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * * @param node The node being viewed. */ - @Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other occurrences to display.", - "DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file."}) + @Messages({ + "DataContentViewerOtherCases.table.noArtifacts=Item has no attributes with which to search.", + "DataContentViewerOtherCases.table.noResultsFound=No results found." + }) private void populateTable(Node node) { String dataSourceName = ""; String deviceId = ""; @@ -683,8 +700,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi // get the attributes we can correlate on correlationAttributes.addAll(getCorrelationAttributesFromNode(node)); - for (CorrelationAttribute corAttr : correlationAttributes) { - Map correlatedNodeDataMap = new HashMap<>(0); + for (CorrelationAttributeInstance corAttr : correlationAttributes) { + Map correlatedNodeDataMap = new HashMap<>(0); // get correlation and reference set instances from DB correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId)); @@ -696,36 +713,45 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } if (correlationAttributes.isEmpty()) { - // @@@ BC: We should have a more descriptive message than this. Mention that the file didn't have a MD5, etc. - displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_noArtifacts()); + tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noArtifacts())); + setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noArtifacts()); } else if (0 == tableModel.getRowCount()) { - displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_isempty()); + tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noResultsFound())); + setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noResultsFound()); } else { - clearMessageOnTableStatusPanel(); setColumnWidths(); } setEarliestCaseDate(); } + /** + * Adjust a given column for the text provided. + * + * @param columnIndex The index of the column to adjust. + * @param text The text whose length will be used to adjust the + * column width. + */ + private void setColumnWidthToText(int columnIndex, String text) { + TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex); + FontMetrics fontMetrics = otherCasesTable.getFontMetrics(otherCasesTable.getFont()); + int stringWidth = fontMetrics.stringWidth(text); + column.setMinWidth(stringWidth + CELL_TEXT_WIDTH_PADDING); + } + + /** + * Adjust column widths to their preferred values. + */ private void setColumnWidths() { for (int idx = 0; idx < tableModel.getColumnCount(); idx++) { TableColumn column = otherCasesTable.getColumnModel().getColumn(idx); - int colWidth = tableModel.getColumnPreferredWidth(idx); - if (0 < colWidth) { - column.setPreferredWidth(colWidth); + column.setMinWidth(DEFAULT_MIN_CELL_WIDTH); + int columnWidth = tableModel.getColumnPreferredWidth(idx); + if (columnWidth > 0) { + column.setPreferredWidth(columnWidth); } } } - private void displayMessageOnTableStatusPanel(String message) { - tableStatusPanelLabel.setText(message); - tableStatusPanelLabel.setVisible(true); - } - - private void clearMessageOnTableStatusPanel() { - tableStatusPanelLabel.setVisible(false); - } - /** * 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 @@ -749,7 +775,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi earliestCaseLabel = new javax.swing.JLabel(); earliestCaseDate = new javax.swing.JLabel(); tableStatusPanel = new javax.swing.JPanel(); - tableStatusPanelLabel = new javax.swing.JLabel(); rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() { public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) { @@ -811,8 +836,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi .addGap(0, 16, Short.MAX_VALUE) ); - tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51)); - javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel); tableContainerPanel.setLayout(tableContainerPanelLayout); tableContainerPanelLayout.setHorizontalGroup( @@ -825,20 +848,16 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi .addComponent(earliestCaseLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(earliestCaseDate) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addContainerGap()) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); tableContainerPanelLayout.setVerticalGroup( tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup() - .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 176, Short.MAX_VALUE) - .addGap(0, 0, 0) - .addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(earliestCaseLabel) - .addComponent(earliestCaseDate)) - .addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE) + .addGap(2, 2, 2) + .addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(earliestCaseLabel) + .addComponent(earliestCaseDate)) .addGap(0, 0, 0) .addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0)) @@ -857,7 +876,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi .addGap(0, 483, Short.MAX_VALUE) .addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(otherCasesPanelLayout.createSequentialGroup() - .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE) + .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE) .addGap(0, 0, 0))) ); @@ -869,7 +888,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE) + .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE) ); }// //GEN-END:initComponents @@ -879,8 +898,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) { int rowIndex = otherCasesTable.getSelectedRow(); OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex); - if (selectedNode.isCentralRepoNode()) { - enableCentralRepoActions = true; + if (selectedNode instanceof OtherOccurrenceNodeInstanceData) { + OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedNode; + enableCentralRepoActions = instanceData.isCentralRepoNode(); } } @@ -904,20 +924,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi private javax.swing.JPanel tableContainerPanel; private javax.swing.JScrollPane tableScrollPane; private javax.swing.JPanel tableStatusPanel; - private javax.swing.JLabel tableStatusPanelLabel; // End of variables declaration//GEN-END:variables /** * Used as a key to ensure we eliminate duplicates from the result set by * not overwriting CR correlation instances. */ - static final class UniquePathKey { + private static final class UniquePathKey { private final String dataSourceID; private final String filePath; private final String type; - UniquePathKey(OtherOccurrenceNodeData nodeData) { + UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) { super(); dataSourceID = nodeData.getDeviceID(); if (nodeData.getFilePath() != null) { @@ -931,10 +950,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi @Override public boolean equals(Object other) { if (other instanceof UniquePathKey) { - UniquePathKey otherKey = (UniquePathKey)(other); - return ( Objects.equals(otherKey.dataSourceID, this.dataSourceID) - && Objects.equals(otherKey.filePath, this.filePath) - && Objects.equals(otherKey.type, this.type)); + UniquePathKey otherKey = (UniquePathKey) (other); + return (Objects.equals(otherKey.getDataSourceID(), this.getDataSourceID()) + && Objects.equals(otherKey.getFilePath(), this.getFilePath()) + && Objects.equals(otherKey.getType(), this.getType())); } return false; } @@ -944,7 +963,34 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi //int hash = 7; //hash = 67 * hash + this.dataSourceID.hashCode(); //hash = 67 * hash + this.filePath.hashCode(); - return Objects.hash(dataSourceID, filePath, type); + return Objects.hash(getDataSourceID(), getFilePath(), getType()); + } + + /** + * Get the type of this UniquePathKey. + * + * @return the type + */ + String getType() { + return type; + } + + /** + * Get the file path for the UniquePathKey. + * + * @return the filePath + */ + String getFilePath() { + return filePath; + } + + /** + * Get the data source id for the UniquePathKey. + * + * @return the dataSourceID + */ + String getDataSourceID() { + return dataSourceID; } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java index 5febf88dc3..1a3527d940 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,20 +22,20 @@ import java.util.ArrayList; import java.util.List; import javax.swing.table.AbstractTableModel; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; /** * Model for cells in data content viewer table */ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { + private static final long serialVersionUID = 1L; + @Messages({"DataContentViewerOtherCasesTableModel.case=Case", "DataContentViewerOtherCasesTableModel.device=Device", "DataContentViewerOtherCasesTableModel.dataSource=Data Source", "DataContentViewerOtherCasesTableModel.path=Path", - "DataContentViewerOtherCasesTableModel.type=Correlation Type", - "DataContentViewerOtherCasesTableModel.value=Correlation Value", + "DataContentViewerOtherCasesTableModel.attribute=Matched Attribute", + "DataContentViewerOtherCasesTableModel.value=Attribute Value", "DataContentViewerOtherCasesTableModel.known=Known", "DataContentViewerOtherCasesTableModel.comment=Comment", "DataContentViewerOtherCasesTableModel.noData=No Data.",}) @@ -44,7 +44,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { // If order is changed, update the CellRenderer to ensure correct row coloring. CASE_NAME(Bundle.DataContentViewerOtherCasesTableModel_case(), 100), DATA_SOURCE(Bundle.DataContentViewerOtherCasesTableModel_dataSource(), 100), - TYPE(Bundle.DataContentViewerOtherCasesTableModel_type(), 100), + ATTRIBUTE(Bundle.DataContentViewerOtherCasesTableModel_attribute(), 125), VALUE(Bundle.DataContentViewerOtherCasesTableModel_value(), 200), KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 50), FILE_PATH(Bundle.DataContentViewerOtherCasesTableModel_path(), 450), @@ -68,7 +68,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { } }; - List nodeDataList; + private final List nodeDataList; DataContentViewerOtherCasesTableModel() { nodeDataList = new ArrayList<>(); @@ -109,26 +109,41 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { return Bundle.DataContentViewerOtherCasesTableModel_noData(); } - return mapValueById(rowIdx, TableColumns.values()[colIdx]); - } - - Object getRow(int rowIdx) { - return nodeDataList.get(rowIdx); + OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx); + TableColumns columnId = TableColumns.values()[colIdx]; + if (nodeData instanceof OtherOccurrenceNodeMessageData) { + return mapNodeMessageData((OtherOccurrenceNodeMessageData) nodeData, columnId); + } + return mapNodeInstanceData((OtherOccurrenceNodeInstanceData) nodeData, columnId); } /** - * Map a rowIdx and colId to the value in that cell. + * Map a column ID to the value in that cell for node message data. * - * @param rowIdx Index of row to search - * @param colId ID of column to search + * @param nodeData The node message data. + * @param columnId The ID of the cell column. * - * @return value in the cell + * @return The value in the cell. */ - private Object mapValueById(int rowIdx, TableColumns colId) { - OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx); + private Object mapNodeMessageData(OtherOccurrenceNodeMessageData nodeData, TableColumns columnId) { + if (columnId == TableColumns.CASE_NAME) { + return nodeData.getDisplayMessage(); + } + return ""; + } + + /** + * Map a column ID to the value in that cell for node instance data. + * + * @param nodeData The node instance data. + * @param columnId The ID of the cell column. + * + * @return The value in the cell. + */ + private Object mapNodeInstanceData(OtherOccurrenceNodeInstanceData nodeData, TableColumns columnId) { String value = Bundle.DataContentViewerOtherCasesTableModel_noData(); - switch (colId) { + switch (columnId) { case CASE_NAME: if (null != nodeData.getCaseName()) { value = nodeData.getCaseName(); @@ -147,7 +162,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { case FILE_PATH: value = nodeData.getFilePath(); break; - case TYPE: + case ATTRIBUTE: value = nodeData.getType(); break; case VALUE: @@ -159,10 +174,16 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { case COMMENT: value = nodeData.getComment(); break; + default: // This shouldn't occur! Use default "No data" value. + break; } return value; } + Object getRow(int rowIdx) { + return nodeDataList.get(rowIdx); + } + @Override public Class getColumnClass(int colIdx) { return String.class; @@ -178,6 +199,9 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { fireTableDataChanged(); } + /** + * Clear the node data table. + */ void clearTable() { nodeDataList.clear(); fireTableDataChanged(); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java index 958068fb14..da4915e75f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java @@ -1,5 +1,5 @@ /* - * Central Repository + * Autopsy Forensic Browser * * Copyright 2018 Basis Technology Corp. * Contact: carrier sleuthkit org @@ -17,217 +17,9 @@ * limitations under the License. */ package org.sleuthkit.autopsy.centralrepository.contentviewer; - -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; -import org.sleuthkit.datamodel.TskDataException; - /** - * Class for populating the Other Occurrences tab + * Marker interface for Other Occurrences nodes. */ -class OtherOccurrenceNodeData { +interface OtherOccurrenceNodeData { - // For now hard code the string for the central repo files type, since - // getting it dynamically can fail. - private static final String FILE_TYPE_STR = "Files"; - - private final String caseName; - private String deviceID; - private String dataSourceName; - private final String filePath; - private final String typeStr; - private final CorrelationAttribute.Type type; - private final String value; - private TskData.FileKnown known; - private String comment; - - private AbstractFile originalAbstractFile = null; - private CorrelationAttributeInstance originalCorrelationInstance = null; - - /** - * Create a node from a central repo instance. - * @param instance The central repo instance - * @param type The type of the instance - * @param value The value of the instance - */ - OtherOccurrenceNodeData(CorrelationAttributeInstance instance, CorrelationAttribute.Type type, String value) { - caseName = instance.getCorrelationCase().getDisplayName(); - deviceID = instance.getCorrelationDataSource().getDeviceID(); - dataSourceName = instance.getCorrelationDataSource().getName(); - filePath = instance.getFilePath(); - this.typeStr = type.getDisplayName(); - this.type = type; - this.value = value; - known = instance.getKnownStatus(); - comment = instance.getComment(); - - originalCorrelationInstance = instance; - } - - /** - * Create a node from an abstract file. - * @param newFile The abstract file - * @param autopsyCase The current case - * @throws EamDbException - */ - OtherOccurrenceNodeData(AbstractFile newFile, Case autopsyCase) throws EamDbException { - caseName = autopsyCase.getDisplayName(); - try { - DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId()); - deviceID = dataSource.getDeviceId(); - dataSourceName = dataSource.getName(); - } catch (TskDataException | TskCoreException ex) { - throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex); - } - - filePath = newFile.getParentPath() + newFile.getName(); - typeStr = FILE_TYPE_STR; - this.type = null; - value = newFile.getMd5Hash(); - known = newFile.getKnown(); - comment = ""; - - originalAbstractFile = newFile; - } - - /** - * Check if this node is a "file" type - * @return true if it is a file type - */ - boolean isFileType() { - return FILE_TYPE_STR.equals(typeStr); - } - - /** - * Update the known status for this node - * @param newKnownStatus The new known status - */ - void updateKnown(TskData.FileKnown newKnownStatus) { - known = newKnownStatus; - } - - /** - * Update the comment for this node - * @param newComment The new comment - */ - void updateComment(String newComment) { - comment = newComment; - } - - /** - * Check if this is a central repo node. - * @return true if this node was created from a central repo instance, false otherwise - */ - boolean isCentralRepoNode() { - return (originalCorrelationInstance != null); - } - - /** - * Uses the saved instance plus type and value to make a new CorrelationAttribute. - * Should only be called if isCentralRepoNode() is true. - * @return the newly created CorrelationAttribute - */ - CorrelationAttribute createCorrelationAttribute() throws EamDbException { - if (! isCentralRepoNode() ) { - throw new EamDbException("Can not create CorrelationAttribute for non central repo node"); - } - CorrelationAttribute attr = new CorrelationAttribute(type, value); - attr.addInstance(originalCorrelationInstance); - return attr; - } - - /** - * Get the case name - * @return the case name - */ - String getCaseName() { - return caseName; - } - - /** - * Get the device ID - * @return the device ID - */ - String getDeviceID() { - return deviceID; - } - - /** - * Get the data source name - * @return the data source name - */ - String getDataSourceName() { - return dataSourceName; - } - - /** - * Get the file path - * @return the file path - */ - String getFilePath() { - return filePath; - } - - /** - * Get the type (as a string) - * @return the type - */ - String getType() { - return typeStr; - } - - /** - * Get the value (MD5 hash for files) - * @return the value - */ - String getValue() { - return value; - } - - /** - * Get the known status - * @return the known status - */ - TskData.FileKnown getKnown() { - return known; - } - - /** - * Get the comment - * @return the comment - */ - String getComment() { - return comment; - } - - /** - * Get the backing abstract file. - * Should only be called if isCentralRepoNode() is false - * @return the original abstract file - */ - AbstractFile getAbstractFile() throws EamDbException { - if (originalCorrelationInstance == null) { - throw new EamDbException("AbstractFile is null"); - } - return originalAbstractFile; - } - - /** - * Get the backing CorrelationAttributeInstance. - * Should only be called if isCentralRepoNode() is true - * @return the original CorrelationAttributeInstance - * @throws EamDbException - */ - CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException { - if (originalCorrelationInstance == null) { - throw new EamDbException("CorrelationAttributeInstance is null"); - } - return originalCorrelationInstance; - } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java new file mode 100644 index 0000000000..b049613f33 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java @@ -0,0 +1,227 @@ +/* + * Central Repository + * + * Copyright 2018 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.centralrepository.contentviewer; + +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.TskDataException; + +/** + * Class for populating the Other Occurrences tab + */ +class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { + + // For now hard code the string for the central repo files type, since + // getting it dynamically can fail. + private static final String FILE_TYPE_STR = "Files"; + + private final String caseName; + private String deviceID; + private String dataSourceName; + private final String filePath; + private final String typeStr; + private final String value; + private TskData.FileKnown known; + private String comment; + + private AbstractFile originalAbstractFile = null; + private CorrelationAttributeInstance originalCorrelationInstance = null; + + /** + * Create a node from a central repo instance. + * @param instance The central repo instance + * @param type The type of the instance + * @param value The value of the instance + */ + OtherOccurrenceNodeInstanceData(CorrelationAttributeInstance instance, CorrelationAttributeInstance.Type type, String value) { + caseName = instance.getCorrelationCase().getDisplayName(); + deviceID = instance.getCorrelationDataSource().getDeviceID(); + dataSourceName = instance.getCorrelationDataSource().getName(); + filePath = instance.getFilePath(); + this.typeStr = type.getDisplayName(); + this.value = value; + known = instance.getKnownStatus(); + comment = instance.getComment(); + + originalCorrelationInstance = instance; + } + + /** + * Create a node from an abstract file. + * @param newFile The abstract file + * @param autopsyCase The current case + * @throws EamDbException + */ + OtherOccurrenceNodeInstanceData(AbstractFile newFile, Case autopsyCase) throws EamDbException { + caseName = autopsyCase.getDisplayName(); + try { + DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId()); + deviceID = dataSource.getDeviceId(); + dataSourceName = dataSource.getName(); + } catch (TskDataException | TskCoreException ex) { + throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex); + } + + filePath = newFile.getParentPath() + newFile.getName(); + typeStr = FILE_TYPE_STR; + value = newFile.getMd5Hash(); + known = newFile.getKnown(); + comment = ""; + + originalAbstractFile = newFile; + } + + /** + * Check if this node is a "file" type + * @return true if it is a file type + */ + boolean isFileType() { + return FILE_TYPE_STR.equals(typeStr); + } + + /** + * Update the known status for this node + * @param newKnownStatus The new known status + */ + void updateKnown(TskData.FileKnown newKnownStatus) { + known = newKnownStatus; + } + + /** + * Update the comment for this node + * @param newComment The new comment + */ + void updateComment(String newComment) { + comment = newComment; + } + + /** + * Check if this is a central repo node. + * @return true if this node was created from a central repo instance, false otherwise + */ + boolean isCentralRepoNode() { + return (originalCorrelationInstance != null); + } + + /** + * Uses the saved instance plus type and value to make a new CorrelationAttribute. + * Should only be called if isCentralRepoNode() is true. + * @return the newly created CorrelationAttribute + */ + CorrelationAttributeInstance getCorrelationAttribute() throws EamDbException { + if (! isCentralRepoNode() ) { + throw new EamDbException("Can not create CorrelationAttribute for non central repo node"); + } + return originalCorrelationInstance; + } + + /** + * Get the case name + * @return the case name + */ + String getCaseName() { + return caseName; + } + + /** + * Get the device ID + * @return the device ID + */ + String getDeviceID() { + return deviceID; + } + + /** + * Get the data source name + * @return the data source name + */ + String getDataSourceName() { + return dataSourceName; + } + + /** + * Get the file path + * @return the file path + */ + String getFilePath() { + return filePath; + } + + /** + * Get the type (as a string) + * @return the type + */ + String getType() { + return typeStr; + } + + /** + * Get the value (MD5 hash for files) + * @return the value + */ + String getValue() { + return value; + } + + /** + * Get the known status + * @return the known status + */ + TskData.FileKnown getKnown() { + return known; + } + + /** + * Get the comment + * @return the comment + */ + String getComment() { + return comment; + } + + /** + * Get the backing abstract file. + * Should only be called if isCentralRepoNode() is false + * @return the original abstract file + */ + AbstractFile getAbstractFile() throws EamDbException { + if (originalCorrelationInstance == null) { + throw new EamDbException("AbstractFile is null"); + } + return originalAbstractFile; + } + + /** + * Get the backing CorrelationAttributeInstance. + * Should only be called if isCentralRepoNode() is true + * @return the original CorrelationAttributeInstance + * @throws EamDbException + */ + CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException { + if (originalCorrelationInstance == null) { + throw new EamDbException("CorrelationAttributeInstance is null"); + } + return originalCorrelationInstance; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java new file mode 100755 index 0000000000..99e530349a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java @@ -0,0 +1,34 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 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.centralrepository.contentviewer; + +/** + * Class for populating the Other Occurrences tab with a single message. + */ +final class OtherOccurrenceNodeMessageData implements OtherOccurrenceNodeData { + private final String displayMessage; + + OtherOccurrenceNodeMessageData(String displayMessage) { + this.displayMessage = displayMessage; + } + + String getDisplayMessage() { + return displayMessage; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index c2e4651808..288363fe95 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -52,11 +52,11 @@ abstract class AbstractSqlEamDb implements EamDb { private final static Logger logger = Logger.getLogger(AbstractSqlEamDb.class.getName()); - protected final List defaultCorrelationTypes; + protected final List defaultCorrelationTypes; private int bulkArtifactsCount; protected int bulkArtifactsThreshold; - private final Map> bulkArtifacts; + private final Map> bulkArtifacts; // Maximum length for the value column in the instance tables static final int MAX_VALUE_LENGTH = 256; @@ -74,7 +74,7 @@ abstract class AbstractSqlEamDb implements EamDb { bulkArtifactsCount = 0; bulkArtifacts = new HashMap<>(); - defaultCorrelationTypes = CorrelationAttribute.getDefaultCorrelationTypes(); + defaultCorrelationTypes = CorrelationAttributeInstance.getDefaultCorrelationTypes(); defaultCorrelationTypes.forEach((type) -> { bulkArtifacts.put(type.getDbTableName(), new ArrayList<>()); }); @@ -88,7 +88,7 @@ abstract class AbstractSqlEamDb implements EamDb { /** * Add a new name/value pair in the db_info table. * - * @param name Key to set + * @param name Key to set * @param value Value to set * * @throws EamDbException @@ -152,7 +152,7 @@ abstract class AbstractSqlEamDb implements EamDb { /** * Update the value for a name in the name/value db_info table. * - * @param name Name to find + * @param name Name to find * @param value Value to assign to name. * * @throws EamDbException @@ -387,6 +387,7 @@ abstract class AbstractSqlEamDb implements EamDb { return eamCaseResult; } + /** * Retrieves Case details based on Case ID * @@ -504,8 +505,8 @@ abstract class AbstractSqlEamDb implements EamDb { /** * Retrieves Data Source details based on data source device ID * - * @param correlationCase the current CorrelationCase used for ensuring - * uniqueness of DataSource + * @param correlationCase the current CorrelationCase used for ensuring + * uniqueness of DataSource * @param dataSourceDeviceId the data source device ID number * * @return The data source @@ -542,12 +543,12 @@ abstract class AbstractSqlEamDb implements EamDb { return eamDataSourceResult; } - + /** * Retrieves Data Source details based on data source ID * - * @param correlationCase the current CorrelationCase used for ensuring - * uniqueness of DataSource + * @param correlationCase the current CorrelationCase used for ensuring + * uniqueness of DataSource * @param dataSourceId the data source ID number * * @return The data source @@ -626,7 +627,52 @@ abstract class AbstractSqlEamDb implements EamDb { * @param eamArtifact The artifact to add */ @Override - public void addArtifact(CorrelationAttribute eamArtifact) throws EamDbException { + public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws EamDbException { + checkAddArtifactInstanceNulls(eamArtifact); + + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + + // @@@ We should cache the case and data source IDs in memory + String tableName = EamDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()); + String sql + = "INSERT INTO " + + tableName + + "(case_id, data_source_id, value, file_path, known_status, comment) " + + "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), " + + "(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?) " + + getConflictClause(); + + try { + preparedStatement = conn.prepareStatement(sql); + + if (!eamArtifact.getCorrelationValue().isEmpty()) { + + preparedStatement.setString(1, eamArtifact.getCorrelationCase().getCaseUUID()); + preparedStatement.setString(2, eamArtifact.getCorrelationDataSource().getDeviceID()); + preparedStatement.setInt(3, eamArtifact.getCorrelationDataSource().getCaseID()); + preparedStatement.setString(4, eamArtifact.getCorrelationValue().toLowerCase()); + preparedStatement.setString(5, eamArtifact.getFilePath().toLowerCase()); + preparedStatement.setByte(6, eamArtifact.getKnownStatus().getFileKnownValue()); + if ("".equals(eamArtifact.getComment())) { + preparedStatement.setNull(7, Types.INTEGER); + } else { + preparedStatement.setString(7, eamArtifact.getComment()); + } + + preparedStatement.executeUpdate(); + } + + } catch (SQLException ex) { + throw new EamDbException("Error inserting new artifact into artifacts table.", ex); // NON-NLS + } finally { + EamDbUtil.closeStatement(preparedStatement); + EamDbUtil.closeConnection(conn); + } + } + + private void checkAddArtifactInstanceNulls(CorrelationAttributeInstance eamArtifact) throws EamDbException { if (eamArtifact == null) { throw new EamDbException("CorrelationAttribute is null"); } @@ -643,56 +689,14 @@ abstract class AbstractSqlEamDb implements EamDb { + "\nCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()); } - - Connection conn = connect(); - - List eamInstances = eamArtifact.getInstances(); - PreparedStatement preparedStatement = null; - - // @@@ We should cache the case and data source IDs in memory - String tableName = EamDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()); - String sql - = "INSERT INTO " - + tableName - + "(case_id, data_source_id, value, file_path, known_status, comment) " - + "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), " - + "(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?) " - + getConflictClause(); - - try { - preparedStatement = conn.prepareStatement(sql); - for (CorrelationAttributeInstance eamInstance : eamInstances) { - if (!eamArtifact.getCorrelationValue().isEmpty()) { - if (eamInstance.getCorrelationCase() == null) { - throw new EamDbException("CorrelationAttributeInstance case is null"); - } - if (eamInstance.getCorrelationDataSource() == null) { - throw new EamDbException("CorrelationAttributeInstance data source is null"); - } - if (eamInstance.getKnownStatus() == null) { - throw new EamDbException("CorrelationAttributeInstance known status is null"); - } - - preparedStatement.setString(1, eamInstance.getCorrelationCase().getCaseUUID()); - preparedStatement.setString(2, eamInstance.getCorrelationDataSource().getDeviceID()); - preparedStatement.setInt(3, eamInstance.getCorrelationDataSource().getCaseID()); - preparedStatement.setString(4, eamArtifact.getCorrelationValue()); - preparedStatement.setString(5, eamInstance.getFilePath()); - preparedStatement.setByte(6, eamInstance.getKnownStatus().getFileKnownValue()); - if ("".equals(eamInstance.getComment())) { - preparedStatement.setNull(7, Types.INTEGER); - } else { - preparedStatement.setString(7, eamInstance.getComment()); - } - - preparedStatement.executeUpdate(); - } - } - } catch (SQLException ex) { - throw new EamDbException("Error inserting new artifact into artifacts table.", ex); // NON-NLS - } finally { - EamDbUtil.closeStatement(preparedStatement); - EamDbUtil.closeConnection(conn); + if (eamArtifact.getCorrelationCase() == null) { + throw new EamDbException("CorrelationAttributeInstance case is null"); + } + if (eamArtifact.getCorrelationDataSource() == null) { + throw new EamDbException("CorrelationAttributeInstance data source is null"); + } + if (eamArtifact.getKnownStatus() == null) { + throw new EamDbException("CorrelationAttributeInstance known status is null"); } } @@ -708,7 +712,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public List getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { + public List getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { if (aType == null) { throw new EamDbException("Correlation type is null"); } @@ -724,7 +728,10 @@ abstract class AbstractSqlEamDb implements EamDb { String sql = "SELECT " + tableName - + ".id, cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + + ".id," + + tableName + + ".value," + + " cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + tableName + " LEFT JOIN cases ON " + tableName @@ -739,7 +746,7 @@ abstract class AbstractSqlEamDb implements EamDb { preparedStatement.setString(1, value); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - artifactInstance = getEamArtifactInstanceFromResultSet(resultSet); + artifactInstance = getEamArtifactInstanceFromResultSet(resultSet, aType); artifactInstances.add(artifactInstance); } } catch (SQLException ex) { @@ -752,12 +759,12 @@ abstract class AbstractSqlEamDb implements EamDb { return artifactInstances; } - + /** * Retrieves eamArtifact instances from the database that are associated * with the aType and filePath * - * @param aType EamArtifact.Type to search for + * @param aType EamArtifact.Type to search for * @param filePath File path to search for * * @return List of 0 or more EamArtifactInstances @@ -765,7 +772,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public List getArtifactInstancesByPath(CorrelationAttribute.Type aType, String filePath) throws EamDbException { + public List getArtifactInstancesByPath(CorrelationAttributeInstance.Type aType, String filePath) throws EamDbException { if (aType == null) { throw new EamDbException("Correlation type is null"); } @@ -784,7 +791,10 @@ abstract class AbstractSqlEamDb implements EamDb { String sql = "SELECT " + tableName - + ".id, cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + + ".id, " + + tableName + + ".value," + + " cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + tableName + " LEFT JOIN cases ON " + tableName @@ -799,7 +809,7 @@ abstract class AbstractSqlEamDb implements EamDb { preparedStatement.setString(1, filePath.toLowerCase()); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - artifactInstance = getEamArtifactInstanceFromResultSet(resultSet); + artifactInstance = getEamArtifactInstanceFromResultSet(resultSet, aType); artifactInstances.add(artifactInstance); } } catch (SQLException ex) { @@ -821,10 +831,10 @@ abstract class AbstractSqlEamDb implements EamDb { * @param value The correlation value * * @return Number of artifact instances having ArtifactType and - * ArtifactValue. + * ArtifactValue. */ @Override - public Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { + public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { if (aType == null) { throw new EamDbException("Correlation type is null"); } @@ -862,7 +872,7 @@ abstract class AbstractSqlEamDb implements EamDb { } @Override - public int getFrequencyPercentage(CorrelationAttribute corAttr) throws EamDbException { + public int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException { if (corAttr == null) { throw new EamDbException("CorrelationAttribute is null"); } @@ -883,7 +893,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @return Number of unique tuples */ @Override - public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { + public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { if (aType == null) { throw new EamDbException("Correlation type is null"); } @@ -950,25 +960,25 @@ abstract class AbstractSqlEamDb implements EamDb { * associated with the caseDisplayName and dataSource of the given * eamArtifact instance. * - * @param caseUUID Case ID to search for + * @param caseUUID Case ID to search for * @param dataSourceID Data source ID to search for * * @return Number of artifact instances having caseDisplayName and - * dataSource + * dataSource */ @Override public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException { Connection conn = connect(); Long instanceCount = 0L; - List artifactTypes = getDefinedCorrelationTypes(); + List artifactTypes = getDefinedCorrelationTypes(); PreparedStatement preparedStatement = null; ResultSet resultSet = null; // Figure out sql variables or subqueries String sql = "SELECT 0 "; - for (CorrelationAttribute.Type type : artifactTypes) { + for (CorrelationAttributeInstance.Type type : artifactTypes) { String table_name = EamDbUtil.correlationTypeToInstanceTableName(type); sql @@ -1002,12 +1012,12 @@ abstract class AbstractSqlEamDb implements EamDb { /** * Adds an eamArtifact to an internal list to be later added to DB. Artifact * can have 1 or more Artifact Instances. Insert will be triggered by a - * threshold or a call to bulkInsertArtifacts(). + * threshold or a call to commitAttributeInstancesBulk(). * * @param eamArtifact The artifact to add */ @Override - public void prepareBulkArtifact(CorrelationAttribute eamArtifact) throws EamDbException { + public void addAttributeInstanceBulk(CorrelationAttributeInstance eamArtifact) throws EamDbException { if (eamArtifact.getCorrelationType() == null) { throw new EamDbException("Correlation type is null"); @@ -1018,7 +1028,7 @@ abstract class AbstractSqlEamDb implements EamDb { bulkArtifactsCount++; if (bulkArtifactsCount >= bulkArtifactsThreshold) { - bulkInsertArtifacts(); + commitAttributeInstancesBulk(); } } } @@ -1032,11 +1042,11 @@ abstract class AbstractSqlEamDb implements EamDb { /** * Executes a bulk insert of the eamArtifacts added from the - * prepareBulkArtifact() method + * addAttributeInstanceBulk() method */ @Override - public void bulkInsertArtifacts() throws EamDbException { - List artifactTypes = getDefinedCorrelationTypes(); + public void commitAttributeInstancesBulk() throws EamDbException { + List artifactTypes = getDefinedCorrelationTypes(); Connection conn = connect(); PreparedStatement bulkPs = null; @@ -1047,7 +1057,7 @@ abstract class AbstractSqlEamDb implements EamDb { return; } - for (CorrelationAttribute.Type type : artifactTypes) { + for (CorrelationAttributeInstance.Type type : artifactTypes) { String tableName = EamDbUtil.correlationTypeToInstanceTableName(type); String sql @@ -1060,60 +1070,58 @@ abstract class AbstractSqlEamDb implements EamDb { bulkPs = conn.prepareStatement(sql); - Collection eamArtifacts = bulkArtifacts.get(type.getDbTableName()); - for (CorrelationAttribute eamArtifact : eamArtifacts) { - List eamInstances = eamArtifact.getInstances(); + Collection eamArtifacts = bulkArtifacts.get(type.getDbTableName()); + for (CorrelationAttributeInstance eamArtifact : eamArtifacts) { - for (CorrelationAttributeInstance eamInstance : eamInstances) { - if (!eamArtifact.getCorrelationValue().isEmpty()) { + if (!eamArtifact.getCorrelationValue().isEmpty()) { - if (eamInstance.getCorrelationCase() == null) { - throw new EamDbException("CorrelationAttributeInstance case is null for: " - + "\n\tCorrelationArtifact ID: " + eamArtifact.getID() - + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() - + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()); - } - if (eamInstance.getCorrelationDataSource() == null) { - throw new EamDbException("CorrelationAttributeInstance data source is null for: " - + "\n\tCorrelationArtifact ID: " + eamArtifact.getID() - + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() - + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()); - } - if (eamInstance.getKnownStatus() == null) { - throw new EamDbException("CorrelationAttributeInstance known status is null for: " - + "\n\tCorrelationArtifact ID: " + eamArtifact.getID() - + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() - + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue() - + "\n\tEam Instance: " - + "\n\t\tCaseId: " + eamInstance.getCorrelationDataSource().getCaseID() - + "\n\t\tDeviceID: " + eamInstance.getCorrelationDataSource().getDeviceID()); - } + if (eamArtifact.getCorrelationCase() == null) { + throw new EamDbException("CorrelationAttributeInstance case is null for: " + + "\n\tCorrelationArtifact ID: " + eamArtifact.getID() + + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() + + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()); + } + if (eamArtifact.getCorrelationDataSource() == null) { + throw new EamDbException("CorrelationAttributeInstance data source is null for: " + + "\n\tCorrelationArtifact ID: " + eamArtifact.getID() + + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() + + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()); + } + if (eamArtifact.getKnownStatus() == null) { + throw new EamDbException("CorrelationAttributeInstance known status is null for: " + + "\n\tCorrelationArtifact ID: " + eamArtifact.getID() + + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() + + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue() + + "\n\tEam Instance: " + + "\n\t\tCaseId: " + eamArtifact.getCorrelationDataSource().getCaseID() + + "\n\t\tDeviceID: " + eamArtifact.getCorrelationDataSource().getDeviceID()); + } - if (eamArtifact.getCorrelationValue().length() < MAX_VALUE_LENGTH) { - bulkPs.setString(1, eamInstance.getCorrelationCase().getCaseUUID()); - bulkPs.setString(2, eamInstance.getCorrelationDataSource().getDeviceID()); - bulkPs.setInt(3, eamInstance.getCorrelationDataSource().getCaseID()); - bulkPs.setString(4, eamArtifact.getCorrelationValue()); - bulkPs.setString(5, eamInstance.getFilePath()); - bulkPs.setByte(6, eamInstance.getKnownStatus().getFileKnownValue()); - if ("".equals(eamInstance.getComment())) { - bulkPs.setNull(7, Types.INTEGER); - } else { - bulkPs.setString(7, eamInstance.getComment()); - } - bulkPs.addBatch(); + if (eamArtifact.getCorrelationValue().length() < MAX_VALUE_LENGTH) { + bulkPs.setString(1, eamArtifact.getCorrelationCase().getCaseUUID()); + bulkPs.setString(2, eamArtifact.getCorrelationDataSource().getDeviceID()); + bulkPs.setInt(3, eamArtifact.getCorrelationDataSource().getCaseID()); + bulkPs.setString(4, eamArtifact.getCorrelationValue()); + bulkPs.setString(5, eamArtifact.getFilePath()); + bulkPs.setByte(6, eamArtifact.getKnownStatus().getFileKnownValue()); + if ("".equals(eamArtifact.getComment())) { + bulkPs.setNull(7, Types.INTEGER); } else { - logger.log(Level.WARNING, ("Artifact value too long for central repository." - + "\n\tCorrelationArtifact ID: " + eamArtifact.getID() - + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() - + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()) - + "\n\tEam Instance: " - + "\n\t\tCaseId: " + eamInstance.getCorrelationDataSource().getCaseID() - + "\n\t\tDeviceID: " + eamInstance.getCorrelationDataSource().getDeviceID() - + "\n\t\tFilePath: " + eamInstance.getFilePath()); + bulkPs.setString(7, eamArtifact.getComment()); } + bulkPs.addBatch(); + } else { + logger.log(Level.WARNING, ("Artifact value too long for central repository." + + "\n\tCorrelationArtifact ID: " + eamArtifact.getID() + + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() + + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()) + + "\n\tEam Instance: " + + "\n\t\tCaseId: " + eamArtifact.getCorrelationDataSource().getCaseID() + + "\n\t\tDeviceID: " + eamArtifact.getCorrelationDataSource().getDeviceID() + + "\n\t\tFilePath: " + eamArtifact.getFilePath()); } } + } bulkPs.executeBatch(); @@ -1219,25 +1227,20 @@ abstract class AbstractSqlEamDb implements EamDb { * associated CorrelationAttribute object. * * @param eamArtifact The correlation attribute whose database instance will - * be updated. + * be updated. * * @throws EamDbException */ @Override - public void updateAttributeInstanceComment(CorrelationAttribute eamArtifact) throws EamDbException { + public void updateAttributeInstanceComment(CorrelationAttributeInstance eamArtifact) throws EamDbException { + if (eamArtifact == null) { - throw new EamDbException("CorrelationAttribute is null"); - } - - CorrelationAttributeInstance eamInstance = eamArtifact.getInstances().get(0); - - if (eamInstance == null) { throw new EamDbException("CorrelationAttributeInstance is null"); } - if (eamInstance.getCorrelationCase() == null) { + if (eamArtifact.getCorrelationCase() == null) { throw new EamDbException("Correlation case is null"); } - if (eamInstance.getCorrelationDataSource() == null) { + if (eamArtifact.getCorrelationDataSource() == null) { throw new EamDbException("Correlation data source is null"); } @@ -1256,14 +1259,14 @@ abstract class AbstractSqlEamDb implements EamDb { try { preparedQuery = conn.prepareStatement(sqlUpdate); - preparedQuery.setString(1, eamInstance.getComment()); - preparedQuery.setString(2, eamInstance.getCorrelationCase().getCaseUUID()); - preparedQuery.setString(3, eamInstance.getCorrelationDataSource().getDeviceID()); - preparedQuery.setString(4, eamArtifact.getCorrelationValue()); - preparedQuery.setString(5, eamInstance.getFilePath()); + preparedQuery.setString(1, eamArtifact.getComment()); + preparedQuery.setString(2, eamArtifact.getCorrelationCase().getCaseUUID()); + preparedQuery.setString(3, eamArtifact.getCorrelationDataSource().getDeviceID()); + preparedQuery.setString(4, eamArtifact.getCorrelationValue().toLowerCase()); + preparedQuery.setString(5, eamArtifact.getFilePath().toLowerCase()); preparedQuery.executeUpdate(); } catch (SQLException ex) { - throw new EamDbException("Error getting/setting artifact instance comment=" + eamInstance.getComment(), ex); // NON-NLS + throw new EamDbException("Error getting/setting artifact instance comment=" + eamArtifact.getComment(), ex); // NON-NLS } finally { EamDbUtil.closeStatement(preparedQuery); EamDbUtil.closeConnection(conn); @@ -1274,18 +1277,18 @@ abstract class AbstractSqlEamDb implements EamDb { * Find a correlation attribute in the Central Repository database given the * instance type, case, data source, value, and file path. * - * @param type The type of instance. - * @param correlationCase The case tied to the instance. + * @param type The type of instance. + * @param correlationCase The case tied to the instance. * @param correlationDataSource The data source tied to the instance. - * @param value The value tied to the instance. - * @param filePath The file path tied to the instance. + * @param value The value tied to the instance. + * @param filePath The file path tied to the instance. * * @return The correlation attribute if it exists; otherwise null. * * @throws EamDbException */ @Override - public CorrelationAttribute getCorrelationAttribute(CorrelationAttribute.Type type, CorrelationCase correlationCase, + public CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase, CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException { if (type == null) { @@ -1308,7 +1311,7 @@ abstract class AbstractSqlEamDb implements EamDb { PreparedStatement preparedStatement = null; ResultSet resultSet = null; - CorrelationAttribute correlationAttribute = null; + CorrelationAttributeInstance correlationAttributeInstance = null; try { String tableName = EamDbUtil.correlationTypeToInstanceTableName(type); @@ -1331,10 +1334,8 @@ abstract class AbstractSqlEamDb implements EamDb { int knownStatus = resultSet.getInt(2); String comment = resultSet.getString(3); - correlationAttribute = new CorrelationAttribute(type, value); - CorrelationAttributeInstance artifactInstance = new CorrelationAttributeInstance( + correlationAttributeInstance = new CorrelationAttributeInstance(type, value, instanceId, correlationCase, correlationDataSource, filePath, comment, TskData.FileKnown.valueOf((byte) knownStatus)); - correlationAttribute.addInstance(artifactInstance); } } catch (SQLException ex) { throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS @@ -1344,7 +1345,7 @@ abstract class AbstractSqlEamDb implements EamDb { EamDbUtil.closeConnection(conn); } - return correlationAttribute; + return correlationAttributeInstance; } /** @@ -1355,27 +1356,21 @@ abstract class AbstractSqlEamDb implements EamDb { * * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. * @param knownStatus The status to change the artifact to. Should never be - * KNOWN + * KNOWN */ @Override - public void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { + public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { if (eamArtifact == null) { throw new EamDbException("CorrelationAttribute is null"); } if (knownStatus == null) { throw new EamDbException("Known status is null"); } - if (1 != eamArtifact.getInstances().size()) { - throw new EamDbException("Error: Artifact must have exactly one (1) Artifact Instance to set as notable."); // NON-NLS - } - List eamInstances = eamArtifact.getInstances(); - CorrelationAttributeInstance eamInstance = eamInstances.get(0); - - if (eamInstance.getCorrelationCase() == null) { + if (eamArtifact.getCorrelationCase() == null) { throw new EamDbException("Correlation case is null"); } - if (eamInstance.getCorrelationDataSource() == null) { + if (eamArtifact.getCorrelationDataSource() == null) { throw new EamDbException("Correlation data source is null"); } @@ -1403,10 +1398,10 @@ abstract class AbstractSqlEamDb implements EamDb { try { preparedQuery = conn.prepareStatement(sqlQuery); - preparedQuery.setString(1, eamInstance.getCorrelationCase().getCaseUUID()); - preparedQuery.setString(2, eamInstance.getCorrelationDataSource().getDeviceID()); + preparedQuery.setString(1, eamArtifact.getCorrelationCase().getCaseUUID()); + preparedQuery.setString(2, eamArtifact.getCorrelationDataSource().getDeviceID()); preparedQuery.setString(3, eamArtifact.getCorrelationValue()); - preparedQuery.setString(4, eamInstance.getFilePath()); + preparedQuery.setString(4, eamArtifact.getFilePath()); resultSet = preparedQuery.executeQuery(); if (resultSet.next()) { int instance_id = resultSet.getInt("id"); @@ -1416,10 +1411,10 @@ abstract class AbstractSqlEamDb implements EamDb { // NOTE: if the user tags the same instance as BAD multiple times, // the comment from the most recent tagging is the one that will // prevail in the DB. - if ("".equals(eamInstance.getComment())) { + if ("".equals(eamArtifact.getComment())) { preparedUpdate.setNull(2, Types.INTEGER); } else { - preparedUpdate.setString(2, eamInstance.getComment()); + preparedUpdate.setString(2, eamArtifact.getComment()); } preparedUpdate.setInt(3, instance_id); @@ -1430,16 +1425,16 @@ abstract class AbstractSqlEamDb implements EamDb { // We could improve effiency by keeping a list of all datasources and cases // in the database, but we don't expect the user to be tagging large numbers // of items (that didn't have the CE ingest module run on them) at once. - CorrelationCase correlationCaseWithId = getCaseByUUID(eamInstance.getCorrelationCase().getCaseUUID()); + CorrelationCase correlationCaseWithId = getCaseByUUID(eamArtifact.getCorrelationCase().getCaseUUID()); if (null == correlationCaseWithId) { - correlationCaseWithId = newCase(eamInstance.getCorrelationCase()); + correlationCaseWithId = newCase(eamArtifact.getCorrelationCase()); } - if (null == getDataSource(correlationCaseWithId, eamInstance.getCorrelationDataSource().getDeviceID())) { - newDataSource(eamInstance.getCorrelationDataSource()); + if (null == getDataSource(correlationCaseWithId, eamArtifact.getCorrelationDataSource().getDeviceID())) { + newDataSource(eamArtifact.getCorrelationDataSource()); } - eamArtifact.getInstances().get(0).setKnownStatus(knownStatus); - addArtifact(eamArtifact); + eamArtifact.setKnownStatus(knownStatus); + addArtifactInstance(eamArtifact); } } catch (SQLException ex) { @@ -1462,7 +1457,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @return List with 0 or more matching eamArtifact instances. */ @Override - public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { + public List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { if (aType == null) { throw new EamDbException("Correlation type is null"); } @@ -1479,7 +1474,10 @@ abstract class AbstractSqlEamDb implements EamDb { String sql = "SELECT " + tableName - + ".id, cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + + ".id, " + + tableName + + ".value, " + + "cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + tableName + " LEFT JOIN cases ON " + tableName @@ -1495,7 +1493,7 @@ abstract class AbstractSqlEamDb implements EamDb { preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue()); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - artifactInstance = getEamArtifactInstanceFromResultSet(resultSet); + artifactInstance = getEamArtifactInstanceFromResultSet(resultSet, aType); artifactInstances.add(artifactInstance); } } catch (SQLException ex) { @@ -1521,7 +1519,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException { + public List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType) throws EamDbException { if (aType == null) { throw new EamDbException("Correlation type is null"); } @@ -1536,7 +1534,7 @@ abstract class AbstractSqlEamDb implements EamDb { String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); String sql - = "SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + = "SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id, id, value FROM " + tableName + " LEFT JOIN cases ON " + tableName @@ -1554,7 +1552,7 @@ abstract class AbstractSqlEamDb implements EamDb { preparedStatement.setByte(1, TskData.FileKnown.BAD.getFileKnownValue()); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - artifactInstance = getEamArtifactInstanceFromResultSet(resultSet); + artifactInstance = getEamArtifactInstanceFromResultSet(resultSet, aType); artifactInstances.add(artifactInstance); } } catch (SQLException ex) { @@ -1577,7 +1575,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @return Number of matching eamArtifacts */ @Override - public Long getCountArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { + public Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { if (aType == null) { throw new EamDbException("Correlation type is null"); } @@ -1620,12 +1618,12 @@ abstract class AbstractSqlEamDb implements EamDb { * @param value Value to search for * * @return List of cases containing this artifact with instances marked as - * bad + * bad * * @throws EamDbException */ @Override - public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { + public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { if (aType == null) { throw new EamDbException("Correlation type is null"); } @@ -1721,7 +1719,7 @@ abstract class AbstractSqlEamDb implements EamDb { String sql = "DELETE FROM %s WHERE reference_set_id=?"; // When other reference types are added, this will need to loop over all the tables - String fileTableName = EamDbUtil.correlationTypeToReferenceTableName(getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID)); + String fileTableName = EamDbUtil.correlationTypeToReferenceTableName(getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID)); try { preparedStatement = conn.prepareStatement(String.format(sql, fileTableName)); @@ -1771,7 +1769,7 @@ abstract class AbstractSqlEamDb implements EamDb { */ @Override public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws EamDbException { - return isValueInReferenceSet(hash, referenceSetID, CorrelationAttribute.FILES_TYPE_ID); + return isValueInReferenceSet(hash, referenceSetID, CorrelationAttributeInstance.FILES_TYPE_ID); } /** @@ -1822,13 +1820,13 @@ abstract class AbstractSqlEamDb implements EamDb { * @return Global known status of the artifact */ @Override - public boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException { + public boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { if (aType == null) { throw new EamDbException("Correlation type is null"); } // TEMP: Only support file correlation type - if (aType.getId() != CorrelationAttribute.FILES_TYPE_ID) { + if (aType.getId() != CorrelationAttributeInstance.FILES_TYPE_ID) { return false; } @@ -1860,13 +1858,13 @@ abstract class AbstractSqlEamDb implements EamDb { /** * Process the Artifact instance in the EamDb * - * @param type EamArtifact.Type to search for + * @param type EamArtifact.Type to search for * @param instanceTableCallback callback to process the instance * * @throws EamDbException */ @Override - public void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException { + public void processInstanceTable(CorrelationAttributeInstance.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException { if (type == null) { throw new EamDbException("Correlation type is null"); } @@ -1905,7 +1903,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException { + public void processInstanceTableWhere(CorrelationAttributeInstance.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException { if (type == null) { throw new EamDbException("Correlation type is null"); } @@ -1913,8 +1911,8 @@ abstract class AbstractSqlEamDb implements EamDb { if (instanceTableCallback == null) { throw new EamDbException("Callback interface is null"); } - - if(whereClause == null) { + + if (whereClause == null) { throw new EamDbException("Where clause is null"); } @@ -1922,11 +1920,11 @@ abstract class AbstractSqlEamDb implements EamDb { PreparedStatement preparedStatement = null; ResultSet resultSet = null; String tableName = EamDbUtil.correlationTypeToInstanceTableName(type); - StringBuilder sql = new StringBuilder(3); - sql.append("select * from "); - sql.append(tableName); - sql.append(" WHERE "); - sql.append(whereClause); + StringBuilder sql = new StringBuilder(300); + sql.append("select * from ") + .append(tableName) + .append(" WHERE ") + .append(whereClause); try { preparedStatement = conn.prepareStatement(sql.toString()); @@ -1940,7 +1938,6 @@ abstract class AbstractSqlEamDb implements EamDb { EamDbUtil.closeConnection(conn); } } - @Override public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException { @@ -2084,7 +2081,7 @@ abstract class AbstractSqlEamDb implements EamDb { * Update an existing organization. * * @param updatedOrganization the values the Organization with the same ID - * will be updated to in the database. + * will be updated to in the database. * * @throws EamDbException */ @@ -2253,7 +2250,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public List getAllReferenceSets(CorrelationAttribute.Type correlationType) throws EamDbException { + public List getAllReferenceSets(CorrelationAttributeInstance.Type correlationType) throws EamDbException { if (correlationType == null) { throw new EamDbException("Correlation type is null"); @@ -2287,13 +2284,12 @@ abstract class AbstractSqlEamDb implements EamDb { * Add a new reference instance * * @param eamGlobalFileInstance The reference instance to add - * @param correlationType Correlation Type that this Reference - * Instance is + * @param correlationType Correlation Type that this Reference Instance is * * @throws EamDbException */ @Override - public void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttribute.Type correlationType) throws EamDbException { + public void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttributeInstance.Type correlationType) throws EamDbException { if (eamGlobalFileInstance.getKnownStatus() == null) { throw new EamDbException("Known status of EamGlobalFileInstance is null"); } @@ -2366,7 +2362,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public void bulkInsertReferenceTypeEntries(Set globalInstances, CorrelationAttribute.Type contentType) throws EamDbException { + public void bulkInsertReferenceTypeEntries(Set globalInstances, CorrelationAttributeInstance.Type contentType) throws EamDbException { if (contentType == null) { throw new EamDbException("Correlation type is null"); } @@ -2416,7 +2412,7 @@ abstract class AbstractSqlEamDb implements EamDb { /** * Get all reference entries having a given correlation type and value * - * @param aType Type to use for matching + * @param aType Type to use for matching * @param aValue Value to use for matching * * @return List of all global file instances with a type and value @@ -2424,7 +2420,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public List getReferenceInstancesByTypeValue(CorrelationAttribute.Type aType, String aValue) throws EamDbException { + public List getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException { if (aType == null) { throw new EamDbException("Correlation type is null"); } @@ -2464,11 +2460,31 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public int newCorrelationType(CorrelationAttribute.Type newType) throws EamDbException { + public int newCorrelationType(CorrelationAttributeInstance.Type newType) throws EamDbException { if (newType == null) { throw new EamDbException("Correlation type is null"); } + int typeId; + if (-1 == newType.getId()) { + typeId = newCorrelationTypeNotKnownId(newType); + } else { + typeId = newCorrelationTypeKnownId(newType); + } + return typeId; + } + + /** + * Helper function which adds a new EamArtifact.Type to the db without an + * id. + * + * @param newType New type to add. + * + * @return ID of this new Correlation Type + * + * @throws EamDbException + */ + public int newCorrelationTypeNotKnownId(CorrelationAttributeInstance.Type newType) throws EamDbException { Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -2478,28 +2494,17 @@ abstract class AbstractSqlEamDb implements EamDb { String insertSql; String querySql; // if we have a known ID, use it, if not (is -1) let the db assign it. - if (-1 == newType.getId()) { - insertSql = "INSERT INTO correlation_types(display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?) " + getConflictClause(); - } else { - insertSql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?) " + getConflictClause(); - } + insertSql = "INSERT INTO correlation_types(display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?) " + getConflictClause(); + querySql = "SELECT * FROM correlation_types WHERE display_name=? AND db_table_name=?"; try { preparedStatement = conn.prepareStatement(insertSql); - if (-1 == newType.getId()) { - preparedStatement.setString(1, newType.getDisplayName()); - preparedStatement.setString(2, newType.getDbTableName()); - preparedStatement.setInt(3, newType.isSupported() ? 1 : 0); - preparedStatement.setInt(4, newType.isEnabled() ? 1 : 0); - } else { - preparedStatement.setInt(1, newType.getId()); - preparedStatement.setString(2, newType.getDisplayName()); - preparedStatement.setString(3, newType.getDbTableName()); - preparedStatement.setInt(4, newType.isSupported() ? 1 : 0); - preparedStatement.setInt(5, newType.isEnabled() ? 1 : 0); - } + preparedStatement.setString(1, newType.getDisplayName()); + preparedStatement.setString(2, newType.getDbTableName()); + preparedStatement.setInt(3, newType.isSupported() ? 1 : 0); + preparedStatement.setInt(4, newType.isEnabled() ? 1 : 0); preparedStatement.executeUpdate(); @@ -2509,7 +2514,61 @@ abstract class AbstractSqlEamDb implements EamDb { resultSet = preparedStatementQuery.executeQuery(); if (resultSet.next()) { - CorrelationAttribute.Type correlationType = getCorrelationTypeFromResultSet(resultSet); + CorrelationAttributeInstance.Type correlationType = getCorrelationTypeFromResultSet(resultSet); + typeId = correlationType.getId(); + } + } catch (SQLException ex) { + throw new EamDbException("Error inserting new correlation type.", ex); // NON-NLS + } finally { + EamDbUtil.closeStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatementQuery); + EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeConnection(conn); + } + return typeId; + } + + /** + * Helper function which adds a new EamArtifact.Type to the db. + * + * @param newType New type to add. + * + * @return ID of this new Correlation Type + * + * @throws EamDbException + */ + private int newCorrelationTypeKnownId(CorrelationAttributeInstance.Type newType) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + PreparedStatement preparedStatementQuery = null; + ResultSet resultSet = null; + int typeId = 0; + String insertSql; + String querySql; + // if we have a known ID, use it, if not (is -1) let the db assign it. + insertSql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?) " + getConflictClause(); + + querySql = "SELECT * FROM correlation_types WHERE display_name=? AND db_table_name=?"; + + try { + preparedStatement = conn.prepareStatement(insertSql); + + preparedStatement.setInt(1, newType.getId()); + preparedStatement.setString(2, newType.getDisplayName()); + preparedStatement.setString(3, newType.getDbTableName()); + preparedStatement.setInt(4, newType.isSupported() ? 1 : 0); + preparedStatement.setInt(5, newType.isEnabled() ? 1 : 0); + + preparedStatement.executeUpdate(); + + preparedStatementQuery = conn.prepareStatement(querySql); + preparedStatementQuery.setString(1, newType.getDisplayName()); + preparedStatementQuery.setString(2, newType.getDbTableName()); + + resultSet = preparedStatementQuery.executeQuery(); + if (resultSet.next()) { + CorrelationAttributeInstance.Type correlationType = getCorrelationTypeFromResultSet(resultSet); typeId = correlationType.getId(); } } catch (SQLException ex) { @@ -2524,10 +2583,10 @@ abstract class AbstractSqlEamDb implements EamDb { } @Override - public List getDefinedCorrelationTypes() throws EamDbException { + public List getDefinedCorrelationTypes() throws EamDbException { Connection conn = connect(); - List aTypes = new ArrayList<>(); + List aTypes = new ArrayList<>(); PreparedStatement preparedStatement = null; ResultSet resultSet = null; String sql = "SELECT * FROM correlation_types"; @@ -2554,15 +2613,15 @@ abstract class AbstractSqlEamDb implements EamDb { * artifacts. * * @return List of enabled EamArtifact.Type's. If none are defined in the - * database, the default list will be returned. + * database, the default list will be returned. * * @throws EamDbException */ @Override - public List getEnabledCorrelationTypes() throws EamDbException { + public List getEnabledCorrelationTypes() throws EamDbException { Connection conn = connect(); - List aTypes = new ArrayList<>(); + List aTypes = new ArrayList<>(); PreparedStatement preparedStatement = null; ResultSet resultSet = null; String sql = "SELECT * FROM correlation_types WHERE enabled=1"; @@ -2589,15 +2648,15 @@ abstract class AbstractSqlEamDb implements EamDb { * correlate artifacts. * * @return List of supported EamArtifact.Type's. If none are defined in the - * database, the default list will be returned. + * database, the default list will be returned. * * @throws EamDbException */ @Override - public List getSupportedCorrelationTypes() throws EamDbException { + public List getSupportedCorrelationTypes() throws EamDbException { Connection conn = connect(); - List aTypes = new ArrayList<>(); + List aTypes = new ArrayList<>(); PreparedStatement preparedStatement = null; ResultSet resultSet = null; String sql = "SELECT * FROM correlation_types WHERE supported=1"; @@ -2627,7 +2686,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public void updateCorrelationType(CorrelationAttribute.Type aType) throws EamDbException { + public void updateCorrelationType(CorrelationAttributeInstance.Type aType) throws EamDbException { Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -2661,10 +2720,10 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public CorrelationAttribute.Type getCorrelationTypeById(int typeId) throws EamDbException { + public CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws EamDbException { Connection conn = connect(); - CorrelationAttribute.Type aType; + CorrelationAttributeInstance.Type aType; PreparedStatement preparedStatement = null; ResultSet resultSet = null; String sql = "SELECT * FROM correlation_types WHERE id=?"; @@ -2693,7 +2752,7 @@ abstract class AbstractSqlEamDb implements EamDb { * Convert a ResultSet to a EamCase object * * @param resultSet A resultSet with a set of values to create a EamCase - * object. + * object. * * @return fully populated EamCase object, or null * @@ -2743,12 +2802,12 @@ abstract class AbstractSqlEamDb implements EamDb { return eamDataSource; } - private CorrelationAttribute.Type getCorrelationTypeFromResultSet(ResultSet resultSet) throws EamDbException, SQLException { + private CorrelationAttributeInstance.Type getCorrelationTypeFromResultSet(ResultSet resultSet) throws EamDbException, SQLException { if (null == resultSet) { return null; } - CorrelationAttribute.Type eamArtifactType = new CorrelationAttribute.Type( + CorrelationAttributeInstance.Type eamArtifactType = new CorrelationAttributeInstance.Type( resultSet.getInt("id"), resultSet.getString("display_name"), resultSet.getString("db_table_name"), @@ -2763,18 +2822,20 @@ abstract class AbstractSqlEamDb implements EamDb { * Convert a ResultSet to a EamArtifactInstance object * * @param resultSet A resultSet with a set of values to create a - * EamArtifactInstance object. + * EamArtifactInstance object. * * @return fully populated EamArtifactInstance, or null * * @throws SQLException when an expected column name is not in the resultSet */ - private CorrelationAttributeInstance getEamArtifactInstanceFromResultSet(ResultSet resultSet) throws SQLException, EamDbException { + private CorrelationAttributeInstance getEamArtifactInstanceFromResultSet(ResultSet resultSet, CorrelationAttributeInstance.Type aType) throws SQLException, EamDbException { if (null == resultSet) { return null; } // @@@ We should have data source ID in the previous query instead of passing -1 into the below constructor return new CorrelationAttributeInstance( + aType, + resultSet.getString("value"), resultSet.getInt("id"), new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), resultSet.getString("case_name")), new CorrelationDataSource(resultSet.getInt("case_id"), resultSet.getInt("data_source_id"), resultSet.getString("device_id"), resultSet.getString("name")), diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttribute.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttribute.java deleted file mode 100644 index 427bbc97bb..0000000000 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttribute.java +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Central Repository - * - * Copyright 2015-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.centralrepository.datamodel; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.regex.Pattern; -import org.openide.util.NbBundle.Messages; - -/** - * Represents a type and value pair that can be used for correlation. - * CorrelationAttributeInstances store information about the actual - * occurrences of the attribute. - */ -public class CorrelationAttribute implements Serializable { - - private static final long serialVersionUID = 1L; - - private String ID; - private String correlationValue; - private Type correlationType; - private final List artifactInstances; - - // Type ID's for Default Correlation Types - public static final int FILES_TYPE_ID = 0; - public static final int DOMAIN_TYPE_ID = 1; - public static final int EMAIL_TYPE_ID = 2; - public static final int PHONE_TYPE_ID = 3; - public static final int USBID_TYPE_ID = 4; - - /** - * Load the default correlation types - * - * @throws EamDbException if the Type's dbTableName has invalid characters/format - */ - @Messages({"CorrelationType.FILES.displayName=Files", - "CorrelationType.DOMAIN.displayName=Domains", - "CorrelationType.EMAIL.displayName=Email Addresses", - "CorrelationType.PHONE.displayName=Phone Numbers", - "CorrelationType.USBID.displayName=USB Devices"}) - public static List getDefaultCorrelationTypes() throws EamDbException { - List DEFAULT_CORRELATION_TYPES = new ArrayList<>(); - DEFAULT_CORRELATION_TYPES.add(new CorrelationAttribute.Type(FILES_TYPE_ID, Bundle.CorrelationType_FILES_displayName(), "file", true, true)); // NON-NLS - DEFAULT_CORRELATION_TYPES.add(new CorrelationAttribute.Type(DOMAIN_TYPE_ID, Bundle.CorrelationType_DOMAIN_displayName(), "domain", true, true)); // NON-NLS - DEFAULT_CORRELATION_TYPES.add(new CorrelationAttribute.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS - DEFAULT_CORRELATION_TYPES.add(new CorrelationAttribute.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS - DEFAULT_CORRELATION_TYPES.add(new CorrelationAttribute.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS - return DEFAULT_CORRELATION_TYPES; - } - - public CorrelationAttribute(Type correlationType, String correlationValue) throws EamDbException { - if(correlationValue == null) { - throw new EamDbException ("Correlation value is null"); - } - this.ID = ""; - this.correlationType = correlationType; - // Lower-case all values to normalize and improve correlation hits, going forward make sure this makes sense for all correlation types - this.correlationValue = correlationValue.toLowerCase(); - this.artifactInstances = new ArrayList<>(); - } - - public Boolean equals(CorrelationAttribute otherArtifact) { - return ((this.getID().equals(otherArtifact.getID())) - && (this.getCorrelationType().equals(otherArtifact.getCorrelationType())) - && (this.getCorrelationValue().equals(otherArtifact.getCorrelationValue())) - && (this.getInstances().equals(otherArtifact.getInstances()))); - } - - @Override - public String toString() { - // NOTE: This string is currently being used in IngestEventsListener to detect if we have already seen - // the value and type pair. Be careful if this method is changed. - String result = this.getID() - + this.getCorrelationType().toString() - + this.getCorrelationValue(); - result = this.getInstances().stream().map((inst) -> inst.toString()).reduce(result, String::concat); - return result; - } - - /** - * @return the ID - */ - public String getID() { - return ID; - } - - /** - * @param ID the ID to set - */ - public void setID(String ID) { - this.ID = ID; - } - - /** - * @return the correlationValue - */ - public String getCorrelationValue() { - return correlationValue; - } - - /** - * @param correlationValue the correlationValue to set - */ - public void setCorrelationValue(String correlationValue) { - // Lower-case all values to normalize and improve correlation hits, going forward make sure this makes sense for all correlation types - this.correlationValue = correlationValue.toLowerCase(); - } - - /** - * @return the correlation Type - */ - public Type getCorrelationType() { - return correlationType; - } - - /** - * @param correlationType the correlation Type to set - */ - public void setCorrelationType(Type correlationType) { - this.correlationType = correlationType; - } - - /** - * @return the List of artifactInstances; empty list of none have been - * added. - */ - public List getInstances() { - return new ArrayList<>(artifactInstances); - } - - /** - * Set the list of artifact instances - * - * @param artifactInstances the List of artifactInstances to set. - */ - public void setInstances(List artifactInstances) { - this.artifactInstances.clear(); - if (null != artifactInstances) { - this.artifactInstances.addAll(artifactInstances); - } - } - - /** - * Add an artifact instance to the list - * - * @param artifactInstance the instance to add - */ - public void addInstance(CorrelationAttributeInstance artifactInstance) { - this.artifactInstances.add(artifactInstance); - } - - public static class Type implements Serializable { - - private int id; - private String displayName; - private String dbTableName; - private Boolean supported; - private Boolean enabled; - private final String DB_NAMES_REGEX = "[a-z][a-z0-9_]*"; - - /** - * - * @param id Unique ID for this Correlation Type - * @param displayName Name of this type displayed in the UI. - * @param dbTableName Central repository db table where data of this type is stored. - * Must start with a lowercase letter and only contain - * lowercase letters, numbers, and '_' characters. - * @param supported Is this Type currently supported - * @param enabled Is this Type currently enabled. - */ - public Type(int id, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { - if(dbTableName == null) { - throw new EamDbException("dbTableName is null"); - } - this.id = id; - this.displayName = displayName; - this.dbTableName = dbTableName; - this.supported = supported; - this.enabled = enabled; - if (!Pattern.matches(DB_NAMES_REGEX, dbTableName)) { - throw new EamDbException("Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); // NON-NLS - } - } - - /** - * Constructor for custom types where we do not know the Type ID until - * the row has been entered into the correlation_types table - * in the central repository. - * - * @param displayName Name of this type displayed in the UI. - * @param dbTableName Central repository db table where data of this type is stored - * Must start with a lowercase letter and only contain - * lowercase letters, numbers, and '_' characters. - * @param supported Is this Type currently supported - * @param enabled Is this Type currently enabled. - */ - public Type(String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { - this(-1, displayName, dbTableName, supported, enabled); - } - - /** - * Determine if 2 Type objects are equal - * - * @param that Type object for comparison. - * - * @return true or false - */ - @Override - public boolean equals(Object that) { - if (this == that) { - return true; - } else if (!(that instanceof CorrelationAttribute.Type)) { - return false; - } else { - return ((CorrelationAttribute.Type) that).sameType(this); - } - } - - /** - * Determines if the content of this artifact type object is equivalent - * to the content of another artifact type object. - * - * @param that the other type - * - * @return true if it is the same type - */ - private boolean sameType(CorrelationAttribute.Type that) { - return this.id == that.getId() - && Objects.equals(this.supported, that.isSupported()) - && Objects.equals(this.enabled, that.isEnabled()); - } - - @Override - public int hashCode() { - int hash = 7; - hash = 67 * hash + Objects.hashCode(this.id); - hash = 67 * hash + Objects.hashCode(this.supported); - hash = 67 * hash + Objects.hashCode(this.enabled); - return hash; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - str.append("(id=").append(getId()); - str.append(", displayName=").append(getDisplayName()); - str.append(", dbTableName=").append(getDbTableName()); - str.append(", supported=").append(isSupported().toString()); - str.append(", enabled=").append(isEnabled().toString()); - str.append(")"); - return str.toString(); - } - - /** - * @return the id - */ - public int getId() { - return id; - } - - /** - * @param id the id to set - */ - public void setId(int id) { - this.id = id; - } - - /** - * Check if this Artifact Type is supported. - * - * @return true or false - */ - public Boolean isSupported() { - return supported; - } - - /** - * Set this Artifact Type as supported or not supported. - * - * @param supported the supported to set - */ - public void setSupported(Boolean supported) { - this.supported = supported; - } - - /** - * Check if this Artifact Type is enabled. - * - * @return true or false - */ - public Boolean isEnabled() { - return enabled; - } - - /** - * Set this Artifact Type as enabled or not enabled. - * - * @param enabled the enabled to set - */ - public void setEnabled(Boolean enabled) { - this.enabled = enabled; - } - - /** - * @return the displayName - */ - public String getDisplayName() { - return displayName; - } - - /** - * @param displayName the displayName to set - */ - public void setDisplayName(String displayName) { - this.displayName = displayName; - } - - /** - * To support having different database tables for each Type, - * this field provides the prefix/suffix of the table name, - * which allows us to automatically compute the table names - * and index names. - * - * It is the prefix for the instances tables *_instances. (i.e. file_instances) - * It is the suffix for the reference tables reference_*. (i.e. reference_file) - * - * When custom Types are added in the future, they are already supported - * by just giving the desired value for the table name for each custom - * Type. Possibly having all custom Types use a common table name. - * - * @return the dbTableName - */ - public String getDbTableName() { - return dbTableName; - } - - /** - * To support having different database tables for each Type, - * this field provides the prefix/suffix of the table name, - * which allows us to automatically compute the table names - * and index names. - * - * It is the prefix for the instances tables *_instances. (i.e. file_instances) - * It is the suffix for the reference tables reference_*. (i.e. reference_file) - * - * When custom Types are added in the future, they are already supported - * by just giving the desired value for the table name for each custom - * Type. Possibly having all custom Types use a common table name. (i.e. custom_instances) - * - * @param dbTableName the dbTableName to set. Must start with lowercase letter - * and can only contain lowercase letters, numbers, and '_' characters. - * - * @throws EamDbException if dbTableName contains invalid characters - */ - public void setDbTableName(String dbTableName) throws EamDbException { - if (!Pattern.matches(DB_NAMES_REGEX, dbTableName)) { - throw new EamDbException("Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); // NON-NLS - } - this.dbTableName = dbTableName; - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index 3ccca9f293..9d64f7dc72 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -19,6 +19,10 @@ package org.sleuthkit.autopsy.centralrepository.datamodel; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; import org.openide.util.NbBundle.Messages; import org.sleuthkit.datamodel.TskData; @@ -37,6 +41,8 @@ public class CorrelationAttributeInstance implements Serializable { private static final long serialVersionUID = 1L; private int ID; + private String correlationValue; + private CorrelationAttributeInstance.Type correlationType; private CorrelationCase correlationCase; private CorrelationDataSource correlationDataSource; private String filePath; @@ -44,26 +50,50 @@ public class CorrelationAttributeInstance implements Serializable { private TskData.FileKnown knownStatus; public CorrelationAttributeInstance( + String correlationValue, + CorrelationAttributeInstance.Type correlationType, CorrelationCase eamCase, CorrelationDataSource eamDataSource, String filePath ) throws EamDbException { - this(-1, eamCase, eamDataSource, filePath, null, TskData.FileKnown.UNKNOWN); + this(correlationType, correlationValue, -1, eamCase, eamDataSource, filePath, null, TskData.FileKnown.UNKNOWN); } - public CorrelationAttributeInstance( + String correlationValue, + CorrelationAttributeInstance.Type correlationType, CorrelationCase eamCase, CorrelationDataSource eamDataSource, String filePath, String comment, TskData.FileKnown knownStatus ) throws EamDbException { - this(-1, eamCase, eamDataSource, filePath, comment, knownStatus); + this(correlationType, correlationValue, -1, eamCase, eamDataSource, filePath, comment, knownStatus); + } + + public CorrelationAttributeInstance( + Type correlationType, + String correlationValue, + CorrelationCase correlationCase, + CorrelationDataSource fromTSKDataSource, + String string) throws EamDbException { + this(correlationType, correlationValue, -1, correlationCase, fromTSKDataSource, string, "", TskData.FileKnown.UNKNOWN); + } + + /** + * NOTE: Only used for when EamDB is NOT enabled. + * + * @param aType CorrelationAttributeInstance.Type + * @param value correlation value + */ + public CorrelationAttributeInstance(Type aType, String value) throws EamDbException { + this(aType, value, -1, null, null, "", "", TskData.FileKnown.UNKNOWN); } CorrelationAttributeInstance( - int ID, + Type type, + String value, + int instanceId, CorrelationCase eamCase, CorrelationDataSource eamDataSource, String filePath, @@ -74,7 +104,13 @@ public class CorrelationAttributeInstance implements Serializable { throw new EamDbException("file path is null"); } - this.ID = ID; + if (value == null) { + throw new EamDbException("correlation value is null"); + } + + this.correlationType = type; + this.correlationValue = value; + this.ID = instanceId; this.correlationCase = eamCase; this.correlationDataSource = eamDataSource; // Lower case paths to normalize paths and improve correlation results, if this causes significant issues on case-sensitive file systems, remove @@ -102,11 +138,40 @@ public class CorrelationAttributeInstance implements Serializable { + this.getComment(); } + /** + * @return the correlationValue + */ + public String getCorrelationValue() { + return correlationValue; + } + + /** + * @param correlationValue the correlationValue to set + */ + public void setCorrelationValue(String correlationValue) { + // Lower-case all values to normalize and improve correlation hits, going forward make sure this makes sense for all correlation types + this.correlationValue = correlationValue.toLowerCase(); + } + + /** + * @return the correlation Type + */ + public Type getCorrelationType() { + return correlationType; + } + + /** + * @param correlationType the correlation Type to set + */ + public void setCorrelationType(Type correlationType) { + this.correlationType = correlationType; + } + /** * Is this a database instance? * * @return True if the instance ID is greater or equal to zero; otherwise - * false. + * false. */ public boolean isDatabaseInstance() { return (ID >= 0); @@ -115,7 +180,7 @@ public class CorrelationAttributeInstance implements Serializable { /** * @return the database ID */ - int getID() { + public int getID() { return ID; } @@ -169,9 +234,259 @@ public class CorrelationAttributeInstance implements Serializable { * as notable and should never be set to KNOWN. * * @param knownStatus Should be BAD if the item is tagged as notable, - * UNKNOWN otherwise + * UNKNOWN otherwise */ public void setKnownStatus(TskData.FileKnown knownStatus) { this.knownStatus = knownStatus; } + + // Type ID's for Default Correlation Types + public static final int FILES_TYPE_ID = 0; + public static final int DOMAIN_TYPE_ID = 1; + public static final int EMAIL_TYPE_ID = 2; + public static final int PHONE_TYPE_ID = 3; + public static final int USBID_TYPE_ID = 4; + + /** + * Load the default correlation types + * + * @throws EamDbException if the Type's dbTableName has invalid + * characters/format + */ + @Messages({"CorrelationType.FILES.displayName=Files", + "CorrelationType.DOMAIN.displayName=Domains", + "CorrelationType.EMAIL.displayName=Email Addresses", + "CorrelationType.PHONE.displayName=Phone Numbers", + "CorrelationType.USBID.displayName=USB Devices"}) + public static List getDefaultCorrelationTypes() throws EamDbException { + List DEFAULT_CORRELATION_TYPES = new ArrayList<>(); + DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(FILES_TYPE_ID, Bundle.CorrelationType_FILES_displayName(), "file", true, true)); // NON-NLS + DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(DOMAIN_TYPE_ID, Bundle.CorrelationType_DOMAIN_displayName(), "domain", true, true)); // NON-NLS + DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS + DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS + DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS + return DEFAULT_CORRELATION_TYPES; + } + + /** + * Correlation Types which determine which table to query in the CR + */ + @SuppressWarnings("serial") + public static class Type implements Serializable { // NOPMD Avoid short class names like Type + + private int typeId; + private String displayName; + private String dbTableName; + private Boolean supported; + private Boolean enabled; + private final static String DB_NAMES_REGEX = "[a-z][a-z0-9_]*"; + + /** + * + * @param id Unique ID for this Correlation Type + * @param displayName Name of this type displayed in the UI. + * @param dbTableName Central repository db table where data of this + * type is stored. Must start with a lowercase letter and only contain + * lowercase letters, numbers, and '_' characters. + * @param supported Is this Type currently supported + * @param enabled Is this Type currently enabled. + */ + public Type(int typeId, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { + if (dbTableName == null) { + throw new EamDbException("dbTableName is null"); + } + this.typeId = typeId; + this.displayName = displayName; + this.dbTableName = dbTableName; + this.supported = supported; + this.enabled = enabled; + if (!Pattern.matches(DB_NAMES_REGEX, dbTableName)) { + throw new EamDbException("Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); // NON-NLS + } + } + + /** + * Constructor for custom types where we do not know the Type ID until + * the row has been entered into the correlation_types table in the + * central repository. + * + * @param displayName Name of this type displayed in the UI. + * @param dbTableName Central repository db table where data of this + * type is stored Must start with a lowercase letter and only contain + * lowercase letters, numbers, and '_' characters. + * @param supported Is this Type currently supported + * @param enabled Is this Type currently enabled. + */ + public Type(String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { + this(-1, displayName, dbTableName, supported, enabled); + } + + /** + * Determine if 2 Type objects are equal + * + * @param that Type object for comparison. + * + * @return true or false + */ + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } else if (!(that instanceof CorrelationAttributeInstance.Type)) { + return false; + } else { + return ((CorrelationAttributeInstance.Type) that).sameType(this); + } + } + + /** + * Determines if the content of this artifact type object is equivalent + * to the content of another artifact type object. + * + * @param that the other type + * + * @return true if it is the same type + */ + private boolean sameType(CorrelationAttributeInstance.Type that) { + return this.typeId == that.getId() + && Objects.equals(this.supported, that.isSupported()) + && Objects.equals(this.enabled, that.isEnabled()); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 67 * hash + Objects.hashCode(this.typeId); + hash = 67 * hash + Objects.hashCode(this.supported); + hash = 67 * hash + Objects.hashCode(this.enabled); + return hash; + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(55); + str.append("(id=") + .append(getId()) + .append(", displayName=") + .append(getDisplayName()) + .append(", dbTableName=") + .append(getDbTableName()) + .append(", supported=") + .append(isSupported().toString()) + .append(", enabled=") + .append(isEnabled().toString()) + .append(')'); + return str.toString(); + } + + /** + * @return the typeId + */ + public int getId() { + return typeId; + } + + /** + * @param id the typeId to set + */ + public void setId(int typeId) { + this.typeId = typeId; + } + + /** + * Check if this Artifact Type is supported. + * + * @return true or false + */ + public Boolean isSupported() { + return supported; + } + + /** + * Set this Artifact Type as supported or not supported. + * + * @param supported the supported to set + */ + public void setSupported(Boolean supported) { + this.supported = supported; + } + + /** + * Check if this Artifact Type is enabled. + * + * @return true or false + */ + public Boolean isEnabled() { + return enabled; + } + + /** + * Set this Artifact Type as enabled or not enabled. + * + * @param enabled the enabled to set + */ + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + /** + * @return the displayName + */ + public String getDisplayName() { + return displayName; + } + + /** + * @param displayName the displayName to set + */ + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + /** + * To support having different database tables for each Type, this field + * provides the prefix/suffix of the table name, which allows us to + * automatically compute the table names and index names. + * + * It is the prefix for the instances tables *_instances. (i.e. + * file_instances) It is the suffix for the reference tables + * reference_*. (i.e. reference_file) + * + * When custom Types are added in the future, they are already supported + * by just giving the desired value for the table name for each custom + * Type. Possibly having all custom Types use a common table name. + * + * @return the dbTableName + */ + public String getDbTableName() { + return dbTableName; + } + + /** + * To support having different database tables for each Type, this field + * provides the prefix/suffix of the table name, which allows us to + * automatically compute the table names and index names. + * + * It is the prefix for the instances tables *_instances. (i.e. + * file_instances) It is the suffix for the reference tables + * reference_*. (i.e. reference_file) + * + * When custom Types are added in the future, they are already supported + * by just giving the desired value for the table name for each custom + * Type. Possibly having all custom Types use a common table name. (i.e. + * custom_instances) + * + * @param dbTableName the dbTableName to set. Must start with lowercase + * letter and can only contain lowercase letters, numbers, and '_' + * characters. + * + * @throws EamDbException if dbTableName contains invalid characters + */ + public void setDbTableName(String dbTableName) throws EamDbException { + if (!Pattern.matches(DB_NAMES_REGEX, dbTableName)) { + throw new EamDbException("Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); // NON-NLS + } + this.dbTableName = dbTableName; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java index 808b314af0..48aea257ba 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java @@ -38,7 +38,6 @@ import org.sleuthkit.datamodel.TskData; */ public class EamArtifactUtil { - private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(EamArtifactUtil.class.getName()); public EamArtifactUtil() { @@ -55,18 +54,18 @@ public class EamArtifactUtil { * EamArtifact with a single EamArtifactInstance within. If not, return * null. * - * @param bbArtifact BlackboardArtifact to examine + * @param bbArtifact BlackboardArtifact to examine * @param addInstanceDetails If true, add instance details from bbArtifact - * into the returned structure - * @param checkEnabled If true, only create a CorrelationAttribute if - * it is enabled + * into the returned structure + * @param checkEnabled If true, only create a CorrelationAttribute if it is + * enabled * * @return List of EamArtifacts */ - public static List getCorrelationAttributeFromBlackboardArtifact(BlackboardArtifact bbArtifact, - boolean addInstanceDetails, boolean checkEnabled) { + public static List makeInstancesFromBlackboardArtifact(BlackboardArtifact bbArtifact, + boolean checkEnabled) { - List eamArtifacts = new ArrayList<>(); + List eamArtifacts = new ArrayList<>(); try { // Cycle through the types and see if there is a correlation attribute that works @@ -74,9 +73,10 @@ public class EamArtifactUtil { // // @@@ This seems ineffecient. Instead of cycling based on correlation type, we should just // have switch based on artifact type - for (CorrelationAttribute.Type aType : EamDb.getInstance().getDefinedCorrelationTypes()) { + for (CorrelationAttributeInstance.Type aType : EamDb.getInstance().getDefinedCorrelationTypes()) { if ((checkEnabled && aType.isEnabled()) || !checkEnabled) { - CorrelationAttribute correlationAttribute = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(aType, bbArtifact); + // Now always adds the instance details associated with this occurance. + CorrelationAttributeInstance correlationAttribute = EamArtifactUtil.makeInstanceFromBlackboardArtifact(aType, bbArtifact); if (correlationAttribute != null) { eamArtifacts.add(correlationAttribute); } @@ -87,42 +87,6 @@ public class EamArtifactUtil { return eamArtifacts; } - // if they asked for it, add the instance details associated with this occurance. - if (!eamArtifacts.isEmpty() && addInstanceDetails) { - try { - Case currentCase = Case.getCurrentCaseThrows(); - AbstractFile bbSourceFile = currentCase.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID()); - if (null == bbSourceFile) { - //@@@ Log this - return eamArtifacts; - } - - // make an instance for the BB source file - CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows()); - if (null == correlationCase) { - correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); - } - CorrelationAttributeInstance eamInstance = new CorrelationAttributeInstance( - correlationCase, - CorrelationDataSource.fromTSKDataSource(correlationCase, bbSourceFile.getDataSource()), - bbSourceFile.getParentPath() + bbSourceFile.getName(), - "", - TskData.FileKnown.UNKNOWN - ); - - // add the instance details - for (CorrelationAttribute eamArtifact : eamArtifacts) { - eamArtifact.addInstance(eamInstance); - } - } catch (TskCoreException | EamDbException ex) { - logger.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS - return eamArtifacts; - } catch (NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Case is closed.", ex); // NON-NLS - return eamArtifacts; - } - } - return eamArtifacts; } @@ -131,12 +95,12 @@ public class EamArtifactUtil { * based on the data in the blackboard artifact. * * @param correlationType The Central Repository artifact type to create - * @param bbArtifact The blackboard artifact to pull data from + * @param bbArtifact The blackboard artifact to pull data from * * @return the new EamArtifact, or null if one was not created because - * bbArtifact did not contain the needed data + * bbArtifact did not contain the needed data */ - private static CorrelationAttribute getCorrelationAttributeFromBlackboardArtifact(CorrelationAttribute.Type correlationType, + private static CorrelationAttributeInstance makeInstanceFromBlackboardArtifact(CorrelationAttributeInstance.Type correlationType, BlackboardArtifact bbArtifact) throws EamDbException { String value = null; int artifactTypeID = bbArtifact.getArtifactTypeID(); @@ -147,10 +111,10 @@ public class EamArtifactUtil { BlackboardAttribute attribute = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); if (attribute != null) { BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong()); - return EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(correlationType, associatedArtifact); + return EamArtifactUtil.makeInstanceFromBlackboardArtifact(correlationType, associatedArtifact); } - } else if (correlationType.getId() == CorrelationAttribute.EMAIL_TYPE_ID + } else if (correlationType.getId() == CorrelationAttributeInstance.EMAIL_TYPE_ID && BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() == artifactTypeID) { BlackboardAttribute setNameAttr = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); @@ -158,7 +122,7 @@ public class EamArtifactUtil { && EamArtifactUtil.getEmailAddressAttrString().equals(setNameAttr.getValueString())) { value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD)).getValueString(); } - } else if (correlationType.getId() == CorrelationAttribute.DOMAIN_TYPE_ID + } else if (correlationType.getId() == CorrelationAttributeInstance.DOMAIN_TYPE_ID && (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID() == artifactTypeID || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID() == artifactTypeID || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == artifactTypeID @@ -166,7 +130,7 @@ public class EamArtifactUtil { // Lower-case this to normalize domains value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN)).getValueString(); - } else if (correlationType.getId() == CorrelationAttribute.PHONE_TYPE_ID + } else if (correlationType.getId() == CorrelationAttributeInstance.PHONE_TYPE_ID && (BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID() == artifactTypeID || BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() == artifactTypeID || BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == artifactTypeID)) { @@ -195,7 +159,7 @@ public class EamArtifactUtil { } } - } else if (correlationType.getId() == CorrelationAttribute.USBID_TYPE_ID + } else if (correlationType.getId() == CorrelationAttributeInstance.USBID_TYPE_ID && BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID() == artifactTypeID) { value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID)).getValueString(); @@ -210,12 +174,54 @@ public class EamArtifactUtil { } if (null != value) { - return new CorrelationAttribute(correlationType, value); + return makeCorrelationAttributeInstanceUsingTypeValue(bbArtifact, correlationType, value); } else { return null; } } + /** + * Uses the determined type and vallue, then looks up instance details to + * create proper CorrelationAttributeInstance. + * + * @param bbArtifact the blackboard artifatc + * @param correlationType the given type + * @param value the artifact value + * @return CorrelationAttributeInstance from details + */ + private static CorrelationAttributeInstance makeCorrelationAttributeInstanceUsingTypeValue(BlackboardArtifact bbArtifact, CorrelationAttributeInstance.Type correlationType, String value) { + try { + Case currentCase = Case.getCurrentCaseThrows(); + AbstractFile bbSourceFile = currentCase.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID()); + if (null == bbSourceFile) { + logger.log(Level.SEVERE, "Error creating artifact instance. Abstract File was null."); // NON-NLS + return null; + } + + // make an instance for the BB source file + CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows()); + if (null == correlationCase) { + correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); + } + return new CorrelationAttributeInstance( + value, + correlationType, + correlationCase, + CorrelationDataSource.fromTSKDataSource(correlationCase, bbSourceFile.getDataSource()), + bbSourceFile.getParentPath() + bbSourceFile.getName(), + "", + TskData.FileKnown.UNKNOWN + ); + + } catch (TskCoreException | EamDbException ex) { + logger.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS + return null; + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Case is closed.", ex); // NON-NLS + return null; + } + } + /** * Retrieve CorrelationAttribute from the given Content. * @@ -223,7 +229,7 @@ public class EamArtifactUtil { * * @return The new CorrelationAttribute, or null if retrieval failed. */ - public static CorrelationAttribute getCorrelationAttributeFromContent(Content content) { + public static CorrelationAttributeInstance getInstanceFromContent(Content content) { if (!(content instanceof AbstractFile)) { return null; @@ -235,15 +241,14 @@ public class EamArtifactUtil { return null; } - CorrelationAttribute correlationAttribute; - CorrelationAttribute.Type type; + CorrelationAttributeInstance.Type type; CorrelationCase correlationCase; CorrelationDataSource correlationDataSource; String value; String filePath; - + try { - type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows()); if (null == correlationCase) { correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); @@ -258,9 +263,10 @@ public class EamArtifactUtil { logger.log(Level.SEVERE, "Case is closed.", ex); return null; } - + + CorrelationAttributeInstance correlationAttributeInstance; try { - correlationAttribute = EamDb.getInstance().getCorrelationAttribute(type, correlationCase, correlationDataSource, value, filePath); + correlationAttributeInstance = EamDb.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, value, filePath); } catch (EamDbException ex) { logger.log(Level.WARNING, String.format( "Correlation attribute could not be retrieved for '%s' (id=%d): %s", @@ -268,7 +274,7 @@ public class EamArtifactUtil { return null; } - return correlationAttribute; + return correlationAttributeInstance; } /** @@ -284,7 +290,7 @@ public class EamArtifactUtil { * * @return The new EamArtifact or null if creation failed */ - public static CorrelationAttribute makeCorrelationAttributeFromContent(Content content) { + public static CorrelationAttributeInstance makeInstanceFromContent(Content content) { if (!(content instanceof AbstractFile)) { return null; @@ -302,20 +308,20 @@ public class EamArtifactUtil { return null; } - CorrelationAttribute eamArtifact; try { - CorrelationAttribute.Type filesType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); - eamArtifact = new CorrelationAttribute(filesType, af.getMd5Hash()); + CorrelationAttributeInstance.Type filesType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); + CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows()); if (null == correlationCase) { correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); } - CorrelationAttributeInstance cei = new CorrelationAttributeInstance( + return new CorrelationAttributeInstance( + filesType, + af.getMd5Hash(), correlationCase, CorrelationDataSource.fromTSKDataSource(correlationCase, af.getDataSource()), af.getParentPath() + af.getName()); - eamArtifact.addInstance(cei); - return eamArtifact; + } catch (TskCoreException | EamDbException ex) { logger.log(Level.SEVERE, "Error making correlation attribute.", ex); return null; @@ -332,7 +338,7 @@ public class EamArtifactUtil { * @param file The file to test * * @return true if the file should be added to the central repo, false - * otherwise + * otherwise */ public static boolean isSupportedAbstractFileType(AbstractFile file) { if (file == null) { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java index e4fb30583e..a58daa4b9b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java @@ -233,7 +233,7 @@ public interface EamDb { * * @param eamArtifact The artifact to add */ - void addArtifact(CorrelationAttribute eamArtifact) throws EamDbException; + void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws EamDbException; /** * Retrieves eamArtifact instances from the database that are associated @@ -244,7 +244,7 @@ public interface EamDb { * * @return List of artifact instances for a given type/value */ - List getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException; + List getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; /** * Retrieves eamArtifact instances from the database that are associated @@ -257,7 +257,7 @@ public interface EamDb { * * @throws EamDbException */ - List getArtifactInstancesByPath(CorrelationAttribute.Type aType, String filePath) throws EamDbException; + List getArtifactInstancesByPath(CorrelationAttributeInstance.Type aType, String filePath) throws EamDbException; /** * Retrieves number of artifact instances in the database that are @@ -269,7 +269,7 @@ public interface EamDb { * @return Number of artifact instances having ArtifactType and * ArtifactValue. */ - Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException; + Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; /** * Calculate the percentage of data sources that have this attribute value. @@ -278,7 +278,7 @@ public interface EamDb { * * @return Int between 0 and 100 */ - int getFrequencyPercentage(CorrelationAttribute corAttr) throws EamDbException; + int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException; /** * Retrieves number of unique caseDisplayName / dataSource tuples in the @@ -290,7 +290,7 @@ public interface EamDb { * * @return Number of unique tuples */ - Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException; + Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; /** * Retrieves number of data sources in the database. @@ -314,18 +314,18 @@ public interface EamDb { /** * Adds an eamArtifact to an internal list to be later added to DB. Artifact - * can have 1 or more Artifact Instances. Insert will be triggered by a - * threshold or a call to bulkInsertArtifacts(). + can have 1 or more Artifact Instances. Insert will be triggered by a + threshold or a call to commitAttributeInstancesBulk(). * * @param eamArtifact The artifact to add */ - void prepareBulkArtifact(CorrelationAttribute eamArtifact) throws EamDbException; + void addAttributeInstanceBulk(CorrelationAttributeInstance eamArtifact) throws EamDbException; /** * Executes a bulk insert of the eamArtifacts added from the - * prepareBulkArtifact() method + addAttributeInstanceBulk() method */ - void bulkInsertArtifacts() throws EamDbException; + void commitAttributeInstancesBulk() throws EamDbException; /** * Executes a bulk insert of the cases @@ -341,7 +341,7 @@ public interface EamDb { * * @throws EamDbException */ - void updateAttributeInstanceComment(CorrelationAttribute eamArtifact) throws EamDbException; + void updateAttributeInstanceComment(CorrelationAttributeInstance eamArtifact) throws EamDbException; /** * Find a correlation attribute in the Central Repository database given the @@ -357,7 +357,7 @@ public interface EamDb { * * @throws EamDbException */ - CorrelationAttribute getCorrelationAttribute(CorrelationAttribute.Type type, CorrelationCase correlationCase, + CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase, CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException; /** @@ -367,7 +367,7 @@ public interface EamDb { * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. * @param knownStatus The status to change the artifact to */ - void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException; + void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException; /** * Gets list of matching eamArtifact instances that have knownStatus = @@ -378,7 +378,7 @@ public interface EamDb { * * @return List with 0 or more matching eamArtifact instances. */ - List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException; + List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; /** * Gets list of matching eamArtifact instances that have knownStatus = @@ -388,7 +388,7 @@ public interface EamDb { * @return List with 0 or more matching eamArtifact instances. * @throws EamDbException */ - List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException; + List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType) throws EamDbException; /** * Count matching eamArtifacts instances that have knownStatus = "Bad". * @@ -397,7 +397,7 @@ public interface EamDb { * * @return Number of matching eamArtifacts */ - Long getCountArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException; + Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; /** * Gets list of distinct case display names, where each case has 1+ Artifact @@ -411,7 +411,7 @@ public interface EamDb { * * @throws EamDbException */ - List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException; + List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; /** * Remove a reference set and all values contained in it. @@ -483,7 +483,7 @@ public interface EamDb { * * @return Global known status of the artifact */ - boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException; + boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; /** * Add a new organization @@ -577,7 +577,7 @@ public interface EamDb { * * @throws EamDbException */ - List getAllReferenceSets(CorrelationAttribute.Type correlationType) throws EamDbException; + List getAllReferenceSets(CorrelationAttributeInstance.Type correlationType) throws EamDbException; /** * Add a new reference instance @@ -588,7 +588,7 @@ public interface EamDb { * * @throws EamDbException */ - void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttribute.Type correlationType) throws EamDbException; + void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttributeInstance.Type correlationType) throws EamDbException; /** * Insert the bulk collection of Global File Instances @@ -599,7 +599,7 @@ public interface EamDb { * * @throws EamDbException */ - void bulkInsertReferenceTypeEntries(Set globalInstances, CorrelationAttribute.Type contentType) throws EamDbException; + void bulkInsertReferenceTypeEntries(Set globalInstances, CorrelationAttributeInstance.Type contentType) throws EamDbException; /** * Get all reference entries having a given correlation type and value @@ -611,7 +611,7 @@ public interface EamDb { * * @throws EamDbException */ - List getReferenceInstancesByTypeValue(CorrelationAttribute.Type aType, String aValue) throws EamDbException; + List getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException; /** * Add a new EamArtifact.Type to the db. @@ -622,7 +622,7 @@ public interface EamDb { * * @throws EamDbException */ - public int newCorrelationType(CorrelationAttribute.Type newType) throws EamDbException; + int newCorrelationType(CorrelationAttributeInstance.Type newType) throws EamDbException; /** * Get the list of EamArtifact.Type's that are defined in the DB and can be @@ -633,7 +633,7 @@ public interface EamDb { * * @throws EamDbException */ - public List getDefinedCorrelationTypes() throws EamDbException; + List getDefinedCorrelationTypes() throws EamDbException; /** * Get the list of enabled EamArtifact.Type's that will be used to correlate @@ -644,7 +644,7 @@ public interface EamDb { * * @throws EamDbException */ - public List getEnabledCorrelationTypes() throws EamDbException; + List getEnabledCorrelationTypes() throws EamDbException; /** * Get the list of supported EamArtifact.Type's that can be used to @@ -655,7 +655,7 @@ public interface EamDb { * * @throws EamDbException */ - public List getSupportedCorrelationTypes() throws EamDbException; + List getSupportedCorrelationTypes() throws EamDbException; /** * Update a EamArtifact.Type. @@ -664,7 +664,7 @@ public interface EamDb { * * @throws EamDbException */ - public void updateCorrelationType(CorrelationAttribute.Type aType) throws EamDbException; + void updateCorrelationType(CorrelationAttributeInstance.Type aType) throws EamDbException; /** * Get the EamArtifact.Type that has the given Type.Id. @@ -675,7 +675,7 @@ public interface EamDb { * * @throws EamDbException */ - public CorrelationAttribute.Type getCorrelationTypeById(int typeId) throws EamDbException; + CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws EamDbException; /** * Upgrade the schema of the database (if needed) @@ -704,7 +704,7 @@ public interface EamDb { * @param instanceTableCallback callback to process the instance * @throws EamDbException */ - void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException; + void processInstanceTable(CorrelationAttributeInstance.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException; /** * Process the Artifact instance in the EamDb @@ -714,6 +714,6 @@ public interface EamDb { * @param whereClause query string to execute * @throws EamDbException */ - void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException; + void processInstanceTableWhere(CorrelationAttributeInstance.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException; } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java index 9b8a4e1ed5..e30ed174eb 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java @@ -104,9 +104,9 @@ public class EamDbUtil { String sql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?)"; try { - List DEFAULT_CORRELATION_TYPES = CorrelationAttribute.getDefaultCorrelationTypes(); + List DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes(); preparedStatement = conn.prepareStatement(sql); - for (CorrelationAttribute.Type newType : DEFAULT_CORRELATION_TYPES) { + for (CorrelationAttributeInstance.Type newType : DEFAULT_CORRELATION_TYPES) { preparedStatement.setInt(1, newType.getId()); preparedStatement.setString(2, newType.getDisplayName()); preparedStatement.setString(3, newType.getDbTableName()); @@ -346,7 +346,7 @@ public class EamDbUtil { * * @return Instance table name for this Type. */ - public static String correlationTypeToInstanceTableName(CorrelationAttribute.Type type) { + public static String correlationTypeToInstanceTableName(CorrelationAttributeInstance.Type type) { return type.getDbTableName() + "_instances"; } @@ -357,7 +357,7 @@ public class EamDbUtil { * * @return Reference table name for this Type. */ - public static String correlationTypeToReferenceTableName(CorrelationAttribute.Type type) { + public static String correlationTypeToReferenceTableName(CorrelationAttributeInstance.Type type) { return "reference_" + type.getDbTableName(); } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalSet.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalSet.java index 722c87407c..e9a230740c 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalSet.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalSet.java @@ -32,7 +32,7 @@ public class EamGlobalSet { private String version; private TskData.FileKnown fileKnownStatus; private boolean isReadOnly; - private CorrelationAttribute.Type type; + private CorrelationAttributeInstance.Type type; private LocalDate importDate; public EamGlobalSet( @@ -42,7 +42,7 @@ public class EamGlobalSet { String version, TskData.FileKnown knownStatus, boolean isReadOnly, - CorrelationAttribute.Type type, + CorrelationAttributeInstance.Type type, LocalDate importDate) { this.globalSetID = globalSetID; this.orgID = orgID; @@ -60,7 +60,7 @@ public class EamGlobalSet { String version, TskData.FileKnown knownStatus, boolean isReadOnly, - CorrelationAttribute.Type type, + CorrelationAttributeInstance.Type type, LocalDate importDate) { this(-1, orgID, setName, version, knownStatus, isReadOnly, type, importDate); } @@ -83,7 +83,7 @@ public class EamGlobalSet { String version, TskData.FileKnown knownStatus, boolean isReadOnly, - CorrelationAttribute.Type type) { + CorrelationAttributeInstance.Type type) { this(-1, orgID, setName, version, knownStatus, isReadOnly, type, LocalDate.now()); } @@ -176,7 +176,7 @@ public class EamGlobalSet { * * @return the type (files, phone numbers, etc) */ - public CorrelationAttribute.Type getType() { + public CorrelationAttributeInstance.Type getType() { return type; } @@ -185,7 +185,7 @@ public class EamGlobalSet { * * @param type */ - void setType(CorrelationAttribute.Type type) { + void setType(CorrelationAttributeInstance.Type type) { this.type = type; } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java index ee1d297b7d..5db232b51e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.centralrepository.datamodel; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; -import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import org.apache.commons.dbcp2.BasicDataSource; @@ -115,10 +114,10 @@ final class PostgresEamDb extends AbstractSqlEamDb { String instancesTemplate = "TRUNCATE TABLE %s_instances RESTART IDENTITY CASCADE"; String referencesTemplate = "TRUNCATE TABLE reference_%s RESTART IDENTITY CASCADE"; - for (CorrelationAttribute.Type type : defaultCorrelationTypes) { + for (CorrelationAttributeInstance.Type type : defaultCorrelationTypes) { dropContent.executeUpdate(String.format(instancesTemplate, type.getDbTableName())); // FUTURE: support other reference types - if (type.getId() == CorrelationAttribute.FILES_TYPE_ID) { + if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { dropContent.executeUpdate(String.format(referencesTemplate, type.getDbTableName())); } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java index 32de4e7646..77ab8c23db 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java @@ -446,11 +446,11 @@ public final class PostgresEamDbSettings { stmt.execute(createDbInfoTable.toString()); // Create a separate instance and reference table for each correlation type - List DEFAULT_CORRELATION_TYPES = CorrelationAttribute.getDefaultCorrelationTypes(); + List DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes(); String reference_type_dbname; String instance_type_dbname; - for (CorrelationAttribute.Type type : DEFAULT_CORRELATION_TYPES) { + for (CorrelationAttributeInstance.Type type : DEFAULT_CORRELATION_TYPES) { reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type); instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type); @@ -461,7 +461,7 @@ public final class PostgresEamDbSettings { stmt.execute(String.format(instancesIdx4, instance_type_dbname, instance_type_dbname)); // FUTURE: allow more than the FILES type - if (type.getId() == CorrelationAttribute.FILES_TYPE_ID) { + if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname)); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java index 74d0c36a66..d300964b5f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java @@ -125,10 +125,10 @@ final class SqliteEamDb extends AbstractSqlEamDb { String instancesTemplate = "DELETE FROM %s_instances"; String referencesTemplate = "DELETE FROM global_files"; - for (CorrelationAttribute.Type type : defaultCorrelationTypes) { + for (CorrelationAttributeInstance.Type type : defaultCorrelationTypes) { dropContent.executeUpdate(String.format(instancesTemplate, type.getDbTableName())); // FUTURE: support other reference types - if (type.getId() == CorrelationAttribute.FILES_TYPE_ID) { + if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { dropContent.executeUpdate(String.format(referencesTemplate, type.getDbTableName())); } } @@ -428,10 +428,10 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @param eamArtifact The artifact to add */ @Override - public void addArtifact(CorrelationAttribute eamArtifact) throws EamDbException { + public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws EamDbException { try { acquireExclusiveLock(); - super.addArtifact(eamArtifact); + super.addArtifactInstance(eamArtifact); } finally { releaseExclusiveLock(); } @@ -447,7 +447,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @return List of artifact instances for a given type/value */ @Override - public List getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { + public List getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { try { acquireSharedLock(); return super.getArtifactInstancesByTypeValue(aType, value); @@ -468,7 +468,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public List getArtifactInstancesByPath(CorrelationAttribute.Type aType, String filePath) throws EamDbException { + public List getArtifactInstancesByPath(CorrelationAttributeInstance.Type aType, String filePath) throws EamDbException { try { acquireSharedLock(); return super.getArtifactInstancesByPath(aType, filePath); @@ -489,7 +489,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { + public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { try { acquireSharedLock(); return super.getCountArtifactInstancesByTypeValue(aType, value); @@ -499,7 +499,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { } @Override - public int getFrequencyPercentage(CorrelationAttribute corAttr) throws EamDbException { + public int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException { try { acquireSharedLock(); return super.getFrequencyPercentage(corAttr); @@ -520,7 +520,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { + public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { try { acquireSharedLock(); return super.getCountUniqueCaseDataSourceTuplesHavingTypeValue(aType, value); @@ -562,13 +562,13 @@ final class SqliteEamDb extends AbstractSqlEamDb { /** * Executes a bulk insert of the eamArtifacts added from the - * prepareBulkArtifact() method + addAttributeInstanceBulk() method */ @Override - public void bulkInsertArtifacts() throws EamDbException { + public void commitAttributeInstancesBulk() throws EamDbException { try { acquireExclusiveLock(); - super.bulkInsertArtifacts(); + super.commitAttributeInstancesBulk(); } finally { releaseExclusiveLock(); } @@ -598,10 +598,10 @@ final class SqliteEamDb extends AbstractSqlEamDb { * KNOWN */ @Override - public void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { + public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { try { acquireExclusiveLock(); - super.setArtifactInstanceKnownStatus(eamArtifact, knownStatus); + super.setAttributeInstanceKnownStatus(eamArtifact, knownStatus); } finally { releaseExclusiveLock(); } @@ -617,7 +617,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @return List with 0 or more matching eamArtifact instances. */ @Override - public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { + public List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { try { acquireSharedLock(); return super.getArtifactInstancesKnownBad(aType, value); @@ -636,7 +636,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException { + public List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType) throws EamDbException { try { acquireSharedLock(); return super.getArtifactInstancesKnownBad(aType); @@ -654,7 +654,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @return Number of matching eamArtifacts */ @Override - public Long getCountArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { + public Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { try { acquireSharedLock(); return super.getCountArtifactInstancesKnownBad(aType, value); @@ -676,7 +676,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { + public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { try { acquireSharedLock(); return super.getListCasesHavingArtifactInstancesKnownBad(aType, value); @@ -727,7 +727,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException { + public void processInstanceTable(CorrelationAttributeInstance.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException { try { acquireSharedLock(); super.processInstanceTable(type, instanceTableCallback); @@ -744,7 +744,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException { + public void processInstanceTableWhere(CorrelationAttributeInstance.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException { try { acquireSharedLock(); super.processInstanceTableWhere(type, whereClause, instanceTableCallback); @@ -782,7 +782,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @return Global known status of the artifact */ @Override - public boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException { + public boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { try { acquireSharedLock(); return super.isArtifactKnownBadByReference(aType, value); @@ -914,7 +914,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public List getAllReferenceSets(CorrelationAttribute.Type correlationType) throws EamDbException { + public List getAllReferenceSets(CorrelationAttributeInstance.Type correlationType) throws EamDbException { try { acquireSharedLock(); return super.getAllReferenceSets(correlationType); @@ -932,7 +932,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttribute.Type correlationType) throws EamDbException { + public void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttributeInstance.Type correlationType) throws EamDbException { try { acquireExclusiveLock(); super.addReferenceInstance(eamGlobalFileInstance, correlationType); @@ -947,7 +947,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public void bulkInsertReferenceTypeEntries(Set globalInstances, CorrelationAttribute.Type contentType) throws EamDbException { + public void bulkInsertReferenceTypeEntries(Set globalInstances, CorrelationAttributeInstance.Type contentType) throws EamDbException { try { acquireExclusiveLock(); super.bulkInsertReferenceTypeEntries(globalInstances, contentType); @@ -967,7 +967,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public List getReferenceInstancesByTypeValue(CorrelationAttribute.Type aType, String aValue) throws EamDbException { + public List getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException { try { acquireSharedLock(); return super.getReferenceInstancesByTypeValue(aType, aValue); @@ -986,7 +986,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public int newCorrelationType(CorrelationAttribute.Type newType) throws EamDbException { + public int newCorrelationType(CorrelationAttributeInstance.Type newType) throws EamDbException { try { acquireExclusiveLock(); return super.newCorrelationType(newType); @@ -1005,7 +1005,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public List getDefinedCorrelationTypes() throws EamDbException { + public List getDefinedCorrelationTypes() throws EamDbException { try { acquireSharedLock(); return super.getDefinedCorrelationTypes(); @@ -1024,7 +1024,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public List getEnabledCorrelationTypes() throws EamDbException { + public List getEnabledCorrelationTypes() throws EamDbException { try { acquireSharedLock(); return super.getEnabledCorrelationTypes(); @@ -1043,7 +1043,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public List getSupportedCorrelationTypes() throws EamDbException { + public List getSupportedCorrelationTypes() throws EamDbException { try { acquireSharedLock(); return super.getSupportedCorrelationTypes(); @@ -1060,7 +1060,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public void updateCorrelationType(CorrelationAttribute.Type aType) throws EamDbException { + public void updateCorrelationType(CorrelationAttributeInstance.Type aType) throws EamDbException { try { acquireExclusiveLock(); super.updateCorrelationType(aType); @@ -1079,7 +1079,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public CorrelationAttribute.Type getCorrelationTypeById(int typeId) throws EamDbException { + public CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws EamDbException { try { acquireSharedLock(); return super.getCorrelationTypeById(typeId); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java index c73fa55a0e..4894a570e6 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java @@ -394,11 +394,11 @@ public final class SqliteEamDbSettings { stmt.execute(createDbInfoTable.toString()); // Create a separate instance and reference table for each artifact type - List DEFAULT_CORRELATION_TYPES = CorrelationAttribute.getDefaultCorrelationTypes(); + List DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes(); String reference_type_dbname; String instance_type_dbname; - for (CorrelationAttribute.Type type : DEFAULT_CORRELATION_TYPES) { + for (CorrelationAttributeInstance.Type type : DEFAULT_CORRELATION_TYPES) { reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type); instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type); @@ -409,7 +409,7 @@ public final class SqliteEamDbSettings { stmt.execute(String.format(instancesIdx4, instance_type_dbname, instance_type_dbname)); // FUTURE: allow more than the FILES type - if (type.getId() == CorrelationAttribute.FILES_TYPE_ID) { + if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname)); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java index a3d49d6ba3..2643c7aac5 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java @@ -36,13 +36,12 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; import org.sleuthkit.autopsy.coreutils.ThreadUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -192,12 +191,12 @@ final class CaseEventListener implements PropertyChangeListener { } } - final CorrelationAttribute eamArtifact = EamArtifactUtil.makeCorrelationAttributeFromContent(af); + final CorrelationAttributeInstance eamArtifact = EamArtifactUtil.makeInstanceFromContent(af); if (eamArtifact != null) { // send update to Central Repository db try { - dbManager.setArtifactInstanceKnownStatus(eamArtifact, knownStatus); + dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus); } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS } @@ -292,11 +291,11 @@ final class CaseEventListener implements PropertyChangeListener { return; } - List convertedArtifacts = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbArtifact, true, true); - for (CorrelationAttribute eamArtifact : convertedArtifacts) { - eamArtifact.getInstances().get(0).setComment(comment); + List convertedArtifacts = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, true); + for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { + eamArtifact.setComment(comment); try { - dbManager.setArtifactInstanceKnownStatus(eamArtifact, knownStatus); + dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus); } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS } @@ -365,9 +364,9 @@ final class CaseEventListener implements PropertyChangeListener { if (!hasTagWithConflictingKnownStatus) { //Get the correlation atttributes that correspond to the current BlackboardArtifactTag if their status should be changed //with the initial set of correlation attributes this should be a single correlation attribute - List convertedArtifacts = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbTag.getArtifact(), true, true); - for (CorrelationAttribute eamArtifact : convertedArtifacts) { - EamDb.getInstance().setArtifactInstanceKnownStatus(eamArtifact, tagName.getKnownStatus()); + List convertedArtifacts = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbTag.getArtifact(), true); + for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { + EamDb.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus()); } } } @@ -401,9 +400,9 @@ final class CaseEventListener implements PropertyChangeListener { } //if the file will have no tags with a status which would prevent the current status from being changed if (!hasTagWithConflictingKnownStatus) { - final CorrelationAttribute eamArtifact = EamArtifactUtil.makeCorrelationAttributeFromContent(contentTag.getContent()); + final CorrelationAttributeInstance eamArtifact = EamArtifactUtil.makeInstanceFromContent(contentTag.getContent()); if (eamArtifact != null) { - EamDb.getInstance().setArtifactInstanceKnownStatus(eamArtifact, tagName.getKnownStatus()); + EamDb.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus()); } } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index e3cfad9fe8..e432c4bf6b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -38,7 +38,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.datamodel.AbstractFile; @@ -54,7 +54,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadUtils; */ public class IngestEventsListener { - private static final Logger LOGGER = Logger.getLogger(CorrelationAttribute.class.getName()); + private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName()); final Collection recentlyAddedCeArtifacts = new LinkedHashSet<>(); private static int correlationModuleInstanceCount; @@ -248,12 +248,12 @@ public class IngestEventsListener { if (null == bbArtifacts) { //the ModuleDataEvents don't always have a collection of artifacts set return; } - List eamArtifacts = new ArrayList<>(); + List eamArtifacts = new ArrayList<>(); for (BlackboardArtifact bbArtifact : bbArtifacts) { // eamArtifact will be null OR a EamArtifact containing one EamArtifactInstance. - List convertedArtifacts = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbArtifact, true, true); - for (CorrelationAttribute eamArtifact : convertedArtifacts) { + List convertedArtifacts = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, true); + for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { try { // Only do something with this artifact if it's unique within the job if (recentlyAddedCeArtifacts.add(eamArtifact.toString())) { @@ -276,9 +276,9 @@ public class IngestEventsListener { } } if (FALSE == eamArtifacts.isEmpty()) { - for (CorrelationAttribute eamArtifact : eamArtifacts) { + for (CorrelationAttributeInstance eamArtifact : eamArtifacts) { try { - dbManager.addArtifact(eamArtifact); + dbManager.addArtifactInstance(eamArtifact); } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error adding artifact to database.", ex); //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java index 9c30cf05c5..441380bcc6 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java @@ -36,7 +36,6 @@ import org.sleuthkit.autopsy.ingest.IngestMessage; import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; @@ -70,7 +69,7 @@ final class IngestModule implements FileIngestModule { private CorrelationCase eamCase; private CorrelationDataSource eamDataSource; private Blackboard blackboard; - private CorrelationAttribute.Type filesType; + private CorrelationAttributeInstance.Type filesType; private final boolean flagTaggedNotableItems; @@ -149,16 +148,16 @@ final class IngestModule implements FileIngestModule { // insert this file into the central repository try { - CorrelationAttribute eamArtifact = new CorrelationAttribute(filesType, md5); CorrelationAttributeInstance cefi = new CorrelationAttributeInstance( + md5, + filesType, eamCase, eamDataSource, abstractFile.getParentPath() + abstractFile.getName(), null, TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database. ); - eamArtifact.addInstance(cefi); - dbManager.prepareBulkArtifact(eamArtifact); + dbManager.addAttributeInstanceBulk(cefi); } catch (EamDbException ex) { logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS return ProcessResult.ERROR; @@ -182,7 +181,7 @@ final class IngestModule implements FileIngestModule { return; } try { - dbManager.bulkInsertArtifacts(); + dbManager.commitAttributeInstancesBulk(); } catch (EamDbException ex) { logger.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS } @@ -264,7 +263,7 @@ final class IngestModule implements FileIngestModule { } try { - filesType = centralRepoDb.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + filesType = centralRepoDb.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); } catch (EamDbException ex) { logger.log(Level.SEVERE, "Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS throw new IngestModuleException("Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java index 3fa46b3306..550244dffe 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java @@ -30,7 +30,7 @@ import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; @@ -43,7 +43,7 @@ final class ManageCorrelationPropertiesDialog extends javax.swing.JDialog { private static final Logger LOGGER = Logger.getLogger(ManageCorrelationPropertiesDialog.class.getName()); - private final List correlationTypes; + private final List correlationTypes; /** * Displays a dialog that allows a user to select which Type(s) should be diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java index 7732c5d393..c8ecb3225b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java @@ -19,7 +19,6 @@ */ package org.sleuthkit.autopsy.commonfilesearch; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.datamodel.AbstractFile; @@ -146,13 +145,12 @@ public abstract class AbstractCommonAttributeInstance { * @return the appropriate leaf node for the results tree * @throws TskCoreException */ - static DisplayableItemNode createNode(CorrelationAttribute attribute, AbstractFile abstractFile, String currentCaseName) throws TskCoreException { + static DisplayableItemNode createNode(CorrelationAttributeInstance attribute, AbstractFile abstractFile, String currentCaseName) throws TskCoreException { DisplayableItemNode leafNode; - CorrelationAttributeInstance attributeInstance = attribute.getInstances().get(0); if (abstractFile == null) { - leafNode = new CentralRepoCommonAttributeInstanceNode(attributeInstance); + leafNode = new CentralRepoCommonAttributeInstanceNode(attribute); } else { final String abstractFileDataSourceName = abstractFile.getDataSource().getName(); leafNode = new CaseDBCommonAttributeInstanceNode(abstractFile, currentCaseName, abstractFileDataSourceName); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java index 0eda8c5e6f..62cbbeb24b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java @@ -42,11 +42,13 @@ public abstract class AbstractCommonAttributeSearcher { private final Map dataSourceIdToNameMap; private boolean filterByMedia; private boolean filterByDoc; + final int frequencyPercentageThreshold; - AbstractCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMedia, boolean filterByDoc){ + AbstractCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMedia, boolean filterByDoc, int percentageThreshold){ this.filterByDoc = filterByDoc; this.filterByMedia = filterByMedia; this.dataSourceIdToNameMap = dataSourceIdMap; + this.frequencyPercentageThreshold = percentageThreshold; } Map getDataSourceIdToNameMap(){ diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java index e11953cfa9..ac84d0188f 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java @@ -41,15 +41,15 @@ public class AllInterCaseCommonAttributeSearcher extends InterCaseCommonAttribut * broadly categorized as document types * @throws EamDbException */ - public AllInterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException { - super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + public AllInterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold); } @Override public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap()); Map interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase()); - return new CommonAttributeSearchResults(interCaseCommonFiles); + return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllIntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllIntraCaseCommonAttributeSearcher.java index eed61b0fc1..35a5c70452 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllIntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllIntraCaseCommonAttributeSearcher.java @@ -37,8 +37,8 @@ final public class AllIntraCaseCommonAttributeSearcher extends IntraCaseCommonAt * @param filterByMediaMimeType match only on files whose mime types can be broadly categorized as media types * @param filterByDocMimeType match only on files whose mime types can be broadly categorized as document types */ - public AllIntraCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { - super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + public AllIntraCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties index fbdc43d143..b8f2e3e0d7 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties @@ -6,8 +6,20 @@ CommonFilesPanel.intraCaseRadio.label=Correlate within current case only CommonFilesPanel.interCaseRadio.label=Correlate amongst all known cases (uses Central Repo) IntraCasePanel.allDataSourcesRadioButton.text=Matches may be from any data source IntraCasePanel.withinDataSourceRadioButton.text=At least one match must appear in the data source selected below: +IntraCasePanel.selectDataSourceComboBox.actionCommand= InterCasePanel.specificCentralRepoCaseRadio.text=Matches must be from the following Central Repo case: InterCasePanel.anyCentralRepoCaseRadio.text=Matches may be from any Central Repo case +CommonAttributePanel.jCheckBox1.text=Hide files found in over +CommonAttributePanel.jLabel1.text=% of data sources in central repository. +CommonAttributePanel.percentageThresholdTextTwo.text_1=% of data sources in central repository. +CommonAttributePanel.percentageThresholdTextOne.text=20 +CommonAttributePanel.percentageThresholdCheck.text_1=Hide files found in over +CommonAttributePanel.intraCaseRadio.text=Within current case +CommonAttributePanel.commonFilesSearchLabel1.text=Find common files to correlate data soures or cases. +CommonAttributePanel.errorText.text=In order to search, you must select a file category. +CommonAttributePanel.categoriesLabel.text=File Types To Include: +CommonAttributePanel.documentsCheckbox.text=Documents +CommonAttributePanel.pictureVideoCheckbox.text=Pictures and Videos CommonAttributePanel.selectedFileCategoriesButton.toolTipText=Select from the options below... CommonAttributePanel.selectedFileCategoriesButton.text=Only the selected file types: CommonAttributePanel.allFileCategoriesRadioButton.toolTipText=No filtering applied to results... @@ -16,9 +28,3 @@ CommonAttributePanel.cancelButton.actionCommand=Cancel CommonAttributePanel.cancelButton.text=Cancel CommonAttributePanel.searchButton.text=Search CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search -CommonAttributePanel.intraCaseRadio.text=Within current case -CommonAttributePanel.commonFilesSearchLabel1.text=Find common files to correlate data soures or cases. -CommonAttributePanel.errorText.text=In order to search, you must select a file category. -CommonAttributePanel.categoriesLabel.text=File Types To Include: -CommonAttributePanel.documentsCheckbox.text=Documents -CommonAttributePanel.pictureVideoCheckbox.text=Pictures and Videos diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java index 994df4c66a..9221a5dc86 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java @@ -63,9 +63,7 @@ final public class CaseDBCommonAttributeInstance extends AbstractCommonAttribute SleuthkitCase tskDb = currentCase.getSleuthkitCase(); - AbstractFile abstractFile = tskDb.findAllFilesWhere(String.format("obj_id in (%s)", this.getAbstractFileObjectId())).get(0); - - return abstractFile; + return tskDb.findAllFilesWhere(String.format("obj_id in (%s)", this.getAbstractFileObjectId())).get(0); } catch (TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.SEVERE, String.format("Unable to find AbstractFile for record with obj_id: %s. Node not created.", new Object[]{this.getAbstractFileObjectId()}), ex); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java index b44855c51c..f105d72498 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java @@ -82,6 +82,7 @@ public class CaseDBCommonAttributeInstanceNode extends FileNode { sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsCsvList(this.getContent()))); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType()))); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName)); this.addTagProperty(sheetSet); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java index 0e70722b6d..6cb58b9643 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java @@ -27,7 +27,6 @@ import java.util.Map; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; @@ -44,7 +43,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr private static final Logger LOGGER = Logger.getLogger(CentralRepoCommonAttributeInstance.class.getName()); private final Integer crFileId; - private CorrelationAttribute currentAttribute; + private CorrelationAttributeInstance currentAttribute; private final Map dataSourceNameToIdMap; CentralRepoCommonAttributeInstance(Integer attrInstId, Map dataSourceIdToNameMap) { @@ -53,7 +52,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr this.dataSourceNameToIdMap = invertMap(dataSourceIdToNameMap); } - void setCurrentAttributeInst(CorrelationAttribute attribute) { + void setCurrentAttributeInst(CorrelationAttributeInstance attribute) { this.currentAttribute = attribute; } @@ -63,7 +62,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr Case currentCase; if (this.currentAttribute != null) { - final CorrelationAttributeInstance currentAttributeInstance = this.currentAttribute.getInstances().get(0); + final CorrelationAttributeInstance currentAttributeInstance = this.currentAttribute; String currentFullPath = currentAttributeInstance.getFilePath(); String currentDataSource = currentAttributeInstance.getCorrelationDataSource().getName(); @@ -109,7 +108,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr // @@@ We should be doing more of this work in teh generateKeys method. We want to do as little as possible in generateNodes InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(); - CorrelationAttribute corrAttr = eamDbAttrInst.findSingleCorrelationAttribute(crFileId); + CorrelationAttributeInstance corrAttr = eamDbAttrInst.findSingleCorrelationAttribute(crFileId); List attrInstNodeList = new ArrayList<>(0); String currCaseDbName = Case.getCurrentCase().getDisplayName(); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index c08c3c2d78..20cbb33fe9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -8,8 +8,11 @@ + + + - + @@ -29,16 +32,23 @@ - + - - + + + + + + + + + @@ -49,46 +59,56 @@ - + + - - + - - - - - - - - - - + + + + + + + - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - @@ -104,7 +124,9 @@ - + + + @@ -116,18 +138,23 @@ - - - + + + - - - - - - + + + + + + + + + + + @@ -238,6 +265,7 @@ + @@ -295,6 +323,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 1b33bb9422..21f2b72624 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.commonfilesearch; +import java.awt.Dimension; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; @@ -29,6 +30,8 @@ import java.util.logging.Level; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import org.netbeans.api.progress.ProgressHandle; import org.openide.explorer.ExplorerManager; import org.openide.util.NbBundle; @@ -52,16 +55,21 @@ import org.sleuthkit.datamodel.TskCoreException; * logic. Nested within CommonFilesDialog. */ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -public final class CommonAttributePanel extends javax.swing.JDialog { +public final class CommonAttributePanel extends javax.swing.JDialog { + private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName()); private static final long serialVersionUID = 1L; private static final Long NO_DATA_SOURCE_SELECTED = -1L; - private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName()); + private final UserInputErrorManager errorManager; + private boolean pictureViewCheckboxState; + private boolean documentsCheckboxState; + private int percentageThresholdValue = 20; + /** * Creates new form CommonFilesPanel */ @@ -74,21 +82,71 @@ public final class CommonAttributePanel extends javax.swing.JDialog { super(new JFrame(Bundle.CommonFilesPanel_frame_title()), Bundle.CommonFilesPanel_frame_msg(), true); initComponents(); + this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - this.errorText.setVisible(false); this.setupDataSources(); - if (CommonAttributePanel.isEamDbAvailable()) { + if (CommonAttributePanel.isEamDbAvailableForIntercaseSearch()) { this.setupCases(); } else { this.disableIntercaseSearch(); } + + if(CommonAttributePanel.isEamDbAvailableForPercentageFrequencyCalculations()){ + this.enablePercentageOptions(); + } else { + this.disablePercentageOptions(); + } + + this.errorManager = new UserInputErrorManager(); + + this.percentageThresholdTextOne.getDocument().addDocumentListener(new DocumentListener(){ + + private Dimension preferredSize = CommonAttributePanel.this.percentageThresholdTextOne.getPreferredSize(); + + private void maintainSize(){ + CommonAttributePanel.this.percentageThresholdTextOne.setSize(preferredSize); + } + + @Override + public void insertUpdate(DocumentEvent event) { + this.maintainSize(); + CommonAttributePanel.this.percentageThresholdChanged(); + } + + @Override + public void removeUpdate(DocumentEvent event) { + this.maintainSize(); + CommonAttributePanel.this.percentageThresholdChanged(); + } + + @Override + public void changedUpdate(DocumentEvent event) { + this.maintainSize(); + CommonAttributePanel.this.percentageThresholdChanged(); + } + }); } - private static boolean isEamDbAvailable() { + private static boolean isEamDbAvailableForIntercaseSearch() { try { - EamDb DbManager = EamDb.getInstance(); - return DbManager != null; + return EamDb.isEnabled() + && EamDb.getInstance() != null + && EamDb.getInstance().getCases().size() > 1 + && Case.isCaseOpen() + && Case.getCurrentCase() != null + && EamDb.getInstance().getCase(Case.getCurrentCase()) != null; + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while checking for EamDB enabled.", ex); + } + return false; + } + + private static boolean isEamDbAvailableForPercentageFrequencyCalculations(){ + try { + return EamDb.isEnabled() + && EamDb.getInstance() != null + && EamDb.getInstance().getCases().size() > 0; } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Unexpected exception while checking for EamDB enabled.", ex); } @@ -132,7 +190,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { @Override @SuppressWarnings({"BoxedValueEquality", "NumberEquality"}) - protected CommonAttributeSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException, Exception { + protected CommonAttributeSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { progress = ProgressHandle.createHandle(Bundle.CommonFilesPanel_search_done_searchProgressGathering()); progress.start(); progress.switchToIndeterminate(); @@ -154,20 +212,27 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } } + int percentageThreshold = CommonAttributePanel.this.percentageThresholdValue; + + if (!CommonAttributePanel.this.percentageThresholdCheck.isSelected()) { + //0 has the effect of disabling the feature + percentageThreshold = 0; + } + if (CommonAttributePanel.this.interCaseRadio.isSelected()) { if (caseId == InterCasePanel.NO_CASE_SELECTED) { - builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); } else { - builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); } } else { if (dataSourceId == CommonAttributePanel.NO_DATA_SOURCE_SELECTED) { - builder = new AllIntraCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + builder = new AllIntraCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); setTitleForAllDataSources(); } else { - builder = new SingleIntraCaseCommonAttributeSearcher(dataSourceId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + builder = new SingleIntraCaseCommonAttributeSearcher(dataSourceId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); setTitleForSingleSource(dataSourceId); } @@ -226,7 +291,6 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }.execute(); } - /** * Sets up the data sources dropdown and returns the data sources map for * future usage. @@ -259,8 +323,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { boolean multipleDataSources = this.caseHasMultipleSources(); CommonAttributePanel.this.intraCasePanel.rigForMultipleDataSources(multipleDataSources); - //TODO this should be attached to the intra/inter radio buttons - CommonAttributePanel.this.setSearchButtonEnabled(true); + CommonAttributePanel.this.updateErrorTextAndSearchBox(); } } @@ -331,7 +394,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } } - private Map mapDataSources(List cases) throws Exception { + private Map mapDataSources(List cases) throws EamDbException { Map casemap = new HashMap<>(); CorrelationCase currentCorCase = EamDb.getInstance().getCase(Case.getCurrentCase()); for (CorrelationCase correlationCase : cases) { @@ -344,7 +407,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } @Override - protected Map doInBackground() throws Exception { + protected Map doInBackground() throws EamDbException { List dataSources = EamDb.getInstance().getCases(); Map caseMap = mapDataSources(dataSources); @@ -401,9 +464,14 @@ public final class CommonAttributePanel extends javax.swing.JDialog { layoutPanel = new java.awt.Panel(); intraCasePanel = new org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel(); interCasePanel = new org.sleuthkit.autopsy.commonfilesearch.InterCasePanel(); + percentageThresholdCheck = new javax.swing.JCheckBox(); + percentageThresholdTextOne = new javax.swing.JTextField(); + percentageThresholdTextTwo = new javax.swing.JLabel(); + filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); + filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 32767)); - setMinimumSize(new java.awt.Dimension(412, 350)); - setPreferredSize(new java.awt.Dimension(412, 350)); + setMaximumSize(new java.awt.Dimension(450, 375)); + setMinimumSize(new java.awt.Dimension(450, 375)); setResizable(false); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosed(java.awt.event.WindowEvent evt) { @@ -411,7 +479,10 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } }); - jPanel1.setPreferredSize(new java.awt.Dimension(412, 350)); + jPanel1.setMaximumSize(new java.awt.Dimension(450, 375)); + jPanel1.setMinimumSize(new java.awt.Dimension(450, 375)); + jPanel1.setPreferredSize(new java.awt.Dimension(450, 375)); + jPanel1.setRequestFocusEnabled(false); org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel2, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel2.text")); // NOI18N commonFilesSearchLabel2.setFocusable(false); @@ -474,6 +545,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { errorText.setForeground(new java.awt.Color(255, 0, 0)); org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.errorText.text")); // NOI18N + errorText.setVerticalAlignment(javax.swing.SwingConstants.TOP); org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel1.text")); // NOI18N commonFilesSearchLabel1.setFocusable(false); @@ -499,42 +571,64 @@ public final class CommonAttributePanel extends javax.swing.JDialog { layoutPanel.add(intraCasePanel, "card3"); layoutPanel.add(interCasePanel, "card2"); + org.openide.awt.Mnemonics.setLocalizedText(percentageThresholdCheck, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdCheck.text_1")); // NOI18N + percentageThresholdCheck.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + percentageThresholdCheckActionPerformed(evt); + } + }); + + percentageThresholdTextOne.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdTextOne.text")); // NOI18N + percentageThresholdTextOne.setMaximumSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setMinimumSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setPreferredSize(new java.awt.Dimension(40, 24)); + + org.openide.awt.Mnemonics.setLocalizedText(percentageThresholdTextTwo, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdTextTwo.text_1")); // NOI18N + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addComponent(searchButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cancelButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(errorText)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(commonFilesSearchLabel2) - .addComponent(intraCaseRadio) - .addComponent(interCaseRadio) - .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(categoriesLabel) - .addComponent(selectedFileCategoriesButton))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(80, 80, 80) + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(errorText))) .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(35, 35, 35) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(documentsCheckbox) - .addComponent(pictureVideoCheckbox))) - .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() - .addComponent(allFileCategoriesRadioButton))) - .addContainerGap()) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(20, 20, 20) - .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(10, 10, 10))) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(commonFilesSearchLabel2) + .addComponent(intraCaseRadio) + .addComponent(interCaseRadio) + .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(categoriesLabel) + .addComponent(selectedFileCategoriesButton) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(29, 29, 29) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(documentsCheckbox) + .addComponent(pictureVideoCheckbox))) + .addComponent(allFileCategoriesRadioButton) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addGap(10, 10, 10) + .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(percentageThresholdCheck) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(percentageThresholdTextTwo))) + .addContainerGap(9, Short.MAX_VALUE)))) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -547,7 +641,9 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addComponent(intraCaseRadio) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(interCaseRadio) - .addGap(79, 79, 79) + .addGap(2, 2, 2) + .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(categoriesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(selectedFileCategoriesButton) @@ -559,85 +655,90 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addComponent(allFileCategoriesRadioButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(searchButton) - .addComponent(cancelButton) - .addComponent(errorText)) + .addComponent(percentageThresholdCheck) + .addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(percentageThresholdTextTwo)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(searchButton) + .addComponent(cancelButton) + .addComponent(errorText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addContainerGap()) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() - .addGap(98, 98, 98) - .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(180, 180, 180))) ); getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents + private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_formWindowClosed + + private void percentageThresholdCheckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdCheckActionPerformed + if (this.percentageThresholdCheck.isSelected()) { + this.percentageThresholdTextOne.setEnabled(true); + } else { + this.percentageThresholdTextOne.setEnabled(false); + } + + this.handleFrequencyPercentageState(); + }//GEN-LAST:event_percentageThresholdCheckActionPerformed + + private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed + ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); + }//GEN-LAST:event_interCaseRadioActionPerformed + + private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed + ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); + }//GEN-LAST:event_intraCaseRadioActionPerformed + + private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_documentsCheckboxActionPerformed + + private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_pictureVideoCheckboxActionPerformed + + private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed + + private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed search(); SwingUtilities.windowForComponent(this).dispose(); }//GEN-LAST:event_searchButtonActionPerformed - private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_cancelButtonActionPerformed + private void percentageThresholdChanged(){ + String percentageString = this.percentageThresholdTextOne.getText(); - private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed - this.manageCheckBoxState(); - this.toggleErrorTextAndSearchBox(); - }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed - - private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed - this.manageCheckBoxState(); - }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed - - private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed - this.toggleErrorTextAndSearchBox(); - }//GEN-LAST:event_pictureVideoCheckboxActionPerformed - - private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed - this.toggleErrorTextAndSearchBox(); - }//GEN-LAST:event_documentsCheckboxActionPerformed - - private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed - ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); - handleIntraCaseSearchCriteriaChanged(); - }//GEN-LAST:event_intraCaseRadioActionPerformed - - public void handleIntraCaseSearchCriteriaChanged() { - if (this.areIntraCaseSearchCriteriaMet()) { - this.searchButton.setEnabled(true); - this.hideErrorMessages(); - } else { - this.searchButton.setEnabled(false); - this.hideErrorMessages(); - this.showIntraCaseErrorMessage(); + try { + this.percentageThresholdValue = Integer.parseInt(percentageString); + + } catch (NumberFormatException exception) { + this.percentageThresholdValue = -1; } + + this.handleFrequencyPercentageState(); } + + private void updateErrorTextAndSearchBox() { - private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed - ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); - handleInterCaseSearchCriteriaChanged(); - }//GEN-LAST:event_interCaseRadioActionPerformed - - private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_formWindowClosed - - public void handleInterCaseSearchCriteriaChanged() { - if (this.areInterCaseSearchCriteriaMet()) { - this.searchButton.setEnabled(true); - this.hideErrorMessages(); - } else { - this.searchButton.setEnabled(false); - this.hideErrorMessages(); - this.showInterCaseErrorMessage(); - } - } - - private void toggleErrorTextAndSearchBox() { - if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) { + if (this.errorManager.anyErrors()) { this.searchButton.setEnabled(false); + //grab the first error error and show it + this.errorText.setText(this.errorManager.getErrors().get(0)); this.errorText.setVisible(true); } else { this.searchButton.setEnabled(true); @@ -645,7 +746,21 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } } - private void manageCheckBoxState() { + private void enablePercentageOptions() { + this.percentageThresholdTextOne.setEnabled(true); + this.percentageThresholdCheck.setEnabled(true); + this.percentageThresholdCheck.setSelected(true); + this.percentageThresholdTextTwo.setEnabled(true); + } + + private void disablePercentageOptions() { + this.percentageThresholdTextOne.setEnabled(false); + this.percentageThresholdCheck.setEnabled(false); + this.percentageThresholdCheck.setSelected(false); + this.percentageThresholdTextTwo.setEnabled(false); + } + + private void handleFileTypeCheckBoxState() { this.pictureViewCheckboxState = this.pictureVideoCheckbox.isSelected(); this.documentsCheckboxState = this.documentsCheckbox.isSelected(); @@ -653,6 +768,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog { if (this.allFileCategoriesRadioButton.isSelected()) { this.pictureVideoCheckbox.setEnabled(false); this.documentsCheckbox.setEnabled(false); + + this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, false); } if (this.selectedFileCategoriesButton.isSelected()) { @@ -663,8 +780,24 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.pictureVideoCheckbox.setEnabled(true); this.documentsCheckbox.setEnabled(true); - this.toggleErrorTextAndSearchBox(); + if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) { + this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, true); + } else { + this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, false); + } } + + this.updateErrorTextAndSearchBox(); + } + + private void handleFrequencyPercentageState() { + if (this.percentageThresholdValue > 0 && this.percentageThresholdValue <= 100) { + this.errorManager.setError(UserInputErrorManager.FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, false); + } else { + this.errorManager.setError(UserInputErrorManager.FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, true); + } + + this.updateErrorTextAndSearchBox(); } // Variables declaration - do not modify//GEN-BEGIN:variables @@ -676,6 +809,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private javax.swing.JCheckBox documentsCheckbox; private javax.swing.JLabel errorText; private javax.swing.ButtonGroup fileTypeFilterButtonGroup; + private javax.swing.Box.Filler filler1; + private javax.swing.Box.Filler filler2; private org.sleuthkit.autopsy.commonfilesearch.InterCasePanel interCasePanel; private javax.swing.JRadioButton interCaseRadio; private javax.swing.ButtonGroup interIntraButtonGroup; @@ -683,34 +818,11 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private javax.swing.JRadioButton intraCaseRadio; private javax.swing.JPanel jPanel1; private java.awt.Panel layoutPanel; + private javax.swing.JCheckBox percentageThresholdCheck; + private javax.swing.JTextField percentageThresholdTextOne; + private javax.swing.JLabel percentageThresholdTextTwo; private javax.swing.JCheckBox pictureVideoCheckbox; private javax.swing.JButton searchButton; private javax.swing.JRadioButton selectedFileCategoriesButton; // End of variables declaration//GEN-END:variables - - void setSearchButtonEnabled(boolean enabled) { - this.searchButton.setEnabled(enabled); - } - - private boolean areIntraCaseSearchCriteriaMet() { - return this.intraCasePanel.areSearchCriteriaMet(); - } - - private boolean areInterCaseSearchCriteriaMet() { - return this.interCasePanel.areSearchCriteriaMet(); - } - - private void hideErrorMessages() { - this.errorText.setVisible(false); - } - - private void showIntraCaseErrorMessage() { - this.errorText.setText(this.intraCasePanel.getErrorMessage()); - this.errorText.setVisible(true); - } - - private void showInterCaseErrorMessage() { - this.errorText.setText(this.interCasePanel.getErrorMessage()); - this.errorText.setVisible(true); - } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java index 42ad7979e1..3aede081f5 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java @@ -19,10 +19,13 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; @@ -65,6 +68,8 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo */ static class InstanceCountNodeFactory extends ChildFactory{ + private static final Logger LOGGER = Logger.getLogger(InstanceCountNodeFactory.class.getName()); + private final CommonAttributeSearchResults searchResults; /** @@ -78,7 +83,11 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo @Override protected boolean createKeys(List list) { - list.addAll(this.searchResults.getMetadata().keySet()); + try { + list.addAll(this.searchResults.getMetadata().keySet()); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Unable to create keys.", ex); + } return true; } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java index 85417fe831..bac59fde12 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -19,27 +19,37 @@ */ package org.sleuthkit.autopsy.commonfilesearch; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; /** * Stores the results from the various types of common attribute searching * Stores results based on how they are currently displayed in the UI */ final public class CommonAttributeSearchResults { - + // maps instance count to list of attribute values. private final Map instanceCountToAttributeValues; + + private final int percentageThreshold; /** * Create a values object which can be handed off to the node factories. - * - * @param values list of CommonAttributeValue indexed by size of + * + * @param values list of CommonAttributeValue indexed by size of * CommonAttributeValue */ - CommonAttributeSearchResults(Map metadata){ - this.instanceCountToAttributeValues = metadata; + CommonAttributeSearchResults(Map metadata, int percentageThreshold) { + //wrap in a new object in case any client code has used an unmodifiable collection + this.instanceCountToAttributeValues = new HashMap<>(metadata); + this.percentageThreshold = percentageThreshold; } /** @@ -48,29 +58,100 @@ final public class CommonAttributeSearchResults { * This is a convenience method - you can also iterate over * getValues(). * - * @param isntanceCound key + * @param instanceCount key * @return list of values which represent matches */ CommonAttributeValueList getAttributeValuesForInstanceCount(Integer instanceCount) { return this.instanceCountToAttributeValues.get(instanceCount); } - /** - * Get an unmodifiable collection of values, indexed by number of - * grandchildren, which represents the common attributes found in the + /** + * Get an unmodifiable collection of values, indexed by number of + * grandchildren, which represents the common attributes found in the * search. + * * @return map of sizes of children to list of matches - */ -public Map getMetadata() { + */ + public Map getMetadata() throws EamDbException { + if(this.percentageThreshold == 0){ + return Collections.unmodifiableMap(this.instanceCountToAttributeValues); + } else { + return this.getMetadata(this.percentageThreshold); + } + } + + /** + * Get an unmodifiable collection of values, indexed by number of + * grandchildren, which represents the common attributes found in the + * search. + * + * Remove results which are not found in the portion of available data + sources described by maximumPercentageThreshold. + * + * @return metadata + */ + private Map getMetadata(int maximumPercentageThreshold) throws EamDbException { + + if(maximumPercentageThreshold == 0){ + return Collections.unmodifiableMap(this.instanceCountToAttributeValues); + } + + CorrelationAttributeInstance.Type fileAttributeType = CorrelationAttributeInstance + .getDefaultCorrelationTypes() + .stream() + .filter(filterType -> filterType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) + .findFirst().get(); + + EamDb eamDb = EamDb.getInstance(); + + Map> itemsToRemove = new HashMap<>(); + + for(Entry listOfValues : Collections.unmodifiableMap(this.instanceCountToAttributeValues).entrySet()){ + + final Integer key = listOfValues.getKey(); + final CommonAttributeValueList values = listOfValues.getValue(); + + for(CommonAttributeValue value : values.getDelayedMetadataList()){ // Need the real metadata + + int frequencyPercentage = eamDb.getFrequencyPercentage(new CorrelationAttributeInstance(fileAttributeType, value.getValue())); + + if(frequencyPercentage > maximumPercentageThreshold){ + if(itemsToRemove.containsKey(key)){ + itemsToRemove.get(key).add(value); + } else { + List toRemove = new ArrayList<>(); + toRemove.add(value); + itemsToRemove.put(key, toRemove); + } + } + } + } + + for(Entry> valuesToRemove : itemsToRemove.entrySet()){ + + final Integer key = valuesToRemove.getKey(); + final List values = valuesToRemove.getValue(); + + for (CommonAttributeValue value : values){ + final CommonAttributeValueList instanceCountValue = this.instanceCountToAttributeValues.get(key); + instanceCountValue.removeMetaData(value); + + if(instanceCountValue.getDelayedMetadataList().isEmpty()){ // Check the real metadata + this.instanceCountToAttributeValues.remove(key); + } + } + } + return Collections.unmodifiableMap(this.instanceCountToAttributeValues); } /** * How many distinct common files exist for this search results? + * * @return number of common files */ public int size() { - + int count = 0; for (CommonAttributeValueList data : this.instanceCountToAttributeValues.values()) { for(CommonAttributeValue md5 : data.getMetadataList()){ diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java index 0d388af932..f6372c89f7 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java @@ -57,8 +57,7 @@ final public class CommonAttributeValue { * @return */ public String getCases() { - final String cases = this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", ")); - return cases; + return this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", ")); } public String getDataSources() { @@ -67,8 +66,7 @@ final public class CommonAttributeValue { sources.add(data.getDataSource()); } - final String dataSources = String.join(", ", sources); - return dataSources; + return String.join(", ", sources); } void addInstance(AbstractCommonAttributeInstance metadata) { diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java index 4797dbfd94..01690b357b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java @@ -67,6 +67,20 @@ final public class CommonAttributeValueList { return Collections.unmodifiableList(this.metadataList); } + /** + * Get the delayed list of value nodes. Only use for + * determining how many CommonAttributeValues + * actually exist in the list. + * @return metadataList the list of nodes + */ + List getDelayedMetadataList() { + return Collections.unmodifiableList(this.delayedMetadataList); + } + + void removeMetaData(CommonAttributeValue commonVal) { + this.delayedMetadataList.remove(commonVal); + } + /** * Return the size of the backing list, in case * displayDelayedMetadata() has not be called yet. diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java index e7160e8d11..c7c5dea58b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java @@ -25,7 +25,6 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.NodeProperty; @@ -36,8 +35,6 @@ import org.sleuthkit.autopsy.datamodel.NodeProperty; */ public class CommonAttributeValueNode extends DisplayableItemNode { - private static final Logger LOGGER = Logger.getLogger(CommonAttributeValueNode.class.getName()); - private final String value; private final int commonFileCount; private final String cases; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java index 80e4f9754a..6cc00256dd 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java @@ -48,9 +48,17 @@ final public class CommonFilesSearchAction extends CallableSystemAction { public boolean isEnabled(){ boolean shouldBeEnabled = false; try { - shouldBeEnabled = Case.isCaseOpen() - && Case.getCurrentCase().getDataSources().size() > 1 - || (EamDb.isEnabled() && EamDb.getInstance().getCases().size() > 1); + //dont refactor any of this to pull out common expressions - order of evaluation of each expression is significant + shouldBeEnabled = + (Case.isCaseOpen() && + Case.getCurrentCase().getDataSources().size() > 1) + || + (EamDb.isEnabled() && + EamDb.getInstance() != null && + EamDb.getInstance().getCases().size() > 1 && + Case.isCaseOpen() && + Case.getCurrentCase() != null && + EamDb.getInstance().getCase(Case.getCurrentCase()) != null); } catch(TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error getting data sources for action enabled check", ex); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java index 2c745b5271..d1bff54c99 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java @@ -43,8 +43,8 @@ abstract class InterCaseCommonAttributeSearcher extends AbstractCommonAttributeS * * @throws EamDbException */ - InterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException { - super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + InterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold); dbManager = EamDb.getInstance(); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java index 2959c51078..8fc4efd01e 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java @@ -24,7 +24,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.swing.ComboBoxModel; -import org.openide.util.NbBundle; /** * UI controls for Common Files Search scenario where the user intends to find @@ -39,16 +38,18 @@ public class InterCasePanel extends javax.swing.JPanel { private ComboBoxModel casesList = new DataSourceComboBoxModel(); private final Map caseMap; - - private String errorMessage; + + //True if we are looking in any or all cases, + // false if we must find matches in a given case plus the current case + private boolean anyCase; /** * Creates new form InterCasePanel */ public InterCasePanel() { initComponents(); - this.errorMessage = ""; this.caseMap = new HashMap<>(); + this.anyCase = true; } private void specificCaseSelected(boolean selected) { @@ -59,10 +60,6 @@ public class InterCasePanel extends javax.swing.JPanel { } } - String getErrorMessage(){ - return this.errorMessage; - } - /** * 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 @@ -127,10 +124,12 @@ public class InterCasePanel extends javax.swing.JPanel { if(this.caseComboBox.isEnabled() && this.caseComboBox.getSelectedItem() == null){ this.caseComboBox.setSelectedIndex(0); } + this.anyCase = false; }//GEN-LAST:event_specificCentralRepoCaseRadioActionPerformed private void anyCentralRepoCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_anyCentralRepoCaseRadioActionPerformed this.caseComboBox.setEnabled(false); + this.anyCase = true; }//GEN-LAST:event_anyCentralRepoCaseRadioActionPerformed @@ -170,6 +169,10 @@ public class InterCasePanel extends javax.swing.JPanel { } Integer getSelectedCaseId(){ + if(this.anyCase){ + return InterCasePanel.NO_CASE_SELECTED; + } + for(Entry entry : this.caseMap.entrySet()){ if(entry.getValue().equals(this.caseComboBox.getSelectedItem())){ return entry.getKey(); @@ -178,16 +181,4 @@ public class InterCasePanel extends javax.swing.JPanel { return InterCasePanel.NO_CASE_SELECTED; } - - @NbBundle.Messages({ - "InterCasePanel.showInterCaseErrorMessage.message=Cannot run intercase correlation search: no cases in Central Repository." - }) - boolean areSearchCriteriaMet() { - if(this.caseMap.isEmpty()){ - this.errorMessage = Bundle.InterCasePanel_showInterCaseErrorMessage_message(); - return false; - } else { - return true; - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java index 942f84133a..16ee2bd23a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java @@ -25,7 +25,7 @@ import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; @@ -55,11 +55,20 @@ final class InterCaseSearchResultsProcessor { + "WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value) " + "AND (case_id=%s OR case_id=%s) GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value"; + /** + * Used in the InterCaseCommonAttributeSearchers to find common attribute instances and generate nodes at the UI level. + * @param dataSources + */ InterCaseSearchResultsProcessor(Map dataSources){ this.dataSources = dataSources; } - InterCaseSearchResultsProcessor(){} + /** + * Used in the CentralRepoCommonAttributeInstance to find common attribute instances and generate nodes at the UI level. + */ + InterCaseSearchResultsProcessor(){ + //intentionally emtpy - we need a constructor which does not set the data sources field + } /** * Finds a single CorrelationAttribute given an id. @@ -67,11 +76,11 @@ final class InterCaseSearchResultsProcessor { * @param attrbuteId Row of CorrelationAttribute to retrieve from the EamDb * @return CorrelationAttribute object representation of retrieved match */ - CorrelationAttribute findSingleCorrelationAttribute(int attrbuteId) { + CorrelationAttributeInstance findSingleCorrelationAttribute(int attrbuteId) { try { InterCaseCommonAttributeRowCallback instancetableCallback = new InterCaseCommonAttributeRowCallback(); EamDb DbManager = EamDb.getInstance(); - CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attrbuteId), instancetableCallback); return instancetableCallback.getCorrelationAttribute(); @@ -93,7 +102,7 @@ final class InterCaseSearchResultsProcessor { try { InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback(); EamDb DbManager = EamDb.getInstance(); - CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); int caseId = DbManager.getCase(currentCase).getID(); DbManager.processInstanceTableWhere(fileType, String.format(interCaseWhereClause, caseId, @@ -120,7 +129,7 @@ final class InterCaseSearchResultsProcessor { try { InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback(); EamDb DbManager = EamDb.getInstance(); - CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); int caseId = DbManager.getCase(currentCase).getID(); int targetCaseId = singleCase.getID(); DbManager.processInstanceTableWhere(fileType, String.format(singleInterCaseWhereClause, caseId, @@ -200,18 +209,18 @@ final class InterCaseSearchResultsProcessor { */ private class InterCaseCommonAttributeRowCallback implements InstanceTableCallback { - CorrelationAttribute correlationAttribute = null; + CorrelationAttributeInstance correlationAttributeInstance = null; @Override public void process(ResultSet resultSet) { try { EamDb DbManager = EamDb.getInstance(); - CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); while (resultSet.next()) { CorrelationCase correlationCase = DbManager.getCaseById(InstanceTableCallback.getCaseId(resultSet)); CorrelationDataSource dataSource = DbManager.getDataSourceById(correlationCase, InstanceTableCallback.getDataSourceId(resultSet)); - correlationAttribute = DbManager.getCorrelationAttribute(fileType, + correlationAttributeInstance = DbManager.getCorrelationAttributeInstance(fileType, correlationCase, dataSource, InstanceTableCallback.getValue(resultSet), @@ -223,8 +232,8 @@ final class InterCaseSearchResultsProcessor { } } - CorrelationAttribute getCorrelationAttribute() { - return correlationAttribute; + CorrelationAttributeInstance getCorrelationAttribute() { + return correlationAttributeInstance; } } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java index 195320917c..f02f26b869 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java @@ -55,8 +55,8 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt * @param filterByDocMimeType match only on files whose mime types can be * broadly categorized as document types */ - IntraCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { - super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + IntraCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold); } /** @@ -130,7 +130,7 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt Map instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); - return new CommonAttributeSearchResults(instanceCollatedCommonFiles); + return new CommonAttributeSearchResults(instanceCollatedCommonFiles, this.frequencyPercentageThreshold); } /** @@ -156,11 +156,13 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt StringBuilder mimeTypeFilter = new StringBuilder(mimeTypesToFilterOn.size()); if (!mimeTypesToFilterOn.isEmpty()) { for (String mimeType : mimeTypesToFilterOn) { - mimeTypeFilter.append("'").append(mimeType).append("',"); + mimeTypeFilter.append(SINGLE_QUOTE).append(mimeType).append(SINGLE_QUTOE_COMMA); } mimeTypeString = mimeTypeFilter.toString().substring(0, mimeTypeFilter.length() - 1); mimeTypeString = String.format(FILTER_BY_MIME_TYPES_WHERE_CLAUSE, new Object[]{mimeTypeString}); } return mimeTypeString; } + static final String SINGLE_QUTOE_COMMA = "',"; + static final String SINGLE_QUOTE = "'"; } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java index e0938df3f8..babbbd4096 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java @@ -24,9 +24,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.swing.ComboBoxModel; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.Logger; - /** * UI controls for Common Files Search scenario where the user intends to find * common files between datasources. It is an inner panel which provides the ability @@ -38,34 +35,17 @@ public class IntraCasePanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; static final long NO_DATA_SOURCE_SELECTED = -1; - private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName()); - private boolean singleDataSource; - private String selectedDataSource; private ComboBoxModel dataSourcesList = new DataSourceComboBoxModel(); private final Map dataSourceMap; - - private String errorMessage; /** * Creates new form IntraCasePanel */ public IntraCasePanel() { initComponents(); - this.errorMessage = ""; this.dataSourceMap = new HashMap<>(); - } - - public boolean isSingleDataSource(){ - return this.singleDataSource; - } - - public String getSelectedDataSource(){ - if(this.singleDataSource && this.selectedDataSource != null){ - return selectedDataSource; - } else { - return ""; - } + this.singleDataSource = true; } public Map getDataSourceMap(){ @@ -73,6 +53,10 @@ public class IntraCasePanel extends javax.swing.JPanel { } Long getSelectedDataSourceId(){ + if(!this.singleDataSource){ + return IntraCasePanel.NO_DATA_SOURCE_SELECTED; + } + for(Entry entry : this.dataSourceMap.entrySet()){ if(entry.getValue().equals(this.selectDataSourceComboBox.getSelectedItem())){ return entry.getKey(); @@ -115,12 +99,8 @@ public class IntraCasePanel extends javax.swing.JPanel { }); selectDataSourceComboBox.setModel(dataSourcesList); + selectDataSourceComboBox.setActionCommand(org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.selectDataSourceComboBox.actionCommand")); // NOI18N selectDataSourceComboBox.setEnabled(false); - selectDataSourceComboBox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - selectDataSourceComboBoxActionPerformed(evt); - } - }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -158,15 +138,6 @@ public class IntraCasePanel extends javax.swing.JPanel { withinDataSourceSelected(withinDataSourceRadioButton.isSelected()); }//GEN-LAST:event_withinDataSourceRadioButtonActionPerformed - private void selectDataSourceComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectDataSourceComboBoxActionPerformed - final Object selectedItem = selectDataSourceComboBox.getSelectedItem(); - if (selectedItem != null) { - selectedDataSource = selectedItem.toString(); - } else { - selectedDataSource = ""; - } - }//GEN-LAST:event_selectDataSourceComboBoxActionPerformed - private void withinDataSourceSelected(boolean selected) { selectDataSourceComboBox.setEnabled(selected); if (selectDataSourceComboBox.isEnabled()) { @@ -199,20 +170,4 @@ public class IntraCasePanel extends javax.swing.JPanel { this.dataSourceMap.clear(); this.dataSourceMap.putAll(dataSourceMap); } - - @NbBundle.Messages({ - "IntraCasePanel.areSearchCriteriaMet.message=Cannot run intra-case correlation search." - }) - boolean areSearchCriteriaMet() { - if(this.dataSourceMap.isEmpty()){ - this.errorMessage = Bundle.IntraCasePanel_areSearchCriteriaMet_message(); - return false; - } else { - return true; - } - } - - String getErrorMessage() { - return this.errorMessage; - } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java index 3974e1684e..5bed8625a0 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java @@ -44,8 +44,8 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri * @param filterByDocMimeType * @throws EamDbException */ - public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException { - super(dataSourceIdMap,filterByMediaMimeType, filterByDocMimeType); + public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException { + super(dataSourceIdMap,filterByMediaMimeType, filterByDocMimeType, percentageThreshold); this.corrleationCaseId = correlationCaseId; this.correlationCaseName = ""; @@ -74,7 +74,7 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap()); Map interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase); - return new CommonAttributeSearchResults(interCaseCommonFiles); + return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java index 02bd480093..f28f0d0bf0 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java @@ -41,8 +41,8 @@ final public class SingleIntraCaseCommonAttributeSearcher extends IntraCaseCommo * @param filterByDocMimeType match only on files whose mime types can be * broadly categorized as document types */ - public SingleIntraCaseCommonAttributeSearcher(Long dataSourceId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { - super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + public SingleIntraCaseCommonAttributeSearcher(Long dataSourceId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold); this.selectedDataSourceId = dataSourceId; this.dataSourceName = dataSourceIdMap.get(this.selectedDataSourceId); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java new file mode 100644 index 0000000000..3e09e32193 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java @@ -0,0 +1,130 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 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.commonfilesearch; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.openide.util.NbBundle; + +/** + * Manager for present state of errors on the Common Files Search. + */ +class UserInputErrorManager { + + static final int FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY = 1; + static final int NO_FILE_CATEGORIES_SELECTED_KEY = 2; + + private final Map currentErrors; + + /** + * Construct a new ErrorManager which can be used to track the status + * of all known error states, retrieve error messages, and determine if + * anything is in an error state. + */ + @NbBundle.Messages({ + "UserInputErrorManager.frequency=Invalid Frequency Percentage: 0 < % < 100.", + "UserInputErrorManager.categories=No file categories are included in the search."}) + UserInputErrorManager (){ + + //when new errors are needed for the dialog, define a key and a value + // and add them to the map. + + this.currentErrors = new HashMap<>(); + this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage(Bundle.UserInputErrorManager_frequency())); + this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage(Bundle.UserInputErrorManager_categories())); + } + + /** + * Toggle the given error message on, or off + * @param errorId the error to toggle + * @param errorState true for on, false for off + */ + void setError(int errorId, boolean errorState){ + if(this.currentErrors.containsKey(errorId)){ + this.currentErrors.get(errorId).setStatus(errorState); + } else { + throw new IllegalArgumentException(String.format("The given errorId is not mapped to an ErrorMessage: %s.", errorId)); + } + } + + /** + * Are any user settings presently in an error state? + * @return true for yes, else false + */ + boolean anyErrors(){ + return this.currentErrors.values().stream().anyMatch(errorMessage -> errorMessage.isErrorSet() == true); + } + + /** + * Get a list of distinct string messages describing the various error states. + */ + List getErrors(){ + return this.currentErrors.values().stream() + .filter(errorMessage -> errorMessage.isErrorSet() == true) + .map(ErrorMessage::getMessage) + .collect(Collectors.toList()); + } + + /** + * Represents an error message for the CommonFilesSearch panel, it's + * uniqueId, and it's status. + */ + private class ErrorMessage { + + private final String message; + private boolean status; + + /** + * Create a message with a unique uniqueId. Default status is false (off). + * @param uniqueId unique uniqueId + * @param message message to display + */ + ErrorMessage(String message){ + this.message = message; + this.status = false; + } + + /** + * Update the status of this message + * @param status + */ + void setStatus(boolean status){ + this.status = status; + } + + /** + * Return the message + * @return + */ + String getMessage(){ + return this.message; + } + + /** + * Return the status (true for error status, false for no error) + * @return + */ + boolean isErrorSet(){ + return this.status; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index eb448f4f8a..2fd30addcc 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017-18 Basis Technology Corp. + * Copyright 2017-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -46,7 +46,7 @@ import org.sleuthkit.datamodel.TskCoreException; final class RelationshipNode extends BlackboardArtifactNode { private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName()); - + RelationshipNode(BlackboardArtifact artifact) { super(artifact); final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); @@ -115,7 +115,7 @@ final class RelationshipNode extends BlackboardArtifactNode { } addTagProperty(sheetSet); - + return sheet; } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 8f08b248fd..a95dd307f5 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -117,8 +117,8 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont drp.open(); drpExplorerManager = drp.getExplorerManager(); - drpExplorerManager.addPropertyChangeListener(evt -> - viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1)); + drpExplorerManager.addPropertyChangeListener(evt + -> viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1)); } /** diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 40a667e3d7..47a33dc578 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -22,23 +22,16 @@ import java.awt.BorderLayout; import java.awt.Component; import java.awt.Cursor; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.TreeMap; import java.util.logging.Level; +import java.util.stream.Collectors; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JOptionPane; @@ -48,14 +41,13 @@ import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.tabulardatareader.AbstractReader; +import org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException; +import org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException; +import org.sleuthkit.autopsy.tabulardatareader.FileReaderFactory; /** * A file content viewer for SQLite database files. @@ -70,7 +62,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { private final SQLiteTableView selectedTableView = new SQLiteTableView(); private AbstractFile sqliteDbFile; private File tmpDbFile; - private Connection connection; + private AbstractReader sqliteReader; private int numRows; // num of rows in the selected table private int currPage = 0; // curr page of rows being displayed @@ -347,13 +339,9 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { numEntriesField.setText(""); // close DB connection to file - if (null != connection) { - try { - connection.close(); - connection = null; - } catch (SQLException ex) { - logger.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS - } + if (null != sqliteReader) { + sqliteReader.close(); + sqliteReader = null; } sqliteDbFile = null; @@ -370,41 +358,16 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { "SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.", "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.", "# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",}) - private void processSQLiteFile() { - + private void processSQLiteFile() { tablesDropdownList.removeAllItems(); - - // Copy the file to temp folder - String tmpDBPathName; try { - tmpDBPathName = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + sqliteDbFile.getName(); - } catch (NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase()); - return; - } - - tmpDbFile = new File(tmpDBPathName); - if (! tmpDbFile.exists()) { - try { - ContentUtils.writeToFile(sqliteDbFile, tmpDbFile); - - // Look for any meta files associated with this DB - WAL, SHM, etc. - findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal"); - findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm"); - } catch (IOException | NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToExtractFile()); - return; - } - } - - try { - // Load the SQLite JDBC driver, if necessary. - Class.forName("org.sqlite.JDBC"); //NON-NLS - connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS - - Map dbTablesMap = getTables(); + String localDiskPath = Case.getCurrentCaseThrows().getTempDirectory() + + File.separator + sqliteDbFile.getName(); + + sqliteReader = FileReaderFactory.createReader(SUPPORTED_MIMETYPES[0], sqliteDbFile, localDiskPath); + + Map dbTablesMap = sqliteReader.getTableSchemas(); + if (dbTablesMap.isEmpty()) { tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry()); tablesDropdownList.setEnabled(false); @@ -413,78 +376,28 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { tablesDropdownList.addItem(tableName); }); } - } catch (ClassNotFoundException ex) { - logger.log(Level.SEVERE, String.format("Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver()); - } catch (SQLException ex) { - logger.log(Level.SEVERE, String.format("Failed to get tables from DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase()); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS + MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase()); + } catch (FileReaderException ex) { + logger.log(Level.SEVERE, String.format( + "Failed to get tables from DB file '%s' (objId=%d)", //NON-NLS + sqliteDbFile.getName(), sqliteDbFile.getId()), ex); + MessageNotifyUtil.Message.error( + Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase()); + } catch (FileReaderInitException ex) { + logger.log(Level.SEVERE, String.format( + "Failed to create a SQLiteReader '%s' (objId=%d)", //NON-NLS + sqliteDbFile.getName(), sqliteDbFile.getId()), ex); } } - /** - * Searches for a meta file associated with the give SQLite db If found, - * copies the file to the temp folder - * - * @param sqliteFile - SQLIte db file being processed - * @param metaFileName name of meta file to look for - */ - private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException { - Case openCase = Case.getCurrentCaseThrows(); - SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase(); - Services services = new Services(sleuthkitCase); - FileManager fileManager = services.getFileManager(); - List metaFiles = fileManager.findFiles(sqliteFile.getDataSource(), metaFileName, sqliteFile.getParent().getName()); - if (metaFiles != null) { - for (AbstractFile metaFile : metaFiles) { - String tmpMetafilePathName = openCase.getTempDirectory() + File.separator + metaFile.getName(); - File tmpMetafile = new File(tmpMetafilePathName); - ContentUtils.writeToFile(metaFile, tmpMetafile); - } - } - } - - /** - * Gets the table names and schemas from the SQLite database file. - * - * @return A mapping of table names to SQL CREATE TABLE statements. - */ - private Map getTables() throws SQLException { - Map dbTablesMap = new TreeMap<>(); - Statement statement = null; - ResultSet resultSet = null; - try { - statement = connection.createStatement(); - resultSet = statement.executeQuery( - "SELECT name, sql FROM sqlite_master " - + " WHERE type= 'table' " - + " ORDER BY name;"); //NON-NLS - while (resultSet.next()) { - String tableName = resultSet.getString("name"); //NON-NLS - String tableSQL = resultSet.getString("sql"); //NON-NLS - dbTablesMap.put(tableName, tableSQL); - } - } finally { - if (null != resultSet) { - resultSet.close(); - } - if (null != statement) { - statement.close(); - } - } - return dbTablesMap; - } - @NbBundle.Messages({"# {0} - tableName", "SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}" }) private void selectTable(String tableName) { - - try (Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery( - "SELECT count (*) as count FROM " + tableName)) { //NON-NLS{ - - numRows = resultSet.getInt("count"); + try { + numRows = sqliteReader.getRowCountFromTable(tableName); numEntriesField.setText(numRows + " entries"); currPage = 1; @@ -503,9 +416,12 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { selectedTableView.setupTable(Collections.emptyList()); } - } catch (SQLException ex) { - logger.log(Level.SEVERE, String.format("Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName)); + } catch (FileReaderException ex) { + logger.log(Level.SEVERE, String.format( + "Failed to load table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS + sqliteDbFile.getName(), sqliteDbFile.getId()), ex); + MessageNotifyUtil.Message.error( + Bundle.SQLiteViewer_selectTable_errorText(tableName)); } } @@ -513,109 +429,108 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { "SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"}) private void readTable(String tableName, int startRow, int numRowsToRead) { - try ( - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery( - "SELECT * FROM " + tableName - + " LIMIT " + Integer.toString(numRowsToRead) - + " OFFSET " + Integer.toString(startRow - 1))) { - - ArrayList> rows = resultSetToArrayList(resultSet); + try { + List> rows = sqliteReader.getRowsFromTable( + tableName, startRow, numRowsToRead); if (Objects.nonNull(rows)) { selectedTableView.setupTable(rows); } else { selectedTableView.setupTable(Collections.emptyList()); } - } catch (SQLException ex) { - logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName)); + } catch (FileReaderException ex) { + logger.log(Level.SEVERE, String.format( + "Failed to read table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS + sqliteDbFile.getName(), sqliteDbFile.getId()), ex); + MessageNotifyUtil.Message.error( + Bundle.SQLiteViewer_readTable_errorText(tableName)); } } - - @NbBundle.Messages("SQLiteViewer.BlobNotShown.message=BLOB Data not shown") - private ArrayList> resultSetToArrayList(ResultSet rs) throws SQLException { - ResultSetMetaData metaData = rs.getMetaData(); - int columns = metaData.getColumnCount(); - ArrayList> rowlist = new ArrayList<>(); - while (rs.next()) { - Map row = new LinkedHashMap<>(columns); - for (int i = 1; i <= columns; ++i) { - if (rs.getObject(i) == null) { - row.put(metaData.getColumnName(i), ""); - } else { - if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) { - row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message()); - } else { - row.put(metaData.getColumnName(i), rs.getObject(i)); - } - } - } - rowlist.add(row); - } - - return rowlist; - } - @NbBundle.Messages({"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.", - "SQLiteViewer.exportTableToCsv.FileName=File name: ", - "SQLiteViewer.exportTableToCsv.TableName=Table name: " + /** + * Converts a sqlite table into a CSV file. + * + * @param file + * @param tableName + * @param rowMap A list of rows in the table, where each row is represented as a column-value + * map. + * @throws FileNotFoundException + * @throws IOException + */ + @NbBundle.Messages({ + "SQLiteViewer.exportTableToCsv.FileName=File name: ", + "SQLiteViewer.exportTableToCsv.TableName=Table name: " + }) + public void exportTableToCSV(File file, String tableName, + List> rowMap) throws FileNotFoundException, IOException{ + + File csvFile; + String fileName = file.getName(); + if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) { + csvFile = file; + } else { + csvFile = new File(file.toString() + ".csv"); + } + + try (FileOutputStream out = new FileOutputStream(csvFile, false)) { + + out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes()); + out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes()); + + String header = createColumnHeader(rowMap.get(0)).concat("\n"); + out.write(header.getBytes()); + + for (Map maps : rowMap) { + String row = maps.values() + .stream() + .map(Object::toString) + .collect(Collectors.joining(",")) + .concat("\n"); + out.write(row.getBytes()); + } + } + } + + @NbBundle.Messages({ + "SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.", }) private void exportTableToCsv(File file) { String tableName = (String) this.tablesDropdownList.getSelectedItem(); - try ( - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT * FROM " + tableName)) { - List> currentTableRows = resultSetToArrayList(resultSet); + try { + List> currentTableRows = + sqliteReader.getRowsFromTable(tableName); if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) { - logger.log(Level.INFO, String.format("The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId())); //NON-NLS + logger.log(Level.INFO, String.format( + "The table %s is empty. (objId=%d)", tableName, //NON-NLS + sqliteDbFile.getId())); } else { - File csvFile; - String fileName = file.getName(); - if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) { - csvFile = file; - } else { - csvFile = new File(file.toString() + ".csv"); - } - - try (FileOutputStream out = new FileOutputStream(csvFile, false)) { - - out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes()); - out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes()); - // Set up the column names - Map row = currentTableRows.get(0); - StringBuffer header = new StringBuffer(); - for (Map.Entry col : row.entrySet()) { - String colName = col.getKey(); - if (header.length() > 0) { - header.append(',').append(colName); - } else { - header.append(colName); - } - } - out.write(header.append('\n').toString().getBytes()); - - for (Map maps : currentTableRows) { - StringBuffer valueLine = new StringBuffer(); - maps.values().forEach((value) -> { - if (valueLine.length() > 0) { - valueLine.append(',').append(value.toString()); - } else { - valueLine.append(value.toString()); - } - }); - out.write(valueLine.append('\n').toString().getBytes()); - } - } + exportTableToCSV(file, tableName, currentTableRows); } - } catch (SQLException ex) { - logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName)); + } catch (FileReaderException ex) { + logger.log(Level.SEVERE, String.format( + "Failed to read table %s from DB file '%s' (objId=%d)", //NON-NLS + tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); + MessageNotifyUtil.Message.error( + Bundle.SQLiteViewer_readTable_errorText(tableName)); } catch (IOException ex) { - logger.log(Level.SEVERE, String.format("Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText()); + logger.log(Level.SEVERE, String.format( + "Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS + MessageNotifyUtil.Message.error( + Bundle.SQLiteViewer_exportTableToCsv_write_errText()); } } - + /** + * Returns a comma seperated header string from the keys of the column + * row map. + * + * @param row column header row map + * @return comma seperated header string + */ + private String createColumnHeader(Map row) { + return row.entrySet() + .stream() + .map(Map.Entry::getKey) + .collect(Collectors.joining(",")); + } } diff --git a/Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java b/Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java new file mode 100644 index 0000000000..bf7cb7cd99 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java @@ -0,0 +1,68 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-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.core; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.api.sendopts.CommandException; +import org.netbeans.spi.sendopts.Env; +import org.netbeans.spi.sendopts.Option; +import org.netbeans.spi.sendopts.OptionProcessor; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; + +/** + * This class can be used to add command line options to Autopsy + * To add more options to autopsy, create a Option variable and add it to the set in getOptions method + * Do your logic for that option in the process method + */ +@ServiceProvider(service=OptionProcessor.class) +public class AutopsyOptionProcessor extends OptionProcessor { + + private static final Logger logger = Logger.getLogger(AutopsyOptionProcessor.class.getName()); + private final Option liveAutopsyOption = Option.optionalArgument('l', "liveAutopsy"); + private final static String PROP_BASECASE = "LBL_BaseCase_PATH"; + + + @Override + protected Set