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">
<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=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"/>
@ -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=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=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 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.util.ArrayList;
import java.util.logging.Level;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
@ -105,7 +103,16 @@ public class GetTagNameAndCommentDialog extends JDialog {
public static TagNameAndComment doDialog(Window owner) {
GetTagNameAndCommentDialog dialog = new GetTagNameAndCommentDialog(owner);
dialog.display();
return dialog.tagNameAndComment;
return dialog.getTagNameAndComment();
}
/**
* Get the TagNameAndComment.
*
* @return the tagNameAndComment
*/
private TagNameAndComment getTagNameAndComment() {
return tagNameAndComment;
}
private GetTagNameAndCommentDialog(Window owner) {
@ -114,14 +121,14 @@ public class GetTagNameAndCommentDialog extends JDialog {
ModalityType.APPLICATION_MODAL);
}
private void display() {
initComponents();
tagCombo.setRenderer(new DefaultListCellRenderer() {
private static final long serialVersionUID = 1L;
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
String status = ((TagName) value).getKnownStatus() == TskData.FileKnown.BAD ?TagsManager.getNotableTagLabel() : "";
String status = ((TagName) value).getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
String newValue = ((TagName) value).getDisplayName() + status;
return super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus);
}
@ -160,7 +167,6 @@ public class GetTagNameAndCommentDialog extends JDialog {
}
});
} catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(GetTagNameAndCommentDialog.class
.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.JLabel tagLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -146,6 +146,8 @@ UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases
UpdateRecentCases.menuItem.empty=-Empty-
AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel
NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive
NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system
NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive.
CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source
CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1}
MissingImageDialog.lbWarning.text=

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)
try {
if (false == PathValidator.isValid(path, Case.getCurrentCaseThrows().getCaseType())) {
if (false == PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) {
pathErrorLabel.setVisible(true);
pathErrorLabel.setText(Bundle.ImageFilePanel_pathValidation_dataSourceOnCDriveError());
}

View File

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

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)
try {
if (!PathValidator.isValid(path, Case.getCurrentCaseThrows().getCaseType())) {
if (!PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) {
errorLabel.setVisible(true);
errorLabel.setText(Bundle.LogicalEvidenceFilePanel_pathValidation_dataSourceOnCDriveError());
return false;

View File

@ -29,6 +29,7 @@ import javax.swing.event.DocumentListener;
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.PathValidator;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
* The JPanel for the first page of the new case wizard.
@ -151,11 +152,24 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener {
*/
caseParentDirWarningLabel.setVisible(false);
String parentDir = getCaseParentDir();
if (!PathValidator.isValid(parentDir, getCaseType())) {
if (!PathValidator.isValidForMultiUserCase(parentDir, getCaseType())) {
caseParentDirWarningLabel.setVisible(true);
caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnCDriveError.text"));
}
/**
* Check the base case directory if it can persist data and show a
* warning if it is a wrong choice
*/
if(!PathValidator.isValidForRunningOnTarget(parentDir)){
caseParentDirWarningLabel.setVisible(true);
if(PlatformUtil.isWindowsOS()){
caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text" ));
} else if(System.getProperty("os.name").toLowerCase().contains("nux")) {
caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text"));
}
}
/**
* Enable the "Next" button for the wizard if there is text entered for
* 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) {
biggestPK = value;
}
outputStatement.executeUpdate("INSERT INTO content_tags (tag_id, obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset) VALUES (" //NON-NLS
outputStatement.executeUpdate("INSERT INTO content_tags (tag_id, obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset, user_name) VALUES (" //NON-NLS
+ value + ","
+ inputResultSet.getLong(2) + ","
+ inputResultSet.getLong(3) + ",'"
+ inputResultSet.getString(4) + "',"
+ inputResultSet.getLong(5) + ","
+ inputResultSet.getLong(6) + ")"); //NON-NLS
+ inputResultSet.getLong(6) + ",'"
+ inputResultSet.getString(7)+ "')"); //NON-NLS
} catch (SQLException ex) {
if (ex.getErrorCode() != 0) { // 0 if the entry already exists
@ -892,7 +893,8 @@ public class SingleUserCaseConverter {
+ value + ","
+ inputResultSet.getLong(2) + ","
+ inputResultSet.getLong(3) + ",'"
+ inputResultSet.getString(4) + "')"); //NON-NLS
+ inputResultSet.getString(4) + "','"
+ inputResultSet.getString(5) + "')"); //NON-NLS
} catch (SQLException ex) {
if (ex.getErrorCode() != 0) { // 0 if the entry already exists

View File

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

View File

@ -47,7 +47,6 @@ import org.sleuthkit.datamodel.TskData;
public class TagsManager implements Closeable {
private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName());
private final SleuthkitCase caseDb;
/**
@ -71,13 +70,14 @@ public class TagsManager implements Closeable {
|| tagDisplayName.contains(";"));
}
@NbBundle.Messages({"TagsManager.notableTagEnding.text= (Notable)"})
/**
* Get String of text which is used to label tags as notable to the user.
*
* @return Bundle message TagsManager.notableTagEnding.text
*/
public static String getNotableTagLabel(){
public static String getNotableTagLabel() {
return Bundle.TagsManager_notableTagEnding_text();
}
@ -166,6 +166,33 @@ public class TagsManager implements Closeable {
return caseDb.getTagNamesInUse();
}
/**
* Gets a list of all tag names currently in use in the case database for
* tagging content or artifacts by the specified user.
*
* @param userName - the user name that you want to get tags for
*
* @return A list, possibly empty, of TagName objects.
*
* @throws TskCoreException If there is an error querying the case database.
*/
public List<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
* which there is at least one matching row in the content_tags or
@ -174,13 +201,44 @@ public class TagsManager implements Closeable {
* @param dsObjId data source object id
*
* @return A list, possibly empty, of TagName data transfer objects (DTOs)
* for the rows.
* for the rows.
*
* @throws TskCoreException
*/
public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException {
return caseDb.getTagNamesInUse(dsObjId);
}
/**
* Selects all of the rows from the tag_names table in the case database for
* which there is at least one matching row in the content_tags or
* blackboard_artifact_tags tables, for the given data source object id and user.
*
* @param dsObjId data source object id
* @param userName - the user name that you want to get tags for
*
* @return A list, possibly empty, of TagName data transfer objects (DTOs)
* for the rows.
*
* @throws TskCoreException
*/
public List<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.
* It has keys for the display names of the standard tag types, the current
@ -416,24 +474,77 @@ public class TagsManager implements Closeable {
return caseDb.getContentTagsCountByTagName(tagName);
}
/**
* Gets content tags count by tag name for the specified user.
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* @param userName - the user name that you want to get tags for
*
* @return A count of the content tags with the specified tag name for the
* specified user.
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getContentTagsCountByTagNameForUser(TagName tagName, String userName) throws TskCoreException {
long count = 0;
List<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
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames and/or addTagName.
* database, which can be obtained by calling getTagNames
* and/or addTagName.
*
* @param dsObjId data source object id
*
* @return A count of the content tags with the specified tag name, and for
* the given data source
* the given data source
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
* the case database.
*/
public long getContentTagsCountByTagName(TagName tagName, long dsObjId) throws TskCoreException {
return caseDb.getContentTagsCountByTagName(tagName, dsObjId);
}
/**
* Gets content tags count by tag name, for the given data source and user
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
*
* @param dsObjId data source object id
* @param userName - the user name that you want to get tags for
*
* @return A count of the content tags with the specified tag name, and for
* the given data source and user
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getContentTagsCountByTagNameForUser(TagName tagName, long dsObjId, String userName) throws TskCoreException {
long count = 0;
List<ContentTag> contentTags = getContentTagsByTagName(tagName, dsObjId);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/**
* Gets a content tag by tag id.
*
@ -463,7 +574,7 @@ public class TagsManager implements Closeable {
return caseDb.getContentTagsByTagName(tagName);
}
/**
/**
* Gets content tags by tag name, for the given data source.
*
* @param tagName The tag name of interest.
@ -581,6 +692,31 @@ public class TagsManager implements Closeable {
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName);
}
/**
* Gets an artifact tags count by tag name for a specific user.
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* @param userName - the user name that you want to get tags for
*
* @return A count of the artifact tags with the specified tag name for the
* specified user.
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getBlackboardArtifactTagsCountByTagNameForUser(TagName tagName, String userName) throws TskCoreException {
long count = 0;
List<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.
*
@ -589,8 +725,8 @@ public class TagsManager implements Closeable {
* and/or addTagName.
* @param dsObjId data source object id
*
* @return A count of the artifact tags with the specified tag name,
* for the given data source.
* @return A count of the artifact tags with the specified tag name, for the
* given data source.
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
@ -599,6 +735,33 @@ public class TagsManager implements Closeable {
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.
*

View File

@ -5,8 +5,6 @@ OpenIDE-Module-Long-Description=\
Correlation Engine ingest module and central database. \n\n\
The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\
Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest.
CentralRepoCommentDialog.fileLabel.text=File:
CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.pathLabel.text=
CentralRepoCommentDialog.okButton.text=&OK
CentralRepoCommentDialog.cancelButton.text=C&ancel

View File

@ -31,14 +31,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" pref="500" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" 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>
<Component id="commentLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="451" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
@ -55,21 +48,16 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace 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"/>
<EmptySpace 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"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="okButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -113,20 +101,6 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
</Events>
</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">
<Properties>
<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();
}
pathLabel.setText(instance.getFilePath());
commentTextArea.setText(instance.getComment());
this.correlationAttribute = correlationAttribute;
@ -103,8 +102,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
commentTextArea = new javax.swing.JTextArea();
okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton();
fileLabel = new javax.swing.JLabel();
pathLabel = new javax.swing.JLabel();
commentLabel = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
@ -131,10 +128,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
}
});
org.openide.awt.Mnemonics.setLocalizedText(fileLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.fileLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.pathLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.commentLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
@ -146,12 +139,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(fileLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pathLabel))
.addComponent(commentLabel))
.addComponent(commentLabel)
.addGap(0, 451, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
@ -164,17 +152,13 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fileLabel)
.addComponent(pathLabel))
.addGap(19, 19, 19)
.addComponent(commentLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(okButton)
.addComponent(cancelButton))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cancelButton)
.addComponent(okButton))
.addContainerGap())
);
@ -197,9 +181,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
private javax.swing.JButton cancelButton;
private javax.swing.JLabel commentLabel;
private javax.swing.JTextArea commentTextArea;
private javax.swing.JLabel fileLabel;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JButton okButton;
private javax.swing.JLabel pathLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -80,7 +80,7 @@
</DimensionLayout>
<DimensionLayout dim="1">
<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>
</DimensionLayout>
</Layout>
@ -106,7 +106,7 @@
<EmptySpace min="0" pref="483" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="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"/>
</Group>
</Group>
@ -133,23 +133,18 @@
<Component id="earliestCaseLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="earliestCaseDate" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="tableStatusPanelLabel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="tableScrollPane" pref="176" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="tableStatusPanelLabel" min="-2" pref="16" max="-2" attributes="0"/>
<Component id="tableScrollPane" pref="27" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="tableStatusPanel" min="-2" max="-2" attributes="0"/>
@ -230,13 +225,6 @@
</DimensionLayout>
</Layout>
</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>
</Container>
</SubComponents>

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.contentviewer;
import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
@ -85,7 +86,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private static final long serialVersionUID = -1L;
private final static Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName());
private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName());
private static final int DEFAULT_MIN_CELL_WIDTH = 15;
private static final int CELL_TEXT_WIDTH_PADDING = 5;
private final DataContentViewerOtherCasesTableModel tableModel;
private final Collection<CorrelationAttribute> correlationAttributes;
@ -125,7 +129,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
showCommonalityDetails();
} else if (jmi.equals(addCommentMenuItem)) {
try {
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(otherCasesTable.getSelectedRow());
OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow());
AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute());
action.actionPerformed(null);
String currentComment = action.getComment();
@ -149,7 +153,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// Set background of every nth row as light grey.
TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer();
otherCasesTable.setDefaultRenderer(Object.class, renderer);
tableStatusPanelLabel.setVisible(false);
}
@ -207,7 +210,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
if (-1 != selectedRowViewIdx) {
EamDb dbManager = EamDb.getInstance();
int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx);
OtherOccurrenceNodeData nodeData = (OtherOccurrenceNodeData) tableModel.getRow(selectedRowModelIdx);
OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) tableModel.getRow(selectedRowModelIdx);
CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase();
if (eamCasePartial == null) {
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
@ -462,11 +465,11 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
@Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."})
/**
* Gets the list of Eam Cases and determines the earliest case creation date.
* Sets the label to display the earliest date string to the user.
* Gets the list of Eam Cases and determines the earliest case creation
* date. Sets the label to display the earliest date string to the user.
*/
private void setEarliestCaseDate() {
String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable();
String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable();
if (EamDb.isEnabled()) {
LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC);
@ -475,12 +478,12 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
EamDb dbManager = EamDb.getInstance();
List<CorrelationCase> cases = dbManager.getCases();
for (CorrelationCase aCase : cases) {
LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate()));
LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate()));
if (caseDate.isBefore(earliestDate)) {
if (caseDate.isBefore(earliestDate)) {
earliestDate = caseDate;
dateStringDisplay = aCase.getCreationDate();
}
}
}
@ -495,10 +498,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
}
/**
* Query the central repo database (if enabled) and the case database to find all
* artifact instances correlated to the given central repository artifact. If the
* central repo is not enabled, this will only return files from the current case
* with matching MD5 hashes.
* Query the central repo database (if enabled) and the case database to
* find all artifact instances correlated to the given central repository
* artifact. If the central repo is not enabled, this will only return files
* from the current case with matching MD5 hashes.
*
* @param corAttr CorrelationAttribute to query for
* @param dataSourceName Data source to filter results
@ -506,18 +509,18 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
*
* @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
try {
final Case openCase = Case.getCurrentCase();
String caseUUID = openCase.getName();
HashMap<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap = new HashMap<>();
HashMap<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap = new HashMap<>();
if (EamDb.isEnabled()) {
List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue());
for (CorrelationAttributeInstance artifactInstance:instances) {
for (CorrelationAttributeInstance artifactInstance : instances) {
// Only add the attribute if it isn't the object the user selected.
// We consider it to be a different object if at least one of the following is true:
@ -530,7 +533,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|| !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)
|| !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) {
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
nodeDataMap.put(uniquePathKey, newNode);
}
@ -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 openCase The current case
*
* @return List of matching AbstractFile objects
*
* @throws NoCurrentCaseException
* @throws TskCoreException
* @throws EamDbException
@ -594,9 +601,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* @throws TskCoreException
* @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
// 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.
if (nodeDataMap.containsKey(uniquePathKey)) {
if (newNode.getKnown() == TskData.FileKnown.BAD) {
OtherOccurrenceNodeData prevInstance = nodeDataMap.get(uniquePathKey);
OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey);
prevInstance.updateKnown(newNode.getKnown());
}
} else {
@ -642,7 +649,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} else {
return this.file != null
&& this.file.getSize() > 0
&& ((this.file.getMd5Hash() != null) && ( ! this.file.getMd5Hash().isEmpty()));
&& ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty()));
}
}
@ -665,8 +672,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
*
* @param node The node being viewed.
*/
@Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other occurrences to display.",
"DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file."})
@Messages({
"DataContentViewerOtherCases.table.noArtifacts=Item has no attributes with which to search.",
"DataContentViewerOtherCases.table.noResultsFound=No results found."
})
private void populateTable(Node node) {
String dataSourceName = "";
String deviceId = "";
@ -684,7 +693,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// get the attributes we can correlate on
correlationAttributes.addAll(getCorrelationAttributesFromNode(node));
for (CorrelationAttribute corAttr : correlationAttributes) {
Map<UniquePathKey,OtherOccurrenceNodeData> correlatedNodeDataMap = new HashMap<>(0);
Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
// get correlation and reference set instances from DB
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
@ -696,36 +705,45 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
}
if (correlationAttributes.isEmpty()) {
// @@@ BC: We should have a more descriptive message than this. Mention that the file didn't have a MD5, etc.
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_noArtifacts());
tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noArtifacts()));
setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noArtifacts());
} else if (0 == tableModel.getRowCount()) {
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_isempty());
tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noResultsFound()));
setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noResultsFound());
} else {
clearMessageOnTableStatusPanel();
setColumnWidths();
}
setEarliestCaseDate();
}
/**
* Adjust a given column for the text provided.
*
* @param columnIndex The index of the column to adjust.
* @param text The text whose length will be used to adjust the
* column width.
*/
private void setColumnWidthToText(int columnIndex, String text) {
TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex);
FontMetrics fontMetrics = otherCasesTable.getFontMetrics(otherCasesTable.getFont());
int stringWidth = fontMetrics.stringWidth(text);
column.setMinWidth(stringWidth + CELL_TEXT_WIDTH_PADDING);
}
/**
* Adjust column widths to their preferred values.
*/
private void setColumnWidths() {
for (int idx = 0; idx < tableModel.getColumnCount(); idx++) {
TableColumn column = otherCasesTable.getColumnModel().getColumn(idx);
int colWidth = tableModel.getColumnPreferredWidth(idx);
if (0 < colWidth) {
column.setPreferredWidth(colWidth);
column.setMinWidth(DEFAULT_MIN_CELL_WIDTH);
int columnWidth = tableModel.getColumnPreferredWidth(idx);
if (columnWidth > 0) {
column.setPreferredWidth(columnWidth);
}
}
}
private void displayMessageOnTableStatusPanel(String message) {
tableStatusPanelLabel.setText(message);
tableStatusPanelLabel.setVisible(true);
}
private void clearMessageOnTableStatusPanel() {
tableStatusPanelLabel.setVisible(false);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
@ -749,7 +767,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
earliestCaseLabel = new javax.swing.JLabel();
earliestCaseDate = new javax.swing.JLabel();
tableStatusPanel = new javax.swing.JPanel();
tableStatusPanelLabel = new javax.swing.JLabel();
rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
@ -811,8 +828,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addGap(0, 16, Short.MAX_VALUE)
);
tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51));
javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel);
tableContainerPanel.setLayout(tableContainerPanelLayout);
tableContainerPanelLayout.setHorizontalGroup(
@ -825,20 +840,16 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addComponent(earliestCaseLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(earliestCaseDate)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
tableContainerPanelLayout.setVerticalGroup(
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 176, Short.MAX_VALUE)
.addGap(0, 0, 0)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(earliestCaseLabel)
.addComponent(earliestCaseDate))
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
.addGap(2, 2, 2)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(earliestCaseLabel)
.addComponent(earliestCaseDate))
.addGap(0, 0, 0)
.addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0))
@ -857,7 +868,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addGap(0, 483, Short.MAX_VALUE)
.addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(otherCasesPanelLayout.createSequentialGroup()
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE)
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
.addGap(0, 0, 0)))
);
@ -869,7 +880,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE)
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
@ -879,8 +890,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) {
int rowIndex = otherCasesTable.getSelectedRow();
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex);
if (selectedNode.isCentralRepoNode()) {
enableCentralRepoActions = true;
if (selectedNode instanceof OtherOccurrenceNodeInstanceData) {
OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedNode;
enableCentralRepoActions = instanceData.isCentralRepoNode();
}
}
@ -904,20 +916,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private javax.swing.JPanel tableContainerPanel;
private javax.swing.JScrollPane tableScrollPane;
private javax.swing.JPanel tableStatusPanel;
private javax.swing.JLabel tableStatusPanelLabel;
// End of variables declaration//GEN-END:variables
/**
* Used as a key to ensure we eliminate duplicates from the result set by
* not overwriting CR correlation instances.
*/
static final class UniquePathKey {
private static final class UniquePathKey {
private final String dataSourceID;
private final String filePath;
private final String type;
UniquePathKey(OtherOccurrenceNodeData nodeData) {
UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) {
super();
dataSourceID = nodeData.getDeviceID();
if (nodeData.getFilePath() != null) {
@ -931,10 +942,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
@Override
public boolean equals(Object other) {
if (other instanceof UniquePathKey) {
UniquePathKey otherKey = (UniquePathKey)(other);
return ( Objects.equals(otherKey.dataSourceID, this.dataSourceID)
&& Objects.equals(otherKey.filePath, this.filePath)
&& Objects.equals(otherKey.type, this.type));
UniquePathKey otherKey = (UniquePathKey) (other);
return (Objects.equals(otherKey.getDataSourceID(), this.getDataSourceID())
&& Objects.equals(otherKey.getFilePath(), this.getFilePath())
&& Objects.equals(otherKey.getType(), this.getType()));
}
return false;
}
@ -944,7 +955,34 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
//int hash = 7;
//hash = 67 * hash + this.dataSourceID.hashCode();
//hash = 67 * hash + this.filePath.hashCode();
return Objects.hash(dataSourceID, filePath, type);
return Objects.hash(getDataSourceID(), getFilePath(), getType());
}
/**
* Get the type of this UniquePathKey.
*
* @return the type
*/
String getType() {
return type;
}
/**
* Get the file path for the UniquePathKey.
*
* @return the filePath
*/
String getFilePath() {
return filePath;
}
/**
* Get the data source id for the UniquePathKey.
*
* @return the dataSourceID
*/
String getDataSourceID() {
return dataSourceID;
}
}

View File

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

View File

@ -1,5 +1,5 @@
/*
* Central Repository
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
@ -18,216 +18,9 @@
*/
package org.sleuthkit.autopsy.centralrepository.contentviewer;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskDataException;
/**
* Class for populating the Other Occurrences tab
* Marker interface for Other Occurrences nodes.
*/
class OtherOccurrenceNodeData {
interface OtherOccurrenceNodeData {
// For now hard code the string for the central repo files type, since
// getting it dynamically can fail.
private static final String FILE_TYPE_STR = "Files";
private final String caseName;
private String deviceID;
private String dataSourceName;
private final String filePath;
private final String typeStr;
private final CorrelationAttribute.Type type;
private final String value;
private TskData.FileKnown known;
private String comment;
private AbstractFile originalAbstractFile = null;
private CorrelationAttributeInstance originalCorrelationInstance = null;
/**
* Create a node from a central repo instance.
* @param instance The central repo instance
* @param type The type of the instance
* @param value The value of the instance
*/
OtherOccurrenceNodeData(CorrelationAttributeInstance instance, CorrelationAttribute.Type type, String value) {
caseName = instance.getCorrelationCase().getDisplayName();
deviceID = instance.getCorrelationDataSource().getDeviceID();
dataSourceName = instance.getCorrelationDataSource().getName();
filePath = instance.getFilePath();
this.typeStr = type.getDisplayName();
this.type = type;
this.value = value;
known = instance.getKnownStatus();
comment = instance.getComment();
originalCorrelationInstance = instance;
}
/**
* Create a node from an abstract file.
* @param newFile The abstract file
* @param autopsyCase The current case
* @throws EamDbException
*/
OtherOccurrenceNodeData(AbstractFile newFile, Case autopsyCase) throws EamDbException {
caseName = autopsyCase.getDisplayName();
try {
DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId());
deviceID = dataSource.getDeviceId();
dataSourceName = dataSource.getName();
} catch (TskDataException | TskCoreException ex) {
throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex);
}
filePath = newFile.getParentPath() + newFile.getName();
typeStr = FILE_TYPE_STR;
this.type = null;
value = newFile.getMd5Hash();
known = newFile.getKnown();
comment = "";
originalAbstractFile = newFile;
}
/**
* Check if this node is a "file" type
* @return true if it is a file type
*/
boolean isFileType() {
return FILE_TYPE_STR.equals(typeStr);
}
/**
* Update the known status for this node
* @param newKnownStatus The new known status
*/
void updateKnown(TskData.FileKnown newKnownStatus) {
known = newKnownStatus;
}
/**
* Update the comment for this node
* @param newComment The new comment
*/
void updateComment(String newComment) {
comment = newComment;
}
/**
* Check if this is a central repo node.
* @return true if this node was created from a central repo instance, false otherwise
*/
boolean isCentralRepoNode() {
return (originalCorrelationInstance != null);
}
/**
* Uses the saved instance plus type and value to make a new CorrelationAttribute.
* Should only be called if isCentralRepoNode() is true.
* @return the newly created CorrelationAttribute
*/
CorrelationAttribute createCorrelationAttribute() throws EamDbException {
if (! isCentralRepoNode() ) {
throw new EamDbException("Can not create CorrelationAttribute for non central repo node");
}
CorrelationAttribute attr = new CorrelationAttribute(type, value);
attr.addInstance(originalCorrelationInstance);
return attr;
}
/**
* Get the case name
* @return the case name
*/
String getCaseName() {
return caseName;
}
/**
* Get the device ID
* @return the device ID
*/
String getDeviceID() {
return deviceID;
}
/**
* Get the data source name
* @return the data source name
*/
String getDataSourceName() {
return dataSourceName;
}
/**
* Get the file path
* @return the file path
*/
String getFilePath() {
return filePath;
}
/**
* Get the type (as a string)
* @return the type
*/
String getType() {
return typeStr;
}
/**
* Get the value (MD5 hash for files)
* @return the value
*/
String getValue() {
return value;
}
/**
* Get the known status
* @return the known status
*/
TskData.FileKnown getKnown() {
return known;
}
/**
* Get the comment
* @return the comment
*/
String getComment() {
return comment;
}
/**
* Get the backing abstract file.
* Should only be called if isCentralRepoNode() is false
* @return the original abstract file
*/
AbstractFile getAbstractFile() throws EamDbException {
if (originalCorrelationInstance == null) {
throw new EamDbException("AbstractFile is null");
}
return originalAbstractFile;
}
/**
* Get the backing CorrelationAttributeInstance.
* Should only be called if isCentralRepoNode() is true
* @return the original CorrelationAttributeInstance
* @throws EamDbException
*/
CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException {
if (originalCorrelationInstance == null) {
throw new EamDbException("CorrelationAttributeInstance is null");
}
return originalCorrelationInstance;
}
}

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;
}
/**
* 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.
@ -503,6 +543,48 @@ abstract class AbstractSqlEamDb implements EamDb {
return eamDataSourceResult;
}
/**
* Retrieves Data Source details based on data source ID
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
* @param dataSourceId the data source ID number
*
* @return The data source
*/
@Override
public CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException {
if (correlationCase == null) {
throw new EamDbException("Correlation case is null");
}
Connection conn = connect();
CorrelationDataSource eamDataSourceResult = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String sql = "SELECT * FROM data_sources WHERE id=? AND case_id=?"; // NON-NLS
try {
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setInt(1, dataSourceId);
preparedStatement.setInt(2, correlationCase.getID());
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
}
} catch (SQLException ex) {
throw new EamDbException("Error getting data source.", ex); // NON-NLS
} finally {
EamDbUtil.closeStatement(preparedStatement);
EamDbUtil.closeResultSet(resultSet);
EamDbUtil.closeConnection(conn);
}
return eamDataSourceResult;
}
/**
* Return a list of data sources in the DB
*
@ -1814,6 +1896,52 @@ abstract class AbstractSqlEamDb implements EamDb {
}
}
/**
* Process the Artifact instance in the EamDb give a where clause
*
* @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance
* @param whereClause query string to execute
* @throws EamDbException
*/
@Override
public void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException {
if (type == null) {
throw new EamDbException("Correlation type is null");
}
if (instanceTableCallback == null) {
throw new EamDbException("Callback interface is null");
}
if(whereClause == null) {
throw new EamDbException("Where clause is null");
}
Connection conn = connect();
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String tableName = EamDbUtil.correlationTypeToInstanceTableName(type);
StringBuilder sql = new StringBuilder(300);
sql.append("select * from ")
.append(tableName)
.append(" WHERE ")
.append(whereClause);
try {
preparedStatement = conn.prepareStatement(sql.toString());
resultSet = preparedStatement.executeQuery();
instanceTableCallback.process(resultSet);
} catch (SQLException ex) {
throw new EamDbException("Error getting all artifact instances from instances table", ex);
} finally {
EamDbUtil.closeStatement(preparedStatement);
EamDbUtil.closeResultSet(resultSet);
EamDbUtil.closeConnection(conn);
}
}
@Override
public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException {
if (eamOrg == null) {

View File

@ -175,6 +175,14 @@ public interface EamDb {
*/
CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException;
/**
* Retrieves Case details based on Case ID
*
* @param caseID unique identifier for a case
*
* @return The retrieved case
*/
CorrelationCase getCaseById(int caseId) throws EamDbException;
/**
* Retrieves cases that are in DB.
*
@ -200,6 +208,18 @@ public interface EamDb {
*/
CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException;
/**
* Retrieves Data Source details based on data source ID
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
* @param dataSourceId the data source ID number
*
* @return The data source
*/
CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException;
/**
* Retrieves data sources that are in DB
*
@ -685,4 +705,15 @@ public interface EamDb {
* @throws EamDbException
*/
void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException;
/**
* Process the Artifact instance in the EamDb
*
* @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance
* @param whereClause query string to execute
* @throws EamDbException
*/
void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException;
}

View File

@ -33,9 +33,9 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
/**
* Sqlite implementation of the Central Repository database.
* All methods in AbstractSqlEamDb that read or write to the database should
* be overriden here and use appropriate locking.
* Sqlite implementation of the Central Repository database. All methods in
* AbstractSqlEamDb that read or write to the database should be overriden here
* and use appropriate locking.
*/
final class SqliteEamDb extends AbstractSqlEamDb {
@ -56,7 +56,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*
* @return the singleton instance of SqliteEamDb
*
* @throws EamDbException if one or more default correlation type(s) have an invalid db table name.
* @throws EamDbException if one or more default correlation type(s) have an
* invalid db table name.
*/
public synchronized static SqliteEamDb getInstance() throws EamDbException {
if (instance == null) {
@ -68,8 +69,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/**
*
* @throws EamDbException if the AbstractSqlEamDb class has one or more default
* correlation type(s) having an invalid db table name.
* @throws EamDbException if the AbstractSqlEamDb class has one or more
* default correlation type(s) having an invalid db table name.
*/
private SqliteEamDb() throws EamDbException {
dbSettings = new SqliteEamDbSettings();
@ -79,7 +80,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
@Override
public void shutdownConnections() throws EamDbException {
try {
synchronized(this) {
synchronized (this) {
if (null != connectionPool) {
connectionPool.close();
connectionPool = null; // force it to be re-created on next connect()
@ -107,7 +108,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
@Override
public void reset() throws EamDbException {
try{
try {
acquireExclusiveLock();
Connection conn = connect();
@ -200,18 +201,17 @@ final class SqliteEamDb extends AbstractSqlEamDb {
return "";
}
/**
* Add a new name/value pair in the db_info table.
*
* @param name Key to set
* @param name Key to set
* @param value Value to set
*
* @throws EamDbException
*/
@Override
public void newDbInfo(String name, String value) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.newDbInfo(name, value);
} finally {
@ -230,7 +230,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public String getDbInfo(String name) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getDbInfo(name);
} finally {
@ -241,14 +241,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/**
* Update the value for a name in the name/value db_info table.
*
* @param name Name to find
* @param name Name to find
* @param value Value to assign to name.
*
* @throws EamDbException
*/
@Override
public void updateDbInfo(String name, String value) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.updateDbInfo(name, value);
} finally {
@ -256,14 +256,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
}
}
/**
/**
* Creates new Case in the database from the given case
*
* @param autopsyCase The case to add
*/
@Override
public CorrelationCase newCase(Case autopsyCase) throws EamDbException {
try{
try {
acquireExclusiveLock();
return super.newCase(autopsyCase);
} finally {
@ -280,7 +280,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException {
try{
try {
acquireExclusiveLock();
return super.newCase(eamCase);
} finally {
@ -295,7 +295,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void updateCase(CorrelationCase eamCase) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.updateCase(eamCase);
} finally {
@ -312,7 +312,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getCaseByUUID(caseUUID);
} finally {
@ -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.
*
@ -327,7 +345,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List<CorrelationCase> getCases() throws EamDbException {
try{
try {
acquireSharedLock();
return super.getCases();
} finally {
@ -342,7 +360,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void newDataSource(CorrelationDataSource eamDataSource) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.newDataSource(eamDataSource);
} finally {
@ -353,14 +371,15 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/**
* Retrieves Data Source details based on data source device ID
*
* @param correlationCase the current CorrelationCase used for ensuring uniqueness of DataSource
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
* @param dataSourceDeviceId the data source device ID number
*
* @return The data source
*/
@Override
public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getDataSource(correlationCase, dataSourceDeviceId);
} finally {
@ -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
*
@ -375,7 +413,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List<CorrelationDataSource> getDataSources() throws EamDbException {
try{
try {
acquireSharedLock();
return super.getDataSources();
} finally {
@ -391,7 +429,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void addArtifact(CorrelationAttribute eamArtifact) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.addArtifact(eamArtifact);
} finally {
@ -403,14 +441,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* Retrieves eamArtifact instances from the database that are associated
* with the eamArtifactType and eamArtifactValue of the given eamArtifact.
*
* @param aType The type of the artifact
* @param value The correlation value
* @param aType The type of the artifact
* @param value The correlation value
*
* @return List of artifact instances for a given type/value
*/
@Override
public List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getArtifactInstancesByTypeValue(aType, value);
} finally {
@ -422,7 +460,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* Retrieves eamArtifact instances from the database that are associated
* with the aType and filePath
*
* @param aType EamArtifact.Type to search for
* @param aType EamArtifact.Type to search for
* @param filePath File path to search for
*
* @return List of 0 or more EamArtifactInstances
@ -431,7 +469,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List<CorrelationAttributeInstance> getArtifactInstancesByPath(CorrelationAttribute.Type aType, String filePath) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getArtifactInstancesByPath(aType, filePath);
} finally {
@ -447,12 +485,12 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value The value to search for
*
* @return Number of artifact instances having ArtifactType and
* ArtifactValue.
* ArtifactValue.
* @throws EamDbException
*/
@Override
public Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getCountArtifactInstancesByTypeValue(aType, value);
} finally {
@ -462,7 +500,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
@Override
public int getFrequencyPercentage(CorrelationAttribute corAttr) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getFrequencyPercentage(corAttr);
} finally {
@ -483,7 +521,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getCountUniqueCaseDataSourceTuplesHavingTypeValue(aType, value);
} finally {
@ -491,10 +529,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
}
}
@Override
public Long getCountUniqueDataSources() throws EamDbException {
try{
try {
acquireSharedLock();
return super.getCountUniqueDataSources();
} finally {
@ -507,15 +544,15 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* associated with the caseDisplayName and dataSource of the given
* eamArtifact instance.
*
* @param caseUUID Case ID to search for
* @param caseUUID Case ID to search for
* @param dataSourceID Data source ID to search for
*
* @return Number of artifact instances having caseDisplayName and
* dataSource
* dataSource
*/
@Override
public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getCountArtifactInstancesByCaseDataSource(caseUUID, dataSourceID);
} finally {
@ -529,7 +566,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void bulkInsertArtifacts() throws EamDbException {
try{
try {
acquireExclusiveLock();
super.bulkInsertArtifacts();
} finally {
@ -542,7 +579,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void bulkInsertCases(List<CorrelationCase> cases) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.bulkInsertCases(cases);
} finally {
@ -551,18 +588,18 @@ final class SqliteEamDb extends AbstractSqlEamDb {
}
/**
* Sets an eamArtifact instance to the given knownStatus.
* knownStatus should be BAD if the file has been tagged with a notable tag and
* UNKNOWN otherwise. If eamArtifact
* exists, it is updated. If eamArtifact does not exist it is added with the
* given status.
* Sets an eamArtifact instance to the given knownStatus. knownStatus should
* be BAD if the file has been tagged with a notable tag and UNKNOWN
* otherwise. If eamArtifact exists, it is updated. If eamArtifact does not
* exist it is added with the given status.
*
* @param eamArtifact Artifact containing exactly one (1) ArtifactInstance.
* @param knownStatus The status to change the artifact to. Should never be KNOWN
* @param knownStatus The status to change the artifact to. Should never be
* KNOWN
*/
@Override
public void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.setArtifactInstanceKnownStatus(eamArtifact, knownStatus);
} finally {
@ -581,7 +618,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getArtifactInstancesKnownBad(aType, value);
} finally {
@ -593,13 +630,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*
* Gets list of matching eamArtifact instances that have knownStatus =
* "Bad".
*
* @param aType EamArtifact.Type to search for
* @return List with 0 or more matching eamArtifact instances.
* @throws EamDbException
*/
@Override
public List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getArtifactInstancesKnownBad(aType);
} finally {
@ -617,7 +655,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public Long getCountArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getCountArtifactInstancesKnownBad(aType, value);
} finally {
@ -633,13 +671,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value Value to search for
*
* @return List of cases containing this artifact with instances marked as
* bad
* bad
*
* @throws EamDbException
*/
@Override
public List<String> getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getListCasesHavingArtifactInstancesKnownBad(aType, value);
} finally {
@ -649,12 +687,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/**
* Remove a reference set and all values contained in it.
*
* @param referenceSetID
* @throws EamDbException
*/
@Override
public void deleteReferenceSet(int referenceSetID) throws EamDbException{
try{
public void deleteReferenceSet(int referenceSetID) throws EamDbException {
try {
acquireExclusiveLock();
super.deleteReferenceSet(referenceSetID);
} finally {
@ -664,6 +703,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/**
* Check if the given hash is in a specific reference set
*
* @param value
* @param referenceSetID
* @param correlationTypeID
@ -671,7 +711,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException {
try{
try {
acquireSharedLock();
return super.isValueInReferenceSet(value, referenceSetID, correlationTypeID);
} finally {
@ -695,9 +735,29 @@ final class SqliteEamDb extends AbstractSqlEamDb {
releaseSharedLock();
}
}
/**
* Check whether a reference set with the given name/version is in the central repo.
* Used to check for name collisions when creating reference sets.
* Process the Artifact instance in the EamDb
*
* @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance
* @throws EamDbException
*/
@Override
public void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException {
try {
acquireSharedLock();
super.processInstanceTableWhere(type, whereClause, instanceTableCallback);
} finally {
releaseSharedLock();
}
}
/**
* Check whether a reference set with the given name/version is in the
* central repo. Used to check for name collisions when creating reference
* sets.
*
* @param referenceSetName
* @param version
* @return true if a matching set is found
@ -705,7 +765,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public boolean referenceSetExists(String referenceSetName, String version) throws EamDbException {
try{
try {
acquireSharedLock();
return super.referenceSetExists(referenceSetName, version);
} finally {
@ -723,7 +783,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException {
try{
try {
acquireSharedLock();
return super.isArtifactKnownBadByReference(aType, value);
} finally {
@ -742,7 +802,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException {
try{
try {
acquireExclusiveLock();
return super.newOrganization(eamOrg);
} finally {
@ -759,7 +819,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List<EamOrganization> getOrganizations() throws EamDbException {
try{
try {
acquireSharedLock();
return super.getOrganizations();
} finally {
@ -778,7 +838,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public EamOrganization getOrganizationByID(int orgID) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getOrganizationByID(orgID);
} finally {
@ -788,7 +848,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
@Override
public void updateOrganization(EamOrganization updatedOrganization) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.updateOrganization(updatedOrganization);
} finally {
@ -798,13 +858,14 @@ final class SqliteEamDb extends AbstractSqlEamDb {
@Override
public void deleteOrganization(EamOrganization organizationToDelete) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.deleteOrganization(organizationToDelete);
} finally {
releaseExclusiveLock();
}
}
/**
* Add a new Global Set
*
@ -816,7 +877,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public int newReferenceSet(EamGlobalSet eamGlobalSet) throws EamDbException {
try{
try {
acquireExclusiveLock();
return super.newReferenceSet(eamGlobalSet);
} finally {
@ -835,7 +896,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public EamGlobalSet getReferenceSetByID(int referenceSetID) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getReferenceSetByID(referenceSetID);
} finally {
@ -854,7 +915,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List<EamGlobalSet> getAllReferenceSets(CorrelationAttribute.Type correlationType) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getAllReferenceSets(correlationType);
} finally {
@ -866,14 +927,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* Add a new reference instance
*
* @param eamGlobalFileInstance The reference instance to add
* @param correlationType Correlation Type that this Reference
* Instance is
* @param correlationType Correlation Type that this Reference Instance is
*
* @throws EamDbException
*/
@Override
public void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttribute.Type correlationType) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.addReferenceInstance(eamGlobalFileInstance, correlationType);
} finally {
@ -888,7 +948,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void bulkInsertReferenceTypeEntries(Set<EamGlobalFileInstance> globalInstances, CorrelationAttribute.Type contentType) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.bulkInsertReferenceTypeEntries(globalInstances, contentType);
} finally {
@ -899,7 +959,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/**
* Get all reference entries having a given correlation type and value
*
* @param aType Type to use for matching
* @param aType Type to use for matching
* @param aValue Value to use for matching
*
* @return List of all global file instances with a type and value
@ -908,7 +968,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public List<EamGlobalFileInstance> getReferenceInstancesByTypeValue(CorrelationAttribute.Type aType, String aValue) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getReferenceInstancesByTypeValue(aType, aValue);
} finally {
@ -927,7 +987,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public int newCorrelationType(CorrelationAttribute.Type newType) throws EamDbException {
try{
try {
acquireExclusiveLock();
return super.newCorrelationType(newType);
} finally {
@ -940,13 +1000,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* artifacts.
*
* @return List of EamArtifact.Type's. If none are defined in the database,
* the default list will be returned.
* the default list will be returned.
*
* @throws EamDbException
*/
@Override
public List<CorrelationAttribute.Type> getDefinedCorrelationTypes() throws EamDbException {
try{
try {
acquireSharedLock();
return super.getDefinedCorrelationTypes();
} finally {
@ -959,13 +1019,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* artifacts.
*
* @return List of enabled EamArtifact.Type's. If none are defined in the
* database, the default list will be returned.
* database, the default list will be returned.
*
* @throws EamDbException
*/
@Override
public List<CorrelationAttribute.Type> getEnabledCorrelationTypes() throws EamDbException {
try{
try {
acquireSharedLock();
return super.getEnabledCorrelationTypes();
} finally {
@ -978,13 +1038,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* correlate artifacts.
*
* @return List of supported EamArtifact.Type's. If none are defined in the
* database, the default list will be returned.
* database, the default list will be returned.
*
* @throws EamDbException
*/
@Override
public List<CorrelationAttribute.Type> getSupportedCorrelationTypes() throws EamDbException {
try{
try {
acquireSharedLock();
return super.getSupportedCorrelationTypes();
} finally {
@ -1001,7 +1061,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public void updateCorrelationType(CorrelationAttribute.Type aType) throws EamDbException {
try{
try {
acquireExclusiveLock();
super.updateCorrelationType(aType);
} finally {
@ -1020,7 +1080,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
*/
@Override
public CorrelationAttribute.Type getCorrelationTypeById(int typeId) throws EamDbException {
try{
try {
acquireSharedLock();
return super.getCorrelationTypeById(typeId);
} finally {
@ -1030,11 +1090,12 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/**
* Upgrade the schema of the database (if needed)
*
* @throws EamDbException
*/
@Override
public void upgradeSchema() throws EamDbException, SQLException {
try{
try {
acquireExclusiveLock();
super.upgradeSchema();
} finally {
@ -1043,50 +1104,52 @@ final class SqliteEamDb extends AbstractSqlEamDb {
}
/**
* Gets an exclusive lock (if applicable).
* Will return the lock if successful, null if unsuccessful because locking
* isn't supported, and throw an exception if we should have been able to get the
* lock but failed (meaning the database is in use).
* Gets an exclusive lock (if applicable). Will return the lock if
* successful, null if unsuccessful because locking isn't supported, and
* throw an exception if we should have been able to get the lock but failed
* (meaning the database is in use).
*
* @return the lock, or null if locking is not supported
* @throws EamDbException if the coordination service is running but we fail to get the lock
* @throws EamDbException if the coordination service is running but we fail
* to get the lock
*/
@Override
public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException{
public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException {
// Multiple users are not supported for SQLite
return null;
}
/**
* Acquire the lock that provides exclusive access to the case database.
* Call this method in a try block with a call to
* the lock release method in an associated finally block.
* Call this method in a try block with a call to the lock release method in
* an associated finally block.
*/
private void acquireExclusiveLock() {
rwLock.writeLock().lock();
}
/**
* Release the lock that provides exclusive access to the database.
* This method should always be called in the finally
* block of a try block in which the lock was acquired.
* Release the lock that provides exclusive access to the database. This
* method should always be called in the finally block of a try block in
* which the lock was acquired.
*/
private void releaseExclusiveLock() {
rwLock.writeLock().unlock();
}
/**
* Acquire the lock that provides shared access to the case database.
* Call this method in a try block with a call to the
* lock release method in an associated finally block.
* Acquire the lock that provides shared access to the case database. Call
* this method in a try block with a call to the lock release method in an
* associated finally block.
*/
private void acquireSharedLock() {
rwLock.readLock().lock();
}
/**
* Release the lock that provides shared access to the database.
* This method should always be called in the finally block
* of a try block in which the lock was acquired.
* Release the lock that provides shared access to the database. This method
* should always be called in the finally block of a try block in which the
* lock was acquired.
*/
private void releaseSharedLock() {
rwLock.readLock().unlock();

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,9 +25,9 @@ import org.sleuthkit.datamodel.TskData.FileKnown;
/**
* Provides logic for selecting common files from all data sources.
*/
final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadataBuilder {
final public class AllIntraCaseCommonAttributeSearcher extends IntraCaseCommonAttributeSearcher {
private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL)%s GROUP BY md5 HAVING COUNT(DISTINCT data_source_obj_id) > 1) order by md5"; //NON-NLS
private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL)%s GROUP BY md5 HAVING COUNT(DISTINCT data_source_obj_id) > 1) order by md5"; //NON-NLS
/**
* Implements the algorithm for getting common files across all data
@ -37,7 +37,7 @@ final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadat
* @param filterByMediaMimeType match only on files whose mime types can be broadly categorized as media types
* @param filterByDocMimeType match only on files whose mime types can be broadly categorized as document types
*/
public AllDataSourcesCommonFilesAlgorithm(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
public AllIntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
}
@ -49,9 +49,9 @@ final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadat
}
@Override
protected String buildTabTitle() {
String buildTabTitle() {
final String buildCategorySelectionString = this.buildCategorySelectionString();
final String titleTemplate = Bundle.CommonFilesMetadataBuilder_buildTabTitle_titleAll();
final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleIntraAll();
return String.format(titleTemplate, new Object[]{buildCategorySelectionString});
}
}

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.allFileCategoriesRadioButton.toolTipText=No filtering applied to results...
CommonFilesPanel.allFileCategoriesRadioButton.text=Match on all file types
CommonFilesPanel.text=Indicate which data sources to consider while searching for duplicates:
CommonFilesPanel.categoriesLabel.text=Indicate which file types to include in results:
CommonFilesPanel.errorText.text=In order to search, you must select a file category.
CommonFilesPanel.jRadioButton1.text=jRadioButton1
CommonFilesPanel.jRadioButton2.text=With previous cases in the Central Repository
CommonFilesPanel.intraCaseRadio.label=Correlate within current case only
CommonFilesPanel.interCaseRadio.label=Correlate amongst all known cases (uses Central Repo)
IntraCasePanel.allDataSourcesRadioButton.text=Matches may be from any data source
IntraCasePanel.withinDataSourceRadioButton.text=At least one match must appear in the data source selected below:
IntraCasePanel.selectDataSourceComboBox.actionCommand=
InterCasePanel.specificCentralRepoCaseRadio.text=Matches must be from the following Central Repo case:
InterCasePanel.anyCentralRepoCaseRadio.text=Matches may be from any Central Repo case
CommonAttributePanel.selectedFileCategoriesButton.toolTipText=Select from the options below...
CommonAttributePanel.selectedFileCategoriesButton.text=Only the selected file types:
CommonAttributePanel.allFileCategoriesRadioButton.toolTipText=No filtering applied to results...
CommonAttributePanel.allFileCategoriesRadioButton.text=All file types
CommonAttributePanel.cancelButton.actionCommand=Cancel
CommonAttributePanel.cancelButton.text=Cancel
CommonAttributePanel.searchButton.text=Search
CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search
CommonAttributePanel.intraCaseRadio.text=Within current case
CommonAttributePanel.commonFilesSearchLabel1.text=<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.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.AbstractFile;
/**
* Used by the Common Files search feature to encapsulate instances of a given
MD5s matched in the search. These nodes will be children of <code>Md5Node</code>s.
* Node that wraps CaseDBCommonAttributeInstance to represent a file instance stored
* in the CaseDB.
*/
public class FileInstanceNode extends FileNode {
public class CaseDBCommonAttributeInstanceNode extends FileNode {
private final String caseName;
private final String dataSource;
/**
@ -41,11 +41,10 @@ public class FileInstanceNode extends FileNode {
* @param fsContent
* @param dataSource
*/
public FileInstanceNode(AbstractFile fsContent, String dataSource) {
public CaseDBCommonAttributeInstanceNode(AbstractFile fsContent, String caseName, String dataSource) {
super(fsContent);
this.caseName = caseName;
this.dataSource = dataSource;
this.setDisplayName(fsContent.getName());
}
@Override
@ -59,11 +58,14 @@ public class FileInstanceNode extends FileNode {
return visitor.visit(this);
}
String getDataSource() {
public String getCase(){
return this.caseName;
}
public String getDataSource() {
return this.dataSource;
}
@NbBundle.Messages({"FileInstanceNode.createSheet.noDescription= "})
@Override
protected Sheet createSheet() {
Sheet sheet = new Sheet();
@ -73,13 +75,14 @@ public class FileInstanceNode extends FileNode {
sheet.put(sheetSet);
}
final String NO_DESCR = Bundle.FileInstanceNode_createSheet_noDescription();
final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText();
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, this.getContent().getName()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsCsvList(this.getContent())));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType())));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName));
this.addTagProperty(sheetSet);

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;
/**
* Wrapper node for <code>Md5Node</code> used to display common files search
* results in the top right pane. Calls <code>InstanceCountNodeFactory</code>.
* Top-level node to store common file search results. Current structure is:
* - 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 {
CommonFilesNode(CommonFilesMetadata metadataList) {
CommonAttributeSearchResultRootNode(CommonAttributeSearchResults metadataList) {
super(Children.create(new InstanceCountNodeFactory(metadataList), true));
}
@ -64,27 +65,27 @@ final public class CommonFilesNode extends DisplayableItemNode {
*/
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.
* @param metadata
* @param searchResults
*/
InstanceCountNodeFactory(CommonFilesMetadata metadata){
this.metadata = metadata;
InstanceCountNodeFactory(CommonAttributeSearchResults searchResults){
this.searchResults = searchResults;
}
@Override
protected boolean createKeys(List<Integer> list) {
list.addAll(this.metadata.getMetadata().keySet());
list.addAll(this.searchResults.getMetadata().keySet());
return true;
}
@Override
protected Node createNodeForKey(Integer instanceCount){
List<Md5Metadata> md5Metadata = this.metadata.getMetadataForMd5(instanceCount);
return new InstanceCountNode(instanceCount, md5Metadata);
List<CommonAttributeValue> attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount);
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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
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 List<FileInstanceMetadata> fileInstances;
private final List<AbstractCommonAttributeInstance> fileInstances;
Md5Metadata(String md5, List<FileInstanceMetadata> fileInstances){
CommonAttributeValue(String md5, List<AbstractCommonAttributeInstance> fileInstances) {
this.md5 = md5;
this.fileInstances = fileInstances;
}
public String getMd5(){
CommonAttributeValue(String md5) {
this.md5 = md5;
this.fileInstances = new ArrayList<>();
}
public String getValue() {
return this.md5;
}
void addFileInstanceMetadata(FileInstanceMetadata metadata){
/**
* concatenate cases this value was seen into a single string
*
* @return
*/
public String getCases() {
return this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", "));
}
public String getDataSources() {
Set<String> sources = new HashSet<>();
for (AbstractCommonAttributeInstance data : this.fileInstances) {
sources.add(data.getDataSource());
}
return String.join(", ", sources);
}
void addInstance(AbstractCommonAttributeInstance metadata) {
this.fileInstances.add(metadata);
}
public Collection<FileInstanceMetadata> getMetadata(){
public Collection<AbstractCommonAttributeInstance> getInstances() {
return Collections.unmodifiableCollection(this.fileInstances);
}
/**
* How many distinct file instances exist for the MD5 represented by this object?
* How many distinct file instances exist for the MD5 represented by this
* object?
*
* @return number of instances
*/
public int size(){
public int getInstanceCount() {
return this.fileInstances.size();
}
public String getDataSources() {
Set<String> sources = new HashSet<> ();
for(FileInstanceMetadata data : this.fileInstances){
sources.add(data.getDataSourceName());
}
return String.join(", ", sources);
}
}

View File

@ -20,34 +20,24 @@
package org.sleuthkit.autopsy.commonfilesearch;
import java.util.List;
import java.util.logging.Level;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
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.DisplayableItemNodeVisitor;
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
* same file and appear as children of this node. This node will simply contain
* the MD5 of the matched files, the data sources those files were found within,
* and a count of the instances represented by the md5.
* Represents the layer in the tree for the value (such as MD5) that was in multiple places.
* Children are instances of that value.
*/
public class Md5Node extends DisplayableItemNode {
public class CommonAttributeValueNode extends DisplayableItemNode {
private static final Logger LOGGER = Logger.getLogger(Md5Node.class.getName());
private final String md5Hash;
private final String value;
private final int commonFileCount;
private final String cases;
private final String dataSources;
@NbBundle.Messages({
@ -57,15 +47,17 @@ public class Md5Node extends DisplayableItemNode {
* Create a Match node whose children will all have this object in common.
* @param data the common feature, and the children
*/
public Md5Node(Md5Metadata data) {
public CommonAttributeValueNode(CommonAttributeValue data) {
super(Children.create(
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.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
}
@ -77,6 +69,10 @@ public class Md5Node extends DisplayableItemNode {
return this.commonFileCount;
}
String getCases(){
return this.cases;
}
/**
* Datasources where these matches occur.
* @return string delimited list of sources
@ -89,8 +85,8 @@ public class Md5Node extends DisplayableItemNode {
* MD5 which is common to these matches
* @return string md5 hash
*/
public String getMd5() {
return this.md5Hash;
public String getValue() {
return this.value;
}
@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;
}
@Override
protected Node createNodeForKey(FileInstanceMetadata file) {
try {
Case currentCase = Case.getCurrentCaseThrows();
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;
protected boolean createKeys(List<AbstractCommonAttributeInstance> list) {
list.addAll(this.descendants.getInstances());
return true;
}
@Override
protected boolean createKeys(List<FileInstanceMetadata> list) {
list.addAll(this.descendants.getMetadata());
return true;
protected Node[] createNodesForKey(AbstractCommonAttributeInstance searchResult) {
return searchResult.generateNodes();
}
}
}
}

View File

@ -31,18 +31,18 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
/**
* <code>DataResultViewerTable</code> which overrides the default column header
* width calculations. The <code>CommonFilesSearchResultsViewerTable</code>
* <code>DataResultViewerTable</code> which overrides the default column
* header width calculations. The <code>CommonAttributesSearchResultsViewerTable</code>
* 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
* 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 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;
@ -51,6 +51,7 @@ public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable {
map.put(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), 260);
map.put(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), 65);
map.put(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), 300);
map.put(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), 200);
map.put(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), 200);
map.put(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), 100);
map.put(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), 130);
@ -60,11 +61,13 @@ public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable {
}
@NbBundle.Messages({
"CommonFilesSearchResultsViewerTable.noDescText= ",
"CommonFilesSearchResultsViewerTable.filesColLbl=Files",
"CommonFilesSearchResultsViewerTable.instancesColLbl=Instances",
"CommonFilesSearchResultsViewerTable.pathColLbl=Parent Path",
"CommonFilesSearchResultsViewerTable.hashsetHitsColLbl=Hash Set Hits",
"CommonFilesSearchResultsViewerTable.dataSourceColLbl=Data Source(s)",
"CommonFilesSearchResultsViewerTable.caseColLbl1=Case",
"CommonFilesSearchResultsViewerTable.dataSourceColLbl=Data Source",
"CommonFilesSearchResultsViewerTable.mimeTypeColLbl=MIME Type",
"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.actions.CallableSystemAction;
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.autopsy.coreutils.Logger;
@ -32,9 +34,10 @@ import org.sleuthkit.autopsy.coreutils.Logger;
*/
final public class CommonFilesSearchAction extends CallableSystemAction {
private static final Logger LOGGER = Logger.getLogger(CommonFilesSearchAction.class.getName());
private static CommonFilesSearchAction instance = null;
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(CommonFilesSearchAction.class.getName());
CommonFilesSearchAction() {
super();
@ -45,9 +48,22 @@ final public class CommonFilesSearchAction extends CallableSystemAction {
public boolean isEnabled(){
boolean shouldBeEnabled = false;
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) {
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;
}
@ -61,12 +77,12 @@ final public class CommonFilesSearchAction extends CallableSystemAction {
@Override
public void actionPerformed(ActionEvent event) {
new CommonFilesDialog().setVisible(true);
new CommonAttributePanel().setVisible(true);
}
@Override
public void performAction() {
new CommonFilesDialog().setVisible(true);
new CommonAttributePanel().setVisible(true);
}
@NbBundle.Messages({

View File

@ -30,7 +30,7 @@ public class DataSourceComboBoxModel extends AbstractListModel<String> implement
private static final long serialVersionUID = 1L;
private final String[] dataSourceList;
String selection = null;
private String selection = null;
/**
* 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 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
* selection of metadata.
* @param instanceCount
* @param md5Metadata
* @param attributeValues
*/
@NbBundle.Messages({
"InstanceCountNode.displayName=Files with %s instances (%s)"
})
public InstanceCountNode(int instanceCount, List<Md5Metadata> md5Metadata) {
super(Children.create(new Md5NodeFactory(md5Metadata), true));
public InstanceCountNode(int instanceCount, List<CommonAttributeValue> attributeValues) {
super(Children.create(new CommonAttributeValueNodeFactory(attributeValues), true));
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
}
@ -73,8 +73,8 @@ final public class InstanceCountNode extends DisplayableItemNode {
* Get a list of metadata for the MD5s which are children of this object.
* @return List<Md5Metadata>
*/
List<Md5Metadata> getMetadata() {
return Collections.unmodifiableList(this.metadataList);
List<CommonAttributeValue> getAttributeValues() {
return Collections.unmodifiableList(this.attributeValues);
}
@Override
@ -113,34 +113,36 @@ final public class InstanceCountNode extends DisplayableItemNode {
* ChildFactory which builds CommonFileParentNodes from the
* 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,
* 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<>();
Iterator<Md5Metadata> iterator = metadata.iterator();
Iterator<CommonAttributeValue> iterator = attributeValues.iterator();
while (iterator.hasNext()) {
Md5Metadata md5Metadata = iterator.next();
this.metadata.put(md5Metadata.getMd5(), md5Metadata);
CommonAttributeValue attributeValue = iterator.next();
this.metadata.put(attributeValue.getValue(), attributeValue);
}
}
@Override
protected Node createNodeForKey(String md5) {
Md5Metadata md5Metadata = this.metadata.get(md5);
return new Md5Node(md5Metadata);
}
@Override
protected boolean createKeys(List<String> list) {
// @@@ We should just use CommonAttributeValue as the key...
list.addAll(this.metadata.keySet());
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.
*/
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 final Long selectedDataSourceId;
@ -41,7 +41,7 @@ final public class SingleDataSource extends CommonFilesMetadataBuilder {
* @param filterByDocMimeType match only on files whose mime types can be
* 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);
this.selectedDataSourceId = dataSourceId;
this.dataSourceName = dataSourceIdMap.get(this.selectedDataSourceId);
@ -50,13 +50,13 @@ final public class SingleDataSource extends CommonFilesMetadataBuilder {
@Override
protected String buildSqlSelectStatement() {
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
protected String buildTabTitle() {
public String buildTabTitle() {
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});
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017-18 Basis Technology Corp.
* Copyright 2017-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* 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();
drpExplorerManager = drp.getExplorerManager();
drpExplorerManager.addPropertyChangeListener(evt ->
viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1));
drpExplorerManager.addPropertyChangeListener(evt
-> 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 runningWithGUIFlagHasBeenSet = false;
private static boolean runningInTarget = false;
private static boolean runningInTargetFlagHasBeenSet = false;
/**
* 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
* GUI.

View File

@ -70,6 +70,7 @@ public final class UserPreferences {
private static final String MAX_NUM_OF_LOG_FILE = "MaximumNumberOfLogFiles";
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 SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags";
// Prevent instantiation.
private UserPreferences() {
@ -196,6 +197,27 @@ public final class UserPreferences {
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.
*
@ -379,6 +401,7 @@ public final class UserPreferences {
/**
* get the maximum number of log files to save
*
* @return Number of log files
*/
public static int getLogFileCount() {
@ -387,13 +410,16 @@ public final class UserPreferences {
/**
* get the default number of log files to save
*
* @return LOG_FILE_COUNT
*/
public static int getDefaultLogFileCount() {
return LOG_FILE_NUM_INT;
}
/**
* Set the maximum number of log files to save
*
* @param count number of log files
*/
public static void setLogFileCount(int count) {

View File

@ -169,7 +169,7 @@ public class FileUtil {
public static String escapeFileName(String fileName) {
//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.
return fileName.replaceAll("[/:\"*?<>|]+", "_");
return fileName.replaceAll("[\\p{Cntrl}/:\"*?<>|]+", "_");
}
/**

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2014 Basis Technology Corp.
* Copyright 2013-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* 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.Pattern;
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)
@ -29,8 +30,17 @@ import org.sleuthkit.autopsy.casemodule.Case;
public final class PathValidator {
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) {
// check that path is not on "C:" drive
@ -44,6 +54,40 @@ public final class PathValidator {
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.
*
@ -55,4 +99,20 @@ public final class PathValidator {
Matcher m = driveLetterPattern.matcher(filePath);
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.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Children;
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.events.ContentTagAddedEvent;
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 static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*;
import static org.sleuthkit.autopsy.datamodel.Bundle.*;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* 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
* Nodes (same properties).
*
* @param <T> extends AbstractFile
*/
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.ContentTagAddedEvent;
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.MessageNotifyUtil;
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.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
@ -59,6 +60,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
this.tag = tag;
}
@Messages({"BlackboardArtifactTagNode.createSheet.userName.text=User Name"})
@Override
protected Sheet 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
contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text");
}
properties.put(new NodeProperty<>(
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"),
"",
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;
}

View File

@ -59,8 +59,8 @@ class ContentTagNode extends DisplayableItemNode {
@Messages({
"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
protected Sheet createSheet() {
Content content = tag.getContent();
@ -115,6 +115,11 @@ class ContentTagNode extends DisplayableItemNode {
Bundle.ContentTagNode_createSheet_artifactMD5_displayName(),
"",
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;
}
@ -123,7 +128,8 @@ class ContentTagNode extends DisplayableItemNode {
List<Action> actions = new ArrayList<>();
actions.addAll(Arrays.asList(super.getActions(context)));
AbstractFile file = getLookup().lookup(AbstractFile.class);
AbstractFile file = getLookup().lookup(AbstractFile.class
);
if (file != null) {
actions.add(ViewFileInTimelineAction.createViewFileAction(file));
}

View File

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

View File

@ -27,7 +27,7 @@ import java.util.List;
*/
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
".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

View File

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

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* 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);
}
}
return actionsList.toArray(new Action[0]);
return actionsList.toArray(new Action[actionsList.size()]);
}
@Override

View File

@ -55,19 +55,29 @@ public class Tags implements AutopsyVisitableItem {
// override of Children.Keys<T>.createNodes().
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 long datasourceObjId;
Tags() {
this(0);
this(0);
}
Tags(long 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() {
return this.datasourceObjId;
}
@ -98,13 +108,11 @@ public class Tags implements AutopsyVisitableItem {
*/
public class RootNode extends DisplayableItemNode {
public RootNode(long objId) {
super(Children.create(new TagNameNodeFactory(objId), true), Lookups.singleton(DISPLAY_NAME));
super.setName(DISPLAY_NAME);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension(ICON_PATH);
}
@Override
@ -134,6 +142,14 @@ public class Tags implements AutopsyVisitableItem {
public String getItemType() {
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 {
@ -197,6 +213,7 @@ public class Tags implements AutopsyVisitableItem {
/**
* Constructor
*
* @param objId data source object id
*/
TagNameNodeFactory(long objId) {
@ -224,11 +241,17 @@ public class Tags implements AutopsyVisitableItem {
@Override
protected boolean createKeys(List<TagName> keys) {
try {
List<TagName> tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId) :
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse()
;
List<TagName> tagNamesInUse;
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
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);
keys.addAll(tagNamesInUse);
} catch (TskCoreException | NoCurrentCaseException ex) {
@ -276,15 +299,24 @@ public class Tags implements AutopsyVisitableItem {
long tagsCount = 0;
try {
TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager();
if (UserPreferences.groupItemsInTreeByDatasource()) {
tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId);
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()) {
tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId);
} else {
tagsCount = tm.getContentTagsCountByTagName(tagName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
}
}
else {
tagsCount = tm.getContentTagsCountByTagName(tagName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
}
} 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
}
@ -387,9 +419,17 @@ public class Tags implements AutopsyVisitableItem {
private void updateDisplayName() {
long tagsCount = 0;
try {
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId) :
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
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) {
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) {
// Use the content tags bearing the specified tag name as the keys.
try {
List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId) :
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName);
keys.addAll(contentTags);
List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId)
: 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);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
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() {
long tagsCount = 0;
try {
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId) :
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
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) {
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) {
try {
// Use the blackboard artifact tags bearing the specified tag name as the keys.
List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId) :
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName);
keys.addAll(artifactTags);
List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId)
: 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);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
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));
String name = ld.getName();
//set icon for name, special case for logical file set
if (ld.isDataSource()) {
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."})
private void warnIfPathIsInvalid(String path) {
try {
if (!PathValidator.isValid(path, Case.getCurrentCaseThrows().getCaseType())) {
if (!PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) {
errorLabel.setVisible(true);
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.noButton.text=No
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.DeleteFileBlackboardArtifactTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.commonfilesearch.FileInstanceNode;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
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.FileTypes.FileTypesNode;
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.LocalFileNode;
import org.sleuthkit.autopsy.datamodel.LocalDirectoryNode;
import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
import org.sleuthkit.autopsy.datamodel.Reports;
import org.sleuthkit.autopsy.datamodel.SlackFileNode;
import org.sleuthkit.autopsy.commonfilesearch.CaseDBCommonAttributeInstanceNode;
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.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
@ -529,12 +530,17 @@ public class DataResultFilterNode extends FilterNode {
}
@Override
public AbstractAction visit(Md5Node md5n){
public AbstractAction visit(CommonAttributeValueNode md5n){
return null;
}
@Override
public AbstractAction visit(FileInstanceNode fin){
public AbstractAction visit(CaseDBCommonAttributeInstanceNode fin){
return null;
}
@Override
public AbstractAction visit(CentralRepoCommonAttributeInstanceNode iccan){
return null;
}

View File

@ -21,7 +21,9 @@
<Component id="backButton" min="-2" max="-2" attributes="0"/>
<EmptySpace 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">
<Component id="showRejectedCheckBox" 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="102" 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"/>
<Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/>
</Group>
@ -151,5 +156,15 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="groupByDatasourceCheckBoxActionPerformed"/>
</Events>
</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>
</Form>

View File

@ -81,6 +81,7 @@ import org.sleuthkit.autopsy.datamodel.InterestingHits;
import org.sleuthkit.autopsy.datamodel.KeywordHits;
import org.sleuthkit.autopsy.datamodel.ResultsNode;
import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory;
import org.sleuthkit.autopsy.datamodel.Tags;
import org.sleuthkit.autopsy.datamodel.ViewsNode;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
@ -137,6 +138,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
forwardButton.setEnabled(false);
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:
refreshContentTreeSafe();
break;
case UserPreferences.SHOW_ONLY_CURRENT_USER_TAGS:
refreshTagsTree();
break;
case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE:
case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE:
// 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();
showRejectedCheckBox = new javax.swing.JCheckBox();
groupByDatasourceCheckBox = new javax.swing.JCheckBox();
showOnlyCurrentUserTagsCheckbox = new javax.swing.JCheckBox();
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);
this.setLayout(layout);
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)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.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)
.addComponent(showRejectedCheckBox)
.addComponent(groupByDatasourceCheckBox))
@ -256,7 +271,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(5, 5, 5)
.addComponent(showRejectedCheckBox)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(showRejectedCheckBox)
.addComponent(showOnlyCurrentUserTagsCheckbox))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(groupByDatasourceCheckBox))
.addGroup(layout.createSequentialGroup()
@ -323,10 +340,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
UserPreferences.setGroupItemsInTreeByDatasource(this.groupByDatasourceCheckBox.isSelected());
}//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
private javax.swing.JButton backButton;
private javax.swing.JButton forwardButton;
private javax.swing.JCheckBox groupByDatasourceCheckBox;
private javax.swing.JCheckBox showOnlyCurrentUserTagsCheckbox;
private javax.swing.JCheckBox showRejectedCheckBox;
private javax.swing.JScrollPane treeView;
// End of variables declaration//GEN-END:variables
@ -890,6 +912,29 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
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.
*

View File

@ -25,7 +25,9 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
@ -33,7 +35,6 @@ import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.Cancellable;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
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.ExtractFscContentVisitor;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* 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 String userDefinedExportPath;
// 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
// 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;
}
/**
* Private constructor for the action.
*/
private ExtractAction() {
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
*
* @param e
* @param event
* @param selectedFile Selected file
*/
@NbBundle.Messages ({"ExtractAction.noOpenCase.errMsg=No open case available."})
private void extractFile(ActionEvent e, AbstractFile selectedFile) {
@NbBundle.Messages({"ExtractAction.noOpenCase.errMsg=No open case available."})
private void extractFile(ActionEvent event, AbstractFile selectedFile) {
Case openCase;
try {
openCase = Case.getCurrentCaseThrows();
} 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
return;
}
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
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<>();
fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile()));
runExtractionTasks(e, fileExtractionTasks);
runExtractionTasks(event, fileExtractionTasks);
}
}
/**
* Called when a user has selected multiple files to extract
*
* @param e
* @param event
* @param selectedFiles Selected files
*/
private void extractFiles(ActionEvent e, Collection<? extends AbstractFile> selectedFiles) {
private void extractFiles(ActionEvent event, Collection<? extends AbstractFile> selectedFiles) {
Case openCase;
try {
openCase = Case.getCurrentCaseThrows();
} 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
return;
}
JFileChooser folderChooser = new JFileChooser();
folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
folderChooser.setCurrentDirectory(new File(openCase.getExportDirectory()));
if (folderChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) {
folderChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
if (folderChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {
File destinationFolder = folderChooser.getSelectedFile();
if (!destinationFolder.exists()) {
try {
destinationFolder.mkdirs();
} 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"));
logger.log(Level.INFO, "Unable to create folder(s) for user " + destinationFolder.getAbsolutePath(), ex); //NON-NLS
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
* email messages in the tree and chose to extract them. */
/*
* 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 email messages in the tree and
* chose to extract them.
*/
Set<AbstractFile> uniqueFiles = new HashSet<>(selectedFiles);
// 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
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
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
* that by adding the unique ID.
* This code assumes that each destination is unique. We previously
* satisfied that by adding the unique ID.
*/
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.title"),
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
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()));
it.remove();
}
@ -210,11 +266,20 @@ public final class ExtractAction extends AbstractAction {
}
}
/**
* Stores source and destination for file extraction.
*/
private class FileExtractionTask {
AbstractFile source;
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) {
this.source = source;
this.destination = destination;
@ -226,11 +291,16 @@ public final class ExtractAction extends AbstractAction {
*/
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 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;
}
@ -275,7 +345,7 @@ public final class ExtractAction extends AbstractAction {
boolean msgDisplayed = false;
try {
super.get();
} catch (Exception ex) {
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS
MessageNotifyUtil.Message.info(
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;
if (file.isFile()) {
workUnits += file.getSize();
} else {
try {
for (Content child : file.getChildren()) {
if (child instanceof AbstractFile) {
workUnits += calculateProgressBarWorkUnits((AbstractFile) child);
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Could not get children of content", ex); //NON-NLS
}
}
return workUnits;
/**
* Calculate the number of work units for the progress bar.
*
* @param file File whose children will be reviewed to get the number of
* work units.
*
* @return The number of work units.
*/
/*
* private int calculateProgressBarWorkUnits(AbstractFile file) { int
* workUnits = 0; if (file.isFile()) { workUnits += file.getSize(); }
* else { try { for (Content child : file.getChildren()) { if (child
* instanceof AbstractFile) { workUnits +=
* calculateProgressBarWorkUnits((AbstractFile) child); } } } catch
* (TskCoreException ex) { logger.log(Level.SEVERE, "Could not get
* children of content", ex); //NON-NLS } } return workUnits;
}
*/
}
}

View File

@ -61,15 +61,23 @@ import org.sleuthkit.datamodel.VolumeSystem;
* Extracts all the unallocated space as a single file
*/
final class ExtractUnallocAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName());
private final List<OutputFileData> filesToExtract = new ArrayList<>();
private static final Set<String> volumesInProgress = new HashSet<>();
private static final Set<Long> imagesInProgress = new HashSet<>();
private static String userDefinedExportPath;
private long currentImage = 0L;
private final boolean isImage;
public ExtractUnallocAction(String title, Volume volume){
/**
* 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) {
super(title);
isImage = false;
try {
@ -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 {
super(title);
isImage = true;
@ -101,17 +116,17 @@ final class ExtractUnallocAction extends AbstractAction {
* Writes the unallocated files to
* $CaseDir/Export/ImgName-Unalloc-ImgObjectID-VolumeID.dat
*
* @param e
* @param event
*/
@NbBundle.Messages({"# {0} - fileName",
"ExtractUnallocAction.volumeInProgress=Already extracting unallocated space into {0} - will skip this volume",
"ExtractUnallocAction.volumeError=Error extracting unallocated space from volume",
"ExtractUnallocAction.noFiles=No unallocated files found on volume",
"ExtractUnallocAction.imageError=Error extracting unallocated space from image",
"ExtractUnallocAction.noOpenCase.errMsg=No open case available."})
"ExtractUnallocAction.volumeInProgress=Already extracting unallocated space into {0} - will skip this volume",
"ExtractUnallocAction.volumeError=Error extracting unallocated space from volume",
"ExtractUnallocAction.noFiles=No unallocated files found on volume",
"ExtractUnallocAction.imageError=Error extracting unallocated space from image",
"ExtractUnallocAction.noOpenCase.errMsg=No open case available."})
@Override
public void actionPerformed(ActionEvent e) {
if (filesToExtract != null && filesToExtract.size() > 0) {
public void actionPerformed(ActionEvent event) {
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,
// but in general it will suffice.
if (isImage && isImageInProgress(currentImage)) {
@ -145,18 +160,21 @@ final class ExtractUnallocAction extends AbstractAction {
}
};
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory()));
fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
fileChooser.setDialogTitle(
NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg"));
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setAcceptAllFileFilterUsed(false);
int returnValue = fileChooser.showSaveDialog((Component) e.getSource());
int returnValue = fileChooser.showSaveDialog((Component) event.getSource());
if (returnValue == JFileChooser.APPROVE_OPTION) {
String destination = fileChooser.getSelectedFile().getPath();
updateExportDirectory(destination, openCase);
for (OutputFileData outputFileData : filesToExtract) {
outputFileData.setPath(destination);
if (outputFileData.layoutFiles != null && outputFileData.layoutFiles.size() > 0 && (! isVolumeInProgress(outputFileData.getFileName()))) {
if (outputFileData.layoutFiles != null && outputFileData.layoutFiles.size() > 0 && (!isVolumeInProgress(outputFileData.getFileName()))) {
//Format for single Unalloc File is ImgName-Unalloc-ImgObjectID-VolumeID.dat
// Check if there is already a file with this name
@ -174,20 +192,20 @@ final class ExtractUnallocAction extends AbstractAction {
}
if (!isImage & !copyList.isEmpty()) {
try{
try {
ExtractUnallocWorker worker = new ExtractUnallocWorker(outputFileData);
worker.execute();
} catch (Exception ex){
logger.log(Level.WARNING, "Already extracting unallocated space into " + outputFileData.getFileName());
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting unallocated space into {0}", outputFileData.getFileName());
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeInProgress", outputFileData.getFileName()));
}
}
} else {
// The output file for this volume could not be created for one of the following reasons
if (outputFileData.layoutFiles == null){
if (outputFileData.layoutFiles == null) {
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeError"));
logger.log(Level.SEVERE, "Tried to get unallocated content but the list of unallocated files was null"); //NON-NLS
} else if (outputFileData.layoutFiles.isEmpty()){
} else if (outputFileData.layoutFiles.isEmpty()) {
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.noFiles"));
logger.log(Level.WARNING, "No unallocated files found in volume"); //NON-NLS
copyList.remove(outputFileData);
@ -204,32 +222,70 @@ final class ExtractUnallocAction extends AbstractAction {
// to extract everything (the worker in the above loop doesn't get created because isImage is true)
// It's also unclear to me why we need the two separate worker types.
if (isImage && !copyList.isEmpty()) {
try{
try {
ExtractUnallocWorker worker = new ExtractUnallocWorker(copyList);
worker.execute();
} catch (Exception ex){
} catch (Exception ex) {
logger.log(Level.WARNING, "Error creating ExtractUnallocWorker", ex);
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.imageError"));
}
}
}
}
}
/**
* 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.
*
* @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.
*/
private List<LayoutFile> getUnallocFiles(Content c) {
UnallocVisitor uv = new UnallocVisitor();
private List<LayoutFile> getUnallocFiles(Content content) {
UnallocVisitor unallocVisitor = new UnallocVisitor();
try {
for (Content contentChild : c.getChildren()) {
for (Content contentChild : content.getChildren()) {
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) {
@ -239,36 +295,35 @@ final class ExtractUnallocAction extends AbstractAction {
}
synchronized static private void addVolumeInProgress(String volumeOutputFileName) throws TskCoreException {
if(volumesInProgress.contains(volumeOutputFileName)){
if (volumesInProgress.contains(volumeOutputFileName)) {
throw new TskCoreException("Already writing unallocated space to " + volumeOutputFileName);
}
volumesInProgress.add(volumeOutputFileName);
}
synchronized static private void removeVolumeInProgress(String volumeOutputFileName){
synchronized static private void removeVolumeInProgress(String volumeOutputFileName) {
volumesInProgress.remove(volumeOutputFileName);
}
synchronized static private boolean isVolumeInProgress(String volumeOutputFileName){
synchronized static private boolean isVolumeInProgress(String volumeOutputFileName) {
return volumesInProgress.contains(volumeOutputFileName);
}
synchronized static private void addImageInProgress(Long id) throws TskCoreException {
if(imagesInProgress.contains(id)){
throw new TskCoreException("Image " + id + " is in use");
synchronized static private void addImageInProgress(Long objId) throws TskCoreException {
if (imagesInProgress.contains(objId)) {
throw new TskCoreException("Image " + objId + " is in use");
}
imagesInProgress.add(id);
imagesInProgress.add(objId);
}
synchronized static private void removeImageInProgress(Long id){
imagesInProgress.remove(id);
synchronized static private void removeImageInProgress(Long objId) {
imagesInProgress.remove(objId);
}
synchronized static private boolean isImageInProgress(Long id){
return imagesInProgress.contains(id);
synchronized static private boolean isImageInProgress(Long objId) {
return imagesInProgress.contains(objId);
}
/**
* Private class for dispatching the file IO in a background thread.
*/
@ -276,9 +331,9 @@ final class ExtractUnallocAction extends AbstractAction {
private ProgressHandle progress;
private boolean canceled = false;
private List<OutputFileData> outputFileDataList = new ArrayList<>();
private final List<OutputFileData> outputFileDataList = new ArrayList<>();
private File currentlyProcessing;
private int totalSizeinMegs;
private final int totalSizeinMegs;
long totalBytes = 0;
ExtractUnallocWorker(OutputFileData outputFileData) throws TskCoreException {
@ -294,18 +349,18 @@ final class ExtractUnallocAction extends AbstractAction {
//Getting the total megs this worker is going to be doing
for (OutputFileData outputFileData : outputFileDataList) {
try{
try {
// If a volume is locked, skip it but continue trying to process any other requested volumes
addVolumeInProgress(outputFileData.getFileName());
totalBytes += outputFileData.getSizeInBytes();
this.outputFileDataList.add(outputFileData);
} catch (TskCoreException ex){
logger.log(Level.WARNING, "Already extracting data into " + outputFileData.getFileName());
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting data into {0}", outputFileData.getFileName());
}
}
// If we don't have anything to output (because of locking), throw an exception
if(this.outputFileDataList.isEmpty()){
if (this.outputFileDataList.isEmpty()) {
throw new TskCoreException("No unallocated files can be extracted");
}
@ -314,9 +369,9 @@ final class ExtractUnallocAction extends AbstractAction {
private int toMb(long bytes) {
if (bytes > 1024 && (bytes / 1024.0) <= Double.MAX_VALUE) {
double Mb = ((bytes / 1024.0) / 1024.0);//Bytes -> Megabytes
if (Mb <= Integer.MAX_VALUE) {
return (int) Math.ceil(Mb);
double megabytes = ((bytes / 1024.0) / 1024.0);//Bytes -> Megabytes
if (megabytes <= Integer.MAX_VALUE) {
return (int) Math.ceil(megabytes);
}
}
return 0;
@ -347,7 +402,7 @@ final class ExtractUnallocAction extends AbstractAction {
int mbs = 0; //Increments every 128th tick of kbs
for (OutputFileData outputFileData : this.outputFileDataList) {
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);
long bytes = 0;
int i = 0;
@ -374,9 +429,9 @@ final class ExtractUnallocAction extends AbstractAction {
if (canceled) {
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 {
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();
@ -589,7 +644,7 @@ final class ExtractUnallocAction extends AbstractAction {
*/
private class OutputFileData {
private List<LayoutFile> layoutFiles;
private final List<LayoutFile> layoutFiles;
private final long sizeInBytes;
private long volumeId;
private long imageId;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015-2017 Basis Technology Corp.
* Copyright 2015-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,16 +19,19 @@
package org.sleuthkit.autopsy.guiutils;
import java.awt.Component;
import java.lang.reflect.InvocationTargetException;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER;
import org.openide.nodes.Node;
import org.openide.util.ImageUtilities;
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
* grays out the cell if the table is disabled. The statuses represented are OK,
* WARNING, and ERROR.
* A JTable and outline view cell renderer that represents a status as a
* center-aligned icon, and grays out the cell if the table is disabled. The
* statuses represented are OK, WARNING, and ERROR.
*/
public class StatusIconCellRenderer extends GrayableCellRenderer {
@ -45,8 +48,20 @@ public class StatusIconCellRenderer extends GrayableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setHorizontalAlignment(CENTER);
if ((value instanceof Status)) {
switch((Status) value) {
Object switchValue = null;
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:
setIcon(OK_ICON);
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"));
break;
}
}
else {
} else {
setIcon(null);
setText("");
}

View File

@ -254,7 +254,7 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction impl
+ " echo %appName%\\bin\\%appName%64.exe does not exist\n"
+ " goto end\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"
+ " echo Could not find %appName% directory\n"
+ " goto end\n"

View File

@ -25,22 +25,27 @@ import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import net.sf.sevenzipjbinding.ArchiveFormat;
import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
import net.sf.sevenzipjbinding.ExtractAskMode;
import net.sf.sevenzipjbinding.ISequentialOutStream;
import net.sf.sevenzipjbinding.ISevenZipInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException;
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.IArchiveExtractCallback;
import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
import net.sf.sevenzipjbinding.PropID;
import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
@ -158,8 +163,12 @@ class SevenZipExtractor {
*
* @param archiveFile the AbstractFile for the parent archive which
* which we are checking
* @param archiveFileItem the current item being extracted from the parent
* archive
* @param inArchive The SevenZip archive currently open for extraction
*
* @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
* depth of all nested archives, key of objectID
* @param escapedFilePath the path to the archiveFileItem which has been
@ -167,19 +176,22 @@ class SevenZipExtractor {
*
* @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 {
final Long archiveItemSize = archiveFileItem.getSize();
final Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE);
//skip the check for small files
if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
return false;
}
final Long archiveItemPackedSize = archiveFileItem.getPackedSize();
final Long archiveItemPackedSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.PACKED_SIZE);
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;
}
@ -366,8 +378,9 @@ class SevenZipExtractor {
*
* @throws SevenZipException
*/
private String getPathInArchive(ISimpleInArchiveItem item, int itemNumber, AbstractFile archiveFile) throws SevenZipException {
String pathInArchive = item.getPath();
private String getPathInArchive(ISevenZipInArchive archive, int inArchiveItemIndex, AbstractFile archiveFile) throws SevenZipException {
String pathInArchive = (String) archive.getProperty(
inArchiveItemIndex, PropID.PATH);
if (pathInArchive == null || pathInArchive.isEmpty()) {
//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) {
pathInArchive = "/" + archName + "/" + Integer.toString(itemNumber);
pathInArchive = "/" + archName + "/" + Integer.toString(inArchiveItemIndex);
} else {
pathInArchive = "/" + useName;
}
@ -428,69 +441,6 @@ class SevenZipExtractor {
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
*
@ -523,7 +473,6 @@ class SevenZipExtractor {
boolean hasEncrypted = false;
boolean fullEncryption = true;
boolean progressStarted = false;
int processedItems = 0;
final String archiveFilePath = getArchiveFilePath(archiveFile);
final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath);
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
progress.start(numItems);
progressStarted = true;
final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
progress.progress(archiveFile.getName() + ": Analyzing archive metadata and creating local files");
//setup the archive local root folder
final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
@ -614,25 +563,18 @@ class SevenZipExtractor {
//currently getFreeDiskSpace always returns 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()) {
String pathInArchive = getPathInArchive(item, itemNumber, archiveFile);
//query for path in db
++itemNumber;
//check if possible zip bomb
if (isZipBombArchiveItemCheck(archiveFile, item, depthMap, escapedArchiveFilePath)) {
Map<Integer, InArchiveItemDetails> archiveDetailsMap = new LinkedHashMap<>();
for (int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) {
if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) {
unpackSuccessful = false;
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) {
logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS
@ -642,20 +584,25 @@ class SevenZipExtractor {
} else {
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
//check if unpacking this file will result in out of disk space
//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 newDiskSpace = freeDiskSpace - item.getSize();
Long archiveItemSize = (Long) inArchive.getProperty(
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) {
String msg = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
escapedArchiveFilePath, item.getPath());
escapedArchiveFilePath, archiveItemPath);
String details = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.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
unpackSuccessful = false;
continue; //skip this file
@ -664,42 +611,61 @@ class SevenZipExtractor {
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
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
if (!localFile.exists()) {
try {
if (item.isFolder()) {
if ((Boolean) inArchive.getProperty(
inArchiveItemIndex, PropID.IS_FOLDER)) {
localFile.mkdirs();
} else {
localFile.getParentFile().mkdirs();
try {
localFile.createNewFile();
} 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) {
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
}
}
// 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) {
continue;
}
//find this node in the hierarchy, create if neede;
unpackedNode = unpackNode(item, unpackedNode, password,
freeDiskSpace, uniqueExtractedName);
if (unpackedNode == null) {
unpackSuccessful = false;
}
//update units for progress bar
++processedItems;
//Store archiveItemIndex with local paths and unpackedNode reference.
//Necessary for the extract call back to write the current archive
//file to the correct disk location and to correctly update it's
//corresponding unpackedNode
archiveDetailsMap.put(inArchiveItemIndex, new InArchiveItemDetails(
unpackedNode, localAbsPath, localRelPath));
}
int[] extractionIndices = getExtractableFilesFromDetailsMap(archiveDetailsMap);
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
// intermediate nodes since the order is not guaranteed
try {
@ -799,6 +765,21 @@ class SevenZipExtractor {
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
*/
@ -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
* 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() {
flaggedAsZipBomb = true;

View File

@ -26,20 +26,21 @@
<Layout>
<DimensionLayout dim="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"/>
<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"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Component id="jScrollPane1" max="32767" attributes="0"/>
</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 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 max="-2" attributes="0"/>
</Group>
@ -47,21 +48,18 @@
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="instructionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="AddValuesToHashDatabaseButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="pasteFromClipboardButton" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="jScrollPane1" pref="274" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="279" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="pasteFromClipboardButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -102,14 +100,14 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pasteFromClipboardButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="AddValuesToHashDatabaseButton">
<Component class="javax.swing.JButton" name="okButton">
<Properties>
<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>
</Properties>
<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>
</Component>
<Component class="javax.swing.JButton" name="cancelButton">

View File

@ -73,7 +73,7 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
} else {
setDefaultCloseOperation(0);
}
AddValuesToHashDatabaseButton.setEnabled(enable);
okButton.setEnabled(enable);
cancelButton.setEnabled(enable);
pasteFromClipboardButton.setEnabled(enable);
}
@ -91,7 +91,7 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
jScrollPane1 = new javax.swing.JScrollPane();
hashValuesTextArea = new javax.swing.JTextArea();
pasteFromClipboardButton = new javax.swing.JButton();
AddValuesToHashDatabaseButton = new javax.swing.JButton();
okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton();
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
AddValuesToHashDatabaseButton.addActionListener(new java.awt.event.ActionListener() {
org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(AddHashValuesToDatabaseDialog.class, "AddHashValuesToDatabaseDialog.okButton.text_2")); // NOI18N
okButton.addActionListener(new java.awt.event.ActionListener() {
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()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1)
.addGroup(layout.createSequentialGroup()
.addComponent(instructionLabel)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(jScrollPane1))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(AddValuesToHashDatabaseButton, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(cancelButton, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(pasteFromClipboardButton, javax.swing.GroupLayout.Alignment.TRAILING))
.addGroup(layout.createSequentialGroup()
.addComponent(pasteFromClipboardButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 121, Short.MAX_VALUE)
.addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(instructionLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(AddValuesToHashDatabaseButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(pasteFromClipboardButton))
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 274, Short.MAX_VALUE))
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 279, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(pasteFromClipboardButton)
.addComponent(okButton)
.addComponent(cancelButton))
.addContainerGap())
);
@ -213,17 +212,17 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
}
}//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());
progressDialog.addHashValuesToDatabase();
}//GEN-LAST:event_AddValuesToHashDatabaseButtonActionPerformed
}//GEN-LAST:event_okButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton AddValuesToHashDatabaseButton;
private javax.swing.JButton cancelButton;
private javax.swing.JTextArea hashValuesTextArea;
private javax.swing.JLabel instructionLabel;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JButton okButton;
private javax.swing.JButton pasteFromClipboardButton;
// 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.hashDbDoesNotExistMsg=The selected hash set does not exist.
HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open hash set at {0}.
HashDbIngestModule.moduleName=Hash Lookup
HashDbIngestModule.moduleDescription=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set.
HashLookupModuleFactory.moduleName.text=Hash Lookup
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.calcHashValueErr=Error encountered while calculating the hash value for {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.instructionLabel.text_1=Paste MD5 hash values (one per line) below:
AddHashValuesToDatabaseDialog.cancelButton.text_2=Cancel
AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2=Add Hashes to Hash Set
AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=Paste From Clipboard
AddHashValuesToDatabaseProgressDialog.okButton.text=OK
AddHashValuesToDatabaseProgressDialog.statusLabel.text=status
@ -237,3 +236,4 @@ HashDbCreateDatabaseDialog.centralRepoRadioButton.text=Remote (Central Repositor
HashDbCreateDatabaseDialog.lbOrg.text=Source Organization:
HashDbCreateDatabaseDialog.orgButton.text=Manage Organizations
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.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
HashDbIngestModule.moduleName=\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.moduleName.text=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7
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.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
@ -86,7 +86,6 @@ HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u60aa
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.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.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
@ -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
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.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
AddHashValuesToDatabaseProgressDialog.okButton.text=OK
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.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
AddHashValuesToDatabaseDialog.okButton.text_2=OK

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 - 2013 Basis Technology Corp.
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* 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.TskException;
@NbBundle.Messages({
/**
* File ingest module to mark files based on hash values.
*/
@Messages({
"HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.",
"HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.",
"HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.",
@ -67,18 +70,21 @@ public class HashDbIngestModule implements FileIngestModule {
private final SleuthkitCase skCase;
private final HashDbManager hashDbManager = HashDbManager.getInstance();
private final HashLookupModuleSettings settings;
private List<HashDb> knownBadHashSets = new ArrayList<>();
private List<HashDb> knownHashSets = new ArrayList<>();
private final List<HashDb> knownBadHashSets = new ArrayList<>();
private final List<HashDb> knownHashSets = new ArrayList<>();
private long jobId;
private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
private Blackboard blackboard;
/**
* A container of values for storing ingest metrics for the job.
*/
private static class IngestJobTotals {
private AtomicLong totalKnownBadCount = new AtomicLong(0);
private AtomicLong totalCalctime = new AtomicLong(0);
private AtomicLong totalLookuptime = new AtomicLong(0);
private final AtomicLong totalKnownBadCount = new AtomicLong(0);
private final AtomicLong totalCalctime = new AtomicLong(0);
private final AtomicLong totalLookuptime = new AtomicLong(0);
}
private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) {
@ -90,6 +96,15 @@ public class HashDbIngestModule implements FileIngestModule {
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 {
this.settings = settings;
skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
@ -140,12 +155,18 @@ public class HashDbIngestModule implements FileIngestModule {
enabledHashSets.add(db);
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting index status for " + db.getDisplayName()+ " hash set", ex); //NON-NLS
logger.log(Level.WARNING, "Error getting index status for " + db.getDisplayName() + " hash set", ex); //NON-NLS
}
}
}
}
@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
public ProcessResult process(AbstractFile file) {
try {
@ -156,8 +177,8 @@ public class HashDbIngestModule implements FileIngestModule {
}
// Skip unallocated space files.
if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) ||
file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
|| file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
return ProcessResult.OK;
}
@ -181,6 +202,7 @@ public class HashDbIngestModule implements FileIngestModule {
// calc hash value
String name = file.getName();
long fileId = file.getId();
String md5Hash = file.getMd5Hash();
if (md5Hash == null || md5Hash.isEmpty()) {
try {
@ -203,15 +225,11 @@ public class HashDbIngestModule implements FileIngestModule {
totals.totalCalctime.addAndGet(delta);
} 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(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.fileReadErrorMsg",
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.calcHashValueErr",
name)));
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", name),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr", name)));
return ProcessResult.ERROR;
}
}
@ -245,21 +263,37 @@ public class HashDbIngestModule implements FileIngestModule {
}
}
postHashSetHitToBlackboard(file, md5Hash, hashSetName, comment, db.getSendIngestMessages());
/*
* 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());
}
} 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);
totals.totalLookuptime.addAndGet(delta);
} 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(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.hashLookupErrorMsg",
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.lookingUpKnownBadHashValueErr",
name)));
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", name),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.lookingUpKnownBadHashValueErr", name)));
ret = ProcessResult.ERROR;
}
}
@ -279,15 +313,12 @@ public class HashDbIngestModule implements FileIngestModule {
totals.totalLookuptime.addAndGet(delta);
} 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(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.hashLookupErrorMsg",
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.lookingUpKnownHashValueErr",
name)));
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", name),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.lookingUpKnownHashValueErr", name)));
ret = ProcessResult.ERROR;
}
}
@ -296,18 +327,29 @@ public class HashDbIngestModule implements FileIngestModule {
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) {
try {
String MODULE_NAME = NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.moduleName");
String moduleName = HashLookupModuleFactory.getModuleName();
BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
Collection<BlackboardAttribute> attributes = new ArrayList<>();
//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);
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, hashSetName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, MODULE_NAME, md5Hash));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, comment));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, hashSetName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment));
badFile.addAttributes(attributes);
@ -351,19 +393,24 @@ public class HashDbIngestModule implements FileIngestModule {
detailsSb.append("</table>"); //NON-NLS
services.postMessage(IngestMessage.createDataMessage(HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.postToBB.knownBadMsg",
abstractFile.getName()),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.knownBadMsg", abstractFile.getName()),
detailsSb.toString(),
abstractFile.getName() + md5Hash,
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) {
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,
List<HashDb> knownBadHashSets, List<HashDb> knownHashSets) {
IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId);
@ -399,8 +446,7 @@ public class HashDbIngestModule implements FileIngestModule {
IngestServices.getInstance().postMessage(IngestMessage.createMessage(
IngestMessage.MessageType.INFO,
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(HashDbIngestModule.class,
"HashDbIngestModule.complete.hashLookupResults"),
NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.hashLookupResults"),
detailsSb.toString()));
}
}

View File

@ -42,13 +42,18 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter {
return getModuleName();
}
/**
* Get the name of the module.
*
* @return The module name.
*/
static String getModuleName() {
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashDbIngestModule.moduleName");
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashLookupModuleFactory.moduleName.text");
}
@Override
public String getModuleDescription() {
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashDbIngestModule.moduleDescription");
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashLookupModuleFactory.moduleDescription.text");
}
@Override
@ -101,8 +106,8 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter {
@Override
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) {
if (!(settings instanceof HashLookupModuleSettings)) {
throw new IllegalArgumentException(
NbBundle.getMessage(this.getClass(), "HashLookupModuleFactory.createFileIngestModule.exception.msg"));
throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(),
"HashLookupModuleFactory.createFileIngestModule.exception.msg"));
}
try {
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 String HTML_SUBDIR = "content";
private Case currentCase;
private SleuthkitCase skCase;
static Integer THUMBNAIL_COLUMNS = 5;
private Map<String, Integer> dataTypes;
@ -109,7 +108,6 @@ class ReportHTML implements TableReportModule {
// Refesh the member variables
private void refresh() throws NoCurrentCaseException {
currentCase = Case.getCurrentCaseThrows();
skCase = currentCase.getSleuthkitCase();
dataTypes = new TreeMap<>();
@ -274,7 +272,7 @@ class ReportHTML implements TableReportModule {
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS
break;
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
iconFileName = "star.png"; //NON-NLS
iconFilePath = subPath + File.separator + iconFileName;
@ -627,12 +625,19 @@ class ReportHTML implements TableReportModule {
int positionCounter = 0;
for (String cell : row) {
// 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
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.
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
builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
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
break;
case 7:
// Right-align the bytes column.
builder.append("\t\t<td class=\"right_align_cell\">").append(cell).append("</td>\n"); //NON-NLS
break;
default:
// Regular case, not a file name nor a byte count
builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
break;
}
++positionCounter;
}
@ -719,7 +724,7 @@ class ReportHTML implements TableReportModule {
for (int i = 0; i < tags.size(); i++) {
ContentTag tag = tags.get(i);
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) {
linkToThumbnail.append(", ");
}
@ -751,12 +756,9 @@ class ReportHTML implements TableReportModule {
return true;
}
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.UNUSED_BLOCKS) {
return true;
}
return false;
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS;
}
/**
@ -782,8 +784,14 @@ class ReportHTML implements TableReportModule {
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();
int lastDotIndex = fileName.lastIndexOf(".");
if (lastDotIndex != -1 && lastDotIndex != 0) {
@ -1052,9 +1060,9 @@ class ReportHTML implements TableReportModule {
* Write the summary of the current case for this report.
*/
private void writeSummary() {
Writer out = null;
Writer output = null;
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();
head.append("<html>\n<head>\n<title>").append( //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("</style>\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");
Date date = new Date();
@ -1119,7 +1127,7 @@ class ReportHTML implements TableReportModule {
}
summary.append("</div>\n"); //NON-NLS
summary.append("</body></html>"); //NON-NLS
out.write(summary.toString());
output.write(summary.toString());
} catch (FileNotFoundException ex) {
logger.log(Level.SEVERE, "Could not find summary.html file to write to."); //NON-NLS
} 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.");
} finally {
try {
if (out != null) {
out.flush();
out.close();
if (output != null) {
output.flush();
output.close();
}
} catch (IOException ex) {
}
@ -1292,9 +1300,24 @@ class ReportHTML implements TableReportModule {
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) {
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) {
return null;
}

View File

@ -38,6 +38,7 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
@ -264,6 +265,7 @@ class TableReportGenerator {
/**
* Make table for tagged files
*/
@Messages({"ReportGenerator.tagTable.header.userName=User Name"})
@SuppressWarnings("deprecation")
private void makeContentTagsTables() {
@ -287,6 +289,7 @@ class TableReportGenerator {
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.tag"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.file"),
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.timeChanged"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeAccessed"),
@ -324,7 +327,7 @@ class TableReportGenerator {
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();
if (content instanceof AbstractFile) {
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.tag"),
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.
for (BlackboardArtifactTag tag : tags) {
@ -396,7 +400,8 @@ class TableReportGenerator {
}
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);
// check if the tag is an image that we should later make a thumbnail for

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* 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
* 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.
*
@ -60,6 +63,7 @@ public class SwingFXMenuUtils extends MenuItem {
private MenuItemAdapter(final JMenuItem jMenuItem) {
super(jMenuItem.getText());
setDisable(jMenuItem.isEnabled() == false);
setOnAction(actionEvent -> SwingUtilities.invokeLater(jMenuItem::doClick));
}
}
@ -77,6 +81,7 @@ public class SwingFXMenuUtils extends MenuItem {
*/
MenuAdapter(final JMenu jMenu) {
super(jMenu.getText());
setDisable(jMenu.isEnabled() == false);
populateSubMenus(jMenu);
}
@ -87,6 +92,7 @@ public class SwingFXMenuUtils extends MenuItem {
*/
MenuAdapter(JPopupMenu jPopupMenu) {
super(jPopupMenu.getLabel());
setDisable(jPopupMenu.isEnabled() == false);
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
}
// Test getting instances with expected resuls
// Test getting instances with expected results
try {
List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(fileType, inAllDataSourcesHash);
assertTrue("getArtifactInstancesByTypeValue returned " + instances.size() + " results - expected 3", instances.size() == 3);
@ -1123,6 +1123,34 @@ public class CentralRepoDatamodelTest extends TestCase {
} catch (EamDbException ex) {
// 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.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 org.sleuthkit.autopsy.commonfilesearch.AllIntraCaseCommonAttributeSearcher;
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults;
import org.sleuthkit.autopsy.commonfilesearch.IntraCaseCommonAttributeSearcher;
import org.sleuthkit.autopsy.commonfilesearch.SingleIntraCaseCommonAttributeSearcher;
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleTemplate;
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
* module.
*/
public class IngestedWithNoFileTypes extends NbTestCase {
public class IngestedWithNoFileTypesIntraCaseTests extends NbTestCase {
public static Test suite() {
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithNoFileTypes.class).
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithNoFileTypesIntraCaseTests.class).
clusters(".*").
enableModules(".*");
return conf.suite();
}
private final IntraCaseUtils utils;
private final IntraCaseTestUtils utils;
public IngestedWithNoFileTypes(String name) {
public IngestedWithNoFileTypesIntraCaseTests(String name) {
super(name);
this.utils = new IntraCaseUtils(this, "IngestedWithNoFileTypes");
this.utils = new IntraCaseTestUtils(this, "IngestedWithNoFileTypes");
}
@Override
@ -77,7 +77,7 @@ public class IngestedWithNoFileTypes extends NbTestCase {
ArrayList<IngestModuleTemplate> templates = new ArrayList<>();
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 {
IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings);
@ -97,20 +97,22 @@ public class IngestedWithNoFileTypes extends NbTestCase {
* find nothing and no errors should arise.
*/
public void testOne() {
try {
Map<Long, String> dataSources = this.utils.getDataSourceMap();
CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, true, false);
CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles();
IntraCaseCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false);
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());
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
} catch (TskCoreException | NoCurrentCaseException | SQLException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}
}
@ -121,18 +123,18 @@ public class IngestedWithNoFileTypes extends NbTestCase {
public void testTwo() {
try {
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);
CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles();
IntraCaseCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, true, false);
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());
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);
}

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