Merge branch 'develop' into 4130-add-excel-to-selector-module

This commit is contained in:
dannysmyda 2018-08-17 14:29:57 -04:00 committed by GitHub
commit ebcbcc550b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
128 changed files with 7063 additions and 3456 deletions

View File

@ -86,7 +86,7 @@
<target name="getTestDataFiles"> <target name="getTestDataFiles">
<mkdir dir="${basedir}/test/qa-functional/data"/> <mkdir dir="${basedir}/test/qa-functional/data"/>
<get src="https://drive.google.com/uc?id=1dLYGctuvRQMmnzfXPppTM_9gB49eLc_g" dest="${test-input}/EmbeddedIM_img1_v1.vhd" skipexisting="true"/> <get src="https://drive.google.com/uc?id=1FkinvA7EFqP4nOSOyTAOli5KefM67ufA" dest="${test-input}/EmbeddedIM_img1_v2.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1JACMDyH4y54ypGzFWl82ZzMQf3qbrioP" dest="${test-input}/BitlockerDetection_img1_v1.vhd" skipexisting="true"/> <get src="https://drive.google.com/uc?id=1JACMDyH4y54ypGzFWl82ZzMQf3qbrioP" dest="${test-input}/BitlockerDetection_img1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=17sGybvmBGsWWJYo1IWKmO04oG9hKpPi3" dest="${test-input}/SqlCipherDetection_img1_v1.vhd" skipexisting="true"/> <get src="https://drive.google.com/uc?id=17sGybvmBGsWWJYo1IWKmO04oG9hKpPi3" dest="${test-input}/SqlCipherDetection_img1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=0BxdBkzm5VKGNT0dGY0dqcHVsU3M" dest="${test-input}/IngestFilters_img1_v1.img" skipexisting="true"/> <get src="https://drive.google.com/uc?id=0BxdBkzm5VKGNT0dGY0dqcHVsU3M" dest="${test-input}/IngestFilters_img1_v1.img" skipexisting="true"/>
@ -97,6 +97,12 @@
<get src="https://drive.google.com/uc?id=1-vmbmAAb2HBLbf58GpAA97ozGUFiYHbN" dest="${test-input}/CommonFiles_img2_v1.vhd" skipexisting="true"/> <get src="https://drive.google.com/uc?id=1-vmbmAAb2HBLbf58GpAA97ozGUFiYHbN" dest="${test-input}/CommonFiles_img2_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1ghDjm0NhI3ShMQ38E-4o7XrGeexpjdJb" dest="${test-input}/CommonFiles_img3_v1.vhd" skipexisting="true"/> <get src="https://drive.google.com/uc?id=1ghDjm0NhI3ShMQ38E-4o7XrGeexpjdJb" dest="${test-input}/CommonFiles_img3_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1SJYJFjiumKEtQmeMPsQ6G3xmABarbKm_" dest="${test-input}/CommonFiles_img4_v1.vhd" skipexisting="true"/> <get src="https://drive.google.com/uc?id=1SJYJFjiumKEtQmeMPsQ6G3xmABarbKm_" dest="${test-input}/CommonFiles_img4_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1HwpDlMa1J7h9AmEd1JhLYGnzihslZJUj" dest="${test-input}/c1ds1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1Z7mWChlLIpIlScD-DVNGFik5A_rnwS_9" dest="${test-input}/c1ds2_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=14ZxYGyng_eKPe2yaiKDkJLoNcKHIHhW5" dest="${test-input}/c2ds1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1xv3Lz9m2QLq35ofDfHQNe9aHVtVzHUsj" dest="${test-input}/c2ds2_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1sg-znklB9yJAWq8i1cF2W-QLOM4FjZyv" dest="${test-input}/c3ds1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1qXyaSlm3hMhv0jl6JkZEficknKjYNOlt" dest="${test-input}/c3ds2_v1.vhd" skipexisting="true"/>
</target> </target>
<target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist,getTestDataFiles"> <target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist,getTestDataFiles">

View File

@ -24,10 +24,8 @@ import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.ActionMap; import javax.swing.ActionMap;
@ -105,7 +103,16 @@ public class GetTagNameAndCommentDialog extends JDialog {
public static TagNameAndComment doDialog(Window owner) { public static TagNameAndComment doDialog(Window owner) {
GetTagNameAndCommentDialog dialog = new GetTagNameAndCommentDialog(owner); GetTagNameAndCommentDialog dialog = new GetTagNameAndCommentDialog(owner);
dialog.display(); dialog.display();
return dialog.tagNameAndComment; return dialog.getTagNameAndComment();
}
/**
* Get the TagNameAndComment.
*
* @return the tagNameAndComment
*/
private TagNameAndComment getTagNameAndComment() {
return tagNameAndComment;
} }
private GetTagNameAndCommentDialog(Window owner) { private GetTagNameAndCommentDialog(Window owner) {
@ -114,11 +121,11 @@ public class GetTagNameAndCommentDialog extends JDialog {
ModalityType.APPLICATION_MODAL); ModalityType.APPLICATION_MODAL);
} }
private void display() { private void display() {
initComponents(); initComponents();
tagCombo.setRenderer(new DefaultListCellRenderer() { tagCombo.setRenderer(new DefaultListCellRenderer() {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 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() : "";
@ -160,7 +167,6 @@ public class GetTagNameAndCommentDialog extends JDialog {
} }
}); });
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(GetTagNameAndCommentDialog.class Logger.getLogger(GetTagNameAndCommentDialog.class
.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS .getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
@ -320,4 +326,5 @@ public class GetTagNameAndCommentDialog extends JDialog {
private javax.swing.JComboBox<TagName> tagCombo; private javax.swing.JComboBox<TagName> tagCombo;
private javax.swing.JLabel tagLabel; private javax.swing.JLabel tagLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -146,6 +146,8 @@ UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases
UpdateRecentCases.menuItem.empty=-Empty- UpdateRecentCases.menuItem.empty=-Empty-
AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel
NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive 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.addingDataSourceStatus.msg={0} adding data source
CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1}
MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.text=

View File

@ -319,7 +319,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
// Display warning if there is one (but don't disable "next" button) // Display warning if there is one (but don't disable "next" button)
try { try {
if (false == PathValidator.isValid(path, Case.getCurrentCaseThrows().getCaseType())) { if (false == PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) {
pathErrorLabel.setVisible(true); pathErrorLabel.setVisible(true);
pathErrorLabel.setText(Bundle.ImageFilePanel_pathValidation_dataSourceOnCDriveError()); pathErrorLabel.setText(Bundle.ImageFilePanel_pathValidation_dataSourceOnCDriveError());
} }

View File

@ -290,7 +290,7 @@ final class LocalFilesPanel extends javax.swing.JPanel {
final Case.CaseType currentCaseType = Case.getCurrentCaseThrows().getCaseType(); final Case.CaseType currentCaseType = Case.getCurrentCaseThrows().getCaseType();
for (String currentPath : pathsList) { for (String currentPath : pathsList) {
if (!PathValidator.isValid(currentPath, currentCaseType)) { if (!PathValidator.isValidForMultiUserCase(currentPath, currentCaseType)) {
errorLabel.setVisible(true); errorLabel.setVisible(true);
errorLabel.setText(Bundle.LocalFilesPanel_pathValidation_dataSourceOnCDriveError()); errorLabel.setText(Bundle.LocalFilesPanel_pathValidation_dataSourceOnCDriveError());
return; return;

View File

@ -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) // display warning if there is one (but don't disable "next" button)
try { try {
if (!PathValidator.isValid(path, Case.getCurrentCaseThrows().getCaseType())) { if (!PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) {
errorLabel.setVisible(true); errorLabel.setVisible(true);
errorLabel.setText(Bundle.LogicalEvidenceFilePanel_pathValidation_dataSourceOnCDriveError()); errorLabel.setText(Bundle.LogicalEvidenceFilePanel_pathValidation_dataSourceOnCDriveError());
return false; return false;

View File

@ -29,6 +29,7 @@ import javax.swing.event.DocumentListener;
import org.sleuthkit.autopsy.casemodule.Case.CaseType; import org.sleuthkit.autopsy.casemodule.Case.CaseType;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.PathValidator; import org.sleuthkit.autopsy.coreutils.PathValidator;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/** /**
* The JPanel for the first page of the new case wizard. * The JPanel for the first page of the new case wizard.
@ -151,11 +152,24 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener {
*/ */
caseParentDirWarningLabel.setVisible(false); caseParentDirWarningLabel.setVisible(false);
String parentDir = getCaseParentDir(); String parentDir = getCaseParentDir();
if (!PathValidator.isValid(parentDir, getCaseType())) { if (!PathValidator.isValidForMultiUserCase(parentDir, getCaseType())) {
caseParentDirWarningLabel.setVisible(true); caseParentDirWarningLabel.setVisible(true);
caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnCDriveError.text")); 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 * Enable the "Next" button for the wizard if there is text entered for
* the case name and base case directory. Also make sure that multi-user * the case name and base case directory. Also make sure that multi-user

View File

@ -859,13 +859,14 @@ public class SingleUserCaseConverter {
if (value > biggestPK) { if (value > biggestPK) {
biggestPK = value; 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 + "," + value + ","
+ inputResultSet.getLong(2) + "," + inputResultSet.getLong(2) + ","
+ inputResultSet.getLong(3) + ",'" + inputResultSet.getLong(3) + ",'"
+ inputResultSet.getString(4) + "'," + inputResultSet.getString(4) + "',"
+ inputResultSet.getLong(5) + "," + inputResultSet.getLong(5) + ","
+ inputResultSet.getLong(6) + ")"); //NON-NLS + inputResultSet.getLong(6) + ",'"
+ inputResultSet.getString(7)+ "')"); //NON-NLS
} catch (SQLException ex) { } catch (SQLException ex) {
if (ex.getErrorCode() != 0) { // 0 if the entry already exists if (ex.getErrorCode() != 0) { // 0 if the entry already exists
@ -892,7 +893,8 @@ public class SingleUserCaseConverter {
+ value + "," + value + ","
+ inputResultSet.getLong(2) + "," + inputResultSet.getLong(2) + ","
+ inputResultSet.getLong(3) + ",'" + inputResultSet.getLong(3) + ",'"
+ inputResultSet.getString(4) + "')"); //NON-NLS + inputResultSet.getString(4) + "','"
+ inputResultSet.getString(5) + "')"); //NON-NLS
} catch (SQLException ex) { } catch (SQLException ex) {
if (ex.getErrorCode() != 0) { // 0 if the entry already exists if (ex.getErrorCode() != 0) { // 0 if the entry already exists

View File

@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.casemodule.services;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -80,8 +79,8 @@ public final class Blackboard implements Closeable {
* *
* @return A type object representing the artifact type. * @return A type object representing the artifact type.
* *
* @throws BlackboardBlackboardException If there is a problem getting or * @throws BlackboardException If there is a problem getting or adding the
* adding the artifact type. * artifact type.
*/ */
public synchronized BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName) throws BlackboardException { public synchronized BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName) throws BlackboardException {
if (null == caseDb) { if (null == caseDb) {
@ -110,8 +109,8 @@ public final class Blackboard implements Closeable {
* *
* @return A type object representing the attribute type. * @return A type object representing the attribute type.
* *
* @throws BlackboardBlackboardException If there is a problem getting or * @throws BlackboardException If there is a problem getting or adding the
* adding the attribute type. * attribute type.
*/ */
public synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws BlackboardException { public synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws BlackboardException {
if (null == caseDb) { if (null == caseDb) {
@ -140,7 +139,6 @@ public final class Blackboard implements Closeable {
caseDb = null; caseDb = null;
} }
/** /**
* A blackboard exception. * A blackboard exception.
*/ */

View File

@ -47,7 +47,6 @@ import org.sleuthkit.datamodel.TskData;
public class TagsManager implements Closeable { public class TagsManager implements Closeable {
private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName()); private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName());
private final SleuthkitCase caseDb; private final SleuthkitCase caseDb;
/** /**
@ -71,6 +70,7 @@ public class TagsManager implements Closeable {
|| tagDisplayName.contains(";")); || tagDisplayName.contains(";"));
} }
@NbBundle.Messages({"TagsManager.notableTagEnding.text= (Notable)"}) @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.
@ -166,6 +166,33 @@ public class TagsManager implements Closeable {
return caseDb.getTagNamesInUse(); 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<TagName> getTagNamesInUseForUser(String userName) throws TskCoreException {
Set<TagName> tagNameSet = new HashSet<>();
List<BlackboardArtifactTag> artifactTags = caseDb.getAllBlackboardArtifactTags();
for (BlackboardArtifactTag tag : artifactTags) {
if (tag.getUserName().equals(userName)) {
tagNameSet.add(tag.getName());
}
}
List<ContentTag> 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 * 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 * which there is at least one matching row in the content_tags or
@ -181,6 +208,37 @@ public class TagsManager implements Closeable {
public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException { public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException {
return caseDb.getTagNamesInUse(dsObjId); 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<TagName> getTagNamesInUseForUser(long dsObjId, String userName) throws TskCoreException {
Set<TagName> tagNameSet = new HashSet<>();
List<BlackboardArtifactTag> artifactTags = caseDb.getAllBlackboardArtifactTags();
for (BlackboardArtifactTag tag : artifactTags) {
if (tag.getUserName().equals(userName) && tag.getArtifact().getDataSource().getId() == dsObjId) {
tagNameSet.add(tag.getName());
}
}
List<ContentTag> 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. * 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 * It has keys for the display names of the standard tag types, the current
@ -416,11 +474,37 @@ public class TagsManager implements Closeable {
return caseDb.getContentTagsCountByTagName(tagName); 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<ContentTag> 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 * Gets content tags count by tag name, for the given data source
* *
* @param tagName The representation of the desired tag type in the case * @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 * @param dsObjId data source object id
* *
@ -434,6 +518,33 @@ public class TagsManager implements Closeable {
return caseDb.getContentTagsCountByTagName(tagName, dsObjId); 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<ContentTag> contentTags = getContentTagsByTagName(tagName, dsObjId);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/** /**
* Gets a content tag by tag id. * Gets a content tag by tag id.
* *
@ -581,6 +692,31 @@ public class TagsManager implements Closeable {
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName); 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<BlackboardArtifactTag> 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. * 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. * and/or addTagName.
* @param dsObjId data source object id * @param dsObjId data source object id
* *
* @return A count of the artifact tags with the specified tag name, * @return A count of the artifact tags with the specified tag name, for the
* for the given data source. * given data source.
* *
* @throws TskCoreException If there is an error getting the tags count from * @throws TskCoreException If there is an error getting the tags count from
* the case database. * the case database.
@ -599,6 +735,33 @@ public class TagsManager implements Closeable {
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName, dsObjId); 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<BlackboardArtifactTag> artifactTags = getBlackboardArtifactTagsByTagName(tagName, dsObjId);
for (BlackboardArtifactTag tag : artifactTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/** /**
* Gets an artifact tag by tag id. * Gets an artifact tag by tag id.
* *

View File

@ -5,8 +5,6 @@ OpenIDE-Module-Long-Description=\
Correlation Engine ingest module and central database. \n\n\ 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\ 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. 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.commentLabel.text=Comment:
CentralRepoCommentDialog.pathLabel.text=
CentralRepoCommentDialog.okButton.text=&OK CentralRepoCommentDialog.okButton.text=&OK
CentralRepoCommentDialog.cancelButton.text=C&ancel CentralRepoCommentDialog.cancelButton.text=C&ancel

View File

@ -31,14 +31,7 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" pref="500" max="32767" attributes="0"/> <Component id="jScrollPane1" pref="500" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Component id="commentLabel" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="fileLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="commentLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="451" max="32767" attributes="0"/> <EmptySpace min="0" pref="451" max="32767" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
@ -55,21 +48,16 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="fileLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pathLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="19" max="-2" attributes="0"/>
<Component id="commentLabel" min="-2" max="-2" attributes="0"/> <Component id="commentLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" max="32767" attributes="0"/> <Component id="jScrollPane1" max="32767" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="okButton" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/> <Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -113,20 +101,6 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="fileLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/Bundle.properties" key="CentralRepoCommentDialog.fileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="pathLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/Bundle.properties" key="CentralRepoCommentDialog.pathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="commentLabel"> <Component class="javax.swing.JLabel" name="commentLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">

View File

@ -52,7 +52,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
currentComment = instance.getComment(); currentComment = instance.getComment();
} }
pathLabel.setText(instance.getFilePath());
commentTextArea.setText(instance.getComment()); commentTextArea.setText(instance.getComment());
this.correlationAttribute = correlationAttribute; this.correlationAttribute = correlationAttribute;
@ -103,8 +102,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
commentTextArea = new javax.swing.JTextArea(); commentTextArea = new javax.swing.JTextArea();
okButton = new javax.swing.JButton(); okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton(); cancelButton = new javax.swing.JButton();
fileLabel = new javax.swing.JLabel();
pathLabel = new javax.swing.JLabel();
commentLabel = new javax.swing.JLabel(); commentLabel = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); 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 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()); 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) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(commentLabel)
.addGroup(layout.createSequentialGroup()
.addComponent(fileLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pathLabel))
.addComponent(commentLabel))
.addGap(0, 451, Short.MAX_VALUE)) .addGap(0, 451, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE) .addGap(0, 0, Short.MAX_VALUE)
@ -164,17 +152,13 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fileLabel)
.addComponent(pathLabel))
.addGap(19, 19, 19)
.addComponent(commentLabel) .addComponent(commentLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1) .addComponent(jScrollPane1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(okButton) .addComponent(cancelButton)
.addComponent(cancelButton)) .addComponent(okButton))
.addContainerGap()) .addContainerGap())
); );
@ -197,9 +181,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
private javax.swing.JButton cancelButton; private javax.swing.JButton cancelButton;
private javax.swing.JLabel commentLabel; private javax.swing.JLabel commentLabel;
private javax.swing.JTextArea commentTextArea; private javax.swing.JTextArea commentTextArea;
private javax.swing.JLabel fileLabel;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JButton okButton; private javax.swing.JButton okButton;
private javax.swing.JLabel pathLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -80,7 +80,7 @@
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="otherCasesPanel" pref="483" max="32767" attributes="0"/> <Component id="otherCasesPanel" pref="59" max="32767" attributes="0"/>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
@ -106,7 +106,7 @@
<EmptySpace min="0" pref="483" max="32767" attributes="0"/> <EmptySpace min="0" pref="483" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0"> <Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="tableContainerPanel" pref="483" max="32767" attributes="0"/> <Component id="tableContainerPanel" pref="59" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/> <EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -133,24 +133,19 @@
<Component id="earliestCaseLabel" min="-2" max="-2" attributes="0"/> <Component id="earliestCaseLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="earliestCaseDate" min="-2" max="-2" attributes="0"/> <Component id="earliestCaseDate" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
<Component id="tableStatusPanelLabel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<Component id="tableScrollPane" pref="176" max="32767" attributes="0"/> <Component id="tableScrollPane" pref="27" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/> <EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Component id="tableStatusPanelLabel" min="-2" pref="16" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/> <EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="tableStatusPanel" min="-2" max="-2" attributes="0"/> <Component id="tableStatusPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/> <EmptySpace min="0" pref="0" max="-2" attributes="0"/>
@ -230,13 +225,6 @@
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
</Container> </Container>
<Component class="javax.swing.JLabel" name="tableStatusPanelLabel">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="33" green="0" red="ff" type="rgb"/>
</Property>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.contentviewer;
import java.awt.Component; import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.io.BufferedWriter; import java.io.BufferedWriter;
@ -85,7 +86,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private static final long serialVersionUID = -1L; 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 DataContentViewerOtherCasesTableModel tableModel;
private final Collection<CorrelationAttribute> correlationAttributes; private final Collection<CorrelationAttribute> correlationAttributes;
@ -125,7 +129,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
showCommonalityDetails(); showCommonalityDetails();
} else if (jmi.equals(addCommentMenuItem)) { } else if (jmi.equals(addCommentMenuItem)) {
try { try {
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(otherCasesTable.getSelectedRow()); OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow());
AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute()); AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute());
action.actionPerformed(null); action.actionPerformed(null);
String currentComment = action.getComment(); String currentComment = action.getComment();
@ -149,7 +153,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// Set background of every nth row as light grey. // Set background of every nth row as light grey.
TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer(); TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer();
otherCasesTable.setDefaultRenderer(Object.class, renderer); otherCasesTable.setDefaultRenderer(Object.class, renderer);
tableStatusPanelLabel.setVisible(false);
} }
@ -207,7 +210,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
if (-1 != selectedRowViewIdx) { if (-1 != selectedRowViewIdx) {
EamDb dbManager = EamDb.getInstance(); EamDb dbManager = EamDb.getInstance();
int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx); int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx);
OtherOccurrenceNodeData nodeData = (OtherOccurrenceNodeData) tableModel.getRow(selectedRowModelIdx); OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) tableModel.getRow(selectedRowModelIdx);
CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase(); CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase();
if (eamCasePartial == null) { if (eamCasePartial == null) {
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
@ -462,8 +465,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
@Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."}) @Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."})
/** /**
* Gets the list of Eam Cases and determines the earliest case creation date. * Gets the list of Eam Cases and determines the earliest case creation
* Sets the label to display the earliest date string to the user. * date. Sets the label to display the earliest date string to the user.
*/ */
private void setEarliestCaseDate() { private void setEarliestCaseDate() {
String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable(); String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable();
@ -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 * Query the central repo database (if enabled) and the case database to
* artifact instances correlated to the given central repository artifact. If the * find all artifact instances correlated to the given central repository
* central repo is not enabled, this will only return files from the current case * artifact. If the central repo is not enabled, this will only return files
* with matching MD5 hashes. * from the current case with matching MD5 hashes.
* *
* @param corAttr CorrelationAttribute to query for * @param corAttr CorrelationAttribute to query for
* @param dataSourceName Data source to filter results * @param dataSourceName Data source to filter results
@ -506,13 +509,13 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* *
* @return A collection of correlated artifact instances * @return A collection of correlated artifact instances
*/ */
private Map<UniquePathKey,OtherOccurrenceNodeData> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) { private Map<UniquePathKey, OtherOccurrenceNodeInstanceData> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) {
// @@@ Check exception // @@@ Check exception
try { try {
final Case openCase = Case.getCurrentCase(); final Case openCase = Case.getCurrentCase();
String caseUUID = openCase.getName(); String caseUUID = openCase.getName();
HashMap<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap = new HashMap<>(); HashMap<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap = new HashMap<>();
if (EamDb.isEnabled()) { if (EamDb.isEnabled()) {
List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()); List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue());
@ -530,7 +533,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|| !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId) || !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)
|| !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) { || !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); UniquePathKey uniquePathKey = new UniquePathKey(newNode);
nodeDataMap.put(uniquePathKey, newNode); nodeDataMap.put(uniquePathKey, newNode);
} }
@ -560,10 +563,14 @@ 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 corAttr The CorrelationAttribute containing the MD5 to search for
* @param openCase The current case * @param openCase The current case
*
* @return List of matching AbstractFile objects * @return List of matching AbstractFile objects
*
* @throws NoCurrentCaseException * @throws NoCurrentCaseException
* @throws TskCoreException * @throws TskCoreException
* @throws EamDbException * @throws EamDbException
@ -594,9 +601,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* @throws TskCoreException * @throws TskCoreException
* @throws EamDbException * @throws EamDbException
*/ */
private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException { private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException {
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(newFile, autopsyCase); OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase);
// If the caseDB object has a notable tag associated with it, update // If the caseDB object has a notable tag associated with it, update
// the known status to BAD // the known status to BAD
@ -619,7 +626,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// Otherwise this is a new node so add the new node to the map. // Otherwise this is a new node so add the new node to the map.
if (nodeDataMap.containsKey(uniquePathKey)) { if (nodeDataMap.containsKey(uniquePathKey)) {
if (newNode.getKnown() == TskData.FileKnown.BAD) { if (newNode.getKnown() == TskData.FileKnown.BAD) {
OtherOccurrenceNodeData prevInstance = nodeDataMap.get(uniquePathKey); OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey);
prevInstance.updateKnown(newNode.getKnown()); prevInstance.updateKnown(newNode.getKnown());
} }
} else { } else {
@ -665,8 +672,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* *
* @param node The node being viewed. * @param node The node being viewed.
*/ */
@Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other occurrences to display.", @Messages({
"DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file."}) "DataContentViewerOtherCases.table.noArtifacts=Item has no attributes with which to search.",
"DataContentViewerOtherCases.table.noResultsFound=No results found."
})
private void populateTable(Node node) { private void populateTable(Node node) {
String dataSourceName = ""; String dataSourceName = "";
String deviceId = ""; String deviceId = "";
@ -684,7 +693,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// get the attributes we can correlate on // get the attributes we can correlate on
correlationAttributes.addAll(getCorrelationAttributesFromNode(node)); correlationAttributes.addAll(getCorrelationAttributesFromNode(node));
for (CorrelationAttribute corAttr : correlationAttributes) { for (CorrelationAttribute corAttr : correlationAttributes) {
Map<UniquePathKey,OtherOccurrenceNodeData> correlatedNodeDataMap = new HashMap<>(0); Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
// get correlation and reference set instances from DB // get correlation and reference set instances from DB
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId)); correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
@ -696,36 +705,45 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} }
if (correlationAttributes.isEmpty()) { if (correlationAttributes.isEmpty()) {
// @@@ BC: We should have a more descriptive message than this. Mention that the file didn't have a MD5, etc. tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noArtifacts()));
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_noArtifacts()); setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noArtifacts());
} else if (0 == tableModel.getRowCount()) { } 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 { } else {
clearMessageOnTableStatusPanel();
setColumnWidths(); setColumnWidths();
} }
setEarliestCaseDate(); 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() { private void setColumnWidths() {
for (int idx = 0; idx < tableModel.getColumnCount(); idx++) { for (int idx = 0; idx < tableModel.getColumnCount(); idx++) {
TableColumn column = otherCasesTable.getColumnModel().getColumn(idx); TableColumn column = otherCasesTable.getColumnModel().getColumn(idx);
int colWidth = tableModel.getColumnPreferredWidth(idx); column.setMinWidth(DEFAULT_MIN_CELL_WIDTH);
if (0 < colWidth) { int columnWidth = tableModel.getColumnPreferredWidth(idx);
column.setPreferredWidth(colWidth); 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. * 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 * 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(); earliestCaseLabel = new javax.swing.JLabel();
earliestCaseDate = new javax.swing.JLabel(); earliestCaseDate = new javax.swing.JLabel();
tableStatusPanel = new javax.swing.JPanel(); tableStatusPanel = new javax.swing.JPanel();
tableStatusPanelLabel = new javax.swing.JLabel();
rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() { rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) { 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) .addGap(0, 16, Short.MAX_VALUE)
); );
tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51));
javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel); javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel);
tableContainerPanel.setLayout(tableContainerPanelLayout); tableContainerPanel.setLayout(tableContainerPanelLayout);
tableContainerPanelLayout.setHorizontalGroup( tableContainerPanelLayout.setHorizontalGroup(
@ -825,20 +840,16 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addComponent(earliestCaseLabel) .addComponent(earliestCaseLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(earliestCaseDate) .addComponent(earliestCaseDate)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
); );
tableContainerPanelLayout.setVerticalGroup( tableContainerPanelLayout.setVerticalGroup(
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 176, Short.MAX_VALUE) .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
.addGap(0, 0, 0) .addGap(2, 2, 2)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(earliestCaseLabel) .addComponent(earliestCaseLabel)
.addComponent(earliestCaseDate)) .addComponent(earliestCaseDate))
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 0, 0) .addGap(0, 0, 0)
.addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)) .addGap(0, 0, 0))
@ -857,7 +868,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addGap(0, 483, Short.MAX_VALUE) .addGap(0, 483, Short.MAX_VALUE)
.addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(otherCasesPanelLayout.createSequentialGroup() .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))) .addGap(0, 0, 0)))
); );
@ -869,7 +880,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 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)
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -879,8 +890,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) { if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) {
int rowIndex = otherCasesTable.getSelectedRow(); int rowIndex = otherCasesTable.getSelectedRow();
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex); OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex);
if (selectedNode.isCentralRepoNode()) { if (selectedNode instanceof OtherOccurrenceNodeInstanceData) {
enableCentralRepoActions = true; 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.JPanel tableContainerPanel;
private javax.swing.JScrollPane tableScrollPane; private javax.swing.JScrollPane tableScrollPane;
private javax.swing.JPanel tableStatusPanel; private javax.swing.JPanel tableStatusPanel;
private javax.swing.JLabel tableStatusPanelLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
/** /**
* Used as a key to ensure we eliminate duplicates from the result set by * Used as a key to ensure we eliminate duplicates from the result set by
* not overwriting CR correlation instances. * not overwriting CR correlation instances.
*/ */
static final class UniquePathKey { private static final class UniquePathKey {
private final String dataSourceID; private final String dataSourceID;
private final String filePath; private final String filePath;
private final String type; private final String type;
UniquePathKey(OtherOccurrenceNodeData nodeData) { UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) {
super(); super();
dataSourceID = nodeData.getDeviceID(); dataSourceID = nodeData.getDeviceID();
if (nodeData.getFilePath() != null) { if (nodeData.getFilePath() != null) {
@ -932,9 +943,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
public boolean equals(Object other) { public boolean equals(Object other) {
if (other instanceof UniquePathKey) { if (other instanceof UniquePathKey) {
UniquePathKey otherKey = (UniquePathKey) (other); UniquePathKey otherKey = (UniquePathKey) (other);
return ( Objects.equals(otherKey.dataSourceID, this.dataSourceID) return (Objects.equals(otherKey.getDataSourceID(), this.getDataSourceID())
&& Objects.equals(otherKey.filePath, this.filePath) && Objects.equals(otherKey.getFilePath(), this.getFilePath())
&& Objects.equals(otherKey.type, this.type)); && Objects.equals(otherKey.getType(), this.getType()));
} }
return false; return false;
} }
@ -944,7 +955,34 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
//int hash = 7; //int hash = 7;
//hash = 67 * hash + this.dataSourceID.hashCode(); //hash = 67 * hash + this.dataSourceID.hashCode();
//hash = 67 * hash + this.filePath.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;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Central Repository * Central Repository
* *
* Copyright 2015-2017 Basis Technology Corp. * Copyright 2015-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -22,20 +22,20 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import org.openide.util.NbBundle.Messages; 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 * Model for cells in data content viewer table
*/ */
public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
@Messages({"DataContentViewerOtherCasesTableModel.case=Case", @Messages({"DataContentViewerOtherCasesTableModel.case=Case",
"DataContentViewerOtherCasesTableModel.device=Device", "DataContentViewerOtherCasesTableModel.device=Device",
"DataContentViewerOtherCasesTableModel.dataSource=Data Source", "DataContentViewerOtherCasesTableModel.dataSource=Data Source",
"DataContentViewerOtherCasesTableModel.path=Path", "DataContentViewerOtherCasesTableModel.path=Path",
"DataContentViewerOtherCasesTableModel.type=Correlation Type", "DataContentViewerOtherCasesTableModel.attribute=Matched Attribute",
"DataContentViewerOtherCasesTableModel.value=Correlation Value", "DataContentViewerOtherCasesTableModel.value=Attribute Value",
"DataContentViewerOtherCasesTableModel.known=Known", "DataContentViewerOtherCasesTableModel.known=Known",
"DataContentViewerOtherCasesTableModel.comment=Comment", "DataContentViewerOtherCasesTableModel.comment=Comment",
"DataContentViewerOtherCasesTableModel.noData=No Data.",}) "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. // If order is changed, update the CellRenderer to ensure correct row coloring.
CASE_NAME(Bundle.DataContentViewerOtherCasesTableModel_case(), 100), CASE_NAME(Bundle.DataContentViewerOtherCasesTableModel_case(), 100),
DATA_SOURCE(Bundle.DataContentViewerOtherCasesTableModel_dataSource(), 100), DATA_SOURCE(Bundle.DataContentViewerOtherCasesTableModel_dataSource(), 100),
TYPE(Bundle.DataContentViewerOtherCasesTableModel_type(), 100), ATTRIBUTE(Bundle.DataContentViewerOtherCasesTableModel_attribute(), 125),
VALUE(Bundle.DataContentViewerOtherCasesTableModel_value(), 200), VALUE(Bundle.DataContentViewerOtherCasesTableModel_value(), 200),
KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 50), KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 50),
FILE_PATH(Bundle.DataContentViewerOtherCasesTableModel_path(), 450), FILE_PATH(Bundle.DataContentViewerOtherCasesTableModel_path(), 450),
@ -68,7 +68,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
} }
}; };
List<OtherOccurrenceNodeData> nodeDataList; private final List<OtherOccurrenceNodeData> nodeDataList;
DataContentViewerOtherCasesTableModel() { DataContentViewerOtherCasesTableModel() {
nodeDataList = new ArrayList<>(); nodeDataList = new ArrayList<>();
@ -109,26 +109,41 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
return Bundle.DataContentViewerOtherCasesTableModel_noData(); return Bundle.DataContentViewerOtherCasesTableModel_noData();
} }
return mapValueById(rowIdx, TableColumns.values()[colIdx]); OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx);
TableColumns columnId = TableColumns.values()[colIdx];
if (nodeData instanceof OtherOccurrenceNodeMessageData) {
return mapNodeMessageData((OtherOccurrenceNodeMessageData) nodeData, columnId);
} }
return mapNodeInstanceData((OtherOccurrenceNodeInstanceData) nodeData, columnId);
Object getRow(int rowIdx) {
return nodeDataList.get(rowIdx);
} }
/** /**
* 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 nodeData The node message data.
* @param colId ID of column to search * @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) { private Object mapNodeMessageData(OtherOccurrenceNodeMessageData nodeData, TableColumns columnId) {
OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx); 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(); String value = Bundle.DataContentViewerOtherCasesTableModel_noData();
switch (colId) { switch (columnId) {
case CASE_NAME: case CASE_NAME:
if (null != nodeData.getCaseName()) { if (null != nodeData.getCaseName()) {
value = nodeData.getCaseName(); value = nodeData.getCaseName();
@ -147,7 +162,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
case FILE_PATH: case FILE_PATH:
value = nodeData.getFilePath(); value = nodeData.getFilePath();
break; break;
case TYPE: case ATTRIBUTE:
value = nodeData.getType(); value = nodeData.getType();
break; break;
case VALUE: case VALUE:
@ -159,10 +174,16 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
case COMMENT: case COMMENT:
value = nodeData.getComment(); value = nodeData.getComment();
break; break;
default: // This shouldn't occur! Use default "No data" value.
break;
} }
return value; return value;
} }
Object getRow(int rowIdx) {
return nodeDataList.get(rowIdx);
}
@Override @Override
public Class<String> getColumnClass(int colIdx) { public Class<String> getColumnClass(int colIdx) {
return String.class; return String.class;
@ -178,6 +199,9 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
fireTableDataChanged(); fireTableDataChanged();
} }
/**
* Clear the node data table.
*/
void clearTable() { void clearTable() {
nodeDataList.clear(); nodeDataList.clear();
fireTableDataChanged(); fireTableDataChanged();

View File

@ -1,5 +1,5 @@
/* /*
* Central Repository * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
@ -18,216 +18,9 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.contentviewer; 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;
}
} }

View File

@ -0,0 +1,233 @@
/*
* Central Repository
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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;
}
}

View File

@ -0,0 +1,34 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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;
}
}

View File

@ -387,6 +387,46 @@ abstract class AbstractSqlEamDb implements EamDb {
return eamCaseResult; 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. * Retrieves cases that are in DB.
@ -503,6 +543,48 @@ abstract class AbstractSqlEamDb implements EamDb {
return eamDataSourceResult; 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 * Return a list of data sources in the DB
* *
@ -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 @Override
public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException { public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException {
if (eamOrg == null) { if (eamOrg == null) {

View File

@ -175,6 +175,14 @@ public interface EamDb {
*/ */
CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException; 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. * Retrieves cases that are in DB.
* *
@ -200,6 +208,18 @@ public interface EamDb {
*/ */
CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException; 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 * Retrieves data sources that are in DB
* *
@ -685,4 +705,15 @@ public interface EamDb {
* @throws EamDbException * @throws EamDbException
*/ */
void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) 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;
} }

View File

@ -33,9 +33,9 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
/** /**
* Sqlite implementation of the Central Repository database. * Sqlite implementation of the Central Repository database. All methods in
* All methods in AbstractSqlEamDb that read or write to the database should * AbstractSqlEamDb that read or write to the database should be overriden here
* be overriden here and use appropriate locking. * and use appropriate locking.
*/ */
final class SqliteEamDb extends AbstractSqlEamDb { final class SqliteEamDb extends AbstractSqlEamDb {
@ -56,7 +56,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* *
* @return 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 { public synchronized static SqliteEamDb getInstance() throws EamDbException {
if (instance == null) { if (instance == null) {
@ -68,8 +69,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* *
* @throws EamDbException if the AbstractSqlEamDb class has one or more default * @throws EamDbException if the AbstractSqlEamDb class has one or more
* correlation type(s) having an invalid db table name. * default correlation type(s) having an invalid db table name.
*/ */
private SqliteEamDb() throws EamDbException { private SqliteEamDb() throws EamDbException {
dbSettings = new SqliteEamDbSettings(); dbSettings = new SqliteEamDbSettings();
@ -200,7 +201,6 @@ final class SqliteEamDb extends AbstractSqlEamDb {
return ""; return "";
} }
/** /**
* Add a new name/value pair in the db_info table. * Add a new name/value pair in the db_info table.
* *
@ -320,6 +320,24 @@ final class SqliteEamDb extends AbstractSqlEamDb {
} }
} }
/**
* 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. * Retrieves cases that are in DB.
* *
@ -353,7 +371,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Retrieves Data Source details based on data source device ID * 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 * @param dataSourceDeviceId the data source device ID number
* *
* @return The data source * @return The data source
@ -368,6 +387,25 @@ final class SqliteEamDb extends AbstractSqlEamDb {
} }
} }
/**
* 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 * Return a list of data sources in the DB
* *
@ -491,7 +529,6 @@ final class SqliteEamDb extends AbstractSqlEamDb {
} }
} }
@Override @Override
public Long getCountUniqueDataSources() throws EamDbException { public Long getCountUniqueDataSources() throws EamDbException {
try { try {
@ -551,14 +588,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
} }
/** /**
* Sets an eamArtifact instance to the given knownStatus. * Sets an eamArtifact instance to the given knownStatus. knownStatus should
* knownStatus should be BAD if the file has been tagged with a notable tag and * be BAD if the file has been tagged with a notable tag and UNKNOWN
* UNKNOWN otherwise. If eamArtifact * otherwise. If eamArtifact exists, it is updated. If eamArtifact does not
* exists, it is updated. If eamArtifact does not exist it is added with the * exist it is added with the given status.
* given status.
* *
* @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. * @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 @Override
public void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { public void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException {
@ -593,6 +630,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* *
* Gets list of matching eamArtifact instances that have knownStatus = * Gets list of matching eamArtifact instances that have knownStatus =
* "Bad". * "Bad".
*
* @param aType EamArtifact.Type to search for * @param aType EamArtifact.Type to search for
* @return List with 0 or more matching eamArtifact instances. * @return List with 0 or more matching eamArtifact instances.
* @throws EamDbException * @throws EamDbException
@ -649,6 +687,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Remove a reference set and all values contained in it. * Remove a reference set and all values contained in it.
*
* @param referenceSetID * @param referenceSetID
* @throws EamDbException * @throws EamDbException
*/ */
@ -664,6 +703,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Check if the given hash is in a specific reference set * Check if the given hash is in a specific reference set
*
* @param value * @param value
* @param referenceSetID * @param referenceSetID
* @param correlationTypeID * @param correlationTypeID
@ -695,9 +735,29 @@ final class SqliteEamDb extends AbstractSqlEamDb {
releaseSharedLock(); releaseSharedLock();
} }
} }
/** /**
* Check whether a reference set with the given name/version is in the central repo. * Process the Artifact instance in the EamDb
* Used to check for name collisions when creating reference sets. *
* @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 referenceSetName
* @param version * @param version
* @return true if a matching set is found * @return true if a matching set is found
@ -805,6 +865,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
releaseExclusiveLock(); releaseExclusiveLock();
} }
} }
/** /**
* Add a new Global Set * Add a new Global Set
* *
@ -866,8 +927,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* Add a new reference instance * Add a new reference instance
* *
* @param eamGlobalFileInstance The reference instance to add * @param eamGlobalFileInstance The reference instance to add
* @param correlationType Correlation Type that this Reference * @param correlationType Correlation Type that this Reference Instance is
* Instance is
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -1030,6 +1090,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Upgrade the schema of the database (if needed) * Upgrade the schema of the database (if needed)
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -1043,12 +1104,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
} }
/** /**
* Gets an exclusive lock (if applicable). * Gets an exclusive lock (if applicable). Will return the lock if
* Will return the lock if successful, null if unsuccessful because locking * successful, null if unsuccessful because locking isn't supported, and
* isn't supported, and throw an exception if we should have been able to get the * throw an exception if we should have been able to get the lock but failed
* lock but failed (meaning the database is in use). * (meaning the database is in use).
*
* @return the lock, or null if locking is not supported * @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 @Override
public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException { public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException {
@ -1058,35 +1121,35 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Acquire the lock that provides exclusive access to the case database. * Acquire the lock that provides exclusive access to the case database.
* Call this method in a try block with a call to * Call this method in a try block with a call to the lock release method in
* the lock release method in an associated finally block. * an associated finally block.
*/ */
private void acquireExclusiveLock() { private void acquireExclusiveLock() {
rwLock.writeLock().lock(); rwLock.writeLock().lock();
} }
/** /**
* Release the lock that provides exclusive access to the database. * Release the lock that provides exclusive access to the database. This
* This method should always be called in the finally * method should always be called in the finally block of a try block in
* block of a try block in which the lock was acquired. * which the lock was acquired.
*/ */
private void releaseExclusiveLock() { private void releaseExclusiveLock() {
rwLock.writeLock().unlock(); rwLock.writeLock().unlock();
} }
/** /**
* Acquire the lock that provides shared access to the case database. * Acquire the lock that provides shared access to the case database. Call
* Call this method in a try block with a call to the * this method in a try block with a call to the lock release method in an
* lock release method in an associated finally block. * associated finally block.
*/ */
private void acquireSharedLock() { private void acquireSharedLock() {
rwLock.readLock().lock(); rwLock.readLock().lock();
} }
/** /**
* Release the lock that provides shared access to the database. * Release the lock that provides shared access to the database. This method
* This method should always be called in the finally block * should always be called in the finally block of a try block in which the
* of a try block in which the lock was acquired. * lock was acquired.
*/ */
private void releaseSharedLock() { private void releaseSharedLock() {
rwLock.readLock().unlock(); rwLock.readLock().unlock();

View File

@ -0,0 +1,163 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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;
}
}

View File

@ -0,0 +1,212 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<Long, String> dataSourceIdToNameMap;
private boolean filterByMedia;
private boolean filterByDoc;
AbstractCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMedia, boolean filterByDoc){
this.filterByDoc = filterByDoc;
this.filterByMedia = filterByMedia;
this.dataSourceIdToNameMap = dataSourceIdMap;
}
Map<Long, String> 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<String> 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<Integer, List<CommonAttributeValue>> collateMatchesByNumberOfInstances(Map<String, CommonAttributeValue> commonFiles) {
//collate matches by number of matching instances - doing this in sql doesnt seem efficient
Map<Integer, List<CommonAttributeValue>> instanceCollatedCommonFiles = new TreeMap<>();
for(CommonAttributeValue md5Metadata : commonFiles.values()){
Integer size = md5Metadata.getInstanceCount();
if(instanceCollatedCommonFiles.containsKey(size)){
instanceCollatedCommonFiles.get(size).add(md5Metadata);
} else {
ArrayList<CommonAttributeValue> 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<String> 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<String> 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;
}
}

View File

@ -0,0 +1,61 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<Long, String> 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<Integer, List<CommonAttributeValue>> 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});
}
}

View File

@ -25,7 +25,7 @@ import org.sleuthkit.datamodel.TskData.FileKnown;
/** /**
* Provides logic for selecting common files from all data sources. * 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
@ -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 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 * @param filterByDocMimeType match only on files whose mime types can be broadly categorized as document types
*/ */
public AllDataSourcesCommonFilesAlgorithm(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { public AllIntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
} }
@ -49,9 +49,9 @@ final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadat
} }
@Override @Override
protected String buildTabTitle() { String buildTabTitle() {
final String buildCategorySelectionString = this.buildCategorySelectionString(); 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}); return String.format(titleTemplate, new Object[]{buildCategorySelectionString});
} }
} }

