diff --git a/Core/build.xml b/Core/build.xml
index f38f7732b2..d246edaa54 100644
--- a/Core/build.xml
+++ b/Core/build.xml
@@ -86,7 +86,7 @@
-
+
@@ -97,6 +97,12 @@
+
+
+
+
+
+
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/SingleUserCaseConverter.java b/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java
index 46727fc570..95dfad8c5d 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java
@@ -859,13 +859,14 @@ public class SingleUserCaseConverter {
if (value > biggestPK) {
biggestPK = value;
}
- outputStatement.executeUpdate("INSERT INTO content_tags (tag_id, obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset) VALUES (" //NON-NLS
+ outputStatement.executeUpdate("INSERT INTO content_tags (tag_id, obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset, user_name) VALUES (" //NON-NLS
+ value + ","
+ inputResultSet.getLong(2) + ","
+ inputResultSet.getLong(3) + ",'"
+ inputResultSet.getString(4) + "',"
+ inputResultSet.getLong(5) + ","
- + inputResultSet.getLong(6) + ")"); //NON-NLS
+ + inputResultSet.getLong(6) + ",'"
+ + inputResultSet.getString(7)+ "')"); //NON-NLS
} catch (SQLException ex) {
if (ex.getErrorCode() != 0) { // 0 if the entry already exists
@@ -892,7 +893,8 @@ public class SingleUserCaseConverter {
+ value + ","
+ inputResultSet.getLong(2) + ","
+ inputResultSet.getLong(3) + ",'"
- + inputResultSet.getString(4) + "')"); //NON-NLS
+ + inputResultSet.getString(4) + "','"
+ + inputResultSet.getString(5) + "')"); //NON-NLS
} catch (SQLException ex) {
if (ex.getErrorCode() != 0) { // 0 if the entry already exists
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Blackboard.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/Blackboard.java
index 58b4f41d6e..6e954ce725 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Blackboard.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Blackboard.java
@@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.casemodule.services;
import java.io.Closeable;
import java.io.IOException;
import org.openide.util.Lookup;
-import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
@@ -38,7 +37,7 @@ import org.sleuthkit.datamodel.TskDataException;
public final class Blackboard implements Closeable {
private SleuthkitCase caseDb;
-
+
/**
* Constructs a representation of the blackboard, a place where artifacts
* and their attributes are posted.
@@ -80,8 +79,8 @@ public final class Blackboard implements Closeable {
*
* @return A type object representing the artifact type.
*
- * @throws BlackboardBlackboardException If there is a problem getting or
- * adding the artifact type.
+ * @throws BlackboardException If there is a problem getting or adding the
+ * artifact type.
*/
public synchronized BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName) throws BlackboardException {
if (null == caseDb) {
@@ -110,8 +109,8 @@ public final class Blackboard implements Closeable {
*
* @return A type object representing the attribute type.
*
- * @throws BlackboardBlackboardException If there is a problem getting or
- * adding the attribute type.
+ * @throws BlackboardException If there is a problem getting or adding the
+ * attribute type.
*/
public synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws BlackboardException {
if (null == caseDb) {
@@ -140,7 +139,6 @@ public final class Blackboard implements Closeable {
caseDb = null;
}
-
/**
* A blackboard exception.
*/
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/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..5325c0fc2f 100755
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.java
@@ -52,7 +52,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
currentComment = instance.getComment();
}
- pathLabel.setText(instance.getFilePath());
commentTextArea.setText(instance.getComment());
this.correlationAttribute = correlationAttribute;
@@ -103,8 +102,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 +128,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 +139,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 +152,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())
);
@@ -197,9 +181,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..8a1ce6864e 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;
@@ -85,7 +86,10 @@ 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;
@@ -125,7 +129,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
showCommonalityDetails();
} else if (jmi.equals(addCommentMenuItem)) {
try {
- OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(otherCasesTable.getSelectedRow());
+ OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow());
AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute());
action.actionPerformed(null);
String currentComment = action.getComment();
@@ -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);
}
@@ -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,
@@ -462,12 +465,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 +478,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 +498,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 +509,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(CorrelationAttribute 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 +533,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,13 +563,17 @@ 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 {
String md5 = corAttr.getCorrelationValue();
@@ -586,18 +593,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 +620,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 +649,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 +672,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 = "";
@@ -684,7 +693,7 @@ 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);
+ Map correlatedNodeDataMap = new HashMap<>(0);
// get correlation and reference set instances from DB
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
@@ -696,36 +705,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 +767,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 +828,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 +840,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 +868,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 +880,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 +890,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 +916,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 +942,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 +955,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
old mode 100644
new mode 100755
index 958068fb14..c10f078313
--- 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
@@ -18,216 +18,9 @@
*/
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..7eb907aba8
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java
@@ -0,0 +1,233 @@
+/*
+ * 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.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
+ */
+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 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
+ */
+ OtherOccurrenceNodeInstanceData(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
+ */
+ 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;
+ 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/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 f535d58ccd..991821bebf 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java
@@ -387,6 +387,46 @@ abstract class AbstractSqlEamDb implements EamDb {
return eamCaseResult;
}
+ /**
+ * Retrieves Case details based on Case ID
+ *
+ * @param caseID unique identifier for a case
+ *
+ * @return The retrieved case
+ */
+ @Override
+ public CorrelationCase getCaseById(int caseId) throws EamDbException {
+ // @@@ We should have a cache here...
+
+ Connection conn = connect();
+
+ CorrelationCase eamCaseResult = null;
+ PreparedStatement preparedStatement = null;
+ ResultSet resultSet = null;
+
+ String sql = "SELECT cases.id as case_id, case_uid, case_name, creation_date, case_number, examiner_name, "
+ + "examiner_email, examiner_phone, notes, organizations.id as org_id, org_name, poc_name, poc_email, poc_phone "
+ + "FROM cases "
+ + "LEFT JOIN organizations ON cases.org_id=organizations.id "
+ + "WHERE cases.id=?";
+
+ try {
+ preparedStatement = conn.prepareStatement(sql);
+ preparedStatement.setInt(1, caseId);
+ resultSet = preparedStatement.executeQuery();
+ if (resultSet.next()) {
+ eamCaseResult = getEamCaseFromResultSet(resultSet);
+ }
+ } catch (SQLException ex) {
+ throw new EamDbException("Error getting case details.", ex); // NON-NLS
+ } finally {
+ EamDbUtil.closeStatement(preparedStatement);
+ EamDbUtil.closeResultSet(resultSet);
+ EamDbUtil.closeConnection(conn);
+ }
+
+ return eamCaseResult;
+ }
/**
* Retrieves cases that are in DB.
@@ -502,6 +542,48 @@ 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 dataSourceId the data source ID number
+ *
+ * @return The data source
+ */
+ @Override
+ public CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException {
+ if (correlationCase == null) {
+ throw new EamDbException("Correlation case is null");
+ }
+
+ Connection conn = connect();
+
+ CorrelationDataSource eamDataSourceResult = null;
+ PreparedStatement preparedStatement = null;
+ ResultSet resultSet = null;
+
+ String sql = "SELECT * FROM data_sources WHERE id=? AND case_id=?"; // NON-NLS
+
+ try {
+ preparedStatement = conn.prepareStatement(sql);
+ preparedStatement.setInt(1, dataSourceId);
+ preparedStatement.setInt(2, correlationCase.getID());
+ resultSet = preparedStatement.executeQuery();
+ if (resultSet.next()) {
+ eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
+ }
+ } catch (SQLException ex) {
+ throw new EamDbException("Error getting data source.", ex); // NON-NLS
+ } finally {
+ EamDbUtil.closeStatement(preparedStatement);
+ EamDbUtil.closeResultSet(resultSet);
+ EamDbUtil.closeConnection(conn);
+ }
+
+ return eamDataSourceResult;
+ }
/**
* Return a list of data sources in the DB
@@ -670,7 +752,7 @@ abstract class AbstractSqlEamDb implements EamDb {
return artifactInstances;
}
-
+
/**
* Retrieves eamArtifact instances from the database that are associated
* with the aType and filePath
@@ -1814,6 +1896,52 @@ abstract class AbstractSqlEamDb implements EamDb {
}
}
+ /**
+ * Process the Artifact instance in the EamDb give a where clause
+ *
+ * @param type EamArtifact.Type to search for
+ * @param instanceTableCallback callback to process the instance
+ * @param whereClause query string to execute
+ * @throws EamDbException
+ */
+ @Override
+ public void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException {
+ if (type == null) {
+ throw new EamDbException("Correlation type is null");
+ }
+
+ if (instanceTableCallback == null) {
+ throw new EamDbException("Callback interface is null");
+ }
+
+ if(whereClause == null) {
+ throw new EamDbException("Where clause is null");
+ }
+
+ Connection conn = connect();
+ PreparedStatement preparedStatement = null;
+ ResultSet resultSet = null;
+ String tableName = EamDbUtil.correlationTypeToInstanceTableName(type);
+ StringBuilder sql = new StringBuilder(300);
+ sql.append("select * from ")
+ .append(tableName)
+ .append(" WHERE ")
+ .append(whereClause);
+
+ try {
+ preparedStatement = conn.prepareStatement(sql.toString());
+ resultSet = preparedStatement.executeQuery();
+ instanceTableCallback.process(resultSet);
+ } catch (SQLException ex) {
+ throw new EamDbException("Error getting all artifact instances from instances table", ex);
+ } finally {
+ EamDbUtil.closeStatement(preparedStatement);
+ EamDbUtil.closeResultSet(resultSet);
+ EamDbUtil.closeConnection(conn);
+ }
+ }
+
+
@Override
public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException {
if (eamOrg == null) {
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java
index 7ced4a1d7d..e4fb30583e 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java
@@ -175,13 +175,21 @@ public interface EamDb {
*/
CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException;
+ /**
+ * Retrieves Case details based on Case ID
+ *
+ * @param caseID unique identifier for a case
+ *
+ * @return The retrieved case
+ */
+ CorrelationCase getCaseById(int caseId) throws EamDbException;
/**
* Retrieves cases that are in DB.
*
* @return List of cases
*/
List getCases() throws EamDbException;
-
+
/**
* Creates new Data Source in the database
*
@@ -200,6 +208,18 @@ public interface EamDb {
*/
CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException;
+
+ /**
+ * Retrieves Data Source details based on data source ID
+ *
+ * @param correlationCase the current CorrelationCase used for ensuring
+ * uniqueness of DataSource
+ * @param dataSourceId the data source ID number
+ *
+ * @return The data source
+ */
+ CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException;
+
/**
* Retrieves data sources that are in DB
*
@@ -225,7 +245,7 @@ public interface EamDb {
* @return List of artifact instances for a given type/value
*/
List getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException;
-
+
/**
* Retrieves eamArtifact instances from the database that are associated
* with the aType and filePath
@@ -685,4 +705,15 @@ public interface EamDb {
* @throws EamDbException
*/
void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException;
+
+ /**
+ * Process the Artifact instance in the EamDb
+ *
+ * @param type EamArtifact.Type to search for
+ * @param instanceTableCallback callback to process the instance
+ * @param whereClause query string to execute
+ * @throws EamDbException
+ */
+ void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException;
+
}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java
index 4a3ef36530..74d0c36a66 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java
@@ -33,9 +33,9 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
/**
- * Sqlite implementation of the Central Repository database.
- * All methods in AbstractSqlEamDb that read or write to the database should
- * be overriden here and use appropriate locking.
+ * Sqlite implementation of the Central Repository database. All methods in
+ * AbstractSqlEamDb that read or write to the database should be overriden here
+ * and use appropriate locking.
*/
final class SqliteEamDb extends AbstractSqlEamDb {
@@ -46,17 +46,18 @@ final class SqliteEamDb extends AbstractSqlEamDb {
private BasicDataSource connectionPool = null;
private final SqliteEamDbSettings dbSettings;
-
+
// While the Sqlite database should only be used for single users, it is still
// possible for multiple threads to attempt to write to the database simultaneously.
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true);
/**
* Get the singleton instance of SqliteEamDb
- *
+ *
* @return the singleton instance of SqliteEamDb
- *
- * @throws EamDbException if one or more default correlation type(s) have an invalid db table name.
+ *
+ * @throws EamDbException if one or more default correlation type(s) have an
+ * invalid db table name.
*/
public synchronized static SqliteEamDb getInstance() throws EamDbException {
if (instance == null) {
@@ -67,9 +68,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
}
/**
- *
- * @throws EamDbException if the AbstractSqlEamDb class has one or more default
- * correlation type(s) having an invalid db table name.
+ *
+ * @throws EamDbException if the AbstractSqlEamDb class has one or more
+ * default correlation type(s) having an invalid db table name.
*/
private SqliteEamDb() throws EamDbException {
dbSettings = new SqliteEamDbSettings();
@@ -79,7 +80,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
@Override
public void shutdownConnections() throws EamDbException {
try {
- synchronized(this) {
+ synchronized (this) {
if (null != connectionPool) {
connectionPool.close();
connectionPool = null; // force it to be re-created on next connect()
@@ -89,7 +90,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
throw new EamDbException("Failed to close existing database connections.", ex); // NON-NLS
}
}
-
+
@Override
public void updateSettings() {
synchronized (this) {
@@ -107,9 +108,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
@Override
public void reset() throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
-
+
Connection conn = connect();
try {
@@ -150,11 +151,11 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*
*/
private void setupConnectionPool() throws EamDbException {
-
+
if (dbSettings.dbFileExists() == false) {
throw new EamDbException("Central repository database missing");
}
-
+
connectionPool = new BasicDataSource();
connectionPool.setDriverClassName(dbSettings.getDriver());
connectionPool.setUrl(dbSettings.getConnectionURL());
@@ -200,25 +201,24 @@ final class SqliteEamDb extends AbstractSqlEamDb {
return "";
}
-
/**
* 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
*/
@Override
public void newDbInfo(String name, String value) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.newDbInfo(name, value);
} finally {
releaseExclusiveLock();
}
}
-
+
/**
* Get the value for the given name from the name/value db_info table.
*
@@ -230,47 +230,47 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public String getDbInfo(String name) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getDbInfo(name);
} finally {
releaseSharedLock();
- }
+ }
}
-
+
/**
* 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
*/
@Override
public void updateDbInfo(String name, String value) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.updateDbInfo(name, value);
} finally {
releaseExclusiveLock();
- }
+ }
}
-
- /**
+
+ /**
* Creates new Case in the database from the given case
- *
+ *
* @param autopsyCase The case to add
*/
@Override
public CorrelationCase newCase(Case autopsyCase) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
return super.newCase(autopsyCase);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Creates new Case in the database
*
@@ -280,14 +280,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
return super.newCase(eamCase);
} finally {
releaseExclusiveLock();
- }
+ }
}
-
+
/**
* Updates an existing Case in the database
*
@@ -295,14 +295,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void updateCase(CorrelationCase eamCase) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.updateCase(eamCase);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Retrieves Case details based on Case UUID
*
@@ -312,14 +312,32 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getCaseByUUID(caseUUID);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
+ /**
+ * Retrieves Case details based on Case ID
+ *
+ * @param caseID unique identifier for a case
+ *
+ * @return The retrieved case
+ */
+ @Override
+ public CorrelationCase getCaseById(int caseId) throws EamDbException {
+ try {
+ acquireSharedLock();
+ return super.getCaseById(caseId);
+ } finally {
+ releaseSharedLock();
+ }
+
+ }
+
/**
* Retrieves cases that are in DB.
*
@@ -327,14 +345,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List getCases() throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getCases();
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Creates new Data Source in the database
*
@@ -342,32 +360,52 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void newDataSource(CorrelationDataSource eamDataSource) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.newDataSource(eamDataSource);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* 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
*/
@Override
public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getDataSource(correlationCase, dataSourceDeviceId);
} finally {
releaseSharedLock();
- }
- }
+ }
+ }
+ /**
+ * Retrieves Data Source details based on data source ID
+ *
+ * @param correlationCase the current CorrelationCase used for ensuring
+ * uniqueness of DataSource
+ * @param dataSourceId the data source ID number
+ *
+ * @return The data source
+ */
+ @Override
+ public CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException {
+ try {
+ acquireSharedLock();
+ return super.getDataSourceById(correlationCase, dataSourceId);
+ } finally {
+ releaseSharedLock();
+ }
+ }
+
/**
* Return a list of data sources in the DB
*
@@ -375,14 +413,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List getDataSources() throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getDataSources();
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Inserts new Artifact(s) into the database. Should add associated Case and
* Data Source first.
@@ -391,38 +429,38 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void addArtifact(CorrelationAttribute eamArtifact) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.addArtifact(eamArtifact);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Retrieves eamArtifact instances from the database that are associated
* with the eamArtifactType and eamArtifactValue of the given eamArtifact.
*
- * @param aType The type of the artifact
- * @param value The correlation value
+ * @param aType The type of the artifact
+ * @param value The correlation value
*
* @return List of artifact instances for a given type/value
*/
@Override
public List getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getArtifactInstancesByTypeValue(aType, value);
} finally {
releaseSharedLock();
- }
+ }
}
/**
* 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
@@ -431,14 +469,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List getArtifactInstancesByPath(CorrelationAttribute.Type aType, String filePath) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getArtifactInstancesByPath(aType, filePath);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Retrieves number of artifact instances in the database that are
* associated with the ArtifactType and artifactValue of the given artifact.
@@ -447,29 +485,29 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value The value to search for
*
* @return Number of artifact instances having ArtifactType and
- * ArtifactValue.
- * @throws EamDbException
+ * ArtifactValue.
+ * @throws EamDbException
*/
@Override
public Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getCountArtifactInstancesByTypeValue(aType, value);
} finally {
releaseSharedLock();
- }
+ }
}
-
+
@Override
public int getFrequencyPercentage(CorrelationAttribute corAttr) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getFrequencyPercentage(corAttr);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Retrieves number of unique caseDisplayName / dataSource tuples in the
* database that are associated with the artifactType and artifactValue of
@@ -483,130 +521,130 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getCountUniqueCaseDataSourceTuplesHavingTypeValue(aType, value);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
@Override
public Long getCountUniqueDataSources() throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getCountUniqueDataSources();
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Retrieves number of eamArtifact instances in the database that are
* 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 {
- try{
+ try {
acquireSharedLock();
return super.getCountArtifactInstancesByCaseDataSource(caseUUID, dataSourceID);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Executes a bulk insert of the eamArtifacts added from the
* prepareBulkArtifact() method
*/
@Override
public void bulkInsertArtifacts() throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.bulkInsertArtifacts();
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Executes a bulk insert of the cases
*/
@Override
public void bulkInsertCases(List cases) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.bulkInsertCases(cases);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
- * Sets an eamArtifact instance to the given knownStatus.
- * knownStatus should be BAD if the file has been tagged with a notable tag and
- * UNKNOWN otherwise. If eamArtifact
- * exists, it is updated. If eamArtifact does not exist it is added with the
- * given status.
+ * Sets an eamArtifact instance to the given knownStatus. knownStatus should
+ * be BAD if the file has been tagged with a notable tag and UNKNOWN
+ * otherwise. If eamArtifact exists, it is updated. If eamArtifact does not
+ * exist it is added with the given status.
*
* @param eamArtifact Artifact containing exactly one (1) ArtifactInstance.
- * @param knownStatus The status to change the artifact to. Should never be KNOWN
+ * @param knownStatus The status to change the artifact to. Should never be
+ * KNOWN
*/
@Override
public void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.setArtifactInstanceKnownStatus(eamArtifact, knownStatus);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Gets list of matching eamArtifact instances that have knownStatus =
* "Bad".
*
* @param aType EamArtifact.Type to search for
* @param value Value to search for
- *
+ *
* @return List with 0 or more matching eamArtifact instances.
*/
@Override
public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getArtifactInstancesKnownBad(aType, value);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
*
* Gets list of matching eamArtifact instances that have knownStatus =
* "Bad".
+ *
* @param aType EamArtifact.Type to search for
* @return List with 0 or more matching eamArtifact instances.
* @throws EamDbException
*/
@Override
public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getArtifactInstancesKnownBad(aType);
} finally {
releaseSharedLock();
- }
+ }
}
-
+
/**
* Count matching eamArtifacts instances that have knownStatus = "Bad".
*
@@ -617,14 +655,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public Long getCountArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getCountArtifactInstancesKnownBad(aType, value);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Gets list of distinct case display names, where each case has 1+ Artifact
* Instance matching eamArtifact with knownStatus = "Bad".
@@ -633,37 +671,39 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @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 {
- try{
+ try {
acquireSharedLock();
return super.getListCasesHavingArtifactInstancesKnownBad(aType, value);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Remove a reference set and all values contained in it.
+ *
* @param referenceSetID
- * @throws EamDbException
+ * @throws EamDbException
*/
@Override
- public void deleteReferenceSet(int referenceSetID) throws EamDbException{
- try{
+ public void deleteReferenceSet(int referenceSetID) throws EamDbException {
+ try {
acquireExclusiveLock();
super.deleteReferenceSet(referenceSetID);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Check if the given hash is in a specific reference set
+ *
* @param value
* @param referenceSetID
* @param correlationTypeID
@@ -671,14 +711,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.isValueInReferenceSet(value, referenceSetID, correlationTypeID);
} finally {
releaseSharedLock();
- }
+ }
}
-
+
/**
* Process the Artifact instance in the EamDb
*
@@ -695,24 +735,44 @@ final class SqliteEamDb extends AbstractSqlEamDb {
releaseSharedLock();
}
}
+
/**
- * Check whether a reference set with the given name/version is in the central repo.
- * Used to check for name collisions when creating reference sets.
+ * Process the Artifact instance in the EamDb
+ *
+ * @param type EamArtifact.Type to search for
+ * @param instanceTableCallback callback to process the instance
+ * @throws EamDbException
+ */
+ @Override
+ public void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException {
+ try {
+ acquireSharedLock();
+ super.processInstanceTableWhere(type, whereClause, instanceTableCallback);
+ } finally {
+ releaseSharedLock();
+ }
+ }
+
+ /**
+ * Check whether a reference set with the given name/version is in the
+ * central repo. Used to check for name collisions when creating reference
+ * sets.
+ *
* @param referenceSetName
* @param version
* @return true if a matching set is found
- * @throws EamDbException
+ * @throws EamDbException
*/
@Override
public boolean referenceSetExists(String referenceSetName, String version) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.referenceSetExists(referenceSetName, version);
} finally {
releaseSharedLock();
- }
+ }
}
-
+
/**
* Is the artifact known as bad according to the reference entries?
*
@@ -722,34 +782,34 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @return Global known status of the artifact
*/
@Override
- public boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException {
- try{
+ public boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException {
+ try {
acquireSharedLock();
return super.isArtifactKnownBadByReference(aType, value);
} finally {
releaseSharedLock();
- }
+ }
}
-
+
/**
* Add a new organization
*
* @return the Organization ID of the newly created organization.
- *
+ *
* @param eamOrg The organization to add
*
* @throws EamDbException
*/
@Override
public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
return super.newOrganization(eamOrg);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Get all organizations
*
@@ -759,14 +819,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List getOrganizations() throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getOrganizations();
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Get an organization having the given ID
*
@@ -778,33 +838,34 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public EamOrganization getOrganizationByID(int orgID) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getOrganizationByID(orgID);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
@Override
public void updateOrganization(EamOrganization updatedOrganization) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.updateOrganization(updatedOrganization);
} finally {
releaseExclusiveLock();
- }
+ }
}
-
+
@Override
public void deleteOrganization(EamOrganization organizationToDelete) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.deleteOrganization(organizationToDelete);
} finally {
releaseExclusiveLock();
- }
+ }
}
+
/**
* Add a new Global Set
*
@@ -816,14 +877,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public int newReferenceSet(EamGlobalSet eamGlobalSet) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
return super.newReferenceSet(eamGlobalSet);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Get a reference set by ID
*
@@ -835,52 +896,51 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public EamGlobalSet getReferenceSetByID(int referenceSetID) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getReferenceSetByID(referenceSetID);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Get all reference sets
*
* @param correlationType Type of sets to return
- *
+ *
* @return List of all reference sets in the central repository
*
* @throws EamDbException
*/
@Override
public List getAllReferenceSets(CorrelationAttribute.Type correlationType) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getAllReferenceSets(correlationType);
} finally {
releaseSharedLock();
- }
+ }
}
-
+
/**
* 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 {
- try{
+ try {
acquireExclusiveLock();
super.addReferenceInstance(eamGlobalFileInstance, correlationType);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Insert the bulk collection of Reference Type Instances
*
@@ -888,18 +948,18 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void bulkInsertReferenceTypeEntries(Set globalInstances, CorrelationAttribute.Type contentType) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.bulkInsertReferenceTypeEntries(globalInstances, contentType);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* 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
@@ -908,14 +968,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List getReferenceInstancesByTypeValue(CorrelationAttribute.Type aType, String aValue) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getReferenceInstancesByTypeValue(aType, aValue);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Add a new EamArtifact.Type to the db.
*
@@ -927,71 +987,71 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public int newCorrelationType(CorrelationAttribute.Type newType) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
return super.newCorrelationType(newType);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Get the list of EamArtifact.Type's that will be used to correlate
* artifacts.
*
* @return List of EamArtifact.Type's. If none are defined in the database,
- * the default list will be returned.
+ * the default list will be returned.
*
* @throws EamDbException
*/
@Override
public List getDefinedCorrelationTypes() throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getDefinedCorrelationTypes();
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Get the list of enabled EamArtifact.Type's that will be used to correlate
* 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 {
- try{
+ try {
acquireSharedLock();
return super.getEnabledCorrelationTypes();
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Get the list of supported EamArtifact.Type's that can be used to
* 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 {
- try{
+ try {
acquireSharedLock();
return super.getSupportedCorrelationTypes();
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Update a EamArtifact.Type.
*
@@ -1001,14 +1061,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void updateCorrelationType(CorrelationAttribute.Type aType) throws EamDbException {
- try{
+ try {
acquireExclusiveLock();
super.updateCorrelationType(aType);
} finally {
releaseExclusiveLock();
- }
- }
-
+ }
+ }
+
/**
* Get the EamArtifact.Type that has the given Type.Id.
*
@@ -1020,73 +1080,76 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public CorrelationAttribute.Type getCorrelationTypeById(int typeId) throws EamDbException {
- try{
+ try {
acquireSharedLock();
return super.getCorrelationTypeById(typeId);
} finally {
releaseSharedLock();
- }
- }
-
+ }
+ }
+
/**
* Upgrade the schema of the database (if needed)
- * @throws EamDbException
+ *
+ * @throws EamDbException
*/
@Override
public void upgradeSchema() throws EamDbException, SQLException {
- try{
+ try {
acquireExclusiveLock();
super.upgradeSchema();
} finally {
releaseExclusiveLock();
- }
+ }
}
-
+
/**
- * Gets an exclusive lock (if applicable).
- * Will return the lock if successful, null if unsuccessful because locking
- * isn't supported, and throw an exception if we should have been able to get the
- * lock but failed (meaning the database is in use).
+ * Gets an exclusive lock (if applicable). Will return the lock if
+ * successful, null if unsuccessful because locking isn't supported, and
+ * throw an exception if we should have been able to get the lock but failed
+ * (meaning the database is in use).
+ *
* @return the lock, or null if locking is not supported
- * @throws EamDbException if the coordination service is running but we fail to get the lock
+ * @throws EamDbException if the coordination service is running but we fail
+ * to get the lock
*/
@Override
- public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException{
+ public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException {
// Multiple users are not supported for SQLite
return null;
}
-
+
/**
- * Acquire the lock that provides exclusive access to the case database.
- * Call this method in a try block with a call to
- * the lock release method in an associated finally block.
+ * Acquire the lock that provides exclusive access to the case database.
+ * Call this method in a try block with a call to the lock release method in
+ * an associated finally block.
*/
private void acquireExclusiveLock() {
rwLock.writeLock().lock();
}
/**
- * Release the lock that provides exclusive access to the database.
- * This method should always be called in the finally
- * block of a try block in which the lock was acquired.
+ * Release the lock that provides exclusive access to the database. This
+ * method should always be called in the finally block of a try block in
+ * which the lock was acquired.
*/
private void releaseExclusiveLock() {
rwLock.writeLock().unlock();
}
/**
- * Acquire the lock that provides shared access to the case database.
- * Call this method in a try block with a call to the
- * lock release method in an associated finally block.
+ * Acquire the lock that provides shared access to the case database. Call
+ * this method in a try block with a call to the lock release method in an
+ * associated finally block.
*/
private void acquireSharedLock() {
rwLock.readLock().lock();
}
/**
- * Release the lock that provides shared access to the database.
- * This method should always be called in the finally block
- * of a try block in which the lock was acquired.
+ * Release the lock that provides shared access to the database. This method
+ * should always be called in the finally block of a try block in which the
+ * lock was acquired.
*/
private void releaseSharedLock() {
rwLock.readLock().unlock();
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java
new file mode 100644
index 0000000000..7732c5d393
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java
@@ -0,0 +1,163 @@
+/*
+ *
+ * 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 org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
+import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
+import org.sleuthkit.datamodel.AbstractFile;
+import org.sleuthkit.datamodel.TskCoreException;
+
+/**
+ * Represents an instance in either the CaseDB or CR that had a common match.
+ * Different implementations know how to get the instance details from either
+ * CaseDB or CR.
+ *
+ * Defines leaf-type nodes used in the Common Files Search results tree. Leaf
+ * nodes, may describe common attributes which exist in the current case DB or
+ * in the Central Repo. When a reference to the AbstractFile is lacking (such as
+ * in the case that a common attribute is found in the Central Repo) not all
+ * features of the Content Viewer, and context menu can be supported. Thus,
+ * multiple types of leaf nodes are required to represent Common Attribute
+ * Instance nodes.
+ */
+public abstract class AbstractCommonAttributeInstance {
+
+ private final Long abstractFileObjectId;
+ private final String caseName;
+ private final String dataSource;
+
+ /**
+ * Create a leaf node for attributes found in files in the current case db.
+ *
+ * @param abstractFileReference file from which the common attribute was
+ * found
+ * @param cachedFiles storage for abstract files which have been used
+ * already so we can avoid extra roundtrips to the case db
+ * @param dataSource datasource where this attribute appears
+ * @param caseName case where this attribute appears
+ */
+ AbstractCommonAttributeInstance(Long abstractFileReference, String dataSource, String caseName) {
+ this.abstractFileObjectId = abstractFileReference;
+ this.caseName = caseName;
+ this.dataSource = dataSource;
+ }
+
+ /**
+ * Create a leaf node for attributes found in the central repo and not
+ * available in the current data case.
+ *
+ * @param cachedFiles storage for abstract files which have been used
+ * already so we can avoid extra roundtrips to the case db
+ */
+ AbstractCommonAttributeInstance() {
+ this.abstractFileObjectId = -1L;
+ this.caseName = "";
+ this.dataSource = "";
+ }
+
+ /**
+ * Get an AbstractFile for this instance if it can be retrieved from the
+ * CaseDB.
+ *
+ * @return AbstractFile corresponding to this common attribute or null if it
+ * cannot be found (for example, in the event that this is a central repo
+ * file)
+ */
+ abstract AbstractFile getAbstractFile();
+
+ /**
+ * Create a list of leaf nodes, to be used to display a row in the tree
+ * table
+ *
+ * @return leaf nodes for tree
+ */
+ abstract DisplayableItemNode[] generateNodes();
+
+ /**
+ * The name of the case where this common attribute is found.
+ *
+ * @return case name
+ */
+ String getCaseName() {
+ return this.caseName;
+ }
+
+ /**
+ * Get string name of the data source where this common attribute appears.
+ *
+ * @return data source name
+ */
+ public String getDataSource() {
+
+ /**
+ * Even though we could get this from the CR record or the AbstractFile,
+ * we want to avoid getting it from the AbstractFile because it would be
+ * an extra database roundtrip.
+ */
+ return this.dataSource;
+ }
+
+ /**
+ * ObjectId of the AbstractFile that is equivalent to the file from which
+ * this common attribute instance
+ *
+ * @return the abstractFileObjectId
+ */
+ public Long getAbstractFileObjectId() {
+ return abstractFileObjectId;
+ }
+
+ /**
+ * Use this to create an AbstractCommonAttributeInstanceNode of the
+ * appropriate type. In any case, we'll get something which extends
+ * DisplayableItemNode which can be used to populate the tree.
+ *
+ * If the common attribute in question could be derived from an AbstractFile
+ * in the present SleuthkitCase, we can use an
+ * IntraCaseCommonAttributeInstanceNode which enables extended functionality
+ * in the context menu and in the content viewer.
+ *
+ * Otherwise, we will get an InterCaseCommonAttributeInstanceNode which
+ * supports only baseline functionality.
+ *
+ * @param attributeInstance common file attribute instance form the central
+ * repo
+ * @param abstractFile an AbstractFile from which the attribute instance was
+ * found - applies to CaseDbCommonAttributeInstance only
+ * @param currentCaseName
+ * @return the appropriate leaf node for the results tree
+ * @throws TskCoreException
+ */
+ static DisplayableItemNode createNode(CorrelationAttribute attribute, AbstractFile abstractFile, String currentCaseName) throws TskCoreException {
+
+ DisplayableItemNode leafNode;
+ CorrelationAttributeInstance attributeInstance = attribute.getInstances().get(0);
+
+ if (abstractFile == null) {
+ leafNode = new CentralRepoCommonAttributeInstanceNode(attributeInstance);
+ } else {
+ final String abstractFileDataSourceName = abstractFile.getDataSource().getName();
+ leafNode = new CaseDBCommonAttributeInstanceNode(abstractFile, currentCaseName, abstractFileDataSourceName);
+ }
+
+ return leafNode;
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java
new file mode 100644
index 0000000000..97456bda04
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java
@@ -0,0 +1,212 @@
+/*
+ *
+ * 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.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.openide.util.NbBundle;
+import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
+import org.sleuthkit.datamodel.TskCoreException;
+
+/**
+ * Prototype for an object which finds files with common attributes.
+ * Subclass this and implement findFiles in order
+ */
+public abstract class AbstractCommonAttributeSearcher {
+
+ private final Map dataSourceIdToNameMap;
+ private boolean filterByMedia;
+ private boolean filterByDoc;
+
+ AbstractCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMedia, boolean filterByDoc){
+ this.filterByDoc = filterByDoc;
+ this.filterByMedia = filterByMedia;
+ this.dataSourceIdToNameMap = dataSourceIdMap;
+ }
+
+ Map getDataSourceIdToNameMap(){
+ return Collections.unmodifiableMap(this.dataSourceIdToNameMap);
+ }
+
+ /**
+ * Implement this to search for files with common attributes. Creates an
+ * object (CommonAttributeSearchResults) which contains all of the information
+ * required to display a tree view in the UI. The view will contain 3 layers:
+ * a top level node, indicating the number matches each of it's children possess,
+ * a mid level node indicating the matched attribute,
+ * @return
+ * @throws TskCoreException
+ * @throws NoCurrentCaseException
+ * @throws SQLException
+ * @throws EamDbException
+ */
+ public abstract CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException;
+
+ /**
+ * Implement this to create a descriptive string for the tab which will display
+ * this data.
+ * @return an informative string
+ */
+ @NbBundle.Messages({
+ "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleIntraAll=Common Files (All Data Sources, %s)",
+ "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleIntraSingle=Common Files (Data Source: %s, %s)",
+ "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleInterAll=Common Files (All Central Repository Cases, %s)",
+ "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleInterSingle=Common Files (Central Repository Case: %s, %s)",
+ })
+ abstract String buildTabTitle();
+
+ @NbBundle.Messages({
+ "AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.doc=Documents",
+ "AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.media=Media",
+ "AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.all=All File Categories"
+ })
+ String buildCategorySelectionString() {
+ if (!this.isFilterByDoc() && !this.isFilterByMedia()) {
+ return Bundle.AbstractCommonFilesMetadataBuilder_buildCategorySelectionString_all();
+ } else {
+ List filters = new ArrayList<>();
+ if (this.isFilterByDoc()) {
+ filters.add(Bundle.AbstractCommonFilesMetadataBuilder_buildCategorySelectionString_doc());
+ }
+ if (this.isFilterByMedia()) {
+ filters.add(Bundle.AbstractCommonFilesMetadataBuilder_buildCategorySelectionString_media());
+ }
+ return String.join(", ", filters);
+ }
+ }
+
+ static Map> collateMatchesByNumberOfInstances(Map commonFiles) {
+ //collate matches by number of matching instances - doing this in sql doesnt seem efficient
+ Map> instanceCollatedCommonFiles = new TreeMap<>();
+ for(CommonAttributeValue md5Metadata : commonFiles.values()){
+ Integer size = md5Metadata.getInstanceCount();
+
+ if(instanceCollatedCommonFiles.containsKey(size)){
+ instanceCollatedCommonFiles.get(size).add(md5Metadata);
+ } else {
+ ArrayList value = new ArrayList<>();
+ value.add(md5Metadata);
+ instanceCollatedCommonFiles.put(size, value);
+ }
+ }
+ return instanceCollatedCommonFiles;
+ }
+
+ /*
+ * The set of the MIME types that will be checked for extension mismatches
+ * when checkType is ONLY_MEDIA.
+ * ".jpg", ".jpeg", ".png", ".psd", ".nef", ".tiff", ".bmp", ".tec"
+ * ".aaf", ".3gp", ".asf", ".avi", ".m1v", ".m2v", //NON-NLS
+ * ".m4v", ".mp4", ".mov", ".mpeg", ".mpg", ".mpe", ".mp4", ".rm", ".wmv", ".mpv", ".flv", ".swf"
+ */
+ static final Set MEDIA_PICS_VIDEO_MIME_TYPES = Stream.of(
+ "image/bmp", //NON-NLS
+ "image/gif", //NON-NLS
+ "image/jpeg", //NON-NLS
+ "image/png", //NON-NLS
+ "image/tiff", //NON-NLS
+ "image/vnd.adobe.photoshop", //NON-NLS
+ "image/x-raw-nikon", //NON-NLS
+ "image/x-ms-bmp", //NON-NLS
+ "image/x-icon", //NON-NLS
+ "video/webm", //NON-NLS
+ "video/3gpp", //NON-NLS
+ "video/3gpp2", //NON-NLS
+ "video/ogg", //NON-NLS
+ "video/mpeg", //NON-NLS
+ "video/mp4", //NON-NLS
+ "video/quicktime", //NON-NLS
+ "video/x-msvideo", //NON-NLS
+ "video/x-flv", //NON-NLS
+ "video/x-m4v", //NON-NLS
+ "video/x-ms-wmv", //NON-NLS
+ "application/vnd.ms-asf", //NON-NLS
+ "application/vnd.rn-realmedia", //NON-NLS
+ "application/x-shockwave-flash" //NON-NLS
+ ).collect(Collectors.toSet());
+
+ /*
+ * The set of the MIME types that will be checked for extension mismatches
+ * when checkType is ONLY_TEXT_FILES.
+ * ".doc", ".docx", ".odt", ".xls", ".xlsx", ".ppt", ".pptx"
+ * ".txt", ".rtf", ".log", ".text", ".xml"
+ * ".html", ".htm", ".css", ".js", ".php", ".aspx"
+ * ".pdf"
+ */
+ static final Set TEXT_FILES_MIME_TYPES = Stream.of(
+ "text/plain", //NON-NLS
+ "application/rtf", //NON-NLS
+ "application/pdf", //NON-NLS
+ "text/css", //NON-NLS
+ "text/html", //NON-NLS
+ "text/csv", //NON-NLS
+ "application/json", //NON-NLS
+ "application/javascript", //NON-NLS
+ "application/xml", //NON-NLS
+ "text/calendar", //NON-NLS
+ "application/x-msoffice", //NON-NLS
+ "application/x-ooxml", //NON-NLS
+ "application/msword", //NON-NLS
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS
+ "application/vnd.ms-powerpoint", //NON-NLS
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS
+ "application/vnd.ms-excel", //NON-NLS
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS
+ "application/vnd.oasis.opendocument.presentation", //NON-NLS
+ "application/vnd.oasis.opendocument.spreadsheet", //NON-NLS
+ "application/vnd.oasis.opendocument.text" //NON-NLS
+ ).collect(Collectors.toSet());
+
+ /**
+ * @return the filterByMedia
+ */
+ boolean isFilterByMedia() {
+ return filterByMedia;
+ }
+
+ /**
+ * @param filterByMedia the filterByMedia to set
+ */
+ void setFilterByMedia(boolean filterByMedia) {
+ this.filterByMedia = filterByMedia;
+ }
+
+ /**
+ * @return the filterByDoc
+ */
+ boolean isFilterByDoc() {
+ return filterByDoc;
+ }
+
+ /**
+ * @param filterByDoc the filterByDoc to set
+ */
+ void setFilterByDoc(boolean filterByDoc) {
+ this.filterByDoc = filterByDoc;
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java
new file mode 100644
index 0000000000..ca436b7809
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * 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.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import org.sleuthkit.autopsy.casemodule.Case;
+import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
+import org.sleuthkit.datamodel.TskCoreException;
+
+/**
+ * Algorithm which finds files anywhere in the Central Repo which also occur in
+ * present case.
+ */
+public class AllInterCaseCommonAttributeSearcher extends InterCaseCommonAttributeSearcher {
+
+ /**
+ *
+ * @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
+ * @throws EamDbException
+ */
+ public AllInterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException {
+ super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
+ }
+
+ @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);
+ }
+
+ @Override
+ String buildTabTitle() {
+ final String buildCategorySelectionString = this.buildCategorySelectionString();
+ final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleInterAll();
+ return String.format(titleTemplate, new Object[]{buildCategorySelectionString});
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllIntraCaseCommonAttributeSearcher.java
similarity index 79%
rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java
rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/AllIntraCaseCommonAttributeSearcher.java
index 111cf4aed9..eed61b0fc1 100644
--- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllIntraCaseCommonAttributeSearcher.java
@@ -25,9 +25,9 @@ import org.sleuthkit.datamodel.TskData.FileKnown;
/**
* Provides logic for selecting common files from all data sources.
*/
-final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadataBuilder {
+final public class AllIntraCaseCommonAttributeSearcher extends IntraCaseCommonAttributeSearcher {
- private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL)%s GROUP BY md5 HAVING COUNT(DISTINCT data_source_obj_id) > 1) order by md5"; //NON-NLS
+ private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL)%s GROUP BY md5 HAVING COUNT(DISTINCT data_source_obj_id) > 1) order by md5"; //NON-NLS
/**
* Implements the algorithm for getting common files across all data
@@ -37,7 +37,7 @@ final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadat
* @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 AllDataSourcesCommonFilesAlgorithm(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
+ public AllIntraCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
}
@@ -49,9 +49,9 @@ final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadat
}
@Override
- protected String buildTabTitle() {
+ String buildTabTitle() {
final String buildCategorySelectionString = this.buildCategorySelectionString();
- final String titleTemplate = Bundle.CommonFilesMetadataBuilder_buildTabTitle_titleAll();
+ final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleIntraAll();
return String.format(titleTemplate, new Object[]{buildCategorySelectionString});
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties
index a2b42155d7..23598648ea 100644
--- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties
@@ -1,15 +1,25 @@
-CommonFilesPanel.searchButton.text=Search
-CommonFilesPanel.withinDataSourceRadioButton.text=At least one instance of a given MD5 must appear in the data source selected below:
-CommonFilesPanel.allDataSourcesRadioButton.text=Files can be in any data source
-CommonFilesPanel.cancelButton.text=Cancel
-CommonFilesPanel.cancelButton.actionCommand=Cancel
-CommonFilesPanel.selectedFileCategoriesButton.text=Match on the following file categories:
-CommonFilesPanel.selectedFileCategoriesButton.toolTipText=Select from the options below...
-CommonFilesPanel.pictureVideoCheckbox.text=Pictures and Videos
-CommonFilesPanel.documentsCheckbox.text=Documents
CommonFilesPanel.commonFilesSearchLabel.text=Find files in multiple data sources in the current case.
-CommonFilesPanel.allFileCategoriesRadioButton.toolTipText=No filtering applied to results...
-CommonFilesPanel.allFileCategoriesRadioButton.text=Match on all file types
CommonFilesPanel.text=Indicate which data sources to consider while searching for duplicates:
-CommonFilesPanel.categoriesLabel.text=Indicate which file types to include in results:
-CommonFilesPanel.errorText.text=In order to search, you must select a file category.
+CommonFilesPanel.jRadioButton1.text=jRadioButton1
+CommonFilesPanel.jRadioButton2.text=With previous cases in the Central Repository
+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.selectedFileCategoriesButton.toolTipText=Select from the options below...
+CommonAttributePanel.selectedFileCategoriesButton.text=Only the selected file types:
+CommonAttributePanel.allFileCategoriesRadioButton.toolTipText=No filtering applied to results...
+CommonAttributePanel.allFileCategoriesRadioButton.text=All file types
+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
new file mode 100644
index 0000000000..9221a5dc86
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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.Arrays;
+import java.util.logging.Level;
+import org.sleuthkit.autopsy.casemodule.Case;
+import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
+import org.sleuthkit.datamodel.AbstractFile;
+import org.sleuthkit.datamodel.SleuthkitCase;
+import org.sleuthkit.datamodel.TskCoreException;
+
+/**
+ * Encapsulates data required to instantiate a FileInstanceNode for an instance in the CaseDB
+ */
+final public class CaseDBCommonAttributeInstance extends AbstractCommonAttributeInstance {
+
+ private static final Logger LOGGER = Logger.getLogger(CaseDBCommonAttributeInstance.class.getName());
+
+
+ /**
+ * Create meta data required to find an abstract file and build a
+ * FileInstanceNode.
+ *
+ * @param objectId id of abstract file to find
+ * @param dataSourceName name of datasource where the object is found
+ */
+ CaseDBCommonAttributeInstance(Long abstractFileReference, String dataSource, String caseName) {
+ super(abstractFileReference, dataSource, caseName);
+ }
+
+ @Override
+ public DisplayableItemNode[] generateNodes() {
+ final CaseDBCommonAttributeInstanceNode intraCaseCommonAttributeInstanceNode = new CaseDBCommonAttributeInstanceNode(this.getAbstractFile(), this.getCaseName(), this.getDataSource());
+ return Arrays.asList(intraCaseCommonAttributeInstanceNode).toArray(new DisplayableItemNode[1]);
+ }
+
+ @Override
+ AbstractFile getAbstractFile() {
+
+ Case currentCase;
+ try {
+ currentCase = Case.getCurrentCaseThrows();
+
+ SleuthkitCase tskDb = currentCase.getSleuthkitCase();
+
+ 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);
+ return null;
+ }
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/FileInstanceNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java
similarity index 81%
rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/FileInstanceNode.java
rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java
index 899aa5abbc..f105d72498 100644
--- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/FileInstanceNode.java
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java
@@ -20,18 +20,18 @@ package org.sleuthkit.autopsy.commonfilesearch;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet;
-import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.AbstractFile;
/**
- * Used by the Common Files search feature to encapsulate instances of a given
- MD5s matched in the search. These nodes will be children of Md5Nodes.
+ * Node that wraps CaseDBCommonAttributeInstance to represent a file instance stored
+ * in the CaseDB.
*/
-public class FileInstanceNode extends FileNode {
+public class CaseDBCommonAttributeInstanceNode extends FileNode {
+ private final String caseName;
private final String dataSource;
/**
@@ -41,11 +41,10 @@ public class FileInstanceNode extends FileNode {
* @param fsContent
* @param dataSource
*/
- public FileInstanceNode(AbstractFile fsContent, String dataSource) {
+ public CaseDBCommonAttributeInstanceNode(AbstractFile fsContent, String caseName, String dataSource) {
super(fsContent);
+ this.caseName = caseName;
this.dataSource = dataSource;
-
- this.setDisplayName(fsContent.getName());
}
@Override
@@ -59,11 +58,14 @@ public class FileInstanceNode extends FileNode {
return visitor.visit(this);
}
- String getDataSource() {
+ public String getCase(){
+ return this.caseName;
+ }
+
+ public String getDataSource() {
return this.dataSource;
}
- @NbBundle.Messages({"FileInstanceNode.createSheet.noDescription= "})
@Override
protected Sheet createSheet() {
Sheet sheet = new Sheet();
@@ -73,13 +75,14 @@ public class FileInstanceNode extends FileNode {
sheet.put(sheetSet);
}
- final String NO_DESCR = Bundle.FileInstanceNode_createSheet_noDescription();
+ final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText();
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, this.getContent().getName()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath()));
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
new file mode 100644
index 0000000000..0e70722b6d
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java
@@ -0,0 +1,137 @@
+/*
+ *
+ * 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.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+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;
+import org.sleuthkit.datamodel.AbstractFile;
+import org.sleuthkit.datamodel.SleuthkitCase;
+import org.sleuthkit.datamodel.TskCoreException;
+
+/**
+ * Represents that a row in the CR was found in multiple cases.
+ *
+ * Generates a DisplayableItmeNode using a CentralRepositoryFile.
+ */
+final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttributeInstance {
+
+ private static final Logger LOGGER = Logger.getLogger(CentralRepoCommonAttributeInstance.class.getName());
+ private final Integer crFileId;
+ private CorrelationAttribute currentAttribute;
+ private final Map dataSourceNameToIdMap;
+
+ CentralRepoCommonAttributeInstance(Integer attrInstId, Map dataSourceIdToNameMap) {
+ super();
+ this.crFileId = attrInstId;
+ this.dataSourceNameToIdMap = invertMap(dataSourceIdToNameMap);
+ }
+
+ void setCurrentAttributeInst(CorrelationAttribute attribute) {
+ this.currentAttribute = attribute;
+ }
+
+ @Override
+ AbstractFile getAbstractFile() {
+
+ Case currentCase;
+ if (this.currentAttribute != null) {
+
+ final CorrelationAttributeInstance currentAttributeInstance = this.currentAttribute.getInstances().get(0);
+
+ String currentFullPath = currentAttributeInstance.getFilePath();
+ String currentDataSource = currentAttributeInstance.getCorrelationDataSource().getName();
+
+
+ if(this.dataSourceNameToIdMap.containsKey(currentDataSource)){
+ Long dataSourceObjectId = this.dataSourceNameToIdMap.get(currentDataSource);
+
+ try {
+ currentCase = Case.getCurrentCaseThrows();
+
+ SleuthkitCase tskDb = currentCase.getSleuthkitCase();
+
+ File fileFromPath = new File(currentFullPath);
+ String fileName = fileFromPath.getName();
+ String parentPath = (fileFromPath.getParent() + File.separator).replace("\\", "/");
+
+ final String whereClause = String.format("lower(name) = '%s' AND md5 = '%s' AND lower(parent_path) = '%s' AND data_source_obj_id = %s", fileName, currentAttribute.getCorrelationValue(), parentPath, dataSourceObjectId);
+ List potentialAbstractFiles = tskDb.findAllFilesWhere(whereClause);
+
+ if(potentialAbstractFiles.isEmpty()){
+ return null;
+ } else if(potentialAbstractFiles.size() > 1){
+ LOGGER.log(Level.WARNING, String.format("Unable to find an exact match for AbstractFile for record with filePath: %s. May have returned the wrong file.", new Object[]{currentFullPath}));
+ return potentialAbstractFiles.get(0);
+ } else {
+ return potentialAbstractFiles.get(0);
+ }
+
+ } catch (TskCoreException | NoCurrentCaseException ex) {
+ LOGGER.log(Level.SEVERE, String.format("Unable to find AbstractFile for record with filePath: %s. Node not created.", new Object[]{currentFullPath}), ex);
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public DisplayableItemNode[] generateNodes() {
+
+ // @@@ 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);
+ List attrInstNodeList = new ArrayList<>(0);
+ String currCaseDbName = Case.getCurrentCase().getDisplayName();
+
+ try {
+ this.setCurrentAttributeInst(corrAttr);
+
+ AbstractFile abstractFileForAttributeInstance = this.getAbstractFile();
+ DisplayableItemNode generatedInstNode = AbstractCommonAttributeInstance.createNode(corrAttr, abstractFileForAttributeInstance, currCaseDbName);
+ attrInstNodeList.add(generatedInstNode);
+
+ } catch (TskCoreException ex) {
+ LOGGER.log(Level.SEVERE, String.format("Unable to get DataSource for record with md5: %s. Node not created.", new Object[]{corrAttr.getCorrelationValue()}), ex);
+ }
+
+ return attrInstNodeList.toArray(new DisplayableItemNode[attrInstNodeList.size()]);
+ }
+
+ private Map invertMap(Map dataSourceIdToNameMap) {
+ HashMap invertedMap = new HashMap<>();
+ for (Map.Entry entry : dataSourceIdToNameMap.entrySet()){
+ invertedMap.put(entry.getValue(), entry.getKey());
+ }
+ return invertedMap;
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstanceNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstanceNode.java
new file mode 100644
index 0000000000..e0b8e928d7
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstanceNode.java
@@ -0,0 +1,116 @@
+/*
+ *
+ * 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.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.swing.Action;
+import org.openide.nodes.Children;
+import org.openide.nodes.Sheet;
+import org.openide.util.lookup.Lookups;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
+import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
+import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
+import org.sleuthkit.autopsy.datamodel.NodeProperty;
+
+/**
+ * Used by the Common Files search feature to encapsulate instances of a given
+ * MD5s matched in the search. These nodes will be children of Md5Nodes.
+ *
+ * Use this type for files which are not in the current case, but from the
+ * Central Repo. Contrast with SleuthkitCase which should be used
+ * when the FileInstance was found in the case presently open in Autopsy.
+ */
+public class CentralRepoCommonAttributeInstanceNode extends DisplayableItemNode {
+
+ private final CorrelationAttributeInstance crFile;
+
+ CentralRepoCommonAttributeInstanceNode(CorrelationAttributeInstance content) {
+ super(Children.LEAF, Lookups.fixed(content));
+ this.crFile = content;
+ this.setDisplayName(new File(this.crFile.getFilePath()).getName());
+ }
+
+ public CorrelationAttributeInstance getCorrelationAttributeInstance(){
+ return this.crFile;
+ }
+
+ @Override
+ public Action[] getActions(boolean context){
+ List actionsList = new ArrayList<>();
+
+ actionsList.addAll(Arrays.asList(super.getActions(true)));
+
+ return actionsList.toArray(new Action[actionsList.size()]);
+ }
+
+ @Override
+ public T accept(DisplayableItemNodeVisitor visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public boolean isLeafTypeNode() {
+ return true;
+ }
+
+ @Override
+ public String getItemType() {
+ //objects of type FileNode will co-occur in the treetable with objects
+ // of this type and they will need to provide the same key
+ return CaseDBCommonAttributeInstanceNode.class.getName();
+ }
+
+ @Override
+ protected Sheet createSheet(){
+ Sheet sheet = new Sheet();
+ Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
+
+ if(sheetSet == null){
+ sheetSet = Sheet.createPropertiesSet();
+ sheet.put(sheetSet);
+ }
+
+ final CorrelationAttributeInstance centralRepoFile = this.getCorrelationAttributeInstance();
+
+ final String fullPath = centralRepoFile.getFilePath();
+ final File file = new File(fullPath);
+
+ final String caseName = centralRepoFile.getCorrelationCase().getDisplayName();
+
+ final String name = file.getName();
+ final String parent = file.getParent();
+
+ final String dataSourceName = centralRepoFile.getCorrelationDataSource().getName();
+
+ final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText();
+
+ sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, name));
+ sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, parent));
+ sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, ""));
+ sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, dataSourceName));
+ sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, ""));
+ sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName));
+
+ return sheet;
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form
new file mode 100644
index 0000000000..c08c3c2d78
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form
@@ -0,0 +1,301 @@
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java
new file mode 100644
index 0000000000..bf1765e310
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java
@@ -0,0 +1,720 @@
+/*
+ * 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.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Level;
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+import org.netbeans.api.progress.ProgressHandle;
+import org.openide.explorer.ExplorerManager;
+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.centralrepository.datamodel.CorrelationCase;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
+import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
+import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
+import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
+import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
+import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
+import org.sleuthkit.datamodel.TskCoreException;
+
+/**
+ * Panel used for common files search configuration and configuration business
+ * logic. Nested within CommonFilesDialog.
+ */
+@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
+public final class CommonAttributePanel extends javax.swing.JDialog {
+
+ 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 boolean pictureViewCheckboxState;
+ private boolean documentsCheckboxState;
+
+ /**
+ * Creates new form CommonFilesPanel
+ */
+ @NbBundle.Messages({
+ "CommonFilesPanel.title=Common Files Panel",
+ "CommonFilesPanel.exception=Unexpected Exception loading DataSources.",
+ "CommonFilesPanel.frame.title=Find Common Files",
+ "CommonFilesPanel.frame.msg=Find Common Files"})
+ public CommonAttributePanel() {
+ 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()) {
+ this.setupCases();
+ } else {
+ this.disableIntercaseSearch();
+ }
+ }
+
+ private static boolean isEamDbAvailable() {
+ try {
+ 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 void disableIntercaseSearch() {
+ this.intraCaseRadio.setSelected(true);
+ this.interCaseRadio.setEnabled(false);
+ }
+
+ @NbBundle.Messages({
+ "CommonFilesPanel.search.results.titleAll=Common Files (All Data Sources)",
+ "CommonFilesPanel.search.results.titleSingle=Common Files (Match Within Data Source: %s)",
+ "CommonFilesPanel.search.results.pathText=Common Files Search Results",
+ "CommonFilesPanel.search.done.searchProgressGathering=Gathering Common Files Search Results.",
+ "CommonFilesPanel.search.done.searchProgressDisplay=Displaying Common Files Search Results.",
+ "CommonFilesPanel.search.done.tskCoreException=Unable to run query against DB.",
+ "CommonFilesPanel.search.done.noCurrentCaseException=Unable to open case file.",
+ "CommonFilesPanel.search.done.exception=Unexpected exception running Common Files Search.",
+ "CommonFilesPanel.search.done.interupted=Something went wrong finding common files.",
+ "CommonFilesPanel.search.done.sqlException=Unable to query db for files or data sources."})
+ private void search() {
+ String pathText = Bundle.CommonFilesPanel_search_results_pathText();
+
+ new SwingWorker() {
+
+ private String tabTitle;
+ private ProgressHandle progress;
+
+ private void setTitleForAllDataSources() {
+ this.tabTitle = Bundle.CommonFilesPanel_search_results_titleAll();
+ }
+
+ private void setTitleForSingleSource(Long dataSourceId) {
+ final String CommonFilesPanel_search_results_titleSingle = Bundle.CommonFilesPanel_search_results_titleSingle();
+ final Object[] dataSourceName = new Object[]{intraCasePanel.getDataSourceMap().get(dataSourceId)};
+
+ this.tabTitle = String.format(CommonFilesPanel_search_results_titleSingle, dataSourceName);
+ }
+
+ @Override
+ @SuppressWarnings({"BoxedValueEquality", "NumberEquality"})
+ protected CommonAttributeSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
+ progress = ProgressHandle.createHandle(Bundle.CommonFilesPanel_search_done_searchProgressGathering());
+ progress.start();
+ progress.switchToIndeterminate();
+
+ Long dataSourceId = intraCasePanel.getSelectedDataSourceId();
+ Integer caseId = interCasePanel.getSelectedCaseId();
+
+ AbstractCommonAttributeSearcher builder;
+ CommonAttributeSearchResults metadata;
+
+ boolean filterByMedia = false;
+ boolean filterByDocuments = false;
+ if (selectedFileCategoriesButton.isSelected()) {
+ if (pictureVideoCheckbox.isSelected()) {
+ filterByMedia = true;
+ }
+ if (documentsCheckbox.isSelected()) {
+ filterByDocuments = true;
+ }
+ }
+
+ if (CommonAttributePanel.this.interCaseRadio.isSelected()) {
+
+ if (caseId == InterCasePanel.NO_CASE_SELECTED) {
+ builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
+ } else {
+ builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
+ }
+ } else {
+ if (dataSourceId == CommonAttributePanel.NO_DATA_SOURCE_SELECTED) {
+ builder = new AllIntraCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
+
+ setTitleForAllDataSources();
+ } else {
+ builder = new SingleIntraCaseCommonAttributeSearcher(dataSourceId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
+
+ setTitleForSingleSource(dataSourceId);
+ }
+ }
+ metadata = builder.findFiles();
+ this.tabTitle = builder.buildTabTitle();
+
+ return metadata;
+ }
+
+ @Override
+ protected void done() {
+ try {
+ super.done();
+
+ CommonAttributeSearchResults metadata = this.get();
+
+ CommonAttributeSearchResultRootNode commonFilesNode = new CommonAttributeSearchResultRootNode(metadata);
+
+ // -3969
+ DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonAttributePanel.this));
+
+ TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode, 3);
+
+ DataResultViewerTable table = new CommonAttributesSearchResultsViewerTable();
+
+ Collection viewers = new ArrayList<>(1);
+ viewers.add(table);
+
+ progress.setDisplayName(Bundle.CommonFilesPanel_search_done_searchProgressDisplay());
+ DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers);
+ progress.finish();
+
+ } catch (InterruptedException ex) {
+ LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files", ex);
+ MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_search_done_interupted());
+ } catch (ExecutionException ex) {
+ String errorMessage;
+ Throwable inner = ex.getCause();
+ if (inner instanceof TskCoreException) {
+ LOGGER.log(Level.SEVERE, "Failed to load files from database.", ex);
+ errorMessage = Bundle.CommonFilesPanel_search_done_tskCoreException();
+ } else if (inner instanceof NoCurrentCaseException) {
+ LOGGER.log(Level.SEVERE, "Current case has been closed.", ex);
+ errorMessage = Bundle.CommonFilesPanel_search_done_noCurrentCaseException();
+ } else if (inner instanceof SQLException) {
+ LOGGER.log(Level.SEVERE, "Unable to query db for files.", ex);
+ errorMessage = Bundle.CommonFilesPanel_search_done_sqlException();
+ } else {
+ LOGGER.log(Level.SEVERE, "Unexpected exception while running Common Files Search.", ex);
+ errorMessage = Bundle.CommonFilesPanel_search_done_exception();
+ }
+ MessageNotifyUtil.Message.error(errorMessage);
+ }
+ }
+ }.execute();
+ }
+
+
+ /**
+ * Sets up the data sources dropdown and returns the data sources map for
+ * future usage.
+ *
+ * @return a mapping of data correlationCase ids to data correlationCase
+ * names
+ */
+ @NbBundle.Messages({
+ "CommonFilesPanel.setupDataSources.done.tskCoreException=Unable to run query against DB.",
+ "CommonFilesPanel.setupDataSources.done.noCurrentCaseException=Unable to open case file.",
+ "CommonFilesPanel.setupDataSources.done.exception=Unexpected exception loading data sources.",
+ "CommonFilesPanel.setupDataSources.done.interupted=Something went wrong building the Common Files Search dialog box.",
+ "CommonFilesPanel.setupDataSources.done.sqlException=Unable to query db for data sources.",
+ "CommonFilesPanel.setupDataSources.updateUi.noDataSources=No data sources were found."})
+ private void setupDataSources() {
+
+ new SwingWorker