View File

@ -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=<html>Find files in multiple data sources in the current case.</html> CommonFilesPanel.commonFilesSearchLabel.text=<html>Find files in multiple data sources in the current case.</html>
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.text=Indicate which data sources to consider while searching for duplicates:
CommonFilesPanel.categoriesLabel.text=Indicate which file types to include in results: CommonFilesPanel.jRadioButton1.text=jRadioButton1
CommonFilesPanel.errorText.text=In order to search, you must select a file category. 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=<html>Find common files to correlate data soures or cases.</html>
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

View File

@ -0,0 +1,73 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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 <code>FileInstanceNode</code> 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;
}
}
}

View File

@ -20,18 +20,18 @@ package org.sleuthkit.autopsy.commonfilesearch;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
/** /**
* Used by the Common Files search feature to encapsulate instances of a given * Node that wraps CaseDBCommonAttributeInstance to represent a file instance stored
MD5s matched in the search. These nodes will be children of <code>Md5Node</code>s. * in the CaseDB.
*/ */
public class FileInstanceNode extends FileNode { public class CaseDBCommonAttributeInstanceNode extends FileNode {
private final String caseName;
private final String dataSource; private final String dataSource;
/** /**
@ -41,11 +41,10 @@ public class FileInstanceNode extends FileNode {
* @param fsContent * @param fsContent
* @param dataSource * @param dataSource
*/ */
public FileInstanceNode(AbstractFile fsContent, String dataSource) { public CaseDBCommonAttributeInstanceNode(AbstractFile fsContent, String caseName, String dataSource) {
super(fsContent); super(fsContent);
this.caseName = caseName;
this.dataSource = dataSource; this.dataSource = dataSource;
this.setDisplayName(fsContent.getName());
} }
@Override @Override
@ -59,11 +58,14 @@ public class FileInstanceNode extends FileNode {
return visitor.visit(this); return visitor.visit(this);
} }
String getDataSource() { public String getCase(){
return this.caseName;
}
public String getDataSource() {
return this.dataSource; return this.dataSource;
} }
@NbBundle.Messages({"FileInstanceNode.createSheet.noDescription= "})
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet sheet = new Sheet(); Sheet sheet = new Sheet();
@ -73,13 +75,14 @@ public class FileInstanceNode extends FileNode {
sheet.put(sheetSet); 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_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_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_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_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_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); this.addTagProperty(sheetSet);

View File

@ -0,0 +1,137 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<String, Long> dataSourceNameToIdMap;
CentralRepoCommonAttributeInstance(Integer attrInstId, Map<Long, String> 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<AbstractFile> 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<DisplayableItemNode> 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<String, Long> invertMap(Map<Long, String> dataSourceIdToNameMap) {
HashMap<String, Long> invertedMap = new HashMap<>();
for (Map.Entry<Long, String> entry : dataSourceIdToNameMap.entrySet()){
invertedMap.put(entry.getValue(), entry.getKey());
}
return invertedMap;
}
}

View File

@ -0,0 +1,116 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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 <code>Md5Node</code>s.
*
* Use this type for files which are not in the current case, but from the
* Central Repo. Contrast with <code>SleuthkitCase</code> 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<Action> actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(true)));
return actionsList.toArray(new Action[actionsList.size()]);
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> 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;
}
}

View File

@ -0,0 +1,301 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="fileTypeFilterButtonGroup">
</Component>
<Component class="javax.swing.ButtonGroup" name="interIntraButtonGroup">
</Component>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[412, 350]"/>
</Property>
<Property name="resizable" type="boolean" value="false"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="2"/>
</SyntheticProperties>
<Events>
<EventHandler event="windowClosed" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowClosed"/>
</Events>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,123,0,0,1,-57"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="jPanel1">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[412, 350]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="errorText" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="commonFilesSearchLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="intraCaseRadio" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="interCaseRadio" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="commonFilesSearchLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="categoriesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="selectedFileCategoriesButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="35" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="documentsCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="pictureVideoCheckbox" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="allFileCategoriesRadioButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
<Component id="layoutPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="commonFilesSearchLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="commonFilesSearchLabel2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="intraCaseRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="interCaseRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="79" max="-2" attributes="0"/>
<Component id="categoriesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="selectedFileCategoriesButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pictureVideoCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="documentsCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="allFileCategoriesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="errorText" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="98" max="-2" attributes="0"/>
<Component id="layoutPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="180" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="commonFilesSearchLabel2">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.commonFilesSearchLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="searchButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.searchButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="searchButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="cancelButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.cancelButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.cancelButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="allFileCategoriesRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="fileTypeFilterButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.allFileCategoriesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.allFileCategoriesRadioButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allFileCategoriesRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="selectedFileCategoriesButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="fileTypeFilterButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.selectedFileCategoriesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.selectedFileCategoriesButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="selectedFileCategoriesButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="pictureVideoCheckbox">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.pictureVideoCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pictureVideoCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="documentsCheckbox">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.documentsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="documentsCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="categoriesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.categoriesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="errorText">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.errorText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="commonFilesSearchLabel1">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.commonFilesSearchLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="intraCaseRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="interIntraButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.intraCaseRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="intraCaseRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="interCaseRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="interIntraButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.jRadioButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="interCaseRadioActionPerformed"/>
</Events>
</Component>
<Container class="java.awt.Panel" name="layoutPanel">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel" name="intraCasePanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="card3"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.commonfilesearch.InterCasePanel" name="interCasePanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="card2"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,720 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<CommonAttributeSearchResults, Void>() {
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<DataResultViewer> 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<Map<Long, String>, Void>() {
private void updateUi() {
final Map<Long, String> dataSourceMap = CommonAttributePanel.this.intraCasePanel.getDataSourceMap();
String[] dataSourcesNames = new String[dataSourceMap.size()];
//only enable all this stuff if we actually have datasources
if (dataSourcesNames.length > 0) {
dataSourcesNames = dataSourceMap.values().toArray(dataSourcesNames);
CommonAttributePanel.this.intraCasePanel.setDataModel(new DataSourceComboBoxModel(dataSourcesNames));
boolean multipleDataSources = this.caseHasMultipleSources();
CommonAttributePanel.this.intraCasePanel.rigForMultipleDataSources(multipleDataSources);
//TODO this should be attached to the intra/inter radio buttons
CommonAttributePanel.this.setSearchButtonEnabled(true);
}
}
private boolean caseHasMultipleSources() {
return CommonAttributePanel.this.intraCasePanel.getDataSourceMap().size() > 2;
}
@Override
protected Map<Long, String> doInBackground() throws NoCurrentCaseException, TskCoreException, SQLException {
DataSourceLoader loader = new DataSourceLoader();
return loader.getDataSourceMap();
}
@Override
protected void done() {
try {
CommonAttributePanel.this.intraCasePanel.setDataSourceMap(this.get());
updateUi();
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex);
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupDataSources_done_interupted());
} catch (ExecutionException ex) {
String errorMessage;
Throwable inner = ex.getCause();
if (inner instanceof TskCoreException) {
LOGGER.log(Level.SEVERE, "Failed to load data sources from database.", ex);
errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_tskCoreException();
} else if (inner instanceof NoCurrentCaseException) {
LOGGER.log(Level.SEVERE, "Current case has been closed.", ex);
errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_noCurrentCaseException();
} else if (inner instanceof SQLException) {
LOGGER.log(Level.SEVERE, "Unable to query db for data sources.", ex);
errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_sqlException();
} else {
LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog panel.", ex);
errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_exception();
}
MessageNotifyUtil.Message.error(errorMessage);
}
}
}.execute();
}
@NbBundle.Messages({
"CommonFilesPanel.setupCases.done.interruptedException=Something went wrong building the Common Files Search dialog box.",
"CommonFilesPanel.setupCases.done.exeutionException=Unexpected exception loading cases."})
private void setupCases() {
new SwingWorker<Map<Integer, String>, Void>() {
private void updateUi() {
final Map<Integer, String> caseMap = CommonAttributePanel.this.interCasePanel.getCaseMap();
String[] caseNames = new String[caseMap.size()];
if (caseNames.length > 0) {
caseNames = caseMap.values().toArray(caseNames);
CommonAttributePanel.this.interCasePanel.setCaseList(new DataSourceComboBoxModel(caseNames));
boolean multipleCases = this.centralRepoHasMultipleCases();
CommonAttributePanel.this.interCasePanel.rigForMultipleCases(multipleCases);
} else {
CommonAttributePanel.this.disableIntercaseSearch();
}
}
private Map<Integer, String> mapDataSources(List<CorrelationCase> cases) throws EamDbException {
Map<Integer, String> casemap = new HashMap<>();
CorrelationCase currentCorCase = EamDb.getInstance().getCase(Case.getCurrentCase());
for (CorrelationCase correlationCase : cases) {
if (currentCorCase.getID() != correlationCase.getID()) { // if not the current Case
casemap.put(correlationCase.getID(), correlationCase.getDisplayName());
}
}
return casemap;
}
@Override
protected Map<Integer, String> doInBackground() throws EamDbException {
List<CorrelationCase> dataSources = EamDb.getInstance().getCases();
Map<Integer, String> caseMap = mapDataSources(dataSources);
return caseMap;
}
@Override
protected void done() {
try {
Map<Integer, String> cases = this.get();
CommonAttributePanel.this.interCasePanel.setCaseMap(cases);
this.updateUi();
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex);
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupCases_done_interruptedException());
} catch (ExecutionException ex) {
LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog.", ex);
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupCases_done_exeutionException());
}
}
private boolean centralRepoHasMultipleCases() {
return CommonAttributePanel.this.interCasePanel.centralRepoHasMultipleCases();
}
}.execute();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
fileTypeFilterButtonGroup = new javax.swing.ButtonGroup();
interIntraButtonGroup = new javax.swing.ButtonGroup();
jPanel1 = new javax.swing.JPanel();
commonFilesSearchLabel2 = new javax.swing.JLabel();
searchButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton();
allFileCategoriesRadioButton = new javax.swing.JRadioButton();
selectedFileCategoriesButton = new javax.swing.JRadioButton();
pictureVideoCheckbox = new javax.swing.JCheckBox();
documentsCheckbox = new javax.swing.JCheckBox();
categoriesLabel = new javax.swing.JLabel();
errorText = new javax.swing.JLabel();
commonFilesSearchLabel1 = new javax.swing.JLabel();
intraCaseRadio = new javax.swing.JRadioButton();
interCaseRadio = new javax.swing.JRadioButton();
layoutPanel = new java.awt.Panel();
intraCasePanel = new org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel();
interCasePanel = new org.sleuthkit.autopsy.commonfilesearch.InterCasePanel();
setMinimumSize(new java.awt.Dimension(412, 350));
setPreferredSize(new java.awt.Dimension(412, 350));
setResizable(false);
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosed(java.awt.event.WindowEvent evt) {
formWindowClosed(evt);
}
});
jPanel1.setPreferredSize(new java.awt.Dimension(412, 350));
org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel2, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel2.text")); // NOI18N
commonFilesSearchLabel2.setFocusable(false);
org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.searchButton.text")); // NOI18N
searchButton.setEnabled(false);
searchButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
searchButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
searchButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.cancelButton.text")); // NOI18N
cancelButton.setActionCommand(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.cancelButton.actionCommand")); // NOI18N
cancelButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
cancelButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelButtonActionPerformed(evt);
}
});
fileTypeFilterButtonGroup.add(allFileCategoriesRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.allFileCategoriesRadioButton.text")); // NOI18N
allFileCategoriesRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N
allFileCategoriesRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allFileCategoriesRadioButtonActionPerformed(evt);
}
});
fileTypeFilterButtonGroup.add(selectedFileCategoriesButton);
selectedFileCategoriesButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.selectedFileCategoriesButton.text")); // NOI18N
selectedFileCategoriesButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.selectedFileCategoriesButton.toolTipText")); // NOI18N
selectedFileCategoriesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
selectedFileCategoriesButtonActionPerformed(evt);
}
});
pictureVideoCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(pictureVideoCheckbox, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.pictureVideoCheckbox.text")); // NOI18N
pictureVideoCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pictureVideoCheckboxActionPerformed(evt);
}
});
documentsCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(documentsCheckbox, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.documentsCheckbox.text")); // NOI18N
documentsCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
documentsCheckboxActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(categoriesLabel, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.categoriesLabel.text")); // NOI18N
categoriesLabel.setName(""); // NOI18N
errorText.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.errorText.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel1.text")); // NOI18N
commonFilesSearchLabel1.setFocusable(false);
interIntraButtonGroup.add(intraCaseRadio);
intraCaseRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(intraCaseRadio, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.intraCaseRadio.text")); // NOI18N
intraCaseRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
intraCaseRadioActionPerformed(evt);
}
});
interIntraButtonGroup.add(interCaseRadio);
org.openide.awt.Mnemonics.setLocalizedText(interCaseRadio, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonFilesPanel.jRadioButton2.text")); // NOI18N
interCaseRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
interCaseRadioActionPerformed(evt);
}
});
layoutPanel.setLayout(new java.awt.CardLayout());
layoutPanel.add(intraCasePanel, "card3");
layoutPanel.add(interCasePanel, "card2");
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(searchButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(errorText))
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(commonFilesSearchLabel2)
.addComponent(intraCaseRadio)
.addComponent(interCaseRadio)
.addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(categoriesLabel)
.addComponent(selectedFileCategoriesButton)))
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(35, 35, 35)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(documentsCheckbox)
.addComponent(pictureVideoCheckbox)))
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(allFileCategoriesRadioButton)))
.addContainerGap())
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(20, 20, 20)
.addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(10, 10, 10)))
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(commonFilesSearchLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(intraCaseRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(interCaseRadio)
.addGap(79, 79, 79)
.addComponent(categoriesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(selectedFileCategoriesButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pictureVideoCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(documentsCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(allFileCategoriesRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(searchButton)
.addComponent(cancelButton)
.addComponent(errorText))
.addContainerGap())
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addGap(98, 98, 98)
.addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(180, 180, 180)))
);
getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
search();
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_searchButtonActionPerformed
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_cancelButtonActionPerformed
private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed
this.manageCheckBoxState();
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed
private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed
this.manageCheckBoxState();
}//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed
private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_pictureVideoCheckboxActionPerformed
private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_documentsCheckboxActionPerformed
private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed
((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel);
handleIntraCaseSearchCriteriaChanged();
}//GEN-LAST:event_intraCaseRadioActionPerformed
public void handleIntraCaseSearchCriteriaChanged() {
if (this.areIntraCaseSearchCriteriaMet()) {
this.searchButton.setEnabled(true);
this.hideErrorMessages();
} else {
this.searchButton.setEnabled(false);
this.hideErrorMessages();
this.showIntraCaseErrorMessage();
}
}
private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed
((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel);
handleInterCaseSearchCriteriaChanged();
}//GEN-LAST:event_interCaseRadioActionPerformed
private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_formWindowClosed
public void handleInterCaseSearchCriteriaChanged() {
if (this.areInterCaseSearchCriteriaMet()) {
this.searchButton.setEnabled(true);
this.hideErrorMessages();
} else {
this.searchButton.setEnabled(false);
this.hideErrorMessages();
this.showInterCaseErrorMessage();
}
}
private void toggleErrorTextAndSearchBox() {
if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) {
this.searchButton.setEnabled(false);
this.errorText.setVisible(true);
} else {
this.searchButton.setEnabled(true);
this.errorText.setVisible(false);
}
}
private void manageCheckBoxState() {
this.pictureViewCheckboxState = this.pictureVideoCheckbox.isSelected();
this.documentsCheckboxState = this.documentsCheckbox.isSelected();
if (this.allFileCategoriesRadioButton.isSelected()) {
this.pictureVideoCheckbox.setEnabled(false);
this.documentsCheckbox.setEnabled(false);
}
if (this.selectedFileCategoriesButton.isSelected()) {
this.pictureVideoCheckbox.setSelected(this.pictureViewCheckboxState);
this.documentsCheckbox.setSelected(this.documentsCheckboxState);
this.pictureVideoCheckbox.setEnabled(true);
this.documentsCheckbox.setEnabled(true);
this.toggleErrorTextAndSearchBox();
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton allFileCategoriesRadioButton;
private javax.swing.JButton cancelButton;
private javax.swing.JLabel categoriesLabel;
private javax.swing.JLabel commonFilesSearchLabel1;
private javax.swing.JLabel commonFilesSearchLabel2;
private javax.swing.JCheckBox documentsCheckbox;
private javax.swing.JLabel errorText;
private javax.swing.ButtonGroup fileTypeFilterButtonGroup;
private org.sleuthkit.autopsy.commonfilesearch.InterCasePanel interCasePanel;
private javax.swing.JRadioButton interCaseRadio;
private javax.swing.ButtonGroup interIntraButtonGroup;
private org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel intraCasePanel;
private javax.swing.JRadioButton intraCaseRadio;
private javax.swing.JPanel jPanel1;
private java.awt.Panel layoutPanel;
private javax.swing.JCheckBox pictureVideoCheckbox;
private javax.swing.JButton searchButton;
private javax.swing.JRadioButton selectedFileCategoriesButton;
// End of variables declaration//GEN-END:variables
void setSearchButtonEnabled(boolean enabled) {
this.searchButton.setEnabled(enabled);
}
private boolean areIntraCaseSearchCriteriaMet() {
return this.intraCasePanel.areSearchCriteriaMet();
}
private boolean areInterCaseSearchCriteriaMet() {
return this.interCasePanel.areSearchCriteriaMet();
}
private void hideErrorMessages() {
this.errorText.setVisible(false);
}
private void showIntraCaseErrorMessage() {
this.errorText.setText(this.intraCasePanel.getErrorMessage());
this.errorText.setVisible(true);
}
private void showInterCaseErrorMessage() {
this.errorText.setText(this.interCasePanel.getErrorMessage());
this.errorText.setVisible(true);
}
}

View File

@ -27,13 +27,14 @@ import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
/** /**
* Wrapper node for <code>Md5Node</code> used to display common files search * Top-level node to store common file search results. Current structure is:
* results in the top right pane. Calls <code>InstanceCountNodeFactory</code>. * - node for number of matches
* -- node for MD5/commmon attribute
* --- node for instance.
*/ */
final public class CommonFilesNode extends DisplayableItemNode { final public class CommonAttributeSearchResultRootNode extends DisplayableItemNode {
CommonAttributeSearchResultRootNode(CommonAttributeSearchResults metadataList) {
CommonFilesNode(CommonFilesMetadata metadataList) {
super(Children.create(new InstanceCountNodeFactory(metadataList), true)); super(Children.create(new InstanceCountNodeFactory(metadataList), true));
} }
@ -64,27 +65,27 @@ final public class CommonFilesNode extends DisplayableItemNode {
*/ */
static class InstanceCountNodeFactory extends ChildFactory<Integer>{ static class InstanceCountNodeFactory extends ChildFactory<Integer>{
private final CommonFilesMetadata metadata; private final CommonAttributeSearchResults searchResults;
/** /**
* Build a factory which converts a <code>CommonFilesMetadata</code> * Build a factory which converts a <code>CommonAttributeSearchResults</code>
* object into <code>DisplayableItemNode</code>s. * object into <code>DisplayableItemNode</code>s.
* @param metadata * @param searchResults
*/ */
InstanceCountNodeFactory(CommonFilesMetadata metadata){ InstanceCountNodeFactory(CommonAttributeSearchResults searchResults){
this.metadata = metadata; this.searchResults = searchResults;
} }
@Override @Override
protected boolean createKeys(List<Integer> list) { protected boolean createKeys(List<Integer> list) {
list.addAll(this.metadata.getMetadata().keySet()); list.addAll(this.searchResults.getMetadata().keySet());
return true; return true;
} }
@Override @Override
protected Node createNodeForKey(Integer instanceCount){ protected Node createNodeForKey(Integer instanceCount){
List<Md5Metadata> md5Metadata = this.metadata.getMetadataForMd5(instanceCount); List<CommonAttributeValue> attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount);
return new InstanceCountNode(instanceCount, md5Metadata); return new InstanceCountNode(instanceCount, attributeValues);
} }
} }
} }

View File

@ -0,0 +1,82 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.Collections;
import java.util.List;
import java.util.Map;
/**
* Stores the results from the various types of common attribute searching
* Stores results based on how they are currently displayed in the UI
*/
final public class CommonAttributeSearchResults {
// maps instance count to list of attribute values.
private final Map<Integer, List<CommonAttributeValue>> instanceCountToAttributeValues;
/**
* Create a values object which can be handed off to the node factories.
*
* @param values list of CommonAttributeValue indexed by size of
* CommonAttributeValue
*/
CommonAttributeSearchResults(Map<Integer, List<CommonAttributeValue>> metadata){
this.instanceCountToAttributeValues = metadata;
}
/**
* Find the child node whose children have the specified number of children.
*
* This is a convenience method - you can also iterate over
* <code>getValues()</code>.
*
* @param isntanceCound key
* @return list of values which represent matches
*/
List<CommonAttributeValue> getAttributeValuesForInstanceCount(Integer instanceCount) {
return this.instanceCountToAttributeValues.get(instanceCount);
}
/**
* Get an unmodifiable collection of values, indexed by number of
* grandchildren, which represents the common attributes found in the
* search.
* @return map of sizes of children to list of matches
*/
public Map<Integer, List<CommonAttributeValue>> getMetadata() {
return Collections.unmodifiableMap(this.instanceCountToAttributeValues);
}
/**
* How many distinct common files exist for this search results?
* @return number of common files
*/
public int size() {
int count = 0;
for (List<CommonAttributeValue> data : this.instanceCountToAttributeValues.values()) {
for(CommonAttributeValue md5 : data){
count += md5.getInstanceCount();
}
}
return count;
}
}

View File

@ -19,50 +19,70 @@
*/ */
package org.sleuthkit.autopsy.commonfilesearch; package org.sleuthkit.autopsy.commonfilesearch;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
/** /**
* Encapsulates data required to instantiate an <code>Md5Node</code>. * Defines a value that was in the common file search results
* as well as information about its instances.
*/ */
final public class Md5Metadata { final public class CommonAttributeValue {
private final String md5; private final String md5;
private final List<FileInstanceMetadata> fileInstances; private final List<AbstractCommonAttributeInstance> fileInstances;
Md5Metadata(String md5, List<FileInstanceMetadata> fileInstances){ CommonAttributeValue(String md5, List<AbstractCommonAttributeInstance> fileInstances) {
this.md5 = md5; this.md5 = md5;
this.fileInstances = fileInstances; this.fileInstances = fileInstances;
} }
public String getMd5(){ CommonAttributeValue(String md5) {
this.md5 = md5;
this.fileInstances = new ArrayList<>();
}
public String getValue() {
return this.md5; return this.md5;
} }
void addFileInstanceMetadata(FileInstanceMetadata metadata){
this.fileInstances.add(metadata);
}
public Collection<FileInstanceMetadata> getMetadata(){
return Collections.unmodifiableCollection(this.fileInstances);
}
/** /**
* How many distinct file instances exist for the MD5 represented by this object? * concatenate cases this value was seen into a single string
* @return number of instances *
* @return
*/ */
public int size(){ public String getCases() {
return this.fileInstances.size(); return this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", "));
} }
public String getDataSources() { public String getDataSources() {
Set<String> sources = new HashSet<>(); Set<String> sources = new HashSet<>();
for(FileInstanceMetadata data : this.fileInstances){ for (AbstractCommonAttributeInstance data : this.fileInstances) {
sources.add(data.getDataSourceName()); sources.add(data.getDataSource());
} }
return String.join(", ", sources); return String.join(", ", sources);
} }
void addInstance(AbstractCommonAttributeInstance metadata) {
this.fileInstances.add(metadata);
}
public Collection<AbstractCommonAttributeInstance> getInstances() {
return Collections.unmodifiableCollection(this.fileInstances);
}
/**
* How many distinct file instances exist for the MD5 represented by this
* object?
*
* @return number of instances
*/
public int getInstanceCount() {
return this.fileInstances.size();
}
} }

View File

@ -20,34 +20,24 @@
package org.sleuthkit.autopsy.commonfilesearch; package org.sleuthkit.autopsy.commonfilesearch;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import org.openide.nodes.ChildFactory; import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
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.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Represents a common files match - two or more files which appear to be the * Represents the layer in the tree for the value (such as MD5) that was in multiple places.
* same file and appear as children of this node. This node will simply contain * Children are instances of that value.
* the MD5 of the matched files, the data sources those files were found within,
* and a count of the instances represented by the md5.
*/ */
public class Md5Node extends DisplayableItemNode { public class CommonAttributeValueNode extends DisplayableItemNode {
private static final Logger LOGGER = Logger.getLogger(Md5Node.class.getName()); private final String value;
private final String md5Hash;
private final int commonFileCount; private final int commonFileCount;
private final String cases;
private final String dataSources; private final String dataSources;
@NbBundle.Messages({ @NbBundle.Messages({
@ -57,15 +47,17 @@ public class Md5Node extends DisplayableItemNode {
* Create a Match node whose children will all have this object in common. * Create a Match node whose children will all have this object in common.
* @param data the common feature, and the children * @param data the common feature, and the children
*/ */
public Md5Node(Md5Metadata data) { public CommonAttributeValueNode(CommonAttributeValue data) {
super(Children.create( super(Children.create(
new FileInstanceNodeFactory(data), true)); new FileInstanceNodeFactory(data), true));
this.commonFileCount = data.size(); this.commonFileCount = data.getInstanceCount();
this.cases = data.getCases();
// @@ We seem to be doing this string concat twice. We also do it in getDataSources()
this.dataSources = String.join(", ", data.getDataSources()); this.dataSources = String.join(", ", data.getDataSources());
this.md5Hash = data.getMd5(); this.value = data.getValue();
this.setDisplayName(String.format(Bundle.Md5Node_Md5Node_format(), this.md5Hash)); this.setDisplayName(String.format(Bundle.Md5Node_Md5Node_format(), this.value));
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS
} }
@ -77,6 +69,10 @@ public class Md5Node extends DisplayableItemNode {
return this.commonFileCount; return this.commonFileCount;
} }
String getCases(){
return this.cases;
}
/** /**
* Datasources where these matches occur. * Datasources where these matches occur.
* @return string delimited list of sources * @return string delimited list of sources
@ -89,8 +85,8 @@ public class Md5Node extends DisplayableItemNode {
* MD5 which is common to these matches * MD5 which is common to these matches
* @return string md5 hash * @return string md5 hash
*/ */
public String getMd5() { public String getValue() {
return this.md5Hash; return this.value;
} }
@NbBundle.Messages({"Md5Node.createSheet.noDescription= "}) @NbBundle.Messages({"Md5Node.createSheet.noDescription= "})
@ -129,35 +125,28 @@ public class Md5Node extends DisplayableItemNode {
} }
/** /**
* Child generator for <code>FileInstanceNode</code> of <code>Md5Node</code>. * Child generator for <code>SleuthkitCaseFileInstanceNode</code> of
* <code>CommonAttributeValueNode</code>.
*/ */
static class FileInstanceNodeFactory extends ChildFactory<FileInstanceMetadata> { static class FileInstanceNodeFactory extends ChildFactory<AbstractCommonAttributeInstance> {
private final Md5Metadata descendants; private final CommonAttributeValue descendants;
FileInstanceNodeFactory(Md5Metadata descendants) { FileInstanceNodeFactory(CommonAttributeValue descendants) {
this.descendants = descendants; this.descendants = descendants;
} }
@Override @Override
protected Node createNodeForKey(FileInstanceMetadata file) { protected boolean createKeys(List<AbstractCommonAttributeInstance> list) {
try { list.addAll(this.descendants.getInstances());
Case currentCase = Case.getCurrentCaseThrows(); return true;
SleuthkitCase tskDb = currentCase.getSleuthkitCase();
AbstractFile abstractFile = tskDb.findAllFilesWhere(String.format("obj_id in (%s)", file.getObjectId())).get(0);
return new FileInstanceNode(abstractFile, file.getDataSourceName());
} catch (NoCurrentCaseException | TskCoreException ex) {
LOGGER.log(Level.SEVERE, String.format("Unable to create node for file with obj_id: %s.", new Object[]{file.getObjectId()}), ex);
}
return null;
} }
@Override @Override
protected boolean createKeys(List<FileInstanceMetadata> list) { protected Node[] createNodesForKey(AbstractCommonAttributeInstance searchResult) {
list.addAll(this.descendants.getMetadata()); return searchResult.generateNodes();
return true;
}
} }
}
} }

View File

@ -31,18 +31,18 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
/** /**
* <code>DataResultViewerTable</code> which overrides the default column header * <code>DataResultViewerTable</code> which overrides the default column
* width calculations. The <code>CommonFilesSearchResultsViewerTable</code> * header width calculations. The <code>CommonAttributesSearchResultsViewerTable</code>
* presents multiple tiers of data which are not always present and it may not * presents multiple tiers of data which are not always present and it may not
* make sense to try to calculate the column widths for such tables by sampling * make sense to try to calculate the column widths for such tables by sampling
* rows and looking for wide cells. Rather, we just pick some reasonable values. * rows and looking for wide cells. Rather, we just pick some reasonable values.
*/ */
public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable { public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTable {
private static final Map<String, Integer> COLUMN_WIDTHS; private static final Map<String, Integer> COLUMN_WIDTHS;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(CommonFilesSearchResultsViewerTable.class.getName()); private static final Logger LOGGER = Logger.getLogger(CommonAttributesSearchResultsViewerTable.class.getName());
private static final int DEFAULT_WIDTH = 100; private static final int DEFAULT_WIDTH = 100;
@ -51,6 +51,7 @@ public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable {
map.put(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), 260); map.put(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), 260);
map.put(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), 65); map.put(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), 65);
map.put(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), 300); map.put(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), 300);
map.put(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), 200);
map.put(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), 200); map.put(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), 200);
map.put(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), 100); map.put(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), 100);
map.put(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), 130); map.put(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), 130);
@ -60,11 +61,13 @@ public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable {
} }
@NbBundle.Messages({ @NbBundle.Messages({
"CommonFilesSearchResultsViewerTable.noDescText= ",
"CommonFilesSearchResultsViewerTable.filesColLbl=Files", "CommonFilesSearchResultsViewerTable.filesColLbl=Files",
"CommonFilesSearchResultsViewerTable.instancesColLbl=Instances", "CommonFilesSearchResultsViewerTable.instancesColLbl=Instances",
"CommonFilesSearchResultsViewerTable.pathColLbl=Parent Path", "CommonFilesSearchResultsViewerTable.pathColLbl=Parent Path",
"CommonFilesSearchResultsViewerTable.hashsetHitsColLbl=Hash Set Hits", "CommonFilesSearchResultsViewerTable.hashsetHitsColLbl=Hash Set Hits",
"CommonFilesSearchResultsViewerTable.dataSourceColLbl=Data Source(s)", "CommonFilesSearchResultsViewerTable.caseColLbl1=Case",
"CommonFilesSearchResultsViewerTable.dataSourceColLbl=Data Source",
"CommonFilesSearchResultsViewerTable.mimeTypeColLbl=MIME Type", "CommonFilesSearchResultsViewerTable.mimeTypeColLbl=MIME Type",
"CommonFilesSearchResultsViewerTable.tagsColLbl1=Tags" "CommonFilesSearchResultsViewerTable.tagsColLbl1=Tags"
}) })

View File

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
<Property name="size" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[340, 320]"/>
</Property>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="true"/>
</SyntheticProperties>
<Events>
<EventHandler event="windowClosed" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowClosed"/>
</Events>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="commonFilesPanel1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="commonFilesPanel1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="org.sleuthkit.autopsy.commonfilesearch.CommonFilesPanel" name="commonFilesPanel1">
</Component>
</SubComponents>
</Form>

View File

@ -1,94 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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 javax.swing.JFrame;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
/**
* Dialog box for configuring and running common files search.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public final class CommonFilesDialog extends javax.swing.JDialog {
private static final long serialVersionUID = 1L;
/**
* Creates new form CommonFilesDialog
*/
@NbBundle.Messages({
"CommonFilesDialog.frame.title=Find Common Files",
"CommonFilesDialog.frame.msg=Find Common Files"})
public CommonFilesDialog() {
super(new JFrame(Bundle.CommonFilesDialog_frame_title()),
Bundle.CommonFilesDialog_frame_msg(), true);
initComponents();
this.setResizable(false);
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
commonFilesPanel1 = new org.sleuthkit.autopsy.commonfilesearch.CommonFilesPanel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setSize(new java.awt.Dimension(340, 320));
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosed(java.awt.event.WindowEvent evt) {
formWindowClosed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(commonFilesPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(commonFilesPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))
);
pack();
setLocationRelativeTo(null);
}// </editor-fold>//GEN-END:initComponents
private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_formWindowClosed
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.commonfilesearch.CommonFilesPanel commonFilesPanel1;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,75 +0,0 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.Collections;
import java.util.List;
import java.util.Map;
/**
* Utility and wrapper model around data required for Common Files Search results.
* Subclass this to implement different selections of files from the case.
*/
final public class CommonFilesMetadata {
private final Map<Integer, List<Md5Metadata>> metadata;
/**
* Create a metadata object which can be handed off to the node
* factories.
*
* @param metadata list of Md5Metadata indexed by size of Md5Metadata
*/
CommonFilesMetadata(Map<Integer, List<Md5Metadata>> metadata){
this.metadata = metadata;
}
/**
* Find the meta data for the given md5.
*
* This is a convenience method - you can also iterate over
* <code>getMetadata()</code>.
*
* @param md5 key
* @return
*/
List<Md5Metadata> getMetadataForMd5(Integer instanceCount) {
return this.metadata.get(instanceCount);
}
public Map<Integer, List<Md5Metadata>> getMetadata() {
return Collections.unmodifiableMap(this.metadata);
}
/**
* How many distinct file instances exist for this metadata?
* @return number of file instances
*/
public int size() {
int count = 0;
for (List<Md5Metadata> data : this.metadata.values()) {
for(Md5Metadata md5 : data){
count += md5.size();
}
}
return count;
}
}

View File

@ -1,277 +0,0 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
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.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.datamodel.HashUtility;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
* Generates a <code>List<CommonFilesMetadata></code> when
* <code>findCommonFiles()</code> is called, which organizes files by md5 to
* prepare to display in viewer.
*
* This entire thing runs on a background thread where exceptions are handled.
*/
@SuppressWarnings("PMD.AbstractNaming")
public abstract class CommonFilesMetadataBuilder {
private final Map<Long, String> dataSourceIdToNameMap;
private final boolean filterByMedia;
private final boolean filterByDoc;
private static final String FILTER_BY_MIME_TYPES_WHERE_CLAUSE = " and mime_type in (%s)"; //NON-NLS // where %s is csv list of mime_types to filter on
/*
* 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"
*/
private static final Set<String> 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"
*/
private static final Set<String> 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());
/**
* Subclass this to implement different algorithms for getting common files.
*
* @param dataSourceIdMap a map of obj_id to datasource name
* @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
*/
CommonFilesMetadataBuilder(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
dataSourceIdToNameMap = dataSourceIdMap;
filterByMedia = filterByMediaMimeType;
filterByDoc = filterByDocMimeType;
}
/**
* Use this as a prefix when building the SQL select statement.
*
* <ul>
* <li>You only have to specify the WHERE clause if you use this.</li>
* <li>If you do not use this string, you must use at least the columns
* selected below, in that order.</li>
* </ul>
*/
static final String SELECT_PREFIX = "SELECT obj_id, md5, data_source_obj_id from tsk_files where"; //NON-NLS
/**
* Should build a SQL SELECT statement to be passed to
* SleuthkitCase.executeQuery(sql) which will select the desired file ids
* and MD5 hashes.
*
* The statement should select obj_id, md5, data_source_obj_id in that
* order.
*
* @return sql string select statement
*/
protected abstract String buildSqlSelectStatement();
/**
* Generate a meta data object which encapsulates everything need to add the
* tree table tab to the top component.
*
* @return a data object with all of the matched files in a hierarchical
* format
* @throws TskCoreException
* @throws NoCurrentCaseException
* @throws SQLException
*/
public CommonFilesMetadata findCommonFiles() throws TskCoreException, NoCurrentCaseException, SQLException {
Map<String, Md5Metadata> commonFiles = new HashMap<>();
SleuthkitCase sleuthkitCase = Case.getCurrentCaseThrows().getSleuthkitCase();
String selectStatement = this.buildSqlSelectStatement();
try (
CaseDbQuery query = sleuthkitCase.executeQuery(selectStatement);
ResultSet resultSet = query.getResultSet()) {
while (resultSet.next()) {
Long objectId = resultSet.getLong(1);
String md5 = resultSet.getString(2);
Long dataSourceId = resultSet.getLong(3);
String dataSource = this.dataSourceIdToNameMap.get(dataSourceId);
if (md5 == null || HashUtility.isNoDataMd5(md5)) {
continue;
}
if (commonFiles.containsKey(md5)) {
final Md5Metadata md5Metadata = commonFiles.get(md5);
md5Metadata.addFileInstanceMetadata(new FileInstanceMetadata(objectId, dataSource));
} else {
final List<FileInstanceMetadata> fileInstances = new ArrayList<>();
fileInstances.add(new FileInstanceMetadata(objectId, dataSource));
Md5Metadata md5Metadata = new Md5Metadata(md5, fileInstances);
commonFiles.put(md5, md5Metadata);
}
}
}
Map<Integer, List<Md5Metadata>> instanceCollatedCommonFiles = new TreeMap<>();
for(Md5Metadata md5Metadata : commonFiles.values()){
Integer size = md5Metadata.size();
if(instanceCollatedCommonFiles.containsKey(size)){
instanceCollatedCommonFiles.get(size).add(md5Metadata);
} else {
ArrayList<Md5Metadata> value = new ArrayList<>();
value.add(md5Metadata);
instanceCollatedCommonFiles.put(size, value);
}
}
return new CommonFilesMetadata(instanceCollatedCommonFiles);
}
/**
* Should be used by subclasses, in their
* <code>buildSqlSelectStatement()</code> function to create an SQL boolean
* expression which will filter our matches based on mime type. The
* expression will be conjoined to base query with an AND operator.
*
* @return sql fragment of the form:
* 'and "mime_type" in ( [comma delimited list of mime types] )'
* or empty string in the event that no types to filter on were given.
*/
String determineMimeTypeFilter() {
Set<String> mimeTypesToFilterOn = new HashSet<>();
String mimeTypeString = "";
if (filterByMedia) {
mimeTypesToFilterOn.addAll(MEDIA_PICS_VIDEO_MIME_TYPES);
}
if (filterByDoc) {
mimeTypesToFilterOn.addAll(TEXT_FILES_MIME_TYPES);
}
StringBuilder mimeTypeFilter = new StringBuilder(mimeTypesToFilterOn.size());
if (!mimeTypesToFilterOn.isEmpty()) {
for (String mimeType : mimeTypesToFilterOn) {
mimeTypeFilter.append("'").append(mimeType).append("',");
}
mimeTypeString = mimeTypeFilter.toString().substring(0, mimeTypeFilter.length() - 1);
mimeTypeString = String.format(FILTER_BY_MIME_TYPES_WHERE_CLAUSE, new Object[]{mimeTypeString});
}
return mimeTypeString;
}
@NbBundle.Messages({
"CommonFilesMetadataBuilder.buildTabTitle.titleAll=Common Files (All Data Sources, %s)",
"CommonFilesMetadataBuilder.buildTabTitle.titleSingle=Common Files (Match Within Data Source: %s, %s)"
})
protected abstract String buildTabTitle();
@NbBundle.Messages({
"CommonFilesMetadataBuilder.buildCategorySelectionString.doc=Documents",
"CommonFilesMetadataBuilder.buildCategorySelectionString.media=Media",
"CommonFilesMetadataBuilder.buildCategorySelectionString.all=All File Categories"
})
protected String buildCategorySelectionString() {
if (!this.filterByDoc && !this.filterByMedia) {
return Bundle.CommonFilesMetadataBuilder_buildCategorySelectionString_all();
} else {
List<String> filters = new ArrayList<>();
if (this.filterByDoc) {
filters.add(Bundle.CommonFilesMetadataBuilder_buildCategorySelectionString_doc());
}
if (this.filterByMedia) {
filters.add(Bundle.CommonFilesMetadataBuilder_buildCategorySelectionString_media());
}
return String.join(", ", filters);
}
}
}

View File

@ -1,261 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="dataSourcesButtonGroup">
</Component>
<Component class="javax.swing.ButtonGroup" name="fileTypeFilterButtonGroup">
</Component>
</NonVisualComponents>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="errorText" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="categoriesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="commonFilesSearchLabel" min="-2" max="-2" attributes="0"/>
<Component id="dataSourceLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="6" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
<Component id="selectDataSourceComboBox" min="-2" pref="261" max="-2" attributes="0"/>
</Group>
<Component id="withinDataSourceRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="allDataSourcesRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="allFileCategoriesRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="selectedFileCategoriesButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="21" pref="21" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="pictureVideoCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="documentsCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</Group>
<EmptySpace min="-2" pref="19" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="commonFilesSearchLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Component id="dataSourceLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="allDataSourcesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="withinDataSourceRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="selectDataSourceComboBox" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="categoriesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="selectedFileCategoriesButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pictureVideoCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="documentsCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="allFileCategoriesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="errorText" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="searchButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.searchButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="searchButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="allDataSourcesRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="dataSourcesButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.allDataSourcesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allDataSourcesRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="withinDataSourceRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="dataSourcesButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.withinDataSourceRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="withinDataSourceRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="selectDataSourceComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="dataSourcesList" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="selectDataSourceComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="commonFilesSearchLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.commonFilesSearchLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="cancelButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.cancelButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.cancelButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="allFileCategoriesRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="fileTypeFilterButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.allFileCategoriesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.allFileCategoriesRadioButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allFileCategoriesRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="selectedFileCategoriesButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="fileTypeFilterButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.selectedFileCategoriesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.selectedFileCategoriesButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="selectedFileCategoriesButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="pictureVideoCheckbox">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.pictureVideoCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pictureVideoCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="documentsCheckbox">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.documentsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="documentsCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="dataSourceLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="categoriesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.categoriesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="errorText">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.errorText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -1,580 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.ComboBoxModel;
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.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
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 CommonFilesPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private static final Long NO_DATA_SOURCE_SELECTED = -1L;
private ComboBoxModel<String> dataSourcesList = new DataSourceComboBoxModel();
private Map<Long, String> dataSourceMap;
private static final Logger LOGGER = Logger.getLogger(CommonFilesPanel.class.getName());
private boolean singleDataSource = false;
private String selectedDataSource = "";
private boolean pictureViewCheckboxState;
private boolean documentsCheckboxState;
/**
* Creates new form CommonFilesPanel
*/
@NbBundle.Messages({
"CommonFilesPanel.title=Common Files Panel",
"CommonFilesPanel.exception=Unexpected Exception loading DataSources."})
public CommonFilesPanel() {
initComponents();
this.setupDataSources();
this.errorText.setVisible(false);
}
/**
* Sets up the data sources dropdown and returns the data sources map for
* future usage.
*
* @return a mapping of data source ids to data source names
*/
@NbBundle.Messages({
"CommonFilesPanel.buildDataSourceMap.done.tskCoreException=Unable to run query against DB.",
"CommonFilesPanel.buildDataSourceMap.done.noCurrentCaseException=Unable to open case file.",
"CommonFilesPanel.buildDataSourceMap.done.exception=Unexpected exception building data sources map.",
"CommonFilesPanel.buildDataSourceMap.done.interupted=Something went wrong building the Common Files Search dialog box.",
"CommonFilesPanel.buildDataSourceMap.done.sqlException=Unable to query db for data sources.",
"CommonFilesPanel.buildDataSourcesMap.updateUi.noDataSources=No data sources were found."})
private void setupDataSources() {
new SwingWorker<Map<Long, String>, Void>() {
private void updateUi() {
String[] dataSourcesNames = new String[CommonFilesPanel.this.dataSourceMap.size()];
//only enable all this stuff if we actually have datasources
if (dataSourcesNames.length > 0) {
dataSourcesNames = CommonFilesPanel.this.dataSourceMap.values().toArray(dataSourcesNames);
CommonFilesPanel.this.dataSourcesList = new DataSourceComboBoxModel(dataSourcesNames);
CommonFilesPanel.this.selectDataSourceComboBox.setModel(CommonFilesPanel.this.dataSourcesList);
boolean multipleDataSources = this.caseHasMultipleSources();
CommonFilesPanel.this.allDataSourcesRadioButton.setEnabled(true);
CommonFilesPanel.this.allDataSourcesRadioButton.setSelected(true);
if (!multipleDataSources) {
CommonFilesPanel.this.withinDataSourceRadioButton.setEnabled(false);
CommonFilesPanel.this.withinDataSourceRadioButton.setSelected(false);
withinDataSourceSelected(false);
CommonFilesPanel.this.selectDataSourceComboBox.setEnabled(false);
}
CommonFilesPanel.this.searchButton.setEnabled(true);
} else {
MessageNotifyUtil.Message.info(Bundle.CommonFilesPanel_buildDataSourcesMap_updateUi_noDataSources());
CommonFilesPanel.this.cancelButtonActionPerformed(null);
}
}
private boolean caseHasMultipleSources() {
return CommonFilesPanel.this.dataSourceMap.size() >= 3;
}
@Override
protected Map<Long, String> doInBackground() throws NoCurrentCaseException, TskCoreException, SQLException {
DataSourceLoader loader = new DataSourceLoader();
return loader.getDataSourceMap();
}
@Override
protected void done() {
try {
CommonFilesPanel.this.dataSourceMap = this.get();
updateUi();
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex);
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_buildDataSourceMap_done_interupted());
} catch (ExecutionException ex) {
String errorMessage;
Throwable inner = ex.getCause();
if (inner instanceof TskCoreException) {
LOGGER.log(Level.SEVERE, "Failed to load data sources from database.", ex);
errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_tskCoreException();
} else if (inner instanceof NoCurrentCaseException) {
LOGGER.log(Level.SEVERE, "Current case has been closed.", ex);
errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_noCurrentCaseException();
} else if (inner instanceof SQLException) {
LOGGER.log(Level.SEVERE, "Unable to query db for data sources.", ex);
errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_sqlException();
} else {
LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog panel.", ex);
errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_exception();
}
MessageNotifyUtil.Message.error(errorMessage);
}
}
}.execute();
}
@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<CommonFilesMetadata, Void>() {
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[]{dataSourceMap.get(dataSourceId)};
this.tabTitle = String.format(CommonFilesPanel_search_results_titleSingle, dataSourceName);
}
private Long determineDataSourceId() {
Long selectedObjId = CommonFilesPanel.NO_DATA_SOURCE_SELECTED;
if (CommonFilesPanel.this.singleDataSource) {
for (Entry<Long, String> dataSource : CommonFilesPanel.this.dataSourceMap.entrySet()) {
if (dataSource.getValue().equals(CommonFilesPanel.this.selectedDataSource)) {
selectedObjId = dataSource.getKey();
break;
}
}
}
return selectedObjId;
}
@Override
@SuppressWarnings({"BoxedValueEquality", "NumberEquality"})
protected CommonFilesMetadata doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
progress = ProgressHandle.createHandle(Bundle.CommonFilesPanel_search_done_searchProgressGathering());
progress.start();
progress.switchToIndeterminate();
Long dataSourceId = determineDataSourceId();
CommonFilesMetadataBuilder builder;
boolean filterByMedia = false;
boolean filterByDocuments = false;
if (selectedFileCategoriesButton.isSelected()) {
if (pictureVideoCheckbox.isSelected()) {
filterByMedia = true;
}
if (documentsCheckbox.isSelected()) {
filterByDocuments = true;
}
}
if (dataSourceId == CommonFilesPanel.NO_DATA_SOURCE_SELECTED) {
builder = new AllDataSourcesCommonFilesAlgorithm(CommonFilesPanel.this.dataSourceMap, filterByMedia, filterByDocuments);
setTitleForAllDataSources();
} else {
builder = new SingleDataSource(dataSourceId, CommonFilesPanel.this.dataSourceMap, filterByMedia, filterByDocuments);
setTitleForSingleSource(dataSourceId);
}
this.tabTitle = builder.buildTabTitle();
CommonFilesMetadata metadata = builder.findCommonFiles();
return metadata;
}
@Override
protected void done() {
try {
super.done();
CommonFilesMetadata metadata = get();
CommonFilesNode commonFilesNode = new CommonFilesNode(metadata);
//TODO this could be enumerating the children!!!
DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonFilesPanel.this));
TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode, 3);
DataResultViewerTable table = new CommonFilesSearchResultsViewerTable();
Collection<DataResultViewer> viewers = new ArrayList<>(1);
viewers.add(table);
progress.setDisplayName(Bundle.CommonFilesPanel_search_done_searchProgressDisplay());
DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers);
} 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);
} finally {
progress.finish();
}
}
}.execute();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
dataSourcesButtonGroup = new javax.swing.ButtonGroup();
fileTypeFilterButtonGroup = new javax.swing.ButtonGroup();
searchButton = new javax.swing.JButton();
allDataSourcesRadioButton = new javax.swing.JRadioButton();
withinDataSourceRadioButton = new javax.swing.JRadioButton();
selectDataSourceComboBox = new javax.swing.JComboBox<>();
commonFilesSearchLabel = new javax.swing.JLabel();
cancelButton = new javax.swing.JButton();
allFileCategoriesRadioButton = new javax.swing.JRadioButton();
selectedFileCategoriesButton = new javax.swing.JRadioButton();
pictureVideoCheckbox = new javax.swing.JCheckBox();
documentsCheckbox = new javax.swing.JCheckBox();
dataSourceLabel = new javax.swing.JLabel();
categoriesLabel = new javax.swing.JLabel();
errorText = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.searchButton.text")); // NOI18N
searchButton.setEnabled(false);
searchButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
searchButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
searchButtonActionPerformed(evt);
}
});
dataSourcesButtonGroup.add(allDataSourcesRadioButton);
allDataSourcesRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(allDataSourcesRadioButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.allDataSourcesRadioButton.text")); // NOI18N
allDataSourcesRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
allDataSourcesRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allDataSourcesRadioButtonActionPerformed(evt);
}
});
dataSourcesButtonGroup.add(withinDataSourceRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(withinDataSourceRadioButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.withinDataSourceRadioButton.text")); // NOI18N
withinDataSourceRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
withinDataSourceRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
withinDataSourceRadioButtonActionPerformed(evt);
}
});
selectDataSourceComboBox.setModel(dataSourcesList);
selectDataSourceComboBox.setEnabled(false);
selectDataSourceComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
selectDataSourceComboBoxActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.commonFilesSearchLabel.text")); // NOI18N
commonFilesSearchLabel.setFocusable(false);
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.cancelButton.text")); // NOI18N
cancelButton.setActionCommand(org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.cancelButton.actionCommand")); // NOI18N
cancelButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
cancelButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelButtonActionPerformed(evt);
}
});
fileTypeFilterButtonGroup.add(allFileCategoriesRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.allFileCategoriesRadioButton.text")); // NOI18N
allFileCategoriesRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N
allFileCategoriesRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allFileCategoriesRadioButtonActionPerformed(evt);
}
});
fileTypeFilterButtonGroup.add(selectedFileCategoriesButton);
selectedFileCategoriesButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.selectedFileCategoriesButton.text")); // NOI18N
selectedFileCategoriesButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.selectedFileCategoriesButton.toolTipText")); // NOI18N
selectedFileCategoriesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
selectedFileCategoriesButtonActionPerformed(evt);
}
});
pictureVideoCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(pictureVideoCheckbox, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.pictureVideoCheckbox.text")); // NOI18N
pictureVideoCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pictureVideoCheckboxActionPerformed(evt);
}
});
documentsCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(documentsCheckbox, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.documentsCheckbox.text")); // NOI18N
documentsCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
documentsCheckboxActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(dataSourceLabel, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.text")); // NOI18N
dataSourceLabel.setName(""); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(categoriesLabel, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.categoriesLabel.text")); // NOI18N
categoriesLabel.setName(""); // NOI18N
errorText.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.errorText.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(errorText)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(searchButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton)
.addContainerGap())
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(categoriesLabel)
.addComponent(commonFilesSearchLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(dataSourceLabel)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(29, 29, 29)
.addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(withinDataSourceRadioButton)
.addComponent(allDataSourcesRadioButton)
.addComponent(allFileCategoriesRadioButton)
.addComponent(selectedFileCategoriesButton)
.addGroup(layout.createSequentialGroup()
.addGap(21, 21, 21)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(pictureVideoCheckbox)
.addComponent(documentsCheckbox))))))
.addGap(19, 19, 19))))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(commonFilesSearchLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(dataSourceLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(allDataSourcesRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(withinDataSourceRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(categoriesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(selectedFileCategoriesButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pictureVideoCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(documentsCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(allFileCategoriesRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cancelButton)
.addComponent(searchButton)
.addComponent(errorText)))
);
}// </editor-fold>//GEN-END:initComponents
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
search();
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_searchButtonActionPerformed
private void allDataSourcesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allDataSourcesRadioButtonActionPerformed
selectDataSourceComboBox.setEnabled(!allDataSourcesRadioButton.isSelected());
singleDataSource = false;
}//GEN-LAST:event_allDataSourcesRadioButtonActionPerformed
private void selectDataSourceComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectDataSourceComboBoxActionPerformed
final Object selectedItem = selectDataSourceComboBox.getSelectedItem();
if (selectedItem != null) {
selectedDataSource = selectedItem.toString();
} else {
selectedDataSource = "";
}
}//GEN-LAST:event_selectDataSourceComboBoxActionPerformed
private void withinDataSourceRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_withinDataSourceRadioButtonActionPerformed
withinDataSourceSelected(withinDataSourceRadioButton.isSelected());
}//GEN-LAST:event_withinDataSourceRadioButtonActionPerformed
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_cancelButtonActionPerformed
private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed
this.manageCheckBoxState();
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed
private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed
this.manageCheckBoxState();
}//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed
private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_pictureVideoCheckboxActionPerformed
private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_documentsCheckboxActionPerformed
private void toggleErrorTextAndSearchBox() {
if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) {
this.searchButton.setEnabled(false);
this.errorText.setVisible(true);
} else {
this.searchButton.setEnabled(true);
this.errorText.setVisible(false);
}
}
private void withinDataSourceSelected(boolean selected) {
selectDataSourceComboBox.setEnabled(selected);
if (selectDataSourceComboBox.isEnabled()) {
selectDataSourceComboBox.setSelectedIndex(0);
singleDataSource = true;
}
}
private void manageCheckBoxState() {
if (this.allFileCategoriesRadioButton.isSelected()) {
this.pictureViewCheckboxState = this.pictureVideoCheckbox.isSelected();
this.documentsCheckboxState = this.documentsCheckbox.isSelected();
this.pictureVideoCheckbox.setEnabled(false);
this.documentsCheckbox.setEnabled(false);
}
if (this.selectedFileCategoriesButton.isSelected()) {
this.pictureVideoCheckbox.setSelected(this.pictureViewCheckboxState);
this.documentsCheckbox.setSelected(this.documentsCheckboxState);
this.pictureVideoCheckbox.setEnabled(true);
this.documentsCheckbox.setEnabled(true);
this.toggleErrorTextAndSearchBox();
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton allDataSourcesRadioButton;
private javax.swing.JRadioButton allFileCategoriesRadioButton;
private javax.swing.JButton cancelButton;
private javax.swing.JLabel categoriesLabel;
private javax.swing.JLabel commonFilesSearchLabel;
private javax.swing.JLabel dataSourceLabel;
private javax.swing.ButtonGroup dataSourcesButtonGroup;
private javax.swing.JCheckBox documentsCheckbox;
private javax.swing.JLabel errorText;
private javax.swing.ButtonGroup fileTypeFilterButtonGroup;
private javax.swing.JCheckBox pictureVideoCheckbox;
private javax.swing.JButton searchButton;
private javax.swing.JComboBox<String> selectDataSourceComboBox;
private javax.swing.JRadioButton selectedFileCategoriesButton;
private javax.swing.JRadioButton withinDataSourceRadioButton;
// End of variables declaration//GEN-END:variables
}

View File

@ -24,6 +24,8 @@ import org.openide.util.HelpCtx;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -32,9 +34,10 @@ import org.sleuthkit.autopsy.coreutils.Logger;
*/ */
final public class CommonFilesSearchAction extends CallableSystemAction { final public class CommonFilesSearchAction extends CallableSystemAction {
private static final Logger LOGGER = Logger.getLogger(CommonFilesSearchAction.class.getName());
private static CommonFilesSearchAction instance = null; private static CommonFilesSearchAction instance = null;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(CommonFilesSearchAction.class.getName());
CommonFilesSearchAction() { CommonFilesSearchAction() {
super(); super();
@ -45,9 +48,22 @@ final public class CommonFilesSearchAction extends CallableSystemAction {
public boolean isEnabled(){ public boolean isEnabled(){
boolean shouldBeEnabled = false; boolean shouldBeEnabled = false;
try { try {
shouldBeEnabled = Case.isCaseOpen() && Case.getCurrentCase().getDataSources().size() > 1; //dont refactor any of this to pull out common expressions - order of evaluation of each expression is significant
shouldBeEnabled =
(Case.isCaseOpen() &&
Case.getCurrentCase().getDataSources().size() > 1)
||
(EamDb.isEnabled() &&
EamDb.getInstance() != null &&
EamDb.getInstance().getCases().size() > 1 &&
Case.isCaseOpen() &&
Case.getCurrentCase() != null &&
EamDb.getInstance().getCase(Case.getCurrentCase()) != null);
} catch(TskCoreException ex) { } catch(TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting data sources for action enabled check", ex); LOGGER.log(Level.SEVERE, "Error getting data sources for action enabled check", ex);
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error getting CR cases for action enabled check", ex);
} }
return super.isEnabled() && shouldBeEnabled; return super.isEnabled() && shouldBeEnabled;
} }
@ -61,12 +77,12 @@ final public class CommonFilesSearchAction extends CallableSystemAction {
@Override @Override
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
new CommonFilesDialog().setVisible(true); new CommonAttributePanel().setVisible(true);
} }
@Override @Override
public void performAction() { public void performAction() {
new CommonFilesDialog().setVisible(true); new CommonAttributePanel().setVisible(true);
} }
@NbBundle.Messages({ @NbBundle.Messages({

View File

@ -30,7 +30,7 @@ public class DataSourceComboBoxModel extends AbstractListModel<String> implement
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final String[] dataSourceList; private final String[] dataSourceList;
String selection = null; private String selection = null;
/** /**
* Use this to initialize the panel * Use this to initialize the panel

View File

@ -1,56 +0,0 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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;
/**
* Encapsulates data required to instantiate a <code>FileInstanceNode</code>.
*/
final public class FileInstanceMetadata {
private final Long objectId;
private final String dataSourceName;
/**
* 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
*/
FileInstanceMetadata(Long objectId, String dataSourceName) {
this.objectId = objectId;
this.dataSourceName = dataSourceName;
}
/**
* obj_id for the file represented by this object
* @return
*/
public Long getObjectId(){
return this.objectId;
}
/**
* Name of datasource where this instance was found.
* @return
*/
public String getDataSourceName(){
return this.dataSourceName;
}
}

View File

@ -40,24 +40,24 @@ import org.sleuthkit.autopsy.datamodel.NodeProperty;
final public class InstanceCountNode extends DisplayableItemNode { final public class InstanceCountNode extends DisplayableItemNode {
final private int instanceCount; final private int instanceCount;
final private List<Md5Metadata> metadataList; final private List<CommonAttributeValue> attributeValues;
/** /**
* Create a node with the given number of instances, and the given * Create a node with the given number of instances, and the given
* selection of metadata. * selection of metadata.
* @param instanceCount * @param instanceCount
* @param md5Metadata * @param attributeValues
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"InstanceCountNode.displayName=Files with %s instances (%s)" "InstanceCountNode.displayName=Files with %s instances (%s)"
}) })
public InstanceCountNode(int instanceCount, List<Md5Metadata> md5Metadata) { public InstanceCountNode(int instanceCount, List<CommonAttributeValue> attributeValues) {
super(Children.create(new Md5NodeFactory(md5Metadata), true)); super(Children.create(new CommonAttributeValueNodeFactory(attributeValues), true));
this.instanceCount = instanceCount; this.instanceCount = instanceCount;
this.metadataList = md5Metadata; this.attributeValues = attributeValues;
this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), md5Metadata.size())); this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), attributeValues.size()));
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS
} }
@ -73,8 +73,8 @@ final public class InstanceCountNode extends DisplayableItemNode {
* Get a list of metadata for the MD5s which are children of this object. * Get a list of metadata for the MD5s which are children of this object.
* @return List<Md5Metadata> * @return List<Md5Metadata>
*/ */
List<Md5Metadata> getMetadata() { List<CommonAttributeValue> getAttributeValues() {
return Collections.unmodifiableList(this.metadataList); return Collections.unmodifiableList(this.attributeValues);
} }
@Override @Override
@ -113,34 +113,36 @@ final public class InstanceCountNode extends DisplayableItemNode {
* ChildFactory which builds CommonFileParentNodes from the * ChildFactory which builds CommonFileParentNodes from the
* CommonFilesMetaaData models. * CommonFilesMetaaData models.
*/ */
static class Md5NodeFactory extends ChildFactory<String> { static class CommonAttributeValueNodeFactory extends ChildFactory<String> {
/** /**
* List of models, each of which is a parent node matching a single md5, * List of models, each of which is a parent node matching a single md5,
* containing children FileNodes. * containing children FileNodes.
*/ */
private final Map<String, Md5Metadata> metadata; // maps sting version of value to value Object (??)
private final Map<String, CommonAttributeValue> metadata;
Md5NodeFactory(List<Md5Metadata> metadata) { CommonAttributeValueNodeFactory(List<CommonAttributeValue> attributeValues) {
this.metadata = new HashMap<>(); this.metadata = new HashMap<>();
Iterator<Md5Metadata> iterator = metadata.iterator(); Iterator<CommonAttributeValue> iterator = attributeValues.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
Md5Metadata md5Metadata = iterator.next(); CommonAttributeValue attributeValue = iterator.next();
this.metadata.put(md5Metadata.getMd5(), md5Metadata); this.metadata.put(attributeValue.getValue(), attributeValue);
} }
} }
@Override
protected Node createNodeForKey(String md5) {
Md5Metadata md5Metadata = this.metadata.get(md5);
return new Md5Node(md5Metadata);
}
@Override @Override
protected boolean createKeys(List<String> list) { protected boolean createKeys(List<String> list) {
// @@@ We should just use CommonAttributeValue as the key...
list.addAll(this.metadata.keySet()); list.addAll(this.metadata.keySet());
return true; return true;
} }
@Override
protected Node createNodeForKey(String attributeValue) {
CommonAttributeValue md5Metadata = this.metadata.get(attributeValue);
return new CommonAttributeValueNode(md5Metadata);
}
} }
} }

View File

@ -0,0 +1,59 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.Map;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
/**
* Provides logic for selecting common files from all data sources and all cases
* in the Central Repo.
*/
abstract class InterCaseCommonAttributeSearcher extends AbstractCommonAttributeSearcher {
private final EamDb dbManager;
/**
* Implements the algorithm for getting common files across all data sources
* and all cases. Can filter on mime types conjoined by logical AND.
*
* @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
*/
InterCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
dbManager = EamDb.getInstance();
}
protected CorrelationCase getCorrelationCaseFromId(int correlationCaseId) throws EamDbException {
for (CorrelationCase cCase : this.dbManager.getCases()) {
if (cCase.getID() == correlationCaseId) {
return cCase;
}
}
throw new IllegalArgumentException("Cannot locate case.");
}
}

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="buttonGroup">
</Component>
</NonVisualComponents>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="anyCentralRepoCaseRadio" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="21" pref="21" max="-2" attributes="0"/>
<Component id="caseComboBox" min="-2" pref="261" max="-2" attributes="0"/>
</Group>
<Component id="specificCentralRepoCaseRadio" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="anyCentralRepoCaseRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="specificCentralRepoCaseRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="caseComboBox" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JRadioButton" name="anyCentralRepoCaseRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="InterCasePanel.anyCentralRepoCaseRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="anyCentralRepoCaseRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="specificCentralRepoCaseRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="InterCasePanel.specificCentralRepoCaseRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="specificCentralRepoCaseRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="caseComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="casesList" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,204 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.ComboBoxModel;
import org.openide.util.NbBundle;
/**
* UI controls for Common Files Search scenario where the user intends to find
* common files between cases in addition to the present case.
*/
public class InterCasePanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
static final int NO_CASE_SELECTED = -1;
private ComboBoxModel<String> casesList = new DataSourceComboBoxModel();
private final Map<Integer, String> caseMap;
private String errorMessage;
//True if we are looking in any or all cases,
// false if we must find matches in a given case plus the current case
private boolean anyCase;
/**
* Creates new form InterCasePanel
*/
public InterCasePanel() {
initComponents();
this.errorMessage = "";
this.caseMap = new HashMap<>();
this.anyCase = true;
}
private void specificCaseSelected(boolean selected) {
this.specificCentralRepoCaseRadio.setEnabled(selected);
if (this.specificCentralRepoCaseRadio.isEnabled()) {
this.caseComboBox.setEnabled(true);
this.caseComboBox.setSelectedIndex(0);
}
}
String getErrorMessage(){
return this.errorMessage;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
buttonGroup = new javax.swing.ButtonGroup();
anyCentralRepoCaseRadio = new javax.swing.JRadioButton();
specificCentralRepoCaseRadio = new javax.swing.JRadioButton();
caseComboBox = new javax.swing.JComboBox<>();
buttonGroup.add(anyCentralRepoCaseRadio);
anyCentralRepoCaseRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(anyCentralRepoCaseRadio, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.anyCentralRepoCaseRadio.text")); // NOI18N
anyCentralRepoCaseRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
anyCentralRepoCaseRadioActionPerformed(evt);
}
});
buttonGroup.add(specificCentralRepoCaseRadio);
org.openide.awt.Mnemonics.setLocalizedText(specificCentralRepoCaseRadio, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.specificCentralRepoCaseRadio.text")); // NOI18N
specificCentralRepoCaseRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
specificCentralRepoCaseRadioActionPerformed(evt);
}
});
caseComboBox.setModel(casesList);
caseComboBox.setEnabled(false);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(anyCentralRepoCaseRadio)
.addGroup(layout.createSequentialGroup()
.addGap(21, 21, 21)
.addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(specificCentralRepoCaseRadio))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(anyCentralRepoCaseRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(specificCentralRepoCaseRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
);
}// </editor-fold>//GEN-END:initComponents
private void specificCentralRepoCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_specificCentralRepoCaseRadioActionPerformed
this.caseComboBox.setEnabled(true);
if(this.caseComboBox.isEnabled() && this.caseComboBox.getSelectedItem() == null){
this.caseComboBox.setSelectedIndex(0);
}
this.anyCase = false;
}//GEN-LAST:event_specificCentralRepoCaseRadioActionPerformed
private void anyCentralRepoCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_anyCentralRepoCaseRadioActionPerformed
this.caseComboBox.setEnabled(false);
this.anyCase = true;
}//GEN-LAST:event_anyCentralRepoCaseRadioActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton anyCentralRepoCaseRadio;
private javax.swing.ButtonGroup buttonGroup;
private javax.swing.JComboBox<String> caseComboBox;
private javax.swing.JRadioButton specificCentralRepoCaseRadio;
// End of variables declaration//GEN-END:variables
Map<Integer, String> getCaseMap() {
return Collections.unmodifiableMap(this.caseMap);
}
void setCaseList(DataSourceComboBoxModel dataSourceComboBoxModel) {
this.casesList = dataSourceComboBoxModel;
this.caseComboBox.setModel(dataSourceComboBoxModel);
}
void rigForMultipleCases(boolean multipleCases) {
this.anyCentralRepoCaseRadio.setEnabled(multipleCases);
this.anyCentralRepoCaseRadio.setEnabled(multipleCases);
if(!multipleCases){
this.specificCentralRepoCaseRadio.setSelected(true);
this.specificCaseSelected(true);
}
}
void setCaseMap(Map<Integer, String> caseMap) {
this.caseMap.clear();
this.caseMap.putAll(caseMap);
}
boolean centralRepoHasMultipleCases() {
return this.caseMap.size() >= 2;
}
Integer getSelectedCaseId(){
if(this.anyCase){
return InterCasePanel.NO_CASE_SELECTED;
}
for(Entry<Integer, String> entry : this.caseMap.entrySet()){
if(entry.getValue().equals(this.caseComboBox.getSelectedItem())){
return entry.getKey();
}
}
return InterCasePanel.NO_CASE_SELECTED;
}
@NbBundle.Messages({
"InterCasePanel.showInterCaseErrorMessage.message=Cannot run intercase correlation search: no cases in Central Repository."
})
boolean areSearchCriteriaMet() {
if(this.caseMap.isEmpty()){
this.errorMessage = Bundle.InterCasePanel_showInterCaseErrorMessage_message();
return false;
} else {
return true;
}
}
}

View File

@ -0,0 +1,241 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
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.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.centralrepository.datamodel.InstanceTableCallback;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.HashUtility;
/**
* Used to process and return CorrelationCase md5s from the EamDB for
* CommonFilesSearch.
*/
final class InterCaseSearchResultsProcessor {
private Map<Long, String> dataSources;
private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName());
private final String interCaseWhereClause = "value IN (SELECT value FROM file_instances"
+ " WHERE value IN (SELECT value FROM file_instances"
+ " WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value)"
+ " GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value";
private final String singleInterCaseWhereClause = "value IN (SELECT value FROM file_instances "
+ "WHERE value IN (SELECT value FROM file_instances "
+ "WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value) "
+ "AND (case_id=%s OR case_id=%s) GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value";
/**
* Used in the InterCaseCommonAttributeSearchers to find common attribute instances and generate nodes at the UI level.
* @param dataSources
*/
InterCaseSearchResultsProcessor(Map<Long, String> dataSources){
this.dataSources = dataSources;
}
/**
* Used in the CentralRepoCommonAttributeInstance to find common attribute instances and generate nodes at the UI level.
*/
InterCaseSearchResultsProcessor(){
//intentionally emtpy - we need a constructor which does not set the data sources field
}
/**
* Finds a single CorrelationAttribute given an id.
*
* @param attrbuteId Row of CorrelationAttribute to retrieve from the EamDb
* @return CorrelationAttribute object representation of retrieved match
*/
CorrelationAttribute findSingleCorrelationAttribute(int attrbuteId) {
try {
InterCaseCommonAttributeRowCallback instancetableCallback = new InterCaseCommonAttributeRowCallback();
EamDb DbManager = EamDb.getInstance();
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attrbuteId), instancetableCallback);
return instancetableCallback.getCorrelationAttribute();
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error accessing EamDb processing InstanceTable row.", ex);
}
return null;
}
/**
* Given the current case, fins all intercase common files from the EamDb
* and builds maps of obj id to md5 and case.
*
* @param currentCase The current TSK Case.
*/
Map<Integer, List<CommonAttributeValue>> findInterCaseCommonAttributeValues(Case currentCase) {
try {
InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback();
EamDb DbManager = EamDb.getInstance();
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
int caseId = DbManager.getCase(currentCase).getID();
DbManager.processInstanceTableWhere(fileType, String.format(interCaseWhereClause, caseId,
TskData.FileKnown.KNOWN.getFileKnownValue()),
instancetableCallback);
return instancetableCallback.getInstanceCollatedCommonFiles();
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex);
}
return new HashMap<>();
}
/**
* Given the current case, and a specific case of interest, finds common
* files which exist between cases from the EamDb. Builds maps of obj id to
* md5 and case.
*
* @param currentCase The current TSK Case.
* @param singleCase The case of interest. Matches must exist in this case.
*/
Map<Integer, List<CommonAttributeValue>> findSingleInterCaseCommonAttributeValues(Case currentCase, CorrelationCase singleCase) {
try {
InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback();
EamDb DbManager = EamDb.getInstance();
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
int caseId = DbManager.getCase(currentCase).getID();
int targetCaseId = singleCase.getID();
DbManager.processInstanceTableWhere(fileType, String.format(singleInterCaseWhereClause, caseId,
TskData.FileKnown.KNOWN.getFileKnownValue(), caseId, targetCaseId), instancetableCallback);
return instancetableCallback.getInstanceCollatedCommonFiles();
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex);
}
return new HashMap<>();
}
/**
* Callback to use with findInterCaseCommonAttributeValues which generates a
* list of md5s for common files search
*/
private class InterCaseCommonAttributesCallback implements InstanceTableCallback {
final Map<Integer, List<CommonAttributeValue>> instanceCollatedCommonFiles = new HashMap<>();
private CommonAttributeValue commonAttributeValue = null;
private String previousRowMd5 = "";
@Override
public void process(ResultSet resultSet) {
try {
while (resultSet.next()) {
int resultId = InstanceTableCallback.getId(resultSet);
String md5Value = InstanceTableCallback.getValue(resultSet);
if (previousRowMd5.isEmpty()) {
previousRowMd5 = md5Value;
}
if (md5Value == null || HashUtility.isNoDataMd5(md5Value)) {
continue;
}
countAndAddCommonAttributes(md5Value, resultId);
}
} catch (SQLException ex) {
LOGGER.log(Level.WARNING, "Error getting artifact instances from database.", ex); // NON-NLS
}
}
private void countAndAddCommonAttributes(String md5Value, int resultId) {
if (commonAttributeValue == null) {
commonAttributeValue = new CommonAttributeValue(md5Value);
}
if (!md5Value.equals(previousRowMd5)) {
int size = commonAttributeValue.getInstanceCount();
if (instanceCollatedCommonFiles.containsKey(size)) {
instanceCollatedCommonFiles.get(size).add(commonAttributeValue);
} else {
ArrayList<CommonAttributeValue> value = new ArrayList<>();
value.add(commonAttributeValue);
instanceCollatedCommonFiles.put(size, value);
}
commonAttributeValue = new CommonAttributeValue(md5Value);
previousRowMd5 = md5Value;
}
// we don't *have* all the information for the rows in the CR,
// so we need to consult the present case via the SleuthkitCase object
// Later, when the FileInstanceNode is built. Therefore, build node generators for now.
AbstractCommonAttributeInstance searchResult = new CentralRepoCommonAttributeInstance(resultId, InterCaseSearchResultsProcessor.this.dataSources);
commonAttributeValue.addInstance(searchResult);
}
Map<Integer, List<CommonAttributeValue>> getInstanceCollatedCommonFiles() {
return Collections.unmodifiableMap(instanceCollatedCommonFiles);
}
}
/**
* Callback to use with findSingleCorrelationAttribute which retrieves a
* single CorrelationAttribute from the EamDb.
*/
private class InterCaseCommonAttributeRowCallback implements InstanceTableCallback {
CorrelationAttribute correlationAttribute = null;
@Override
public void process(ResultSet resultSet) {
try {
EamDb DbManager = EamDb.getInstance();
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
while (resultSet.next()) {
CorrelationCase correlationCase = DbManager.getCaseById(InstanceTableCallback.getCaseId(resultSet));
CorrelationDataSource dataSource = DbManager.getDataSourceById(correlationCase, InstanceTableCallback.getDataSourceId(resultSet));
correlationAttribute = DbManager.getCorrelationAttribute(fileType,
correlationCase,
dataSource,
InstanceTableCallback.getValue(resultSet),
InstanceTableCallback.getFilePath(resultSet));
}
} catch (SQLException | EamDbException ex) {
LOGGER.log(Level.WARNING, "Error getting single correlation artifact instance from database.", ex); // NON-NLS
}
}
CorrelationAttribute getCorrelationAttribute() {
return correlationAttribute;
}
}
}

View File

@ -0,0 +1,168 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.datamodel.HashUtility;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
* Generates a <code>List<CommonFilesMetadata></code> when
* <code>findFiles()</code> is called, which organizes files by md5 to
* prepare to display in viewer.
*
* This entire thing runs on a background thread where exceptions are handled.
*/
@SuppressWarnings("PMD.AbstractNaming")
public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAttributeSearcher {
private static final String FILTER_BY_MIME_TYPES_WHERE_CLAUSE = " and mime_type in (%s)"; //NON-NLS // where %s is csv list of mime_types to filter on
/**
* Subclass this to implement different algorithms for getting common files.
*
* @param dataSourceIdMap a map of obj_id to datasource name
* @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
*/
IntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
}
/**
* Use this as a prefix when building the SQL select statement.
*
* <ul>
* <li>You only have to specify the WHERE clause if you use this.</li>
* <li>If you do not use this string, you must use at least the columns
* selected below, in that order.</li>
* </ul>
*/
static final String SELECT_PREFIX = "SELECT obj_id, md5, data_source_obj_id from tsk_files where"; //NON-NLS
/**
* Should build a SQL SELECT statement to be passed to
* SleuthkitCase.executeQuery(sql) which will select the desired file ids
* and MD5 hashes.
*
* The statement should select obj_id, md5, data_source_obj_id in that
* order.
*
* @return sql string select statement
*/
protected abstract String buildSqlSelectStatement();
/**
* Generate a meta data object which encapsulates everything need to add the
* tree table tab to the top component.
*
* @return a data object with all of the matched files in a hierarchical
* format
* @throws TskCoreException
* @throws NoCurrentCaseException
* @throws SQLException
*/
@Override
public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException {
Map<String, CommonAttributeValue> commonFiles = new HashMap<>();
final Case currentCase = Case.getCurrentCaseThrows();
final String caseName = currentCase.getDisplayName();
SleuthkitCase sleuthkitCase = currentCase.getSleuthkitCase();
String selectStatement = this.buildSqlSelectStatement();
try (
CaseDbQuery query = sleuthkitCase.executeQuery(selectStatement);
ResultSet resultSet = query.getResultSet()) {
while (resultSet.next()) {
Long objectId = resultSet.getLong(1);
String md5 = resultSet.getString(2);
Long dataSourceId = resultSet.getLong(3);
String dataSource = this.getDataSourceIdToNameMap().get(dataSourceId);
if (md5 == null || HashUtility.isNoDataMd5(md5)) {
continue;
}
if (commonFiles.containsKey(md5)) {
final CommonAttributeValue commonAttributeValue = commonFiles.get(md5);
commonAttributeValue.addInstance(new CaseDBCommonAttributeInstance(objectId, dataSource, caseName));
} else {
final CommonAttributeValue commonAttributeValue = new CommonAttributeValue(md5);
commonAttributeValue.addInstance(new CaseDBCommonAttributeInstance(objectId, dataSource, caseName));
commonFiles.put(md5, commonAttributeValue);
}
}
}
Map<Integer, List<CommonAttributeValue>> instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles);
return new CommonAttributeSearchResults(instanceCollatedCommonFiles);
}
/**
* Should be used by subclasses, in their
* <code>buildSqlSelectStatement()</code> function to create an SQL boolean
* expression which will filter our matches based on mime type. The
* expression will be conjoined to base query with an AND operator.
*
* @return sql fragment of the form:
* 'and "mime_type" in ( [comma delimited list of mime types] )'
* or empty string in the event that no types to filter on were given.
*/
String determineMimeTypeFilter() {
Set<String> mimeTypesToFilterOn = new HashSet<>();
String mimeTypeString = "";
if (isFilterByMedia()) {
mimeTypesToFilterOn.addAll(MEDIA_PICS_VIDEO_MIME_TYPES);
}
if (isFilterByDoc()) {
mimeTypesToFilterOn.addAll(TEXT_FILES_MIME_TYPES);
}
StringBuilder mimeTypeFilter = new StringBuilder(mimeTypesToFilterOn.size());
if (!mimeTypesToFilterOn.isEmpty()) {
for (String mimeType : mimeTypesToFilterOn) {
mimeTypeFilter.append(SINGLE_QUOTE).append(mimeType).append(SINGLE_QUTOE_COMMA);
}
mimeTypeString = mimeTypeFilter.toString().substring(0, mimeTypeFilter.length() - 1);
mimeTypeString = String.format(FILTER_BY_MIME_TYPES_WHERE_CLAUSE, new Object[]{mimeTypeString});
}
return mimeTypeString;
}
static final String SINGLE_QUTOE_COMMA = "',";
static final String SINGLE_QUOTE = "'";
}

View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="buttonGroup">
</Component>
</NonVisualComponents>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="allDataSourcesRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="withinDataSourceRadioButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="27" max="-2" attributes="0"/>
<Component id="selectDataSourceComboBox" min="-2" pref="261" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="allDataSourcesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="withinDataSourceRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="selectDataSourceComboBox" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JRadioButton" name="allDataSourcesRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="IntraCasePanel.allDataSourcesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allDataSourcesRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="withinDataSourceRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="IntraCasePanel.withinDataSourceRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="withinDataSourceRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="selectDataSourceComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="dataSourcesList" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="selectDataSourceComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,194 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.ComboBoxModel;
import org.openide.util.NbBundle;
/**
* UI controls for Common Files Search scenario where the user intends to find
* common files between datasources. It is an inner panel which provides the ability
* to select all datasources or a single datasource from a dropdown list of
* sources in the current case.
*/
public class IntraCasePanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
static final long NO_DATA_SOURCE_SELECTED = -1;
private boolean singleDataSource;
private ComboBoxModel<String> dataSourcesList = new DataSourceComboBoxModel();
private final Map<Long, String> dataSourceMap;
private String errorMessage;
/**
* Creates new form IntraCasePanel
*/
public IntraCasePanel() {
initComponents();
this.errorMessage = "";
this.dataSourceMap = new HashMap<>();
this.singleDataSource = true;
}
public Map<Long, String> getDataSourceMap(){
return Collections.unmodifiableMap(this.dataSourceMap);
}
Long getSelectedDataSourceId(){
if(!this.singleDataSource){
return IntraCasePanel.NO_DATA_SOURCE_SELECTED;
}
for(Entry<Long, String> entry : this.dataSourceMap.entrySet()){
if(entry.getValue().equals(this.selectDataSourceComboBox.getSelectedItem())){
return entry.getKey();
}
}
return IntraCasePanel.NO_DATA_SOURCE_SELECTED;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
buttonGroup = new javax.swing.ButtonGroup();
allDataSourcesRadioButton = new javax.swing.JRadioButton();
withinDataSourceRadioButton = new javax.swing.JRadioButton();
selectDataSourceComboBox = new javax.swing.JComboBox<>();
buttonGroup.add(allDataSourcesRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(allDataSourcesRadioButton, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.allDataSourcesRadioButton.text")); // NOI18N
allDataSourcesRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
allDataSourcesRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allDataSourcesRadioButtonActionPerformed(evt);
}
});
buttonGroup.add(withinDataSourceRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(withinDataSourceRadioButton, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.withinDataSourceRadioButton.text")); // NOI18N
withinDataSourceRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
withinDataSourceRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
withinDataSourceRadioButtonActionPerformed(evt);
}
});
selectDataSourceComboBox.setModel(dataSourcesList);
selectDataSourceComboBox.setActionCommand(org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.selectDataSourceComboBox.actionCommand")); // NOI18N
selectDataSourceComboBox.setEnabled(false);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(allDataSourcesRadioButton)
.addComponent(withinDataSourceRadioButton)))
.addGroup(layout.createSequentialGroup()
.addGap(27, 27, 27)
.addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(allDataSourcesRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(withinDataSourceRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
);
}// </editor-fold>//GEN-END:initComponents
private void allDataSourcesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allDataSourcesRadioButtonActionPerformed
selectDataSourceComboBox.setEnabled(!allDataSourcesRadioButton.isSelected());
singleDataSource = false;
}//GEN-LAST:event_allDataSourcesRadioButtonActionPerformed
private void withinDataSourceRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_withinDataSourceRadioButtonActionPerformed
withinDataSourceSelected(withinDataSourceRadioButton.isSelected());
}//GEN-LAST:event_withinDataSourceRadioButtonActionPerformed
private void withinDataSourceSelected(boolean selected) {
selectDataSourceComboBox.setEnabled(selected);
if (selectDataSourceComboBox.isEnabled()) {
selectDataSourceComboBox.setSelectedIndex(0);
singleDataSource = true;
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton allDataSourcesRadioButton;
private javax.swing.ButtonGroup buttonGroup;
private javax.swing.JComboBox<String> selectDataSourceComboBox;
private javax.swing.JRadioButton withinDataSourceRadioButton;
// End of variables declaration//GEN-END:variables
void setDataModel(DataSourceComboBoxModel dataSourceComboBoxModel) {
this.dataSourcesList = dataSourceComboBoxModel;
this.selectDataSourceComboBox.setModel(dataSourcesList);
}
void rigForMultipleDataSources(boolean multipleDataSources) {
this.withinDataSourceRadioButton.setEnabled(multipleDataSources);
this.allDataSourcesRadioButton.setSelected(!multipleDataSources);
this.withinDataSourceRadioButton.setSelected(multipleDataSources);
this.withinDataSourceSelected(multipleDataSources);
}
void setDataSourceMap(Map<Long, String> dataSourceMap) {
this.dataSourceMap.clear();
this.dataSourceMap.putAll(dataSourceMap);
}
@NbBundle.Messages({
"IntraCasePanel.areSearchCriteriaMet.message=Cannot run intra-case correlation search."
})
boolean areSearchCriteriaMet() {
if(this.dataSourceMap.isEmpty()){
this.errorMessage = Bundle.IntraCasePanel_areSearchCriteriaMet_message();
return false;
} else {
return true;
}
}
String getErrorMessage() {
return this.errorMessage;
}
}

View File

@ -0,0 +1,86 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
*
*/
public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttributeSearcher {
private final int corrleationCaseId;
private String correlationCaseName;
/**
*
* @param correlationCaseId
* @param filterByMediaMimeType
* @param filterByDocMimeType
* @throws EamDbException
*/
public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException {
super(dataSourceIdMap,filterByMediaMimeType, filterByDocMimeType);
this.corrleationCaseId = correlationCaseId;
this.correlationCaseName = "";
}
/**
* Collect metadata required to render the tree table where matches must
* occur in the case with the given ID.
*
* @param correlationCaseId id of case where matches must occur (no other matches will be shown)
* @return business object needed to populate tree table with results
* @throws TskCoreException
* @throws NoCurrentCaseException
* @throws SQLException
* @throws EamDbException
*/
@Override
public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
CorrelationCase cCase = this.getCorrelationCaseFromId(this.corrleationCaseId);
correlationCaseName = cCase.getDisplayName();
return this.findFiles(cCase);
}
CommonAttributeSearchResults findFiles(CorrelationCase correlationCase) throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap());
Map<Integer, List<CommonAttributeValue>> interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase);
return new CommonAttributeSearchResults(interCaseCommonFiles);
}
@Override
String buildTabTitle() {
final String buildCategorySelectionString = this.buildCategorySelectionString();
final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleInterSingle();
return String.format(titleTemplate, new Object[]{correlationCaseName, buildCategorySelectionString});
}
}

View File

@ -25,7 +25,7 @@ import org.sleuthkit.datamodel.TskData.FileKnown;
/** /**
* Provides logic for selecting common files from a single data source. * Provides logic for selecting common files from a single data source.
*/ */
final public class SingleDataSource extends CommonFilesMetadataBuilder { final public class SingleIntraCaseCommonAttributeSearcher extends IntraCaseCommonAttributeSearcher {
private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL) and data_source_obj_id=%s%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 md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL) and data_source_obj_id=%s%s) GROUP BY md5 HAVING COUNT(DISTINCT data_source_obj_id) > 1) order by md5"; //NON-NLS
private final Long selectedDataSourceId; private final Long selectedDataSourceId;
@ -41,7 +41,7 @@ final public class SingleDataSource extends CommonFilesMetadataBuilder {
* @param filterByDocMimeType match only on files whose mime types can be * @param filterByDocMimeType match only on files whose mime types can be
* broadly categorized as document types * broadly categorized as document types
*/ */
public SingleDataSource(Long dataSourceId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { public SingleIntraCaseCommonAttributeSearcher(Long dataSourceId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
this.selectedDataSourceId = dataSourceId; this.selectedDataSourceId = dataSourceId;
this.dataSourceName = dataSourceIdMap.get(this.selectedDataSourceId); this.dataSourceName = dataSourceIdMap.get(this.selectedDataSourceId);
@ -50,13 +50,13 @@ final public class SingleDataSource extends CommonFilesMetadataBuilder {
@Override @Override
protected String buildSqlSelectStatement() { protected String buildSqlSelectStatement() {
Object[] args = new String[]{SELECT_PREFIX, Long.toString(this.selectedDataSourceId), determineMimeTypeFilter()}; Object[] args = new String[]{SELECT_PREFIX, Long.toString(this.selectedDataSourceId), determineMimeTypeFilter()};
return String.format(SingleDataSource.WHERE_CLAUSE, args); return String.format(SingleIntraCaseCommonAttributeSearcher.WHERE_CLAUSE, args);
} }
@Override @Override
protected String buildTabTitle() { public String buildTabTitle() {
final String buildCategorySelectionString = this.buildCategorySelectionString(); final String buildCategorySelectionString = this.buildCategorySelectionString();
final String titleTemplate = Bundle.CommonFilesMetadataBuilder_buildTabTitle_titleSingle(); final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleIntraSingle();
return String.format(titleTemplate, new Object[]{this.dataSourceName, buildCategorySelectionString}); return String.format(titleTemplate, new Object[]{this.dataSourceName, buildCategorySelectionString});
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2017-18 Basis Technology Corp. * Copyright 2017-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -117,8 +117,8 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
drp.open(); drp.open();
drpExplorerManager = drp.getExplorerManager(); drpExplorerManager = drp.getExplorerManager();
drpExplorerManager.addPropertyChangeListener(evt -> drpExplorerManager.addPropertyChangeListener(evt
viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1)); -> viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1));
} }
/** /**

View File

@ -0,0 +1,66 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.core;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.sendopts.CommandException;
import org.netbeans.spi.sendopts.Env;
import org.netbeans.spi.sendopts.Option;
import org.netbeans.spi.sendopts.OptionProcessor;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
* This class can be used to add command line options to Autopsy
* To add more options to autopsy, create a Option variable and add it to the set in getOptions method
* Do your logic for that option in the process method
*/
@ServiceProvider(service=OptionProcessor.class)
public class AutopsyOptionProcessor extends OptionProcessor {
private static final Logger logger = Logger.getLogger(AutopsyOptionProcessor.class.getName());
private final Option liveAutopsyOption = Option.withoutArgument('l', "liveAutopsy");
private final static String PROP_BASECASE = "LBL_BaseCase_PATH";
@Override
protected Set<Option> getOptions() {
Set<Option> set = new HashSet<>();
set.add(liveAutopsyOption);
return set;
}
@Override
protected void process(Env env, Map<Option, String[]> values) throws CommandException {
if(values.containsKey(liveAutopsyOption)){
try {
RuntimeProperties.setRunningInTarget(true);
ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_BASECASE , PlatformUtil.getUserDirectory().toString());
} catch (RuntimeProperties.RuntimePropertiesException ex) {
logger.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
}

View File

@ -26,6 +26,8 @@ public class RuntimeProperties {
private static boolean runningWithGUI = true; private static boolean runningWithGUI = true;
private static boolean runningWithGUIFlagHasBeenSet = false; private static boolean runningWithGUIFlagHasBeenSet = false;
private static boolean runningInTarget = false;
private static boolean runningInTargetFlagHasBeenSet = false;
/** /**
* Sets or unsets a flag indicating whether or not the application is * Sets or unsets a flag indicating whether or not the application is
@ -45,6 +47,33 @@ public class RuntimeProperties {
} }
} }
/**
* Sets or unsets a flag indicating whether or not the application is running in a target system.
* The flag can only be set once per application innvocation
*
* @param runningInTarget
*
* @throws RuntimePropertiesException if the flag has already been set
*/
public synchronized static void setRunningInTarget(boolean runningInTarget) throws RuntimePropertiesException{
if(!runningInTargetFlagHasBeenSet){
RuntimeProperties.runningInTarget = runningInTarget;
runningInTargetFlagHasBeenSet = true;
} else {
throw new RuntimePropertiesException("The runningLive Flag has already been set and cannot be changed");
}
}
/**
* Gets a flag indicating whether or not the application is running in a target system
*
* @return True or false.
*/
public synchronized static boolean isRunningInTarget() {
return runningInTarget;
}
/** /**
* Gets a flag indicating whether or not the application is running with a * Gets a flag indicating whether or not the application is running with a
* GUI. * GUI.

View File

@ -70,6 +70,7 @@ public final class UserPreferences {
private static final String MAX_NUM_OF_LOG_FILE = "MaximumNumberOfLogFiles"; private static final String MAX_NUM_OF_LOG_FILE = "MaximumNumberOfLogFiles";
private static final int LOG_FILE_NUM_INT = 10; private static final int LOG_FILE_NUM_INT = 10;
public static final String GROUP_ITEMS_IN_TREE_BY_DATASOURCE = "GroupItemsInTreeByDataSource"; //NON-NLS public static final String GROUP_ITEMS_IN_TREE_BY_DATASOURCE = "GroupItemsInTreeByDataSource"; //NON-NLS
public static final String SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags";
// Prevent instantiation. // Prevent instantiation.
private UserPreferences() { private UserPreferences() {
@ -196,6 +197,27 @@ public final class UserPreferences {
preferences.putBoolean(GROUP_ITEMS_IN_TREE_BY_DATASOURCE, value); preferences.putBoolean(GROUP_ITEMS_IN_TREE_BY_DATASOURCE, value);
} }
/**
* Get the user preference which identifies whether tags should be shown for
* only the current user or all users.
*
* @return true for just the current user, false for all users
*/
public static boolean showOnlyCurrentUserTags() {
return preferences.getBoolean(SHOW_ONLY_CURRENT_USER_TAGS, false);
}
/**
* Set the user preference which identifies whether tags should be shown for
* only the current user or all users.
*
* @param value - true for just the current user, false for all users
*/
public static void setShowOnlyCurrentUserTags(boolean value) {
preferences.putBoolean(SHOW_ONLY_CURRENT_USER_TAGS, value);
}
/** /**
* Reads persisted case database connection info. * Reads persisted case database connection info.
* *
@ -379,6 +401,7 @@ public final class UserPreferences {
/** /**
* get the maximum number of log files to save * get the maximum number of log files to save
*
* @return Number of log files * @return Number of log files
*/ */
public static int getLogFileCount() { public static int getLogFileCount() {
@ -387,13 +410,16 @@ public final class UserPreferences {
/** /**
* get the default number of log files to save * get the default number of log files to save
*
* @return LOG_FILE_COUNT * @return LOG_FILE_COUNT
*/ */
public static int getDefaultLogFileCount() { public static int getDefaultLogFileCount() {
return LOG_FILE_NUM_INT; return LOG_FILE_NUM_INT;
} }
/** /**
* Set the maximum number of log files to save * Set the maximum number of log files to save
*
* @param count number of log files * @param count number of log files
*/ */
public static void setLogFileCount(int count) { public static void setLogFileCount(int count) {

View File

@ -169,7 +169,7 @@ public class FileUtil {
public static String escapeFileName(String fileName) { public static String escapeFileName(String fileName) {
//for now escaping /:"*?<>| (not valid in file name, at least on Windows) //for now escaping /:"*?<>| (not valid in file name, at least on Windows)
//with underscores. We are only keeping \ as it could be part of the path. //with underscores. We are only keeping \ as it could be part of the path.
return fileName.replaceAll("[/:\"*?<>|]+", "_"); return fileName.replaceAll("[\\p{Cntrl}/:\"*?<>|]+", "_");
} }
/** /**

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2014 Basis Technology Corp. * Copyright 2013-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.coreutils;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.RuntimeProperties;
/** /**
* Validates absolute path (e.g. to a data source or case output folder) * Validates absolute path (e.g. to a data source or case output folder)
@ -29,8 +30,17 @@ import org.sleuthkit.autopsy.casemodule.Case;
public final class PathValidator { public final class PathValidator {
private static final Pattern driveLetterPattern = Pattern.compile("^[Cc]:.*$"); private static final Pattern driveLetterPattern = Pattern.compile("^[Cc]:.*$");
private static final Pattern unixMediaDrivePattern = Pattern.compile("^\\/(media|mnt)\\/.*$");
public static boolean isValid(String path, Case.CaseType caseType) { /**
* Checks if the provided path is valid given the case type.
*
* @param path - the path to validate
* @param caseType - the type of case which the path is being validated for
*
* @return - boolean true for valid path, false for invalid path
*/
public static boolean isValidForMultiUserCase(String path, Case.CaseType caseType) {
if (caseType == Case.CaseType.MULTI_USER_CASE) { if (caseType == Case.CaseType.MULTI_USER_CASE) {
// check that path is not on "C:" drive // check that path is not on "C:" drive
@ -44,6 +54,40 @@ public final class PathValidator {
return true; return true;
} }
public static boolean isValidForRunningOnTarget(String path) {
if (checkForLiveAutopsy()) {
if (PlatformUtil.isWindowsOS()) {
if (pathOnCDrive(path)) {
return false;
}
} else if (System.getProperty("os.name").toLowerCase().contains("nux") && !pathIsMedia(path)) {
return false;
}
}
return true;
}
/**
* Checks whether Autopsy is running from the external disk
*
* @return true if Autopsy is running from external USB or CD
*/
private static boolean checkForLiveAutopsy() {
return RuntimeProperties.isRunningInTarget();
}
/**
* Checks whether a file path contains "/mnt" or "/media"
*
* @param filePath Input file absolute path
*
* @return true if path matches the pattern, false otherwise
*/
private static boolean pathIsMedia(String filePath) {
Matcher matcher = unixMediaDrivePattern.matcher(filePath);
return matcher.find();
}
/** /**
* Checks whether a file path contains drive letter defined by pattern. * Checks whether a file path contains drive letter defined by pattern.
* *
@ -55,4 +99,20 @@ public final class PathValidator {
Matcher m = driveLetterPattern.matcher(filePath); Matcher m = driveLetterPattern.matcher(filePath);
return m.find(); return m.find();
} }
/**
* Checks if the provided path is valid given the case type.
*
* @param path - the path to validate
* @param caseType - the type of case which the path is being validated for
*
* @return - boolean true for valid path, false for invalid path
*
* @deprecated - PathValidator.isValidForMultiUserCase directly replaces
* PathValidator.isValid
*/
@Deprecated
public static boolean isValid(String path, Case.CaseType caseType) {
return isValidForMultiUserCase(path, caseType);
}
} }

View File

@ -21,14 +21,12 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
@ -38,9 +36,6 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*; import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*;
import static org.sleuthkit.autopsy.datamodel.Bundle.*; import static org.sleuthkit.autopsy.datamodel.Bundle.*;

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -29,6 +29,7 @@ import org.sleuthkit.datamodel.AbstractFile;
* Abstract class that implements the commonality between File and Directory * Abstract class that implements the commonality between File and Directory
* Nodes (same properties). * Nodes (same properties).
* *
* @param <T> extends AbstractFile
*/ */
public abstract class AbstractFsContentNode<T extends AbstractFile> extends AbstractAbstractFileNode<T> { public abstract class AbstractFsContentNode<T extends AbstractFile> extends AbstractAbstractFileNode<T> {

View File

@ -48,8 +48,6 @@ import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked; import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;

View File

@ -27,6 +27,7 @@ import javax.swing.Action;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
@ -59,6 +60,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
this.tag = tag; this.tag = tag;
} }
@Messages({"BlackboardArtifactTagNode.createSheet.userName.text=User Name"})
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet propertySheet = super.createSheet(); Sheet propertySheet = super.createSheet();
@ -80,6 +82,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
Logger.getLogger(ContentTagNode.class.getName()).log(Level.SEVERE, "Failed to get path for content (id = " + tag.getContent().getId() + ")", ex); //NON-NLS Logger.getLogger(ContentTagNode.class.getName()).log(Level.SEVERE, "Failed to get path for content (id = " + tag.getContent().getId() + ")", ex); //NON-NLS
contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text"); contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text");
} }
properties.put(new NodeProperty<>( properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
@ -95,7 +98,11 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.comment.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.comment.text"),
"", "",
tag.getComment())); tag.getComment()));
properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.userName.text"),
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.userName.text"),
"",
tag.getUserName()));
return propertySheet; return propertySheet;
} }

View File

@ -59,8 +59,8 @@ class ContentTagNode extends DisplayableItemNode {
@Messages({ @Messages({
"ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash", "ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash",
"ContentTagNode.createSheet.artifactMD5.name=MD5 Hash" "ContentTagNode.createSheet.artifactMD5.name=MD5 Hash",
}) "ContentTagNode.createSheet.userName.text=User Name"})
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Content content = tag.getContent(); Content content = tag.getContent();
@ -115,6 +115,11 @@ class ContentTagNode extends DisplayableItemNode {
Bundle.ContentTagNode_createSheet_artifactMD5_displayName(), Bundle.ContentTagNode_createSheet_artifactMD5_displayName(),
"", "",
file != null ? StringUtils.defaultString(file.getMd5Hash()) : "")); file != null ? StringUtils.defaultString(file.getMd5Hash()) : ""));
properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.userName.text"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.userName.text"),
"",
tag.getUserName()));
return propertySheet; return propertySheet;
} }
@ -123,7 +128,8 @@ class ContentTagNode extends DisplayableItemNode {
List<Action> actions = new ArrayList<>(); List<Action> actions = new ArrayList<>();
actions.addAll(Arrays.asList(super.getActions(context))); actions.addAll(Arrays.asList(super.getActions(context)));
AbstractFile file = getLookup().lookup(AbstractFile.class); AbstractFile file = getLookup().lookup(AbstractFile.class
);
if (file != null) { if (file != null) {
actions.add(ViewFileInTimelineAction.createViewFileAction(file)); actions.add(ViewFileInTimelineAction.createViewFileAction(file));
} }

View File

@ -18,10 +18,11 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import org.sleuthkit.autopsy.commonfilesearch.CommonFilesNode; import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstanceNode;
import org.sleuthkit.autopsy.commonfilesearch.FileInstanceNode; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResultRootNode;
import org.sleuthkit.autopsy.commonfilesearch.InstanceCountNode; import org.sleuthkit.autopsy.commonfilesearch.InstanceCountNode;
import org.sleuthkit.autopsy.commonfilesearch.Md5Node; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueNode;
import org.sleuthkit.autopsy.commonfilesearch.CaseDBCommonAttributeInstanceNode;
import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsChildren.DeletedContentNode; import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsChildren.DeletedContentNode;
import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsNode; import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsNode;
import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNode; import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNode;
@ -115,11 +116,13 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(InterestingHits.SetNameNode ihsn); T visit(InterestingHits.SetNameNode ihsn);
T visit(Md5Node mn); T visit(CommonAttributeValueNode cavn);
T visit(CommonFilesNode cfn); T visit(CommonAttributeSearchResultRootNode cfn);
T visit(FileInstanceNode fin); T visit(CaseDBCommonAttributeInstanceNode fin);
T visit(CentralRepoCommonAttributeInstanceNode crfin);
T visit(InstanceCountNode icn); T visit(InstanceCountNode icn);
@ -192,17 +195,17 @@ public interface DisplayableItemNodeVisitor<T> {
protected abstract T defaultVisit(DisplayableItemNode c); protected abstract T defaultVisit(DisplayableItemNode c);
@Override @Override
public T visit(FileInstanceNode fin) { public T visit(CaseDBCommonAttributeInstanceNode fin) {
return defaultVisit(fin); return defaultVisit(fin);
} }
@Override @Override
public T visit(Md5Node mn) { public T visit(CommonAttributeValueNode cavn) {
return defaultVisit(mn); return defaultVisit(cavn);
} }
@Override @Override
public T visit(CommonFilesNode cfn) { public T visit(CommonAttributeSearchResultRootNode cfn) {
return defaultVisit(cfn); return defaultVisit(cfn);
} }
@ -211,6 +214,11 @@ public interface DisplayableItemNodeVisitor<T> {
return defaultVisit(icn); return defaultVisit(icn);
} }
@Override
public T visit(CentralRepoCommonAttributeInstanceNode crfin){
return defaultVisit(crfin);
}
@Override @Override
public T visit(DirectoryNode dn) { public T visit(DirectoryNode dn) {
return defaultVisit(dn); return defaultVisit(dn);

View File

@ -27,7 +27,7 @@ import java.util.List;
*/ */
public class FileTypeExtensions { public class FileTypeExtensions {
private final static List<String> IMAGE_EXTENSIONS = Arrays.asList(".jpg", ".jpeg", ".png", ".psd", ".nef", ".tiff", ".bmp", ".tec"); //NON-NLS private final static List<String> IMAGE_EXTENSIONS = Arrays.asList(".jpg", ".jpeg", ".png", ".psd", ".nef", ".tiff", ".bmp", ".tec", ".tif"); //NON-NLS
private final static List<String> VIDEO_EXTENSIONS = Arrays.asList(".aaf", ".3gp", ".asf", ".avi", ".m1v", ".m2v", //NON-NLS private final static List<String> VIDEO_EXTENSIONS = Arrays.asList(".aaf", ".3gp", ".asf", ".avi", ".m1v", ".m2v", //NON-NLS
".m4v", ".mp4", ".mov", ".mpeg", ".mpg", ".mpe", ".mp4", ".rm", ".wmv", ".mpv", ".flv", ".swf"); //NON-NLS ".m4v", ".mp4", ".mov", ".mpeg", ".mpg", ".mpe", ".mp4", ".rm", ".wmv", ".mpv", ".flv", ".swf"); //NON-NLS
private final static List<String> AUDIO_EXTENSIONS = Arrays.asList(".aiff", ".aif", ".flac", ".wav", ".m4a", ".ape", //NON-NLS private final static List<String> AUDIO_EXTENSIONS = Arrays.asList(".aiff", ".aif", ".flac", ".wav", ".m4a", ".ape", //NON-NLS

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -115,9 +116,7 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
@Override @Override
public Action[] getActions(boolean context) { public Action[] getActions(boolean context) {
List<Action> actionsList = new ArrayList<>(); List<Action> actionsList = new ArrayList<>();
for (Action a : super.getActions(true)) { actionsList.addAll(Arrays.asList(super.getActions(true)));
actionsList.add(a);
}
actionsList.add(new NewWindowViewAction( actionsList.add(new NewWindowViewAction(
NbBundle.getMessage(this.getClass(), "LayoutFileNode.getActions.viewInNewWin.text"), this)); NbBundle.getMessage(this.getClass(), "LayoutFileNode.getActions.viewInNewWin.text"), this));
actionsList.add(new ExternalViewerAction( actionsList.add(new ExternalViewerAction(
@ -127,8 +126,8 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
actionsList.add(null); // creates a menu separator actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance()); actionsList.add(AddContentTagAction.getInstance());
final Collection<AbstractFile> selectedFilesList = final Collection<AbstractFile> selectedFilesList
new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
if (selectedFilesList.size() == 1) { if (selectedFilesList.size() == 1) {
actionsList.add(DeleteFileContentTagAction.getInstance()); actionsList.add(DeleteFileContentTagAction.getInstance());
} }
@ -137,7 +136,6 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
return actionsList.toArray(new Action[actionsList.size()]); return actionsList.toArray(new Action[actionsList.size()]);
} }
void fillPropertyMap(Map<String, Object> map) { void fillPropertyMap(Map<String, Object> map) {
AbstractAbstractFileNode.fillPropertyMap(map, getContent()); AbstractAbstractFileNode.fillPropertyMap(map, getContent());
map.put(LayoutContentPropertyType.PARTS.toString(), content.getNumParts()); map.put(LayoutContentPropertyType.PARTS.toString(), content.getNumParts());

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -125,7 +125,7 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
logger.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex); logger.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex);
} }
} }
return actionsList.toArray(new Action[0]); return actionsList.toArray(new Action[actionsList.size()]);
} }
@Override @Override

View File

@ -55,7 +55,8 @@ public class Tags implements AutopsyVisitableItem {
// override of Children.Keys<T>.createNodes(). // override of Children.Keys<T>.createNodes().
private final TagResults tagResults = new TagResults(); private final TagResults tagResults = new TagResults();
private final String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text"); private final static String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text");
private static final String USER_NAME_PROPERTY = "user.name"; //NON-NLS
private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS
private final long datasourceObjId; private final long datasourceObjId;
@ -68,6 +69,15 @@ public class Tags implements AutopsyVisitableItem {
this.datasourceObjId = dsObjId; this.datasourceObjId = dsObjId;
} }
/**
* Return the display name used by the tags node in the tree.
*
* @return - DISPLAY_NAME
*/
public static String getTagsDisplayName() {
return DISPLAY_NAME;
}
long filteringDataSourceObjId() { long filteringDataSourceObjId() {
return this.datasourceObjId; return this.datasourceObjId;
} }
@ -98,13 +108,11 @@ public class Tags implements AutopsyVisitableItem {
*/ */
public class RootNode extends DisplayableItemNode { public class RootNode extends DisplayableItemNode {
public RootNode(long objId) { public RootNode(long objId) {
super(Children.create(new TagNameNodeFactory(objId), true), Lookups.singleton(DISPLAY_NAME)); super(Children.create(new TagNameNodeFactory(objId), true), Lookups.singleton(DISPLAY_NAME));
super.setName(DISPLAY_NAME); super.setName(DISPLAY_NAME);
super.setDisplayName(DISPLAY_NAME); super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension(ICON_PATH); this.setIconBaseWithExtension(ICON_PATH);
} }
@Override @Override
@ -134,6 +142,14 @@ public class Tags implements AutopsyVisitableItem {
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();
} }
/**
* Cause the contents of the RootNode and its children to be updated.
*/
public void refresh() {
tagResults.update();
}
} }
private class TagNameNodeFactory extends ChildFactory.Detachable<TagName> implements Observer { private class TagNameNodeFactory extends ChildFactory.Detachable<TagName> implements Observer {
@ -197,6 +213,7 @@ public class Tags implements AutopsyVisitableItem {
/** /**
* Constructor * Constructor
*
* @param objId data source object id * @param objId data source object id
*/ */
TagNameNodeFactory(long objId) { TagNameNodeFactory(long objId) {
@ -224,11 +241,17 @@ public class Tags implements AutopsyVisitableItem {
@Override @Override
protected boolean createKeys(List<TagName> keys) { protected boolean createKeys(List<TagName> keys) {
try { try {
List<TagName> tagNamesInUse;
List<TagName> tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource() ? if (UserPreferences.showOnlyCurrentUserTags()) {
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId) : String userName = System.getProperty(USER_NAME_PROPERTY);
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse() tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource()
; ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(userName);
} else {
tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse();
}
Collections.sort(tagNamesInUse); Collections.sort(tagNamesInUse);
keys.addAll(tagNamesInUse); keys.addAll(tagNamesInUse);
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
@ -276,15 +299,24 @@ public class Tags implements AutopsyVisitableItem {
long tagsCount = 0; long tagsCount = 0;
try { try {
TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager(); TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager();
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
if (UserPreferences.groupItemsInTreeByDatasource()) {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
} else {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
}
} else {
if (UserPreferences.groupItemsInTreeByDatasource()) { if (UserPreferences.groupItemsInTreeByDatasource()) {
tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId); tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId); tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId);
} } else {
else {
tagsCount = tm.getContentTagsCountByTagName(tagName); tagsCount = tm.getContentTagsCountByTagName(tagName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName); tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
} }
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(TagNameNode.class.getName()).log(Level.SEVERE, "Failed to get tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS Logger.getLogger(TagNameNode.class.getName()).log(Level.SEVERE, "Failed to get tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
} }
@ -387,9 +419,17 @@ public class Tags implements AutopsyVisitableItem {
private void updateDisplayName() { private void updateDisplayName() {
long tagsCount = 0; long tagsCount = 0;
try { try {
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId) : if (UserPreferences.showOnlyCurrentUserTags()) {
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName); String userName = System.getProperty(USER_NAME_PROPERTY);
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, userName);
} else {
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(ContentTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get content tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS Logger.getLogger(ContentTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get content tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
} }
@ -444,11 +484,19 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List<ContentTag> keys) { protected boolean createKeys(List<ContentTag> keys) {
// Use the content tags bearing the specified tag name as the keys. // Use the content tags bearing the specified tag name as the keys.
try { try {
List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource() ? List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource()
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId) : ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId)
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName); : Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
keys.add(tag);
}
}
} else {
keys.addAll(contentTags); keys.addAll(contentTags);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(ContentTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS Logger.getLogger(ContentTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
} }
@ -492,9 +540,16 @@ public class Tags implements AutopsyVisitableItem {
private void updateDisplayName() { private void updateDisplayName() {
long tagsCount = 0; long tagsCount = 0;
try { try {
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ? if (UserPreferences.showOnlyCurrentUserTags()) {
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId) : String userName = System.getProperty(USER_NAME_PROPERTY);
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName); tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
} else {
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(BlackboardArtifactTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get blackboard artifact tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS Logger.getLogger(BlackboardArtifactTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get blackboard artifact tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
} }
@ -549,10 +604,19 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List<BlackboardArtifactTag> keys) { protected boolean createKeys(List<BlackboardArtifactTag> keys) {
try { try {
// Use the blackboard artifact tags bearing the specified tag name as the keys. // Use the blackboard artifact tags bearing the specified tag name as the keys.
List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource() ? List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource()
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId) : ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId)
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName); : Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
for (BlackboardArtifactTag tag : artifactTags) {
if (userName.equals(tag.getUserName())) {
keys.add(tag);
}
}
} else {
keys.addAll(artifactTags); keys.addAll(artifactTags);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(BlackboardArtifactTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS Logger.getLogger(BlackboardArtifactTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
} }

View File

@ -50,8 +50,6 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode {
this.setDisplayName(nameForVirtualDirectory(ld)); this.setDisplayName(nameForVirtualDirectory(ld));
String name = ld.getName();
//set icon for name, special case for logical file set //set icon for name, special case for logical file set
if (ld.isDataSource()) { if (ld.isDataSource()) {
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS

View File

@ -305,7 +305,7 @@ final class RawDSInputPanel extends JPanel implements DocumentListener {
"RawDSInputPanel.noOpenCase.errMsg=Exception while getting open case."}) "RawDSInputPanel.noOpenCase.errMsg=Exception while getting open case."})
private void warnIfPathIsInvalid(String path) { private void warnIfPathIsInvalid(String path) {
try { try {
if (!PathValidator.isValid(path, Case.getCurrentCaseThrows().getCaseType())) { if (!PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) {
errorLabel.setVisible(true); errorLabel.setVisible(true);
errorLabel.setText(Bundle.RawDSInputPanel_error_text()); errorLabel.setText(Bundle.RawDSInputPanel_error_text());
} }

View File

@ -125,3 +125,4 @@ GroupDataSourcesDialog.queryLabel.text=Would you like to group by data source fo
GroupDataSourcesDialog.yesButton.text=Yes GroupDataSourcesDialog.yesButton.text=Yes
GroupDataSourcesDialog.noButton.text=No GroupDataSourcesDialog.noButton.text=No
GroupDataSourcesDialog.title=Group by Data Source? GroupDataSourcesDialog.title=Group by Data Source?
DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text=Hide Other User's Tags

View File

@ -41,7 +41,6 @@ import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction;
import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.commonfilesearch.FileInstanceNode;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -56,15 +55,17 @@ import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.datamodel.FileTypeExtensions; import org.sleuthkit.autopsy.datamodel.FileTypeExtensions;
import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode;
import org.sleuthkit.autopsy.commonfilesearch.InstanceCountNode; import org.sleuthkit.autopsy.commonfilesearch.InstanceCountNode;
import org.sleuthkit.autopsy.commonfilesearch.Md5Node; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueNode;
import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstanceNode;
import org.sleuthkit.autopsy.datamodel.LayoutFileNode; import org.sleuthkit.autopsy.datamodel.LayoutFileNode;
import org.sleuthkit.autopsy.datamodel.LocalFileNode; import org.sleuthkit.autopsy.datamodel.LocalFileNode;
import org.sleuthkit.autopsy.datamodel.LocalDirectoryNode; import org.sleuthkit.autopsy.datamodel.LocalDirectoryNode;
import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
import org.sleuthkit.autopsy.datamodel.Reports; import org.sleuthkit.autopsy.datamodel.Reports;
import org.sleuthkit.autopsy.datamodel.SlackFileNode; import org.sleuthkit.autopsy.datamodel.SlackFileNode;
import org.sleuthkit.autopsy.commonfilesearch.CaseDBCommonAttributeInstanceNode;
import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode; import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode;
import static org.sleuthkit.autopsy.directorytree.Bundle.*; import static org.sleuthkit.autopsy.directorytree.Bundle.DataResultFilterNode_viewSourceArtifact_text;
import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction; import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
@ -529,12 +530,17 @@ public class DataResultFilterNode extends FilterNode {
} }
@Override @Override
public AbstractAction visit(Md5Node md5n){ public AbstractAction visit(CommonAttributeValueNode md5n){
return null; return null;
} }
@Override @Override
public AbstractAction visit(FileInstanceNode fin){ public AbstractAction visit(CaseDBCommonAttributeInstanceNode fin){
return null;
}
@Override
public AbstractAction visit(CentralRepoCommonAttributeInstanceNode iccan){
return null; return null;
} }

View File

@ -21,7 +21,9 @@
<Component id="backButton" min="-2" max="-2" attributes="0"/> <Component id="backButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="forwardButton" min="-2" max="-2" attributes="0"/> <Component id="forwardButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="51" max="32767" attributes="0"/> <EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="showOnlyCurrentUserTagsCheckbox" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/> <Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/>
<Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/> <Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/>
@ -36,7 +38,10 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/> <EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0">
<Component id="showRejectedCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="showOnlyCurrentUserTagsCheckbox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/> <Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/>
</Group> </Group>
@ -151,5 +156,15 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="groupByDatasourceCheckBoxActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="groupByDatasourceCheckBoxActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="showOnlyCurrentUserTagsCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showOnlyCurrentUserTagsCheckboxActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -81,6 +81,7 @@ import org.sleuthkit.autopsy.datamodel.InterestingHits;
import org.sleuthkit.autopsy.datamodel.KeywordHits; import org.sleuthkit.autopsy.datamodel.KeywordHits;
import org.sleuthkit.autopsy.datamodel.ResultsNode; import org.sleuthkit.autopsy.datamodel.ResultsNode;
import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory; import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory;
import org.sleuthkit.autopsy.datamodel.Tags;
import org.sleuthkit.autopsy.datamodel.ViewsNode; import org.sleuthkit.autopsy.datamodel.ViewsNode;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts; import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.autopsy.datamodel.accounts.BINRange; import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
@ -137,6 +138,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
forwardButton.setEnabled(false); forwardButton.setEnabled(false);
groupByDatasourceCheckBox.setSelected(UserPreferences.groupItemsInTreeByDatasource()); groupByDatasourceCheckBox.setSelected(UserPreferences.groupItemsInTreeByDatasource());
showOnlyCurrentUserTagsCheckbox.setSelected(UserPreferences.showOnlyCurrentUserTags());
} }
/** /**
@ -152,6 +154,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
case UserPreferences.GROUP_ITEMS_IN_TREE_BY_DATASOURCE: case UserPreferences.GROUP_ITEMS_IN_TREE_BY_DATASOURCE:
refreshContentTreeSafe(); refreshContentTreeSafe();
break; break;
case UserPreferences.SHOW_ONLY_CURRENT_USER_TAGS:
refreshTagsTree();
break;
case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE: case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE:
case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE: case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE:
// TODO: Need a way to refresh the Views subtree // TODO: Need a way to refresh the Views subtree
@ -191,6 +196,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
forwardButton = new javax.swing.JButton(); forwardButton = new javax.swing.JButton();
showRejectedCheckBox = new javax.swing.JCheckBox(); showRejectedCheckBox = new javax.swing.JCheckBox();
groupByDatasourceCheckBox = new javax.swing.JCheckBox(); groupByDatasourceCheckBox = new javax.swing.JCheckBox();
showOnlyCurrentUserTagsCheckbox = new javax.swing.JCheckBox();
treeView.setBorder(null); treeView.setBorder(null);
@ -235,6 +241,13 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(showOnlyCurrentUserTagsCheckbox, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text")); // NOI18N
showOnlyCurrentUserTagsCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
showOnlyCurrentUserTagsCheckboxActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -244,7 +257,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 51, Short.MAX_VALUE) .addGap(18, 18, 18)
.addComponent(showOnlyCurrentUserTagsCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(showRejectedCheckBox) .addComponent(showRejectedCheckBox)
.addComponent(groupByDatasourceCheckBox)) .addComponent(groupByDatasourceCheckBox))
@ -256,7 +271,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(5, 5, 5) .addGap(5, 5, 5)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(showRejectedCheckBox) .addComponent(showRejectedCheckBox)
.addComponent(showOnlyCurrentUserTagsCheckbox))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(groupByDatasourceCheckBox)) .addComponent(groupByDatasourceCheckBox))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
@ -323,10 +340,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
UserPreferences.setGroupItemsInTreeByDatasource(this.groupByDatasourceCheckBox.isSelected()); UserPreferences.setGroupItemsInTreeByDatasource(this.groupByDatasourceCheckBox.isSelected());
}//GEN-LAST:event_groupByDatasourceCheckBoxActionPerformed }//GEN-LAST:event_groupByDatasourceCheckBoxActionPerformed
private void showOnlyCurrentUserTagsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showOnlyCurrentUserTagsCheckboxActionPerformed
UserPreferences.setShowOnlyCurrentUserTags(this.showOnlyCurrentUserTagsCheckbox.isSelected());
}//GEN-LAST:event_showOnlyCurrentUserTagsCheckboxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton backButton; private javax.swing.JButton backButton;
private javax.swing.JButton forwardButton; private javax.swing.JButton forwardButton;
private javax.swing.JCheckBox groupByDatasourceCheckBox; private javax.swing.JCheckBox groupByDatasourceCheckBox;
private javax.swing.JCheckBox showOnlyCurrentUserTagsCheckbox;
private javax.swing.JCheckBox showRejectedCheckBox; private javax.swing.JCheckBox showRejectedCheckBox;
private javax.swing.JScrollPane treeView; private javax.swing.JScrollPane treeView;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ -890,6 +912,29 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
SwingUtilities.invokeLater(this::rebuildTree); SwingUtilities.invokeLater(this::rebuildTree);
} }
/**
* Refresh only the tags subtree(s) of the tree view.
*/
private void refreshTagsTree() {
SwingUtilities.invokeLater(() -> {
// if no open case or has no data then there is no tree to rebuild
if (UserPreferences.groupItemsInTreeByDatasource()) {
for (Node dataSource : autopsyTreeChildren.getNodes()) {
Node tagsNode = dataSource.getChildren().findChild(Tags.getTagsDisplayName());
if (tagsNode != null) {
//Reports is at the same level as the data sources so we want to ignore it
((Tags.RootNode)tagsNode).refresh();
}
}
} else {
Node tagsNode = autopsyTreeChildren.findChild(Tags.getTagsDisplayName());
if (tagsNode != null) {
((Tags.RootNode)tagsNode).refresh();
}
}
});
}
/** /**
* Rebuilds the autopsy tree. * Rebuilds the autopsy tree.
* *

View File

@ -25,7 +25,9 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
@ -33,7 +35,6 @@ import javax.swing.JOptionPane;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.Cancellable; import org.openide.util.Cancellable;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.Utilities; import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -44,8 +45,6 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor; import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Extracts AbstractFiles to a location selected by the user. * Extracts AbstractFiles to a location selected by the user.
@ -54,6 +53,8 @@ public final class ExtractAction extends AbstractAction {
private Logger logger = Logger.getLogger(ExtractAction.class.getName()); private Logger logger = Logger.getLogger(ExtractAction.class.getName());
private String userDefinedExportPath;
// This class is a singleton to support multi-selection of nodes, since // This class is a singleton to support multi-selection of nodes, since
// org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
// node in the array returns a reference to the same action object from Node.getActions(boolean). // node in the array returns a reference to the same action object from Node.getActions(boolean).
@ -66,6 +67,9 @@ public final class ExtractAction extends AbstractAction {
return instance; return instance;
} }
/**
* Private constructor for the action.
*/
private ExtractAction() { private ExtractAction() {
super(NbBundle.getMessage(ExtractAction.class, "ExtractAction.title.extractFiles.text")); super(NbBundle.getMessage(ExtractAction.class, "ExtractAction.title.extractFiles.text"));
} }
@ -94,64 +98,70 @@ public final class ExtractAction extends AbstractAction {
/** /**
* Called when user has selected a single file to extract * Called when user has selected a single file to extract
* *
* @param e * @param event
* @param selectedFile Selected file * @param selectedFile Selected file
*/ */
@NbBundle.Messages({"ExtractAction.noOpenCase.errMsg=No open case available."}) @NbBundle.Messages({"ExtractAction.noOpenCase.errMsg=No open case available."})
private void extractFile(ActionEvent e, AbstractFile selectedFile) { private void extractFile(ActionEvent event, AbstractFile selectedFile) {
Case openCase; Case openCase;
try { try {
openCase = Case.getCurrentCaseThrows(); openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExtractAction_noOpenCase_errMsg()); JOptionPane.showMessageDialog((Component) event.getSource(), Bundle.ExtractAction_noOpenCase_errMsg());
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
return; return;
} }
JFileChooser fileChooser = new JFileChooser(); JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
// If there is an attribute name, change the ":". Otherwise the extracted file will be hidden // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName()))); fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName())));
if (fileChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) { if (fileChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {
updateExportDirectory(fileChooser.getSelectedFile().getParent(), openCase);
ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>(); ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>();
fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile())); fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile()));
runExtractionTasks(e, fileExtractionTasks); runExtractionTasks(event, fileExtractionTasks);
} }
} }
/** /**
* Called when a user has selected multiple files to extract * Called when a user has selected multiple files to extract
* *
* @param e * @param event
* @param selectedFiles Selected files * @param selectedFiles Selected files
*/ */
private void extractFiles(ActionEvent e, Collection<? extends AbstractFile> selectedFiles) { private void extractFiles(ActionEvent event, Collection<? extends AbstractFile> selectedFiles) {
Case openCase; Case openCase;
try { try {
openCase = Case.getCurrentCaseThrows(); openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExtractAction_noOpenCase_errMsg()); JOptionPane.showMessageDialog((Component) event.getSource(), Bundle.ExtractAction_noOpenCase_errMsg());
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
return; return;
} }
JFileChooser folderChooser = new JFileChooser(); JFileChooser folderChooser = new JFileChooser();
folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
folderChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); folderChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
if (folderChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) { if (folderChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {
File destinationFolder = folderChooser.getSelectedFile(); File destinationFolder = folderChooser.getSelectedFile();
if (!destinationFolder.exists()) { if (!destinationFolder.exists()) {
try { try {
destinationFolder.mkdirs(); destinationFolder.mkdirs();
} catch (Exception ex) { } catch (Exception ex) {
JOptionPane.showMessageDialog((Component) e.getSource(), NbBundle.getMessage(this.getClass(), JOptionPane.showMessageDialog((Component) event.getSource(), NbBundle.getMessage(this.getClass(),
"ExtractAction.extractFiles.cantCreateFolderErr.msg")); "ExtractAction.extractFiles.cantCreateFolderErr.msg"));
logger.log(Level.INFO, "Unable to create folder(s) for user " + destinationFolder.getAbsolutePath(), ex); //NON-NLS logger.log(Level.INFO, "Unable to create folder(s) for user " + destinationFolder.getAbsolutePath(), ex); //NON-NLS
return; return;
} }
} }
updateExportDirectory(destinationFolder.getPath(), openCase);
/* get the unique set of files from the list. A user once reported extraction taking /*
* days because it was extracting the same PST file 20k times. They selected 20k * get the unique set of files from the list. A user once reported
* email messages in the tree and chose to extract them. */ * extraction taking days because it was extracting the same PST
* file 20k times. They selected 20k email messages in the tree and
* chose to extract them.
*/
Set<AbstractFile> uniqueFiles = new HashSet<>(selectedFiles); Set<AbstractFile> uniqueFiles = new HashSet<>(selectedFiles);
// make a task for each file // make a task for each file
@ -160,11 +170,57 @@ public final class ExtractAction extends AbstractAction {
// If there is an attribute name, change the ":". Otherwise the extracted file will be hidden // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
fileExtractionTasks.add(new FileExtractionTask(source, new File(destinationFolder, source.getId() + "-" + FileUtil.escapeFileName(source.getName())))); fileExtractionTasks.add(new FileExtractionTask(source, new File(destinationFolder, source.getId() + "-" + FileUtil.escapeFileName(source.getName()))));
} }
runExtractionTasks(e, fileExtractionTasks); runExtractionTasks(event, fileExtractionTasks);
} }
} }
private void runExtractionTasks(ActionEvent e, ArrayList<FileExtractionTask> fileExtractionTasks) { /**
* Get the export directory path.
*
* @param openCase The current case.
*
* @return The export directory path.
*/
private String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) {
return caseExportPath;
}
File file = new File(userDefinedExportPath);
if (file.exists() == false || file.isDirectory() == false) {
return caseExportPath;
}
return userDefinedExportPath;
}
/**
* Update the default export directory. If the directory path matches the
* case export directory, then the directory used will always match the
* export directory of any given case. Otherwise, the path last used will be
* saved.
*
* @param exportPath The export path.
* @param openCase The current case.
*/
private void updateExportDirectory(String exportPath, Case openCase) {
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
userDefinedExportPath = null;
} else {
userDefinedExportPath = exportPath;
}
}
/**
* Execute a series of file extraction tasks.
*
* @param event ActionEvent whose source will be used for
* centering popup dialogs.
* @param fileExtractionTasks List of file extraction tasks.
*/
private void runExtractionTasks(ActionEvent event, List<FileExtractionTask> fileExtractionTasks) {
// verify all of the sources and destinations are OK // verify all of the sources and destinations are OK
for (Iterator<FileExtractionTask> it = fileExtractionTasks.iterator(); it.hasNext();) { for (Iterator<FileExtractionTask> it = fileExtractionTasks.iterator(); it.hasNext();) {
@ -177,16 +233,16 @@ public final class ExtractAction extends AbstractAction {
} }
/* /*
* This code assumes that each destination is unique. We previously satisfied * This code assumes that each destination is unique. We previously
* that by adding the unique ID. * satisfied that by adding the unique ID.
*/ */
if (task.destination.exists()) { if (task.destination.exists()) {
if (JOptionPane.showConfirmDialog((Component) e.getSource(), if (JOptionPane.showConfirmDialog((Component) event.getSource(),
NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.msg", task.destination.getAbsolutePath()), NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.msg", task.destination.getAbsolutePath()),
NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.title"), NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.title"),
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
if (!FileUtil.deleteFileDir(task.destination)) { if (!FileUtil.deleteFileDir(task.destination)) {
JOptionPane.showMessageDialog((Component) e.getSource(), JOptionPane.showMessageDialog((Component) event.getSource(),
NbBundle.getMessage(this.getClass(), "ExtractAction.msgDlg.cantOverwriteFile.msg", task.destination.getAbsolutePath())); NbBundle.getMessage(this.getClass(), "ExtractAction.msgDlg.cantOverwriteFile.msg", task.destination.getAbsolutePath()));
it.remove(); it.remove();
} }
@ -210,11 +266,20 @@ public final class ExtractAction extends AbstractAction {
} }
} }
/**
* Stores source and destination for file extraction.
*/
private class FileExtractionTask { private class FileExtractionTask {
AbstractFile source; AbstractFile source;
File destination; File destination;
/**
* Create an instance of the FileExtractionTask.
*
* @param source The file to be extracted.
* @param destination The destination for the extraction.
*/
FileExtractionTask(AbstractFile source, File destination) { FileExtractionTask(AbstractFile source, File destination) {
this.source = source; this.source = source;
this.destination = destination; this.destination = destination;
@ -226,11 +291,16 @@ public final class ExtractAction extends AbstractAction {
*/ */
private class FileExtracter extends SwingWorker<Object, Void> { private class FileExtracter extends SwingWorker<Object, Void> {
private Logger logger = Logger.getLogger(FileExtracter.class.getName()); private final Logger logger = Logger.getLogger(FileExtracter.class.getName());
private ProgressHandle progress; private ProgressHandle progress;
private ArrayList<FileExtractionTask> extractionTasks; private final List<FileExtractionTask> extractionTasks;
FileExtracter(ArrayList<FileExtractionTask> extractionTasks) { /**
* Create an instance of the FileExtracter.
*
* @param extractionTasks List of file extraction tasks.
*/
FileExtracter(List<FileExtractionTask> extractionTasks) {
this.extractionTasks = extractionTasks; this.extractionTasks = extractionTasks;
} }
@ -275,7 +345,7 @@ public final class ExtractAction extends AbstractAction {
boolean msgDisplayed = false; boolean msgDisplayed = false;
try { try {
super.get(); super.get();
} catch (Exception ex) { } catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS
MessageNotifyUtil.Message.info( MessageNotifyUtil.Message.info(
NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.extractErr", ex.getMessage())); NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.extractErr", ex.getMessage()));
@ -289,22 +359,23 @@ public final class ExtractAction extends AbstractAction {
} }
} }
private int calculateProgressBarWorkUnits(AbstractFile file) { /**
int workUnits = 0; * Calculate the number of work units for the progress bar.
if (file.isFile()) { *
workUnits += file.getSize(); * @param file File whose children will be reviewed to get the number of
} else { * work units.
try { *
for (Content child : file.getChildren()) { * @return The number of work units.
if (child instanceof AbstractFile) { */
workUnits += calculateProgressBarWorkUnits((AbstractFile) child); /*
} * private int calculateProgressBarWorkUnits(AbstractFile file) { int
} * workUnits = 0; if (file.isFile()) { workUnits += file.getSize(); }
} catch (TskCoreException ex) { * else { try { for (Content child : file.getChildren()) { if (child
logger.log(Level.SEVERE, "Could not get children of content", ex); //NON-NLS * instanceof AbstractFile) { workUnits +=
} * calculateProgressBarWorkUnits((AbstractFile) child); } } } catch
} * (TskCoreException ex) { logger.log(Level.SEVERE, "Could not get
return workUnits; * children of content", ex); //NON-NLS } } return workUnits;
} }
*/
} }
} }

View File

@ -61,14 +61,22 @@ import org.sleuthkit.datamodel.VolumeSystem;
* Extracts all the unallocated space as a single file * Extracts all the unallocated space as a single file
*/ */
final class ExtractUnallocAction extends AbstractAction { final class ExtractUnallocAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName()); private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName());
private final List<OutputFileData> filesToExtract = new ArrayList<>(); private final List<OutputFileData> filesToExtract = new ArrayList<>();
private static final Set<String> volumesInProgress = new HashSet<>(); private static final Set<String> volumesInProgress = new HashSet<>();
private static final Set<Long> imagesInProgress = new HashSet<>(); private static final Set<Long> imagesInProgress = new HashSet<>();
private static String userDefinedExportPath;
private long currentImage = 0L; private long currentImage = 0L;
private final boolean isImage; private final boolean isImage;
/**
* Create an instance of ExtractUnallocAction with a volume.
*
* @param title The title
* @param volume The volume set for extraction.
*/
public ExtractUnallocAction(String title, Volume volume) { public ExtractUnallocAction(String title, Volume volume) {
super(title); super(title);
isImage = false; isImage = false;
@ -82,6 +90,13 @@ final class ExtractUnallocAction extends AbstractAction {
} }
/**
* Create an instance of ExtractUnallocAction with an image.
*
* @param title The title.
* @param image The image set for extraction.
* @throws NoCurrentCaseException If no case is open.
*/
public ExtractUnallocAction(String title, Image image) throws NoCurrentCaseException { public ExtractUnallocAction(String title, Image image) throws NoCurrentCaseException {
super(title); super(title);
isImage = true; isImage = true;
@ -101,7 +116,7 @@ final class ExtractUnallocAction extends AbstractAction {
* Writes the unallocated files to * Writes the unallocated files to
* $CaseDir/Export/ImgName-Unalloc-ImgObjectID-VolumeID.dat * $CaseDir/Export/ImgName-Unalloc-ImgObjectID-VolumeID.dat
* *
* @param e * @param event
*/ */
@NbBundle.Messages({"# {0} - fileName", @NbBundle.Messages({"# {0} - fileName",
"ExtractUnallocAction.volumeInProgress=Already extracting unallocated space into {0} - will skip this volume", "ExtractUnallocAction.volumeInProgress=Already extracting unallocated space into {0} - will skip this volume",
@ -110,8 +125,8 @@ final class ExtractUnallocAction extends AbstractAction {
"ExtractUnallocAction.imageError=Error extracting unallocated space from image", "ExtractUnallocAction.imageError=Error extracting unallocated space from image",
"ExtractUnallocAction.noOpenCase.errMsg=No open case available."}) "ExtractUnallocAction.noOpenCase.errMsg=No open case available."})
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent event) {
if (filesToExtract != null && filesToExtract.size() > 0) { if (filesToExtract != null && filesToExtract.isEmpty() == false) {
// This check doesn't absolutely guarantee that the image won't be in progress when we make the worker, // This check doesn't absolutely guarantee that the image won't be in progress when we make the worker,
// but in general it will suffice. // but in general it will suffice.
if (isImage && isImageInProgress(currentImage)) { if (isImage && isImageInProgress(currentImage)) {
@ -145,14 +160,17 @@ final class ExtractUnallocAction extends AbstractAction {
} }
}; };
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
fileChooser.setDialogTitle( fileChooser.setDialogTitle(
NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg")); NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg"));
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setAcceptAllFileFilterUsed(false); fileChooser.setAcceptAllFileFilterUsed(false);
int returnValue = fileChooser.showSaveDialog((Component) e.getSource()); int returnValue = fileChooser.showSaveDialog((Component) event.getSource());
if (returnValue == JFileChooser.APPROVE_OPTION) { if (returnValue == JFileChooser.APPROVE_OPTION) {
String destination = fileChooser.getSelectedFile().getPath(); String destination = fileChooser.getSelectedFile().getPath();
updateExportDirectory(destination, openCase);
for (OutputFileData outputFileData : filesToExtract) { for (OutputFileData outputFileData : filesToExtract) {
outputFileData.setPath(destination); outputFileData.setPath(destination);
@ -177,8 +195,8 @@ final class ExtractUnallocAction extends AbstractAction {
try { try {
ExtractUnallocWorker worker = new ExtractUnallocWorker(outputFileData); ExtractUnallocWorker worker = new ExtractUnallocWorker(outputFileData);
worker.execute(); worker.execute();
} catch (Exception ex){ } catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting unallocated space into " + outputFileData.getFileName()); logger.log(Level.WARNING, "Already extracting unallocated space into {0}", outputFileData.getFileName());
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeInProgress", outputFileData.getFileName())); MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeInProgress", outputFileData.getFileName()));
} }
} }
@ -214,22 +232,60 @@ final class ExtractUnallocAction extends AbstractAction {
} }
} }
} }
}
/**
* Get the export directory path.
*
* @param openCase The current case.
*
* @return The export directory path.
*/
private String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) {
return caseExportPath;
}
File file = new File(userDefinedExportPath);
if (file.exists() == false || file.isDirectory() == false) {
return caseExportPath;
}
return userDefinedExportPath;
}
/**
* Update the default export directory. If the directory path matches the
* case export directory, then the directory used will always match the
* export directory of any given case. Otherwise, the path last used will be
* saved.
*
* @param exportPath The export path.
* @param openCase The current case.
*/
private void updateExportDirectory(String exportPath, Case openCase) {
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
userDefinedExportPath = null;
} else {
userDefinedExportPath = exportPath;
}
} }
/** /**
* Gets all the unallocated files in a given Content. * Gets all the unallocated files in a given Content.
* *
* @param c Content to get Unallocated Files from * @param content Content to get Unallocated Files from
* *
* @return A list<LayoutFile> if it didn't crash List may be empty. * @return A list<LayoutFile> if it didn't crash List may be empty.
*/ */
private List<LayoutFile> getUnallocFiles(Content c) { private List<LayoutFile> getUnallocFiles(Content content) {
UnallocVisitor uv = new UnallocVisitor(); UnallocVisitor unallocVisitor = new UnallocVisitor();
try { try {
for (Content contentChild : c.getChildren()) { for (Content contentChild : content.getChildren()) {
if (contentChild instanceof AbstractContent) { if (contentChild instanceof AbstractContent) {
return contentChild.accept(uv); //call on first non-artifact child added to database return contentChild.accept(unallocVisitor); //call on first non-artifact child added to database
} }
} }
} catch (TskCoreException tce) { } catch (TskCoreException tce) {
@ -253,22 +309,21 @@ final class ExtractUnallocAction extends AbstractAction {
return volumesInProgress.contains(volumeOutputFileName); return volumesInProgress.contains(volumeOutputFileName);
} }
synchronized static private void addImageInProgress(Long id) throws TskCoreException { synchronized static private void addImageInProgress(Long objId) throws TskCoreException {
if(imagesInProgress.contains(id)){ if (imagesInProgress.contains(objId)) {
throw new TskCoreException("Image " + id + " is in use"); throw new TskCoreException("Image " + objId + " is in use");
} }
imagesInProgress.add(id); imagesInProgress.add(objId);
} }
synchronized static private void removeImageInProgress(Long id){ synchronized static private void removeImageInProgress(Long objId) {
imagesInProgress.remove(id); imagesInProgress.remove(objId);
} }
synchronized static private boolean isImageInProgress(Long id){ synchronized static private boolean isImageInProgress(Long objId) {
return imagesInProgress.contains(id); return imagesInProgress.contains(objId);
} }
/** /**
* Private class for dispatching the file IO in a background thread. * Private class for dispatching the file IO in a background thread.
*/ */
@ -276,9 +331,9 @@ final class ExtractUnallocAction extends AbstractAction {
private ProgressHandle progress; private ProgressHandle progress;
private boolean canceled = false; private boolean canceled = false;
private List<OutputFileData> outputFileDataList = new ArrayList<>(); private final List<OutputFileData> outputFileDataList = new ArrayList<>();
private File currentlyProcessing; private File currentlyProcessing;
private int totalSizeinMegs; private final int totalSizeinMegs;
long totalBytes = 0; long totalBytes = 0;
ExtractUnallocWorker(OutputFileData outputFileData) throws TskCoreException { ExtractUnallocWorker(OutputFileData outputFileData) throws TskCoreException {
@ -300,7 +355,7 @@ final class ExtractUnallocAction extends AbstractAction {
totalBytes += outputFileData.getSizeInBytes(); totalBytes += outputFileData.getSizeInBytes();
this.outputFileDataList.add(outputFileData); this.outputFileDataList.add(outputFileData);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting data into " + outputFileData.getFileName()); logger.log(Level.WARNING, "Already extracting data into {0}", outputFileData.getFileName());
} }
} }
@ -314,9 +369,9 @@ final class ExtractUnallocAction extends AbstractAction {
private int toMb(long bytes) { private int toMb(long bytes) {
if (bytes > 1024 && (bytes / 1024.0) <= Double.MAX_VALUE) { if (bytes > 1024 && (bytes / 1024.0) <= Double.MAX_VALUE) {
double Mb = ((bytes / 1024.0) / 1024.0);//Bytes -> Megabytes double megabytes = ((bytes / 1024.0) / 1024.0);//Bytes -> Megabytes
if (Mb <= Integer.MAX_VALUE) { if (megabytes <= Integer.MAX_VALUE) {
return (int) Math.ceil(Mb); return (int) Math.ceil(megabytes);
} }
} }
return 0; return 0;
@ -347,7 +402,7 @@ final class ExtractUnallocAction extends AbstractAction {
int mbs = 0; //Increments every 128th tick of kbs int mbs = 0; //Increments every 128th tick of kbs
for (OutputFileData outputFileData : this.outputFileDataList) { for (OutputFileData outputFileData : this.outputFileDataList) {
currentlyProcessing = outputFileData.getFile(); currentlyProcessing = outputFileData.getFile();
logger.log(Level.INFO, "Writing Unalloc file to " + currentlyProcessing.getPath()); //NON-NLS logger.log(Level.INFO, "Writing Unalloc file to {0}", currentlyProcessing.getPath()); //NON-NLS
OutputStream outputStream = new FileOutputStream(currentlyProcessing); OutputStream outputStream = new FileOutputStream(currentlyProcessing);
long bytes = 0; long bytes = 0;
int i = 0; int i = 0;
@ -374,9 +429,9 @@ final class ExtractUnallocAction extends AbstractAction {
if (canceled) { if (canceled) {
outputFileData.getFile().delete(); outputFileData.getFile().delete();
logger.log(Level.INFO, "Canceled extraction of " + outputFileData.getFileName() + " and deleted file"); //NON-NLS logger.log(Level.INFO, "Canceled extraction of {0} and deleted file", outputFileData.getFileName()); //NON-NLS
} else { } else {
logger.log(Level.INFO, "Finished writing unalloc file " + outputFileData.getFile().getPath()); //NON-NLS logger.log(Level.INFO, "Finished writing unalloc file {0}", outputFileData.getFile().getPath()); //NON-NLS
} }
} }
progress.finish(); progress.finish();
@ -589,7 +644,7 @@ final class ExtractUnallocAction extends AbstractAction {
*/ */
private class OutputFileData { private class OutputFileData {
private List<LayoutFile> layoutFiles; private final List<LayoutFile> layoutFiles;
private final long sizeInBytes; private final long sizeInBytes;
private long volumeId; private long volumeId;
private long imageId; private long imageId;

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2015-2017 Basis Technology Corp. * Copyright 2015-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,16 +19,19 @@
package org.sleuthkit.autopsy.guiutils; package org.sleuthkit.autopsy.guiutils;
import java.awt.Component; import java.awt.Component;
import java.lang.reflect.InvocationTargetException;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JTable; import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER; import static javax.swing.SwingConstants.CENTER;
import org.openide.nodes.Node;
import org.openide.util.ImageUtilities; import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/** /**
* A JTable cell renderer that represents a status as a center-aligned icon, and * A JTable and outline view cell renderer that represents a status as a
* grays out the cell if the table is disabled. The statuses represented are OK, * center-aligned icon, and grays out the cell if the table is disabled. The
* WARNING, and ERROR. * statuses represented are OK, WARNING, and ERROR.
*/ */
public class StatusIconCellRenderer extends GrayableCellRenderer { public class StatusIconCellRenderer extends GrayableCellRenderer {
@ -45,8 +48,20 @@ public class StatusIconCellRenderer extends GrayableCellRenderer {
@Override @Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setHorizontalAlignment(CENTER); setHorizontalAlignment(CENTER);
if ((value instanceof Status)) { Object switchValue = null;
switch((Status) value) { if ((value instanceof NodeProperty)) {
//The Outline view has properties in the cell, the value contained in the property is what we want
try {
switchValue = ((Node.Property) value).getValue();
} catch (IllegalAccessException | InvocationTargetException ex) {
//Unable to get the value from the NodeProperty no Icon will be displayed
}
} else {
//JTables contain the value we want directly in the cell
switchValue = value;
}
if ((switchValue instanceof Status)) {
switch ((Status) switchValue) {
case OK: case OK:
setIcon(OK_ICON); setIcon(OK_ICON);
setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.ok")); setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.ok"));
@ -60,8 +75,7 @@ public class StatusIconCellRenderer extends GrayableCellRenderer {
setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.error")); setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.error"));
break; break;
} }
} } else {
else {
setIcon(null); setIcon(null);
setText(""); setText("");
} }

View File

@ -254,7 +254,7 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction impl
+ " echo %appName%\\bin\\%appName%64.exe does not exist\n" + " echo %appName%\\bin\\%appName%64.exe does not exist\n"
+ " goto end\n" + " goto end\n"
+ " )\n" + " )\n"
+ " %appName%\\bin\\%appName%64.exe --userdir ..\\configData\\userdir --cachedir ..\\configData\\cachedir -J-Djava.io.tmpdir=..\\configData\\temp\n" + " %appName%\\bin\\%appName%64.exe --userdir ..\\configData\\userdir --cachedir ..\\configData\\cachedir -J-Djava.io.tmpdir=..\\configData\\temp --liveAutopsy\n"
+ ") else (\n" + ") else (\n"
+ " echo Could not find %appName% directory\n" + " echo Could not find %appName% directory\n"
+ " goto end\n" + " goto end\n"

View File

@ -25,22 +25,27 @@ import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level; import java.util.logging.Level;
import net.sf.sevenzipjbinding.ArchiveFormat; import net.sf.sevenzipjbinding.ArchiveFormat;
import static net.sf.sevenzipjbinding.ArchiveFormat.RAR; import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
import net.sf.sevenzipjbinding.ExtractAskMode;
import net.sf.sevenzipjbinding.ISequentialOutStream; import net.sf.sevenzipjbinding.ISequentialOutStream;
import net.sf.sevenzipjbinding.ISevenZipInArchive; import net.sf.sevenzipjbinding.ISevenZipInArchive;
import net.sf.sevenzipjbinding.SevenZip; import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException; import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.SevenZipNativeInitializationException; import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import net.sf.sevenzipjbinding.ExtractOperationResult; import net.sf.sevenzipjbinding.ExtractOperationResult;
import net.sf.sevenzipjbinding.IArchiveExtractCallback;
import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
import net.sf.sevenzipjbinding.PropID;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
@ -158,8 +163,12 @@ class SevenZipExtractor {
* *
* @param archiveFile the AbstractFile for the parent archive which * @param archiveFile the AbstractFile for the parent archive which
* which we are checking * which we are checking
* @param archiveFileItem the current item being extracted from the parent * @param inArchive The SevenZip archive currently open for extraction
* archive *
* @param inArchiveItemIndex Index of item inside the SevenZip archive. Each
* file inside an archive is associated with a unique
* integer
*
* @param depthMap a concurrent hashmap which keeps track of the * @param depthMap a concurrent hashmap which keeps track of the
* depth of all nested archives, key of objectID * depth of all nested archives, key of objectID
* @param escapedFilePath the path to the archiveFileItem which has been * @param escapedFilePath the path to the archiveFileItem which has been
@ -167,19 +176,22 @@ class SevenZipExtractor {
* *
* @return true if potential zip bomb, false otherwise * @return true if potential zip bomb, false otherwise
*/ */
private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISimpleInArchiveItem archiveFileItem, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) { private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
try { try {
final Long archiveItemSize = archiveFileItem.getSize(); final Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE);
//skip the check for small files //skip the check for small files
if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) { if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
return false; return false;
} }
final Long archiveItemPackedSize = archiveFileItem.getPackedSize(); final Long archiveItemPackedSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.PACKED_SIZE);
if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) { if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", new Object[]{archiveFile.getName(), archiveFileItem.getPath()}); //NON-NLS logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", //NON-NLS
new Object[]{archiveFile.getName(), (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH)}); //NON-NLS
return false; return false;
} }
@ -366,8 +378,9 @@ class SevenZipExtractor {
* *
* @throws SevenZipException * @throws SevenZipException
*/ */
private String getPathInArchive(ISimpleInArchiveItem item, int itemNumber, AbstractFile archiveFile) throws SevenZipException { private String getPathInArchive(ISevenZipInArchive archive, int inArchiveItemIndex, AbstractFile archiveFile) throws SevenZipException {
String pathInArchive = item.getPath(); String pathInArchive = (String) archive.getProperty(
inArchiveItemIndex, PropID.PATH);
if (pathInArchive == null || pathInArchive.isEmpty()) { if (pathInArchive == null || pathInArchive.isEmpty()) {
//some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path //some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path
@ -400,7 +413,7 @@ class SevenZipExtractor {
} }
} }
if (useName == null) { if (useName == null) {
pathInArchive = "/" + archName + "/" + Integer.toString(itemNumber); pathInArchive = "/" + archName + "/" + Integer.toString(inArchiveItemIndex);
} else { } else {
pathInArchive = "/" + useName; pathInArchive = "/" + useName;
} }
@ -428,69 +441,6 @@ class SevenZipExtractor {
return node == null ? null : archiveFilePath + "/" + node.getFileName(); return node == null ? null : archiveFilePath + "/" + node.getFileName();
} }
/**
* Unpack an archive item to the disk using a password if specified.
*
* @param item - the archive item to unpack
* @param unpackedNode - the unpackedNode to add derivedInfo to
* @param password - the password for the archive, null if not
* used
* @param freeDiskSpace - the amount of free disk space
* @param uniqueExtractedName - the name of the file to extract the item to
*
* @return unpackedNode - the updated unpackedNode
*
* @throws SevenZipException
*/
private SevenZipExtractor.UnpackedTree.UnpackedNode unpackNode(ISimpleInArchiveItem item, SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode, String password, long freeDiskSpace, String uniqueExtractedName) throws SevenZipException {
//unpack locally if a file
final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
final Date createTime = item.getCreationTime();
final Date accessTime = item.getLastAccessTime();
final Date writeTime = item.getLastWriteTime();
final long createtime = createTime == null ? 0L : createTime.getTime() / 1000;
final long modtime = writeTime == null ? 0L : writeTime.getTime() / 1000;
final long accesstime = accessTime == null ? 0L : accessTime.getTime() / 1000;
SevenZipExtractor.UnpackStream unpackStream = null;
boolean isDir = item.isFolder();
if (!isDir) {
try {
// NOTE: item.getSize() may return null in case of certain
// archiving formats. Eg: BZ2
if (item.getSize() != null) {
unpackStream = new SevenZipExtractor.KnownSizeUnpackStream(localAbsPath, item.getSize());
} else {
unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream(localAbsPath, freeDiskSpace);
}
ExtractOperationResult result;
if (password == null) {
result = item.extractSlow(unpackStream);
} else {
result = item.extractSlow(unpackStream, password);
}
if (result != ExtractOperationResult.OK) {
logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", new Object[]{localAbsPath, result}); //NON-NLS
return null;
}
} catch (SevenZipException e) {
//could be something unexpected with this file, move on
logger.log(Level.WARNING, "Could not extract file from archive: " + localAbsPath, e); //NON-NLS
} finally {
if (unpackStream != null) {
//record derived data in unode, to be traversed later after unpacking the archive
unpackedNode.addDerivedInfo(unpackStream.getSize(), !isDir,
0L, createtime, accesstime, modtime, localRelPath);
unpackStream.close();
}
}
} else { // this is a directory, size is always 0
unpackedNode.addDerivedInfo(0, !isDir, 0L, createtime, accesstime, modtime, localRelPath);
}
return unpackedNode;
}
/** /**
* Unpack the file to local folder and return a list of derived files * Unpack the file to local folder and return a list of derived files
* *
@ -523,7 +473,6 @@ class SevenZipExtractor {
boolean hasEncrypted = false; boolean hasEncrypted = false;
boolean fullEncryption = true; boolean fullEncryption = true;
boolean progressStarted = false; boolean progressStarted = false;
int processedItems = 0;
final String archiveFilePath = getArchiveFilePath(archiveFile); final String archiveFilePath = getArchiveFilePath(archiveFile);
final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath); final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath);
HashMap<String, ZipFileStatusWrapper> statusMap = new HashMap<>(); HashMap<String, ZipFileStatusWrapper> statusMap = new HashMap<>();
@ -590,7 +539,7 @@ class SevenZipExtractor {
logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{escapedArchiveFilePath, numItems}); //NON-NLS logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{escapedArchiveFilePath, numItems}); //NON-NLS
progress.start(numItems); progress.start(numItems);
progressStarted = true; progressStarted = true;
final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface(); progress.progress(archiveFile.getName() + ": Analyzing archive metadata and creating local files");
//setup the archive local root folder //setup the archive local root folder
final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)); final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
@ -614,25 +563,18 @@ class SevenZipExtractor {
//currently getFreeDiskSpace always returns DISK_FREE_SPACE_UNKNOWN //currently getFreeDiskSpace always returns DISK_FREE_SPACE_UNKNOWN
freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN; freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN;
} }
//unpack and process every item in archive
int itemNumber = 0;
for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) { Map<Integer, InArchiveItemDetails> archiveDetailsMap = new LinkedHashMap<>();
String pathInArchive = getPathInArchive(item, itemNumber, archiveFile); for (int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) {
if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) {
//query for path in db
++itemNumber;
//check if possible zip bomb
if (isZipBombArchiveItemCheck(archiveFile, item, depthMap, escapedArchiveFilePath)) {
unpackSuccessful = false; unpackSuccessful = false;
return unpackSuccessful; return unpackSuccessful;
} }
SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
//update progress bar
progress.progress(archiveFile.getName() + ": " + item.getPath(), processedItems);
final boolean isEncrypted = item.isEncrypted(); String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile);
SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED);
if (isEncrypted && password == null) { if (isEncrypted && password == null) {
logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS
@ -642,20 +584,25 @@ class SevenZipExtractor {
} else { } else {
fullEncryption = false; fullEncryption = false;
} }
// NOTE: item.getSize() may return null in case of certain
// NOTE: item size may return null in case of certain
// archiving formats. Eg: BZ2 // archiving formats. Eg: BZ2
//check if unpacking this file will result in out of disk space //check if unpacking this file will result in out of disk space
//this is additional to zip bomb prevention mechanism //this is additional to zip bomb prevention mechanism
if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && item.getSize() != null && item.getSize() > 0) { //if free space is known and file is not empty. Long archiveItemSize = (Long) inArchive.getProperty(
long newDiskSpace = freeDiskSpace - item.getSize(); inArchiveItemIndex, PropID.SIZE);
if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) { //if free space is known and file is not empty.
String archiveItemPath = (String) inArchive.getProperty(
inArchiveItemIndex, PropID.PATH);
long newDiskSpace = freeDiskSpace - archiveItemSize;
if (newDiskSpace < MIN_FREE_DISK_SPACE) { if (newDiskSpace < MIN_FREE_DISK_SPACE) {
String msg = NbBundle.getMessage(SevenZipExtractor.class, String msg = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg", "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
escapedArchiveFilePath, item.getPath()); escapedArchiveFilePath, archiveItemPath);
String details = NbBundle.getMessage(SevenZipExtractor.class, String details = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details"); "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new String[]{escapedArchiveFilePath, item.getPath()}); //NON-NLS logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new String[]{escapedArchiveFilePath, archiveItemPath}); //NON-NLS
logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS
unpackSuccessful = false; unpackSuccessful = false;
continue; //skip this file continue; //skip this file
@ -664,42 +611,61 @@ class SevenZipExtractor {
freeDiskSpace = newDiskSpace; freeDiskSpace = newDiskSpace;
} }
} }
final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (item.getItemIndex() / 1000) + File.separator + item.getItemIndex() + "_" + new File(pathInArchive).getName()); final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex + "_" + new File(pathInArchive).getName());
final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
//create local dirs and empty files before extracted //create local dirs and empty files before extracted
File localFile = new java.io.File(moduleDirAbsolute + File.separator + uniqueExtractedName); File localFile = new java.io.File(localAbsPath);
//cannot rely on files in top-bottom order //cannot rely on files in top-bottom order
if (!localFile.exists()) { if (!localFile.exists()) {
try { try {
if (item.isFolder()) { if ((Boolean) inArchive.getProperty(
inArchiveItemIndex, PropID.IS_FOLDER)) {
localFile.mkdirs(); localFile.mkdirs();
} else { } else {
localFile.getParentFile().mkdirs(); localFile.getParentFile().mkdirs();
try { try {
localFile.createNewFile(); localFile.createNewFile();
} catch (IOException e) { } catch (IOException e) {
logger.log(Level.SEVERE, "Error creating extracted file: " + localFile.getAbsolutePath(), e); //NON-NLS logger.log(Level.SEVERE, "Error creating extracted file: "//NON-NLS
+ localFile.getAbsolutePath(), e);
} }
} }
} catch (SecurityException e) { } catch (SecurityException e) {
logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", pathInArchive); //NON-NLS logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", //NON-NLS
pathInArchive); //NON-NLS
//TODO consider bail out / msg to the user //TODO consider bail out / msg to the user
} }
} }
// skip the rest of this loop if we couldn't create the file // skip the rest of this loop if we couldn't create the file
//continue will skip details from being added to the map
if (localFile.exists() == false) { if (localFile.exists() == false) {
continue; continue;
} }
//find this node in the hierarchy, create if neede;
unpackedNode = unpackNode(item, unpackedNode, password, //Store archiveItemIndex with local paths and unpackedNode reference.
freeDiskSpace, uniqueExtractedName); //Necessary for the extract call back to write the current archive
if (unpackedNode == null) { //file to the correct disk location and to correctly update it's
unpackSuccessful = false; //corresponding unpackedNode
archiveDetailsMap.put(inArchiveItemIndex, new InArchiveItemDetails(
unpackedNode, localAbsPath, localRelPath));
} }
//update units for progress bar int[] extractionIndices = getExtractableFilesFromDetailsMap(archiveDetailsMap);
++processedItems;
} StandardIArchiveExtractCallback archiveCallBack
= new StandardIArchiveExtractCallback(
inArchive, archiveFile, progress,
archiveDetailsMap, password, freeDiskSpace);
//According to the documentation, indices in sorted order are optimal
//for efficiency. Hence, the LinkedHashMap and linear processing of
//inArchiveItemIndex. False indicates non-test mode
inArchive.extract(extractionIndices, false, archiveCallBack);
unpackSuccessful = unpackSuccessful & archiveCallBack.wasSuccessful();
// add them to the DB. We wait until the end so that we have the metadata on all of the // add them to the DB. We wait until the end so that we have the metadata on all of the
// intermediate nodes since the order is not guaranteed // intermediate nodes since the order is not guaranteed
try { try {
@ -799,6 +765,21 @@ class SevenZipExtractor {
return unpackSuccessful; return unpackSuccessful;
} }
/**
* Produce a list of archive indices needed for the call to extract, which
* will open the archive and begin unpacking the files.
*/
private int[] getExtractableFilesFromDetailsMap(
Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
.toArray(new Integer[archiveDetailsMap.size()]);
return Arrays.stream(wrappedExtractionIndices)
.mapToInt(Integer::intValue)
.toArray();
}
/** /**
* Stream used to unpack the archive to local file * Stream used to unpack the archive to local file
*/ */
@ -933,6 +914,207 @@ class SevenZipExtractor {
} }
} }
/**
* Wrapper for necessary details used in StandardIArchiveExtractCallback
*/
private static class InArchiveItemDetails {
private final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode;
private final String localAbsPath;
private final String localRelPath;
public InArchiveItemDetails(
SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode,
String localAbsPath, String localRelPath) {
this.unpackedNode = unpackedNode;
this.localAbsPath = localAbsPath;
this.localRelPath = localRelPath;
}
public SevenZipExtractor.UnpackedTree.UnpackedNode getUnpackedNode() {
return unpackedNode;
}
public String getLocalAbsPath() {
return localAbsPath;
}
public String getLocalRelPath() {
return localRelPath;
}
}
/**
* Call back class used by extract to expand archive files. This is the most
* efficient way to process according to the sevenzip binding documentation.
*/
private static class StandardIArchiveExtractCallback
implements IArchiveExtractCallback, ICryptoGetTextPassword {
private final AbstractFile archiveFile;
private final ISevenZipInArchive inArchive;
private SevenZipExtractor.UnpackStream unpackStream = null;
private final Map<Integer, InArchiveItemDetails> archiveDetailsMap;
private final ProgressHandle progressHandle;
private int inArchiveItemIndex;
private final long freeDiskSpace;
private long createTimeInSeconds;
private long modTimeInSeconds;
private long accessTimeInSeconds;
private boolean isFolder;
private final String password;
private boolean unpackSuccessful = true;
public StandardIArchiveExtractCallback(ISevenZipInArchive inArchive,
AbstractFile archiveFile, ProgressHandle progressHandle,
Map<Integer, InArchiveItemDetails> archiveDetailsMap,
String password, long freeDiskSpace) {
this.inArchive = inArchive;
this.freeDiskSpace = freeDiskSpace;
this.progressHandle = progressHandle;
this.archiveFile = archiveFile;
this.archiveDetailsMap = archiveDetailsMap;
this.password = password;
}
/**
* Get stream is called by the internal framework as it traverses
* the archive structure. The ISequentialOutStream is where the
* archive file contents will be expanded and written to the local disk.
*
* Skips folders, as there is nothing to extract.
*
* @param inArchiveItemIndex current location of the
* @param mode Will always be EXTRACT
* @return
* @throws SevenZipException
*/
@Override
public ISequentialOutStream getStream(int inArchiveItemIndex,
ExtractAskMode mode) throws SevenZipException {
this.inArchiveItemIndex = inArchiveItemIndex;
isFolder = (Boolean) inArchive
.getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
if (isFolder || mode != ExtractAskMode.EXTRACT) {
return null;
}
final Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE);
final String localAbsPath = archiveDetailsMap.get(
inArchiveItemIndex).getLocalAbsPath();
if (archiveItemSize != null) {
unpackStream = new SevenZipExtractor.KnownSizeUnpackStream(
localAbsPath, archiveItemSize);
} else {
unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream(
localAbsPath, freeDiskSpace);
}
return unpackStream;
}
/**
* Retrieves the file metadata from the archive before extraction.
* Called after getStream.
*
* @param mode Will always be EXTRACT.
* @throws SevenZipException
*/
@Override
public void prepareOperation(ExtractAskMode mode) throws SevenZipException {
final Date createTime = (Date) inArchive.getProperty(
inArchiveItemIndex, PropID.CREATION_TIME);
final Date accessTime = (Date) inArchive.getProperty(
inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
final Date writeTime = (Date) inArchive.getProperty(
inArchiveItemIndex, PropID.LAST_WRITE_TIME);
createTimeInSeconds = createTime == null ? 0L
: createTime.getTime() / 1000;
modTimeInSeconds = writeTime == null ? 0L
: writeTime.getTime() / 1000;
accessTimeInSeconds = accessTime == null ? 0L
: accessTime.getTime() / 1000;
}
/**
* Updates the unpackedNode data in the tree after the archive has been
* expanded to local disk.
*
* @param EOR - ExtractOperationResult
*
* @throws SevenZipException
*/
@Override
public void setOperationResult(ExtractOperationResult result) throws SevenZipException {
progressHandle.progress(archiveFile.getName() + ": "
+ (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
inArchiveItemIndex);
final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
= archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
final String localRelPath = archiveDetailsMap.get(
inArchiveItemIndex).getLocalRelPath();
if (isFolder) {
unpackedNode.addDerivedInfo(0,
!(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds,
localRelPath);
return;
}
final String localAbsPath = archiveDetailsMap.get(
inArchiveItemIndex).getLocalAbsPath();
if (result != ExtractOperationResult.OK) {
logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", //NON-NLS
new Object[]{localAbsPath, result});
unpackSuccessful = false;
}
//record derived data in unode, to be traversed later after unpacking the archive
unpackedNode.addDerivedInfo(unpackStream.getSize(),
!(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds, localRelPath);
unpackStream.close();
}
@Override
public void setTotal(long value) throws SevenZipException {
//Not necessary for extract, left intenionally blank
}
@Override
public void setCompleted(long value) throws SevenZipException {
//Not necessary for extract, left intenionally blank
}
/**
* Called when opening encrypted archive files.
*
* @return - Password supplied by user
*
* @throws SevenZipException
*/
@Override
public String cryptoGetTextPassword() throws SevenZipException {
return password;
}
public boolean wasSuccessful() {
return unpackSuccessful;
}
}
/** /**
* Representation of the files in the archive. Used to track of local tree * Representation of the files in the archive. Used to track of local tree
* file hierarchy, archive depth, and files created to easily and reliably * file hierarchy, archive depth, and files created to easily and reliably
@ -1290,7 +1472,8 @@ class SevenZipExtractor {
} }
/** /**
* Set the flag which identifies whether this file has been determined to be a zip bomb to true. * Set the flag which identifies whether this file has been determined
* to be a zip bomb to true.
*/ */
synchronized void flagAsZipBomb() { synchronized void flagAsZipBomb() {
flaggedAsZipBomb = true; flaggedAsZipBomb = true;

View File

@ -26,20 +26,21 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Component id="jScrollPane1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="instructionLabel" min="-2" max="-2" attributes="0"/> <Component id="instructionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
<Component id="jScrollPane1" max="32767" attributes="0"/> <Group type="102" alignment="0" attributes="0">
<Component id="pasteFromClipboardButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="121" max="32767" attributes="0"/>
<Component id="okButton" min="-2" pref="84" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" pref="84" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="AddValuesToHashDatabaseButton" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="pasteFromClipboardButton" alignment="1" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -47,21 +48,18 @@
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="instructionLabel" min="-2" max="-2" attributes="0"/> <Component id="instructionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Component id="jScrollPane1" pref="279" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="AddValuesToHashDatabaseButton" min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0">
<EmptySpace max="-2" attributes="0"/> <Component id="pasteFromClipboardButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/> <Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pasteFromClipboardButton" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Component id="jScrollPane1" pref="274" max="32767" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -102,14 +100,14 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pasteFromClipboardButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pasteFromClipboardButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JButton" name="AddValuesToHashDatabaseButton"> <Component class="javax.swing.JButton" name="okButton">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="AddHashValuesToDatabaseDialog.okButton.text_2" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="AddValuesToHashDatabaseButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JButton" name="cancelButton"> <Component class="javax.swing.JButton" name="cancelButton">

View File

@ -73,7 +73,7 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
} else { } else {
setDefaultCloseOperation(0); setDefaultCloseOperation(0);
} }
AddValuesToHashDatabaseButton.setEnabled(enable); okButton.setEnabled(enable);
cancelButton.setEnabled(enable); cancelButton.setEnabled(enable);
pasteFromClipboardButton.setEnabled(enable); pasteFromClipboardButton.setEnabled(enable);
} }
@ -91,7 +91,7 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
jScrollPane1 = new javax.swing.JScrollPane(); jScrollPane1 = new javax.swing.JScrollPane();
hashValuesTextArea = new javax.swing.JTextArea(); hashValuesTextArea = new javax.swing.JTextArea();
pasteFromClipboardButton = new javax.swing.JButton(); pasteFromClipboardButton = new javax.swing.JButton();
AddValuesToHashDatabaseButton = new javax.swing.JButton(); okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton(); cancelButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
@ -115,10 +115,10 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(AddValuesToHashDatabaseButton, org.openide.util.NbBundle.getMessage(AddHashValuesToDatabaseDialog.class, "AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(AddHashValuesToDatabaseDialog.class, "AddHashValuesToDatabaseDialog.okButton.text_2")); // NOI18N
AddValuesToHashDatabaseButton.addActionListener(new java.awt.event.ActionListener() { okButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
AddValuesToHashDatabaseButtonActionPerformed(evt); okButtonActionPerformed(evt);
} }
}); });
@ -136,31 +136,30 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(instructionLabel) .addComponent(instructionLabel)
.addGap(0, 0, Short.MAX_VALUE)) .addGap(0, 0, Short.MAX_VALUE))
.addComponent(jScrollPane1)) .addGroup(layout.createSequentialGroup()
.addGap(18, 18, 18) .addComponent(pasteFromClipboardButton)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 121, Short.MAX_VALUE)
.addComponent(AddValuesToHashDatabaseButton, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(cancelButton, javax.swing.GroupLayout.Alignment.TRAILING) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pasteFromClipboardButton, javax.swing.GroupLayout.Alignment.TRAILING)) .addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap()) .addContainerGap())
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addComponent(instructionLabel) .addComponent(instructionLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 279, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(AddValuesToHashDatabaseButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pasteFromClipboardButton)
.addComponent(pasteFromClipboardButton)) .addComponent(okButton)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 274, Short.MAX_VALUE)) .addComponent(cancelButton))
.addContainerGap()) .addContainerGap())
); );
@ -213,17 +212,17 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
} }
}//GEN-LAST:event_hashValuesTextAreaMouseClicked }//GEN-LAST:event_hashValuesTextAreaMouseClicked
private void AddValuesToHashDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_AddValuesToHashDatabaseButtonActionPerformed private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
AddHashValuesToDatabaseProgressDialog progressDialog = new AddHashValuesToDatabaseProgressDialog(this, hashDb, hashValuesTextArea.getText()); AddHashValuesToDatabaseProgressDialog progressDialog = new AddHashValuesToDatabaseProgressDialog(this, hashDb, hashValuesTextArea.getText());
progressDialog.addHashValuesToDatabase(); progressDialog.addHashValuesToDatabase();
}//GEN-LAST:event_AddValuesToHashDatabaseButtonActionPerformed }//GEN-LAST:event_okButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton AddValuesToHashDatabaseButton;
private javax.swing.JButton cancelButton; private javax.swing.JButton cancelButton;
private javax.swing.JTextArea hashValuesTextArea; private javax.swing.JTextArea hashValuesTextArea;
private javax.swing.JLabel instructionLabel; private javax.swing.JLabel instructionLabel;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JButton okButton;
private javax.swing.JButton pasteFromClipboardButton; private javax.swing.JButton pasteFromClipboardButton;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -87,8 +87,8 @@ HashDbImportDatabaseDialog.importHashDbErr=Import Hash Set Error
HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=A hash set file path must be selected. HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=A hash set file path must be selected.
HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=The selected hash set does not exist. HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=The selected hash set does not exist.
HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open hash set at {0}. HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open hash set at {0}.
HashDbIngestModule.moduleName=Hash Lookup HashLookupModuleFactory.moduleName.text=Hash Lookup
HashDbIngestModule.moduleDescription=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set. HashLookupModuleFactory.moduleDescription.text=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set.
HashDbIngestModule.fileReadErrorMsg=Read Error\: {0} HashDbIngestModule.fileReadErrorMsg=Read Error\: {0}
HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0}. HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0}.
HashDbIngestModule.hashLookupErrorMsg=Hash Lookup Error\: {0} HashDbIngestModule.hashLookupErrorMsg=Hash Lookup Error\: {0}
@ -172,7 +172,6 @@ HashDbSearchPanel.hashTable.defaultModel.title.text=MD5 Hashes
AddHashValuesToDatabaseDialog.JDialog.Title=Add Hashes to Hash Set AddHashValuesToDatabaseDialog.JDialog.Title=Add Hashes to Hash Set
AddHashValuesToDatabaseDialog.instructionLabel.text_1=Paste MD5 hash values (one per line) below: AddHashValuesToDatabaseDialog.instructionLabel.text_1=Paste MD5 hash values (one per line) below:
AddHashValuesToDatabaseDialog.cancelButton.text_2=Cancel AddHashValuesToDatabaseDialog.cancelButton.text_2=Cancel
AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2=Add Hashes to Hash Set
AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=Paste From Clipboard AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=Paste From Clipboard
AddHashValuesToDatabaseProgressDialog.okButton.text=OK AddHashValuesToDatabaseProgressDialog.okButton.text=OK
AddHashValuesToDatabaseProgressDialog.statusLabel.text=status AddHashValuesToDatabaseProgressDialog.statusLabel.text=status
@ -237,3 +236,4 @@ HashDbCreateDatabaseDialog.centralRepoRadioButton.text=Remote (Central Repositor
HashDbCreateDatabaseDialog.lbOrg.text=Source Organization: HashDbCreateDatabaseDialog.lbOrg.text=Source Organization:
HashDbCreateDatabaseDialog.orgButton.text=Manage Organizations HashDbCreateDatabaseDialog.orgButton.text=Manage Organizations
HashDbCreateDatabaseDialog.databasePathLabel.text=Hash Set Path: HashDbCreateDatabaseDialog.databasePathLabel.text=Hash Set Path:
AddHashValuesToDatabaseDialog.okButton.text_2=OK

View File

@ -76,8 +76,8 @@ HashDbImportDatabaseDialog.importHashDbErr=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\
HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u306e\u9078\u629e\u304c\u5fc5\u8981\u3067\u3059\u3002 HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u306e\u9078\u629e\u304c\u5fc5\u8981\u3067\u3059\u3002
HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=\u9078\u629e\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=\u9078\u629e\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002
HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg={0}\u3067\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u304f\u306e\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg={0}\u3067\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u304f\u306e\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.moduleName=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7 HashLookupModuleFactory.moduleName.text=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7
HashDbIngestModule.moduleDescription=\u6a19\u6e96\u306eNSRL\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306a\u3069\u3001\u63d0\u4f9b\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u5229\u7528\u3057\u3066\u3001\u65e2\u77e5\u307e\u305f\u306f\u7591\u308f\u3057\u3044\u3082\u306e\u3092\u691c\u77e5\u3057\u307e\u3059\u3002 HashLookupModuleFactory.moduleDescription.text=\u6a19\u6e96\u306eNSRL\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306a\u3069\u3001\u63d0\u4f9b\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u5229\u7528\u3057\u3066\u3001\u65e2\u77e5\u307e\u305f\u306f\u7591\u308f\u3057\u3044\u3082\u306e\u3092\u691c\u77e5\u3057\u307e\u3059\u3002
HashDbIngestModule.noKnownHashDbSetMsg=\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 HashDbIngestModule.noKnownHashDbSetMsg=\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\u3002
HashDbIngestModule.knownFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u304c\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002 HashDbIngestModule.knownFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u304c\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002
HashDbIngestModule.noKnownBadHashDbSetMsg=\u65e2\u77e5\u306e\u60aa\u8cea\u306a\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30bb\u30c3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002 HashDbIngestModule.noKnownBadHashDbSetMsg=\u65e2\u77e5\u306e\u60aa\u8cea\u306a\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30bb\u30c3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002
@ -86,7 +86,6 @@ HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u60aa
HashDbIngestModule.fileReadErrorMsg=\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc\uff1a {0} HashDbIngestModule.fileReadErrorMsg=\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc\uff1a {0}
HashDbIngestModule.calcHashValueErr={0}\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8a08\u7b97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 HashDbIngestModule.calcHashValueErr={0}\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8a08\u7b97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.hashLookupErrorMsg=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u30a8\u30e9\u30fc\uff1a {0} HashDbIngestModule.hashLookupErrorMsg=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u30a8\u30e9\u30fc\uff1a {0}
HashDbIngestModule.settingKnownBadStateErr={0}\u306e\u65e2\u77e5\u306e\u60aa\u8cea\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u8a2d\u5b9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.lookingUpKnownBadHashValueErr={0}\u306e\u65e2\u77e5\u306e\u60aa\u8cea\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 HashDbIngestModule.lookingUpKnownBadHashValueErr={0}\u306e\u65e2\u77e5\u306e\u60aa\u8cea\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.lookingUpKnownHashValueErr={0}\u306e\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 HashDbIngestModule.lookingUpKnownHashValueErr={0}\u306e\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.postToBB.fileName=\u30d5\u30a1\u30a4\u30eb\u540d HashDbIngestModule.postToBB.fileName=\u30d5\u30a1\u30a4\u30eb\u540d
@ -172,7 +171,6 @@ AddHashValuesToDatabaseDialog.JDialog.Title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9
HashLookupSettingsPanel.addHashesToDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0 HashLookupSettingsPanel.addHashesToDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0
AddHashValuesToDatabaseDialog.instructionLabel.text_1=\u4e0b\u8a18\u306bMD5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8cbc\u308a\u4ed8\u3051\u308b\uff08\u30e9\u30a4\u30f3\u3054\u3068\u306b\u4e00\u3064\u305a\u3064\uff09\uff1a AddHashValuesToDatabaseDialog.instructionLabel.text_1=\u4e0b\u8a18\u306bMD5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8cbc\u308a\u4ed8\u3051\u308b\uff08\u30e9\u30a4\u30f3\u3054\u3068\u306b\u4e00\u3064\u305a\u3064\uff09\uff1a
AddHashValuesToDatabaseDialog.cancelButton.text_2=\u30ad\u30e3\u30f3\u30bb\u30eb AddHashValuesToDatabaseDialog.cancelButton.text_2=\u30ad\u30e3\u30f3\u30bb\u30eb
AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0
AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u304b\u3089\u8cbc\u308a\u4ed8\u3051\u308b AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u304b\u3089\u8cbc\u308a\u4ed8\u3051\u308b
AddHashValuesToDatabaseProgressDialog.okButton.text=OK AddHashValuesToDatabaseProgressDialog.okButton.text=OK
AddHashValuesToDatabaseProgressDialog.statusLabel.text=\u30b9\u30c6\u30fc\u30bf\u30b9 AddHashValuesToDatabaseProgressDialog.statusLabel.text=\u30b9\u30c6\u30fc\u30bf\u30b9
@ -207,3 +205,4 @@ HashLookupSettingsPanel.nameLabel.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c
HashLookupSettingsPanel.hashDatabasesLabel.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\uff1a HashLookupSettingsPanel.hashDatabasesLabel.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\uff1a
HashLookupSettingsPanel.importDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 HashLookupSettingsPanel.importDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30dd\u30fc\u30c8
HashDbCreateDatabaseDialog.databasePathLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\uff1a HashDbCreateDatabaseDialog.databasePathLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\uff1a
AddHashValuesToDatabaseDialog.okButton.text_2=OK

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011 - 2013 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -53,7 +53,10 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskException;
@NbBundle.Messages({ /**
* File ingest module to mark files based on hash values.
*/
@Messages({
"HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.", "HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.",
"HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.", "HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.",
"HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.", "HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.",
@ -67,18 +70,21 @@ public class HashDbIngestModule implements FileIngestModule {
private final SleuthkitCase skCase; private final SleuthkitCase skCase;
private final HashDbManager hashDbManager = HashDbManager.getInstance(); private final HashDbManager hashDbManager = HashDbManager.getInstance();
private final HashLookupModuleSettings settings; private final HashLookupModuleSettings settings;
private List<HashDb> knownBadHashSets = new ArrayList<>(); private final List<HashDb> knownBadHashSets = new ArrayList<>();
private List<HashDb> knownHashSets = new ArrayList<>(); private final List<HashDb> knownHashSets = new ArrayList<>();
private long jobId; private long jobId;
private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>(); private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
private Blackboard blackboard; private Blackboard blackboard;
/**
* A container of values for storing ingest metrics for the job.
*/
private static class IngestJobTotals { private static class IngestJobTotals {
private AtomicLong totalKnownBadCount = new AtomicLong(0); private final AtomicLong totalKnownBadCount = new AtomicLong(0);
private AtomicLong totalCalctime = new AtomicLong(0); private final AtomicLong totalCalctime = new AtomicLong(0);
private AtomicLong totalLookuptime = new AtomicLong(0); private final AtomicLong totalLookuptime = new AtomicLong(0);
} }
private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) { private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) {
@ -90,6 +96,15 @@ public class HashDbIngestModule implements FileIngestModule {
return totals; return totals;
} }
/**
* Create a HashDbIngestModule object that will mark files based on a
* supplied list of hash values. The supplied HashLookupModuleSettings
* object is used to configure the module.
*
* @param settings The module settings.
*
* @throws NoCurrentCaseException If there is no open case.
*/
HashDbIngestModule(HashLookupModuleSettings settings) throws NoCurrentCaseException { HashDbIngestModule(HashLookupModuleSettings settings) throws NoCurrentCaseException {
this.settings = settings; this.settings = settings;
skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
@ -146,6 +161,12 @@ public class HashDbIngestModule implements FileIngestModule {
} }
} }
@Messages({
"# {0} - File name",
"HashDbIngestModule.dialogTitle.errorFindingArtifacts=Error Finding Artifacts: {0}",
"# {0} - File name",
"HashDbIngestModule.errorMessage.lookingForFileArtifacts=Error encountered while looking for existing artifacts for {0}."
})
@Override @Override
public ProcessResult process(AbstractFile file) { public ProcessResult process(AbstractFile file) {
try { try {
@ -156,8 +177,8 @@ public class HashDbIngestModule implements FileIngestModule {
} }
// Skip unallocated space files. // Skip unallocated space files.
if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) || if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) { || file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
return ProcessResult.OK; return ProcessResult.OK;
} }
@ -181,6 +202,7 @@ public class HashDbIngestModule implements FileIngestModule {
// calc hash value // calc hash value
String name = file.getName(); String name = file.getName();
long fileId = file.getId();
String md5Hash = file.getMd5Hash(); String md5Hash = file.getMd5Hash();
if (md5Hash == null || md5Hash.isEmpty()) { if (md5Hash == null || md5Hash.isEmpty()) {
try { try {
@ -203,15 +225,11 @@ public class HashDbIngestModule implements FileIngestModule {
totals.totalCalctime.addAndGet(delta); totals.totalCalctime.addAndGet(delta);
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Error calculating hash of file " + name, ex); //NON-NLS logger.log(Level.WARNING, String.format("Error calculating hash of file '%s' (id=%d).", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage( services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(), HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", name),
"HashDbIngestModule.fileReadErrorMsg", NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr", name)));
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.calcHashValueErr",
name)));
return ProcessResult.ERROR; return ProcessResult.ERROR;
} }
} }
@ -245,21 +263,37 @@ public class HashDbIngestModule implements FileIngestModule {
} }
} }
/*
* We have a match. Now create an artifact if it is
* determined that one hasn't been created yet.
*/
List<BlackboardAttribute> attributesList = new ArrayList<>();
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, HashLookupModuleFactory.getModuleName(), hashSetName));
try {
org.sleuthkit.datamodel.Blackboard tskBlackboard = skCase.getBlackboard();
if (tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, attributesList) == false) {
postHashSetHitToBlackboard(file, md5Hash, hashSetName, comment, db.getSendIngestMessages()); postHashSetHitToBlackboard(file, md5Hash, hashSetName, comment, db.getSendIngestMessages());
} }
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"A problem occurred while checking for existing artifacts for file '%s' (id=%d).", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
Bundle.HashDbIngestModule_dialogTitle_errorFindingArtifacts(name),
Bundle.HashDbIngestModule_errorMessage_lookingForFileArtifacts(name)));
ret = ProcessResult.ERROR;
}
}
long delta = (System.currentTimeMillis() - lookupstart); long delta = (System.currentTimeMillis() - lookupstart);
totals.totalLookuptime.addAndGet(delta); totals.totalLookuptime.addAndGet(delta);
} catch (TskException ex) { } catch (TskException ex) {
logger.log(Level.WARNING, "Couldn't lookup notable hash for file " + name + " - see sleuthkit log for details", ex); //NON-NLS logger.log(Level.WARNING, String.format(
"Couldn't lookup notable hash for file '%s' (id=%d) - see sleuthkit log for details", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage( services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(), HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", name),
"HashDbIngestModule.hashLookupErrorMsg", NbBundle.getMessage(this.getClass(), "HashDbIngestModule.lookingUpKnownBadHashValueErr", name)));
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.lookingUpKnownBadHashValueErr",
name)));
ret = ProcessResult.ERROR; ret = ProcessResult.ERROR;
} }
} }
@ -279,15 +313,12 @@ public class HashDbIngestModule implements FileIngestModule {
totals.totalLookuptime.addAndGet(delta); totals.totalLookuptime.addAndGet(delta);
} catch (TskException ex) { } catch (TskException ex) {
logger.log(Level.WARNING, "Couldn't lookup known hash for file " + name + " - see sleuthkit log for details", ex); //NON-NLS logger.log(Level.WARNING, String.format(
"Couldn't lookup known hash for file '%s' (id=%d) - see sleuthkit log for details", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage( services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(), HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", name),
"HashDbIngestModule.hashLookupErrorMsg", NbBundle.getMessage(this.getClass(), "HashDbIngestModule.lookingUpKnownHashValueErr", name)));
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.lookingUpKnownHashValueErr",
name)));
ret = ProcessResult.ERROR; ret = ProcessResult.ERROR;
} }
} }
@ -296,18 +327,29 @@ public class HashDbIngestModule implements FileIngestModule {
return ret; return ret;
} }
@Messages({"HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."}) /**
* Post a hash set hit to the blackboard.
*
* @param abstractFile The file to be processed.
* @param md5Hash The MD5 hash value of the file.
* @param hashSetName The name of the hash set with which to associate
* the hit.
* @param comment A comment to be attached to the artifact.
* @param showInboxMessage Show a message in the inbox?
*/
@Messages({
"HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."
})
private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) { private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) {
try { try {
String MODULE_NAME = NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.moduleName"); String moduleName = HashLookupModuleFactory.getModuleName();
BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT); BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
Collection<BlackboardAttribute> attributes = new ArrayList<>(); Collection<BlackboardAttribute> attributes = new ArrayList<>();
//TODO Revisit usage of deprecated constructor as per TSK-583 //TODO Revisit usage of deprecated constructor as per TSK-583
//BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName); //BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName);
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, hashSetName)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, hashSetName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, MODULE_NAME, md5Hash)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, comment)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment));
badFile.addAttributes(attributes); badFile.addAttributes(attributes);
@ -351,19 +393,24 @@ public class HashDbIngestModule implements FileIngestModule {
detailsSb.append("</table>"); //NON-NLS detailsSb.append("</table>"); //NON-NLS
services.postMessage(IngestMessage.createDataMessage(HashLookupModuleFactory.getModuleName(), services.postMessage(IngestMessage.createDataMessage(HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.knownBadMsg", abstractFile.getName()),
"HashDbIngestModule.postToBB.knownBadMsg",
abstractFile.getName()),
detailsSb.toString(), detailsSb.toString(),
abstractFile.getName() + md5Hash, abstractFile.getName() + md5Hash,
badFile)); badFile));
} }
services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, ARTIFACT_TYPE.TSK_HASHSET_HIT, Collections.singletonList(badFile))); services.fireModuleDataEvent(new ModuleDataEvent(moduleName, ARTIFACT_TYPE.TSK_HASHSET_HIT, Collections.singletonList(badFile)));
} catch (TskException ex) { } catch (TskException ex) {
logger.log(Level.WARNING, "Error creating blackboard artifact", ex); //NON-NLS logger.log(Level.WARNING, "Error creating blackboard artifact", ex); //NON-NLS
} }
} }
/**
* Post a message summarizing the results of the ingest.
*
* @param jobId The ID of the job.
* @param knownBadHashSets The list of hash sets for "known bad" files.
* @param knownHashSets The list of hash sets for "known" files.
*/
private static synchronized void postSummary(long jobId, private static synchronized void postSummary(long jobId,
List<HashDb> knownBadHashSets, List<HashDb> knownHashSets) { List<HashDb> knownBadHashSets, List<HashDb> knownHashSets) {
IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId); IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId);
@ -399,8 +446,7 @@ public class HashDbIngestModule implements FileIngestModule {
IngestServices.getInstance().postMessage(IngestMessage.createMessage( IngestServices.getInstance().postMessage(IngestMessage.createMessage(
IngestMessage.MessageType.INFO, IngestMessage.MessageType.INFO,
HashLookupModuleFactory.getModuleName(), HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(HashDbIngestModule.class, NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.hashLookupResults"),
"HashDbIngestModule.complete.hashLookupResults"),
detailsSb.toString())); detailsSb.toString()));
} }
} }

View File

@ -42,13 +42,18 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter {
return getModuleName(); return getModuleName();
} }
/**
* Get the name of the module.
*
* @return The module name.
*/
static String getModuleName() { static String getModuleName() {
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashDbIngestModule.moduleName"); return NbBundle.getMessage(HashLookupModuleFactory.class, "HashLookupModuleFactory.moduleName.text");
} }
@Override @Override
public String getModuleDescription() { public String getModuleDescription() {
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashDbIngestModule.moduleDescription"); return NbBundle.getMessage(HashLookupModuleFactory.class, "HashLookupModuleFactory.moduleDescription.text");
} }
@Override @Override
@ -101,8 +106,8 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter {
@Override @Override
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) { public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) {
if (!(settings instanceof HashLookupModuleSettings)) { if (!(settings instanceof HashLookupModuleSettings)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(),
NbBundle.getMessage(this.getClass(), "HashLookupModuleFactory.createFileIngestModule.exception.msg")); "HashLookupModuleFactory.createFileIngestModule.exception.msg"));
} }
try { try {
return new HashDbIngestModule((HashLookupModuleSettings) settings); return new HashDbIngestModule((HashLookupModuleSettings) settings);

View File

@ -80,7 +80,6 @@ class ReportHTML implements TableReportModule {
private static final int MAX_THUMBS_PER_PAGE = 1000; private static final int MAX_THUMBS_PER_PAGE = 1000;
private static final String HTML_SUBDIR = "content"; private static final String HTML_SUBDIR = "content";
private Case currentCase; private Case currentCase;
private SleuthkitCase skCase;
static Integer THUMBNAIL_COLUMNS = 5; static Integer THUMBNAIL_COLUMNS = 5;
private Map<String, Integer> dataTypes; private Map<String, Integer> dataTypes;
@ -109,7 +108,6 @@ class ReportHTML implements TableReportModule {
// Refesh the member variables // Refesh the member variables
private void refresh() throws NoCurrentCaseException { private void refresh() throws NoCurrentCaseException {
currentCase = Case.getCurrentCaseThrows(); currentCase = Case.getCurrentCaseThrows();
skCase = currentCase.getSleuthkitCase();
dataTypes = new TreeMap<>(); dataTypes = new TreeMap<>();
@ -274,7 +272,7 @@ class ReportHTML implements TableReportModule {
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS
break; break;
default: default:
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = " + dataType); //NON-NLS logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
iconFileName = "star.png"; //NON-NLS iconFileName = "star.png"; //NON-NLS
iconFilePath = subPath + File.separator + iconFileName; iconFilePath = subPath + File.separator + iconFileName;
@ -627,12 +625,19 @@ class ReportHTML implements TableReportModule {
int positionCounter = 0; int positionCounter = 0;
for (String cell : row) { for (String cell : row) {
// position-dependent code used to format this report. Not great, but understandable for formatting. // position-dependent code used to format this report. Not great, but understandable for formatting.
if (positionCounter == 1) { // Convert the file name to a hyperlink and left-align it switch (positionCounter) {
case 1:
// Convert the file name to a hyperlink and left-align it
builder.append("\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append("</a></td>\n"); //NON-NLS builder.append("\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append("</a></td>\n"); //NON-NLS
} else if (positionCounter == 7) { // Right-align the bytes column. break;
case 7:
// Right-align the bytes column.
builder.append("\t\t<td class=\"right_align_cell\">").append(cell).append("</td>\n"); //NON-NLS builder.append("\t\t<td class=\"right_align_cell\">").append(cell).append("</td>\n"); //NON-NLS
} else { // Regular case, not a file name nor a byte count break;
default:
// Regular case, not a file name nor a byte count
builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
break;
} }
++positionCounter; ++positionCounter;
} }
@ -719,7 +724,7 @@ class ReportHTML implements TableReportModule {
for (int i = 0; i < tags.size(); i++) { for (int i = 0; i < tags.size(); i++) {
ContentTag tag = tags.get(i); ContentTag tag = tags.get(i);
String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : ""; String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
linkToThumbnail.append(tag.getName().getDisplayName() + notableString); linkToThumbnail.append(tag.getName().getDisplayName()).append(notableString);
if (i != tags.size() - 1) { if (i != tags.size() - 1) {
linkToThumbnail.append(", "); linkToThumbnail.append(", ");
} }
@ -751,12 +756,9 @@ class ReportHTML implements TableReportModule {
return true; return true;
} }
AbstractFile file = (AbstractFile) c; AbstractFile file = (AbstractFile) c;
if (file.isDir() return file.isDir()
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) { || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS;
return true;
}
return false;
} }
/** /**
@ -782,8 +784,14 @@ class ReportHTML implements TableReportModule {
localFileFolder.mkdirs(); localFileFolder.mkdirs();
} }
// Construct a file tagName for the local file that incorporates the file id to ensure uniqueness. /*
String fileName = file.getName(); * Construct a file tagName for the local file that incorporates the
* file ID to ensure uniqueness.
*
* Note: File name is normalized to account for possible attribute name
* which will be separated by a ':' character.
*/
String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
String objectIdSuffix = "_" + file.getId(); String objectIdSuffix = "_" + file.getId();
int lastDotIndex = fileName.lastIndexOf("."); int lastDotIndex = fileName.lastIndexOf(".");
if (lastDotIndex != -1 && lastDotIndex != 0) { if (lastDotIndex != -1 && lastDotIndex != 0) {
@ -1052,9 +1060,9 @@ class ReportHTML implements TableReportModule {
* Write the summary of the current case for this report. * Write the summary of the current case for this report.
*/ */
private void writeSummary() { private void writeSummary() {
Writer out = null; Writer output = null;
try { try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + "summary.html"), "UTF-8")); //NON-NLS output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + "summary.html"), "UTF-8")); //NON-NLS
StringBuilder head = new StringBuilder(); StringBuilder head = new StringBuilder();
head.append("<html>\n<head>\n<title>").append( //NON-NLS head.append("<html>\n<head>\n<title>").append( //NON-NLS
NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.title")).append("</title>\n"); //NON-NLS NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.title")).append("</title>\n"); //NON-NLS
@ -1080,7 +1088,7 @@ class ReportHTML implements TableReportModule {
head.append("li {padding-bottom: 5px;}"); head.append("li {padding-bottom: 5px;}");
head.append("</style>\n"); //NON-NLS head.append("</style>\n"); //NON-NLS
head.append("</head>\n<body>\n"); //NON-NLS head.append("</head>\n<body>\n"); //NON-NLS
out.write(head.toString()); output.write(head.toString());
DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date(); Date date = new Date();
@ -1119,7 +1127,7 @@ class ReportHTML implements TableReportModule {
} }
summary.append("</div>\n"); //NON-NLS summary.append("</div>\n"); //NON-NLS
summary.append("</body></html>"); //NON-NLS summary.append("</body></html>"); //NON-NLS
out.write(summary.toString()); output.write(summary.toString());
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
logger.log(Level.SEVERE, "Could not find summary.html file to write to."); //NON-NLS logger.log(Level.SEVERE, "Could not find summary.html file to write to."); //NON-NLS
} catch (UnsupportedEncodingException ex) { } catch (UnsupportedEncodingException ex) {
@ -1130,9 +1138,9 @@ class ReportHTML implements TableReportModule {
logger.log(Level.WARNING, "Unable to get current sleuthkit Case for the HTML report."); logger.log(Level.WARNING, "Unable to get current sleuthkit Case for the HTML report.");
} finally { } finally {
try { try {
if (out != null) { if (output != null) {
out.flush(); output.flush();
out.close(); output.close();
} }
} catch (IOException ex) { } catch (IOException ex) {
} }
@ -1292,9 +1300,24 @@ class ReportHTML implements TableReportModule {
return summary; return summary;
} }
/**
* Create a thumbnail of a given file.
*
* @param file The file from which to create the thumbnail.
*
* @return The path to the thumbnail file, or null if a thumbnail couldn't
* be created.
*/
private String prepareThumbnail(AbstractFile file) { private String prepareThumbnail(AbstractFile file) {
BufferedImage bufferedThumb = ImageUtils.getThumbnail(file, ImageUtils.ICON_SIZE_MEDIUM); BufferedImage bufferedThumb = ImageUtils.getThumbnail(file, ImageUtils.ICON_SIZE_MEDIUM);
File thumbFile = Paths.get(thumbsPath, file.getName() + ".png").toFile();
/*
* File name is normalized to account for possible attribute name which
* will be separated by a ':' character.
*/
String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
File thumbFile = Paths.get(thumbsPath, fileName + ".png").toFile();
if (bufferedThumb == null) { if (bufferedThumb == null) {
return null; return null;
} }

View File

@ -38,6 +38,7 @@ import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.logging.Level; import java.util.logging.Level;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.casemodule.services.TagsManager;
@ -264,6 +265,7 @@ class TableReportGenerator {
/** /**
* Make table for tagged files * Make table for tagged files
*/ */
@Messages({"ReportGenerator.tagTable.header.userName=User Name"})
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private void makeContentTagsTables() { private void makeContentTagsTables() {
@ -287,6 +289,7 @@ class TableReportGenerator {
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.tag"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.tag"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.file"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.file"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.comment"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.comment"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.userName"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeModified"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeModified"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeChanged"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeChanged"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeAccessed"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeAccessed"),
@ -324,7 +327,7 @@ class TableReportGenerator {
fileName = tag.getContent().getName(); fileName = tag.getContent().getName();
} }
ArrayList<String> rowData = new ArrayList<>(Arrays.asList(tag.getName().getDisplayName() + notableString, fileName, tag.getComment())); ArrayList<String> rowData = new ArrayList<>(Arrays.asList(tag.getName().getDisplayName() + notableString, fileName, tag.getComment(), tag.getUserName()));
Content content = tag.getContent(); Content content = tag.getContent();
if (content instanceof AbstractFile) { if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content; AbstractFile file = (AbstractFile) content;
@ -386,7 +389,8 @@ class TableReportGenerator {
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.resultType"), NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.resultType"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.tag"), NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.tag"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.comment"), NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.comment"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.srcFile")))); NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.srcFile"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.userName"))));
// Give the modules the rows for the content tags. // Give the modules the rows for the content tags.
for (BlackboardArtifactTag tag : tags) { for (BlackboardArtifactTag tag : tags) {
@ -396,7 +400,8 @@ class TableReportGenerator {
} }
List<String> row; List<String> row;
row = new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName() + notableString, tag.getComment(), tag.getContent().getName())); row = new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName() + notableString,
tag.getComment(), tag.getContent().getName(), tag.getUserName()));
tableReport.addRow(row); tableReport.addRow(row);
// check if the tag is an image that we should later make a thumbnail for // check if the tag is an image that we should later make a thumbnail for

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2016 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -31,10 +31,13 @@ import javax.swing.SwingUtilities;
* Allows creation of JavaFX menus with the same structure as Swing menus and * Allows creation of JavaFX menus with the same structure as Swing menus and
* which invoke the same actions. * which invoke the same actions.
*/ */
public class SwingFXMenuUtils extends MenuItem { class SwingFXMenuUtils {
private SwingFXMenuUtils() {
}
/** /**
* Factory method that creates a JavaFX MenuItem backed by a MenuElement * Factory method that creates a JavaFX MenuItem backed by a swing MenuElement
* *
* @param jMenuElement The MenuElement to create a JavaFX menu for. * @param jMenuElement The MenuElement to create a JavaFX menu for.
* *
@ -60,6 +63,7 @@ public class SwingFXMenuUtils extends MenuItem {
private MenuItemAdapter(final JMenuItem jMenuItem) { private MenuItemAdapter(final JMenuItem jMenuItem) {
super(jMenuItem.getText()); super(jMenuItem.getText());
setDisable(jMenuItem.isEnabled() == false);
setOnAction(actionEvent -> SwingUtilities.invokeLater(jMenuItem::doClick)); setOnAction(actionEvent -> SwingUtilities.invokeLater(jMenuItem::doClick));
} }
} }
@ -77,6 +81,7 @@ public class SwingFXMenuUtils extends MenuItem {
*/ */
MenuAdapter(final JMenu jMenu) { MenuAdapter(final JMenu jMenu) {
super(jMenu.getText()); super(jMenu.getText());
setDisable(jMenu.isEnabled() == false);
populateSubMenus(jMenu); populateSubMenus(jMenu);
} }
@ -87,6 +92,7 @@ public class SwingFXMenuUtils extends MenuItem {
*/ */
MenuAdapter(JPopupMenu jPopupMenu) { MenuAdapter(JPopupMenu jPopupMenu) {
super(jPopupMenu.getLabel()); super(jPopupMenu.getLabel());
setDisable(jPopupMenu.isEnabled() == false);
populateSubMenus(jPopupMenu); populateSubMenus(jPopupMenu);
} }
@ -114,4 +120,5 @@ public class SwingFXMenuUtils extends MenuItem {
} }
} }
} }
} }

View File

@ -838,7 +838,7 @@ public class CentralRepoDatamodelTest extends TestCase {
// This is the expected behavior // This is the expected behavior
} }
// Test getting instances with expected resuls // Test getting instances with expected results
try { try {
List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(fileType, inAllDataSourcesHash); List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(fileType, inAllDataSourcesHash);
assertTrue("getArtifactInstancesByTypeValue returned " + instances.size() + " results - expected 3", instances.size() == 3); assertTrue("getArtifactInstancesByTypeValue returned " + instances.size() + " results - expected 3", instances.size() == 3);
@ -1123,6 +1123,34 @@ public class CentralRepoDatamodelTest extends TestCase {
} catch (EamDbException ex) { } catch (EamDbException ex) {
// This is the expected // This is the expected
} }
// Test running processinstance which queries all rows from instances table
try {
// Add two instances to the central repository and use the callback query to verify we can see them
CorrelationAttribute attr = new CorrelationAttribute(fileType, callbackTestFileHash);
CorrelationAttributeInstance inst1 = new CorrelationAttributeInstance(case1, dataSource1fromCase1, callbackTestFilePath1);
CorrelationAttributeInstance inst2 = new CorrelationAttributeInstance(case1, dataSource1fromCase1, callbackTestFilePath2);
attr.addInstance(inst1);
attr.addInstance(inst2);
EamDb DbManager = EamDb.getInstance();
DbManager.addArtifact(attr);
AttributeInstanceTableCallback instancetableCallback = new AttributeInstanceTableCallback();
DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attr.getID()), instancetableCallback);
int count1 = instancetableCallback.getCounter();
int count2 = instancetableCallback.getCounterNamingConvention();
assertTrue("Process Instance count with filepath naming convention: " + count2 + "-expected 2", count2 == 2);
assertTrue("Process Instance count with filepath without naming convention: " + count1 + "-expected greater than 0", count1 > 0);
} catch (EamDbException ex) {
Exceptions.printStackTrace(ex);
}
try {
//test null inputs
EamDb.getInstance().processInstanceTableWhere(null, null, null);
Assert.fail("processinstance method failed to throw exception for null type value");
} catch (EamDbException ex) {
// This is the expected
}
} }
/** /**

View File

@ -1,465 +0,0 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.commonfilessearch;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import junit.framework.Test;
import org.netbeans.junit.NbModuleSuite;
import org.netbeans.junit.NbTestCase;
import org.openide.util.Exceptions;
import org.python.icu.impl.Assert;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm;
import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata;
import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder;
import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource;
import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.*;
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestJobSettings.IngestType;
import org.sleuthkit.autopsy.ingest.IngestModuleTemplate;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory;
import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory;
import org.sleuthkit.autopsy.testutils.IngestUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Add set 1, set 2, set 3, and set 4 to case and ingest with hash algorithm.
*/
public class IngestedWithHashAndFileType extends NbTestCase {
public static Test suite() {
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithHashAndFileType.class).
clusters(".*").
enableModules(".*");
return conf.suite();
}
private final IntraCaseUtils utils;
public IngestedWithHashAndFileType(String name) {
super(name);
this.utils = new IntraCaseUtils(this, "IngestedWithHashAndFileTypeTests");
}
@Override
public void setUp() {
this.utils.setUp();
IngestModuleTemplate hashLookupTemplate = IngestUtils.getIngestModuleTemplate(new HashLookupModuleFactory());
IngestModuleTemplate mimeTypeLookupTemplate = IngestUtils.getIngestModuleTemplate(new FileTypeIdModuleFactory());
ArrayList<IngestModuleTemplate> templates = new ArrayList<>();
templates.add(hashLookupTemplate);
templates.add(mimeTypeLookupTemplate);
IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileType.class.getCanonicalName(), IngestType.FILES_ONLY, templates);
try {
IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings);
} catch (NoCurrentCaseException | TskCoreException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
@Override
public void tearDown() {
this.utils.tearDown();
}
/**
* Find all matches & all file types. Confirm file.jpg is found on all three
* and file.docx is found on two.
*/
public void testOneA() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, false);
CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles();
Map<Long, String> objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet());
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(IntraCaseUtils.verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find all matches & only image types. Confirm file.jpg is found on all
* three.
*/
public void testOneB() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, true, false);
CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find all matches & only image types. Confirm file.jpg is found on all
* three.
*/
public void testOneC() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, true);
CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 1 & all file types. Confirm same results.
*
*/
public void testTwoA() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long first = getDataSourceIdByName(SET1, dataSources);
CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, false);
CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 1 & only media types. Confirm same results.
*
*/
public void testTwoB() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long first = getDataSourceIdByName(SET1, dataSources);
CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, true, false);
CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 1 & all file types. Confirm same results.
*
*/
public void testTwoC() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long first = getDataSourceIdByName(SET1, dataSources);
CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, true);
CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 2 & all file types: Confirm file.jpg.
*
*/
public void testThree() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long second = getDataSourceIdByName(SET2, dataSources);
CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(second, dataSources, false, false);
CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 4 & all file types: Confirm nothing is found.
*/
public void testFour() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long last = getDataSourceIdByName(SET4, dataSources);
CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(last, dataSources, false, false);
CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 3 & all file types: Confirm file.jpg and file.docx.
*/
public void testFive() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long third = getDataSourceIdByName(SET3, dataSources);
CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(third, dataSources, false, false);
CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
}

View File

@ -0,0 +1,185 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.commonfilessearch;
import java.sql.SQLException;
import java.util.Map;
import junit.framework.Test;
import org.netbeans.junit.NbModuleSuite;
import org.netbeans.junit.NbTestCase;
import org.openide.util.Exceptions;
import org.python.icu.impl.Assert;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeSearcher;
import org.sleuthkit.autopsy.commonfilesearch.AllInterCaseCommonAttributeSearcher;
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults;
import org.sleuthkit.autopsy.commonfilesearch.SingleInterCaseCommonAttributeSearcher;
import static org.sleuthkit.autopsy.commonfilessearch.InterCaseTestUtils.*;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Tests with case 3 as the current case.
*
* If I use the search all cases option: One node for Hash A (1_1_A.jpg,
* 1_2_A.jpg, 3_1_A.jpg) If I search for matches only in Case 1: One node for
* Hash A (1_1_A.jpg, 1_2_A.jpg, 3_1_A.jpg) If I search for matches only in Case
* 2: No matches If I only search in the current case (existing mode), allowing
* all data sources: One node for Hash C (3_1_C.jpg, 3_2_C.jpg)
*/
public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase {
private final InterCaseTestUtils utils;
public static Test suite() {
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithHashAndFileTypeInterCaseTests.class).
clusters(".*").
enableModules(".*");
return conf.suite();
}
public IngestedWithHashAndFileTypeInterCaseTests(String name) {
super(name);
this.utils = new InterCaseTestUtils(this);
}
@Override
public void setUp(){
this.utils.clearTestDir();
try {
this.utils.enableCentralRepo();
this.utils.createCases(this.utils.getIngestSettingsForHashAndFileType(), InterCaseTestUtils.CASE3);
} catch (TskCoreException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
@Override
public void tearDown(){
this.utils.clearTestDir();
this.utils.tearDown();
}
/**
* Search All cases with no file type filtering.
*/
public void testOne() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
//note that the params false and false are presently meaningless because that feature is not supported yet
AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false);
CommonAttributeSearchResults metadata = builder.findFiles();
assertTrue("Results should not be empty", metadata.size() != 0);
//case 1 data set 1
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_1, CASE1, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_1, CASE1, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_1, CASE1, 1));
//case 1 data set 2
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_2, CASE1, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_2, CASE1, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_2, CASE1, 1));
//case 2 data set 1
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_PDF, CASE2_DATASET_1, CASE2, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_JPG, CASE2_DATASET_1, CASE2, 0));
//case 2 data set 2
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE2_DATASET_2, CASE2, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE2_DATASET_2, CASE2, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE2_DATASET_2, CASE2, 1));
//case 3 data set 1
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE3_DATASET_1, CASE3, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE3_DATASET_1, CASE3, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_1, CASE3, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_1, CASE3, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_JPG, CASE3_DATASET_1, CASE3, 0));
//case 3 data set 2
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_2, CASE3, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_2, CASE3, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1));
} catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Search All cases with no file type filtering.
*/
public void testTwo() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
int matchesMustAlsoBeFoundInThisCase = this.utils.getCaseMap().get(CASE2);
AbstractCommonAttributeSearcher builder = new SingleInterCaseCommonAttributeSearcher(matchesMustAlsoBeFoundInThisCase, dataSources, false, false);
CommonAttributeSearchResults metadata = builder.findFiles();
assertTrue("Results should not be empty", metadata.size() != 0);
//case 1 data set 1
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_1, CASE1, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_1, CASE1, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_1, CASE1, 1));
//case 1 data set 2
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_2, CASE1, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_2, CASE1, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_2, CASE1, 1));
//case 2 data set 1
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_PDF, CASE2_DATASET_1, CASE2, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_JPG, CASE2_DATASET_1, CASE2, 0));
//case 2 data set 2
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE2_DATASET_2, CASE2, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE2_DATASET_2, CASE2, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE2_DATASET_2, CASE2, 1));
//case 3 data set 1
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE3_DATASET_1, CASE3, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE3_DATASET_1, CASE3, 1));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_1, CASE3, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_1, CASE3, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_JPG, CASE3_DATASET_1, CASE3, 0));
//case 3 data set 2
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_2, CASE3, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_2, CASE3, 0));
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1));
} catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
}

View File

@ -0,0 +1,466 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.commonfilessearch;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import junit.framework.Test;
import org.netbeans.junit.NbModuleSuite;
import org.netbeans.junit.NbTestCase;
import org.openide.util.Exceptions;
import org.python.icu.impl.Assert;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeSearcher;
import org.sleuthkit.autopsy.commonfilesearch.AllIntraCaseCommonAttributeSearcher;
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults;
import org.sleuthkit.autopsy.commonfilesearch.SingleIntraCaseCommonAttributeSearcher;
import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseTestUtils.*;
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestJobSettings.IngestType;
import org.sleuthkit.autopsy.ingest.IngestModuleTemplate;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory;
import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory;
import org.sleuthkit.autopsy.testutils.IngestUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Add set 1, set 2, set 3, and set 4 to case and ingest with hash algorithm.
*/
public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase {
public static Test suite() {
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithHashAndFileTypeIntraCaseTests.class).
clusters(".*").
enableModules(".*");
return conf.suite();
}
private final IntraCaseTestUtils utils;
public IngestedWithHashAndFileTypeIntraCaseTests(String name) {
super(name);
this.utils = new IntraCaseTestUtils(this, "IngestedWithHashAndFileTypeTests");
}
@Override
public void setUp() {
this.utils.setUp();
IngestModuleTemplate hashLookupTemplate = IngestUtils.getIngestModuleTemplate(new HashLookupModuleFactory());
IngestModuleTemplate mimeTypeLookupTemplate = IngestUtils.getIngestModuleTemplate(new FileTypeIdModuleFactory());
ArrayList<IngestModuleTemplate> templates = new ArrayList<>();
templates.add(hashLookupTemplate);
templates.add(mimeTypeLookupTemplate);
IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileTypeIntraCaseTests.class.getCanonicalName(), IngestType.FILES_ONLY, templates);
try {
IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings);
} catch (NoCurrentCaseException | TskCoreException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
@Override
public void tearDown() {
this.utils.tearDown();
}
/**
* Find all matches & all file types. Confirm file.jpg is found on all three
* and file.docx is found on two.
*/
public void testOneA() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false);
CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles();
Map<Long, String> objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = IntraCaseTestUtils.getFiles(objectIdToDataSource.keySet());
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find all matches & only image types. Confirm file.jpg is found on all
* three.
*/
public void testOneB() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false);
CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find all matches & only image types. Confirm file.jpg is found on all
* three.
*/
public void testOneC() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, true);
CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 1 & all file types. Confirm same results.
*
*/
public void testTwoA() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long first = getDataSourceIdByName(SET1, dataSources);
AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, false);
CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 1 & only media types. Confirm same results.
*
*/
public void testTwoB() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long first = getDataSourceIdByName(SET1, dataSources);
AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, true, false);
CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 1 & all file types. Confirm same results.
*
*/
public void testTwoC() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long first = getDataSourceIdByName(SET1, dataSources);
AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, true);
CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 2 & all file types: Confirm file.jpg.
*
*/
public void testThree() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long second = getDataSourceIdByName(SET2, dataSources);
AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(second, dataSources, false, false);
CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 4 & all file types: Confirm nothing is found.
*/
public void testFour() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long last = getDataSourceIdByName(SET4, dataSources);
AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(last, dataSources, false, false);
CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
/**
* Find matches on set 3 & all file types: Confirm file.jpg and file.docx.
*/
public void testFive() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long third = getDataSourceIdByName(SET3, dataSources);
AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, false, false);
CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles();
Map<Long, String> objectIdToDataSource = mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = getFiles(objectIdToDataSource.keySet());
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0));
assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0));
} catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
}

View File

@ -31,10 +31,10 @@ import org.openide.util.Exceptions;
import org.python.icu.impl.Assert; import org.python.icu.impl.Assert;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; import org.sleuthkit.autopsy.commonfilesearch.AllIntraCaseCommonAttributeSearcher;
import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults;
import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; import org.sleuthkit.autopsy.commonfilesearch.IntraCaseCommonAttributeSearcher;
import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource; import org.sleuthkit.autopsy.commonfilesearch.SingleIntraCaseCommonAttributeSearcher;
import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; import org.sleuthkit.autopsy.ingest.IngestModuleTemplate;
import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory;
@ -51,21 +51,21 @@ import org.sleuthkit.datamodel.TskCoreException;
* Add images set 1, set 2, set 3, and set 4 to case. Do not run mime type * Add images set 1, set 2, set 3, and set 4 to case. Do not run mime type
* module. * module.
*/ */
public class IngestedWithNoFileTypes extends NbTestCase { public class IngestedWithNoFileTypesIntraCaseTests extends NbTestCase {
public static Test suite() { public static Test suite() {
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithNoFileTypes.class). NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithNoFileTypesIntraCaseTests.class).
clusters(".*"). clusters(".*").
enableModules(".*"); enableModules(".*");
return conf.suite(); return conf.suite();
} }
private final IntraCaseUtils utils; private final IntraCaseTestUtils utils;
public IngestedWithNoFileTypes(String name) { public IngestedWithNoFileTypesIntraCaseTests(String name) {
super(name); super(name);
this.utils = new IntraCaseUtils(this, "IngestedWithNoFileTypes"); this.utils = new IntraCaseTestUtils(this, "IngestedWithNoFileTypes");
} }
@Override @Override
@ -77,7 +77,7 @@ public class IngestedWithNoFileTypes extends NbTestCase {
ArrayList<IngestModuleTemplate> templates = new ArrayList<>(); ArrayList<IngestModuleTemplate> templates = new ArrayList<>();
templates.add(hashLookupTemplate); templates.add(hashLookupTemplate);
IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileType.class.getCanonicalName(), IngestJobSettings.IngestType.FILES_ONLY, templates); IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithNoFileTypesIntraCaseTests.class.getCanonicalName(), IngestJobSettings.IngestType.FILES_ONLY, templates);
try { try {
IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings);
@ -97,20 +97,22 @@ public class IngestedWithNoFileTypes extends NbTestCase {
* find nothing and no errors should arise. * find nothing and no errors should arise.
*/ */
public void testOne() { public void testOne() {
try { try {
Map<Long, String> dataSources = this.utils.getDataSourceMap(); Map<Long, String> dataSources = this.utils.getDataSourceMap();
CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, true, false); IntraCaseCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false);
CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles();
Map<Long, String> objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); Map<Long, String> objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); List<AbstractFile> files = IntraCaseTestUtils.getFiles(objectIdToDataSource.keySet());
assertTrue(files.isEmpty()); assertTrue(files.isEmpty());
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) { } catch (TskCoreException | NoCurrentCaseException | SQLException ex) {
Exceptions.printStackTrace(ex); Exceptions.printStackTrace(ex);
Assert.fail(ex);
} }
} }
@ -121,18 +123,18 @@ public class IngestedWithNoFileTypes extends NbTestCase {
public void testTwo() { public void testTwo() {
try { try {
Map<Long, String> dataSources = this.utils.getDataSourceMap(); Map<Long, String> dataSources = this.utils.getDataSourceMap();
Long third = IntraCaseUtils.getDataSourceIdByName(IntraCaseUtils.SET3, dataSources); Long third = IntraCaseTestUtils.getDataSourceIdByName(IntraCaseTestUtils.SET3, dataSources);
CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(third, dataSources, true, false); IntraCaseCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, true, false);
CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles();
Map<Long, String> objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); Map<Long, String> objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata);
List<AbstractFile> files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); List<AbstractFile> files = IntraCaseTestUtils.getFiles(objectIdToDataSource.keySet());
assertTrue(files.isEmpty()); assertTrue(files.isEmpty());
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) { } catch (Exception ex) {
Exceptions.printStackTrace(ex); Exceptions.printStackTrace(ex);
Assert.fail(ex); Assert.fail(ex);
} }

Some files were not shown because too many files have changed in this diff Show More