mirror of
https://github.com/elisspace/autopsy.git
synced 2026-10-01 14:59:50 +00:00
Merge branch 'develop' of https://github.com/sleuthkit/autopsy into 3870-pure-callable
# Conflicts: # Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java # Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java # Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java # Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java
This commit is contained in:
+1
-1
@@ -86,7 +86,7 @@
|
||||
|
||||
<target name="getTestDataFiles">
|
||||
<mkdir dir="${basedir}/test/qa-functional/data"/>
|
||||
<get src="https://drive.google.com/uc?id=1_xPSnp0UDOO9sIPpvdtW_dRtD5SW9EID" dest="${test-input}/EmbeddedIM_img2_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"/>
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
<dependency conf="core->default" org="org.apache.commons" name="commons-dbcp2" rev="2.1.1"/>
|
||||
<dependency conf="core->default" org="org.apache.commons" name="commons-pool2" rev="2.4.2"/>
|
||||
<dependency org="com.monitorjbl" name="xlsx-streamer" rev="1.2.1"/>
|
||||
|
||||
<dependency conf="core->default" org="org.jsoup" name="jsoup" rev="1.10.3"/>
|
||||
<dependency conf="core->default" org="com.googlecode.plist" name="dd-plist" rev="1.20"/>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ file.reference.postgresql-9.4.1211.jre7.jar=release/modules/ext/postgresql-9.4.1
|
||||
file.reference.Rejistry-1.0-SNAPSHOT.jar=release/modules/ext/Rejistry-1.0-SNAPSHOT.jar
|
||||
file.reference.sevenzipjbinding-AllPlatforms.jar=release/modules/ext/sevenzipjbinding-AllPlatforms.jar
|
||||
file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar
|
||||
file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar
|
||||
file.reference.sqlite-jdbc-3.8.11.jar=release\\modules\\ext\\sqlite-jdbc-3.8.11.jar
|
||||
file.reference.StixLib.jar=release/modules/ext/StixLib.jar
|
||||
file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar
|
||||
file.reference.jackcess-2.1.8.jar=release/modules/ext/jackcess-2.1.8.jar
|
||||
@@ -35,6 +35,7 @@ file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar
|
||||
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
|
||||
file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar
|
||||
file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar
|
||||
file.reference.xlsx-streamer-1.2.1.jar=release/modules/ext/xlsx-streamer-1.2.1.jar
|
||||
file.reference.xmpcore-5.1.3.jar=release/modules/ext/xmpcore-5.1.3.jar
|
||||
file.reference.xz-1.6.jar=release/modules/ext/xz-1.6.jar
|
||||
file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar
|
||||
|
||||
@@ -337,6 +337,7 @@
|
||||
<package>org.sleuthkit.autopsy.modules.vmextractor</package>
|
||||
<package>org.sleuthkit.autopsy.progress</package>
|
||||
<package>org.sleuthkit.autopsy.report</package>
|
||||
<package>org.sleuthkit.autopsy.tabulardatareader</package>
|
||||
<package>org.sleuthkit.datamodel</package>
|
||||
</public-packages>
|
||||
<class-path-extension>
|
||||
@@ -387,6 +388,10 @@
|
||||
<runtime-relative-path>ext/sevenzipjbinding.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sevenzipjbinding.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sleuthkit-postgresql-4.6.2.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/mchange-commons-java-0.2.9.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/mchange-commons-java-0.2.9.jar</binary-origin>
|
||||
@@ -411,10 +416,6 @@
|
||||
<runtime-relative-path>ext/metadata-extractor-2.10.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/metadata-extractor-2.10.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sleuthkit-postgresql-4.6.2.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/tika-core-1.17.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/tika-core-1.17.jar</binary-origin>
|
||||
@@ -441,7 +442,7 @@
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sqlite-jdbc-3.8.11.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sqlite-jdbc-3.8.11.jar</binary-origin>
|
||||
<binary-origin>release\modules\ext\sqlite-jdbc-3.8.11.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/activemq-all-5.11.1.jar</runtime-relative-path>
|
||||
@@ -487,6 +488,14 @@
|
||||
<runtime-relative-path>ext/jdom-2.0.5-contrib.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/jdom-2.0.5-contrib.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/SparseBitSet-1.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/SparseBitSet-1.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/xlsx-streamer-1.2.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/xlsx-streamer-1.2.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/pdfbox-2.0.8.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/pdfbox-2.0.8.jar</binary-origin>
|
||||
@@ -499,10 +508,6 @@
|
||||
<runtime-relative-path>ext/xmpcore-5.1.3.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/xmpcore-5.1.3.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/SparseBitSet-1.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/SparseBitSet-1.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
</data>
|
||||
</configuration>
|
||||
</project>
|
||||
|
||||
@@ -24,10 +24,8 @@ import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.ActionMap;
|
||||
@@ -57,7 +55,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
private final List<TagName> tagNamesList = new ArrayList<>();
|
||||
private final List<TagName> standardTagNamesList = new ArrayList<>();
|
||||
private TagNameAndComment tagNameAndComment = null;
|
||||
|
||||
|
||||
public static class TagNameAndComment {
|
||||
|
||||
private final TagName tagName;
|
||||
@@ -105,7 +103,16 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
public static TagNameAndComment doDialog(Window owner) {
|
||||
GetTagNameAndCommentDialog dialog = new GetTagNameAndCommentDialog(owner);
|
||||
dialog.display();
|
||||
return dialog.tagNameAndComment;
|
||||
return dialog.getTagNameAndComment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the TagNameAndComment.
|
||||
*
|
||||
* @return the tagNameAndComment
|
||||
*/
|
||||
private TagNameAndComment getTagNameAndComment() {
|
||||
return tagNameAndComment;
|
||||
}
|
||||
|
||||
private GetTagNameAndCommentDialog(Window owner) {
|
||||
@@ -114,14 +121,14 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
ModalityType.APPLICATION_MODAL);
|
||||
}
|
||||
|
||||
|
||||
private void display() {
|
||||
initComponents();
|
||||
tagCombo.setRenderer(new DefaultListCellRenderer() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
String status = ((TagName) value).getKnownStatus() == TskData.FileKnown.BAD ?TagsManager.getNotableTagLabel() : "";
|
||||
String status = ((TagName) value).getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
|
||||
String newValue = ((TagName) value).getDisplayName() + status;
|
||||
return super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus);
|
||||
}
|
||||
@@ -151,7 +158,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
|
||||
List<String> standardTagNames = TagsManager.getStandardTagNames();
|
||||
Map<String, TagName> tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap());
|
||||
|
||||
|
||||
tagNamesMap.entrySet().stream().map((entry) -> entry.getValue()).forEachOrdered((tagName) -> {
|
||||
if (standardTagNames.contains(tagName.getDisplayName())) {
|
||||
standardTagNamesList.add(tagName);
|
||||
@@ -159,7 +166,6 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
tagNamesList.add(tagName);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
Logger.getLogger(GetTagNameAndCommentDialog.class
|
||||
@@ -320,4 +326,5 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
private javax.swing.JComboBox<TagName> tagCombo;
|
||||
private javax.swing.JLabel tagLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -29,6 +29,7 @@ import javax.swing.event.DocumentListener;
|
||||
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.PathValidator;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
|
||||
/**
|
||||
* The JPanel for the first page of the new case wizard.
|
||||
@@ -151,10 +152,23 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener {
|
||||
*/
|
||||
caseParentDirWarningLabel.setVisible(false);
|
||||
String parentDir = getCaseParentDir();
|
||||
if (!PathValidator.isValid(parentDir, getCaseType())) {
|
||||
if (!PathValidator.isValidForMultiUserCase(parentDir, getCaseType())) {
|
||||
caseParentDirWarningLabel.setVisible(true);
|
||||
caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnCDriveError.text"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the base case directory if it can persist data and show a
|
||||
* warning if it is a wrong choice
|
||||
*/
|
||||
if(!PathValidator.isValidForRunningOnTarget(parentDir)){
|
||||
caseParentDirWarningLabel.setVisible(true);
|
||||
if(PlatformUtil.isWindowsOS()){
|
||||
caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text" ));
|
||||
} else if(System.getProperty("os.name").toLowerCase().contains("nux")) {
|
||||
caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the "Next" button for the wizard if there is text entered for
|
||||
|
||||
@@ -47,7 +47,6 @@ import org.sleuthkit.datamodel.TskData;
|
||||
public class TagsManager implements Closeable {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName());
|
||||
|
||||
private final SleuthkitCase caseDb;
|
||||
|
||||
/**
|
||||
@@ -71,13 +70,14 @@ public class TagsManager implements Closeable {
|
||||
|| tagDisplayName.contains(";"));
|
||||
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"TagsManager.notableTagEnding.text= (Notable)"})
|
||||
/**
|
||||
* Get String of text which is used to label tags as notable to the user.
|
||||
*
|
||||
* Get String of text which is used to label tags as notable to the user.
|
||||
*
|
||||
* @return Bundle message TagsManager.notableTagEnding.text
|
||||
*/
|
||||
public static String getNotableTagLabel(){
|
||||
public static String getNotableTagLabel() {
|
||||
return Bundle.TagsManager_notableTagEnding_text();
|
||||
}
|
||||
|
||||
@@ -123,13 +123,13 @@ public class TagsManager implements Closeable {
|
||||
|
||||
/**
|
||||
* Returns a list of names of standard/predefined tags
|
||||
*
|
||||
*
|
||||
* @return list of predefined tag names
|
||||
*/
|
||||
public static List<String> getStandardTagNames() {
|
||||
return TagNameDefinition.getStandardTagNames();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a per case Autopsy service that manages the addition of
|
||||
* content and artifact tags to the case database.
|
||||
@@ -166,21 +166,79 @@ public class TagsManager implements Closeable {
|
||||
return caseDb.getTagNamesInUse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all tag names currently in use in the case database for
|
||||
* tagging content or artifacts by the specified user.
|
||||
*
|
||||
* @param userName - the user name that you want to get tags for
|
||||
*
|
||||
* @return A list, possibly empty, of TagName objects.
|
||||
*
|
||||
* @throws TskCoreException If there is an error querying the case database.
|
||||
*/
|
||||
public List<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
|
||||
* blackboard_artifact_tags tables, for the given data source object id.
|
||||
*
|
||||
* @param dsObjId data source object id
|
||||
*
|
||||
*
|
||||
* @return A list, possibly empty, of TagName data transfer objects (DTOs)
|
||||
* for the rows.
|
||||
* for the rows.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public List<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,11 +574,11 @@ public class TagsManager implements Closeable {
|
||||
return caseDb.getContentTagsByTagName(tagName);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Gets content tags by tag name, for the given data source.
|
||||
*
|
||||
* @param tagName The tag name of interest.
|
||||
*
|
||||
*
|
||||
* @param dsObjId data source object id
|
||||
*
|
||||
* @return A list, possibly empty, of the content tags with the specified
|
||||
@@ -479,7 +590,7 @@ public class TagsManager implements Closeable {
|
||||
public List<ContentTag> getContentTagsByTagName(TagName tagName, long dsObjId) throws TskCoreException {
|
||||
return caseDb.getContentTagsByTagName(tagName, dsObjId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets content tags count by content.
|
||||
*
|
||||
@@ -581,6 +692,31 @@ public class TagsManager implements Closeable {
|
||||
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an artifact tags count by tag name for a specific user.
|
||||
*
|
||||
* @param tagName The representation of the desired tag type in the case
|
||||
* database, which can be obtained by calling getTagNames
|
||||
* and/or addTagName.
|
||||
* @param userName - the user name that you want to get tags for
|
||||
*
|
||||
* @return A count of the artifact tags with the specified tag name for the
|
||||
* specified user.
|
||||
*
|
||||
* @throws TskCoreException If there is an error getting the tags count from
|
||||
* the case database.
|
||||
*/
|
||||
public long getBlackboardArtifactTagsCountByTagNameForUser(TagName tagName, String userName) throws TskCoreException {
|
||||
long count = 0;
|
||||
List<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.
|
||||
@@ -598,7 +734,34 @@ public class TagsManager implements Closeable {
|
||||
public long getBlackboardArtifactTagsCountByTagName(TagName tagName, long dsObjId) throws TskCoreException {
|
||||
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName, dsObjId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets an artifact tags count by tag name, for the given data source and
|
||||
* user.
|
||||
*
|
||||
* @param tagName The representation of the desired tag type in the case
|
||||
* database, which can be obtained by calling getTagNames
|
||||
* and/or addTagName.
|
||||
* @param dsObjId data source object id
|
||||
* @param userName - the user name that you want to get tags for
|
||||
*
|
||||
* @return A count of the artifact tags with the specified tag name, for the
|
||||
* given data source and user.
|
||||
*
|
||||
* @throws TskCoreException If there is an error getting the tags count from
|
||||
* the case database.
|
||||
*/
|
||||
public long getBlackboardArtifactTagsCountByTagNameForUser(TagName tagName, long dsObjId, String userName) throws TskCoreException {
|
||||
long count = 0;
|
||||
List<BlackboardArtifactTag> artifactTags = getBlackboardArtifactTagsByTagName(tagName, dsObjId);
|
||||
for (BlackboardArtifactTag tag : artifactTags) {
|
||||
if (userName.equals(tag.getUserName())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an artifact tag by tag id.
|
||||
*
|
||||
@@ -647,7 +810,7 @@ public class TagsManager implements Closeable {
|
||||
public List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName tagName, long dsObjId) throws TskCoreException {
|
||||
return caseDb.getBlackboardArtifactTagsByTagName(tagName, dsObjId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets artifact tags for a particular artifact.
|
||||
*
|
||||
|
||||
+12
-13
@@ -23,9 +23,8 @@ import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.openide.DialogDisplayer;
|
||||
import org.openide.NotifyDescriptor;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
@@ -42,7 +41,7 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
|
||||
private static final Logger logger = Logger.getLogger(AddEditCentralRepoCommentAction.class.getName());
|
||||
|
||||
private boolean addToDatabase;
|
||||
private CorrelationAttribute correlationAttribute;
|
||||
private CorrelationAttributeInstance correlationAttributeInstance;
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
@@ -50,9 +49,9 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
|
||||
*
|
||||
* @param correlationAttribute The correlation attribute to modify.
|
||||
*/
|
||||
public AddEditCentralRepoCommentAction(CorrelationAttribute correlationAttribute) {
|
||||
public AddEditCentralRepoCommentAction(CorrelationAttributeInstance correlationAttribute) {
|
||||
super(Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment());
|
||||
this.correlationAttribute = correlationAttribute;
|
||||
this.correlationAttributeInstance = correlationAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,10 +62,10 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
|
||||
*/
|
||||
public AddEditCentralRepoCommentAction(AbstractFile file) {
|
||||
super(Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment());
|
||||
correlationAttribute = EamArtifactUtil.getCorrelationAttributeFromContent(file);
|
||||
if (correlationAttribute == null) {
|
||||
correlationAttributeInstance = EamArtifactUtil.getInstanceFromContent(file);
|
||||
if (correlationAttributeInstance == null) {
|
||||
addToDatabase = true;
|
||||
correlationAttribute = EamArtifactUtil.makeCorrelationAttributeFromContent(file);
|
||||
correlationAttributeInstance = EamArtifactUtil.makeInstanceFromContent(file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +82,7 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
|
||||
*/
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
CentralRepoCommentDialog centralRepoCommentDialog = new CentralRepoCommentDialog(correlationAttribute);
|
||||
CentralRepoCommentDialog centralRepoCommentDialog = new CentralRepoCommentDialog(correlationAttributeInstance);
|
||||
centralRepoCommentDialog.display();
|
||||
|
||||
comment = null;
|
||||
@@ -95,9 +94,9 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
|
||||
dbManager = EamDb.getInstance();
|
||||
|
||||
if (addToDatabase) {
|
||||
dbManager.addArtifact(correlationAttribute);
|
||||
dbManager.addArtifactInstance(correlationAttributeInstance);
|
||||
} else {
|
||||
dbManager.updateAttributeInstanceComment(correlationAttribute);
|
||||
dbManager.updateAttributeInstanceComment(correlationAttributeInstance);
|
||||
}
|
||||
|
||||
comment = centralRepoCommentDialog.getComment();
|
||||
@@ -127,7 +126,7 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
|
||||
*
|
||||
* @return The correlation attribute.
|
||||
*/
|
||||
public CorrelationAttribute getCorrelationAttribute() {
|
||||
return correlationAttribute;
|
||||
public CorrelationAttributeInstance getCorrelationAttribute() {
|
||||
return correlationAttributeInstance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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">
|
||||
|
||||
@@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.centralrepository;
|
||||
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
|
||||
/**
|
||||
@@ -31,31 +30,30 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
|
||||
private final CorrelationAttribute correlationAttribute;
|
||||
private final CorrelationAttributeInstance correlationAttributeInstance;
|
||||
private boolean commentUpdated = false;
|
||||
private String currentComment = "";
|
||||
|
||||
/**
|
||||
* Create an instance.
|
||||
*
|
||||
* @param correlationAttribute The correlation attribute to be modified.
|
||||
* @param correlationAttributeInstance The correlation attribute to be modified.
|
||||
*/
|
||||
CentralRepoCommentDialog(CorrelationAttribute correlationAttribute) {
|
||||
CentralRepoCommentDialog(CorrelationAttributeInstance correlationAttributeInstance) {
|
||||
super(WindowManager.getDefault().getMainWindow(), Bundle.CentralRepoCommentDialog_title_addEditCentralRepoComment());
|
||||
|
||||
initComponents();
|
||||
|
||||
CorrelationAttributeInstance instance = correlationAttribute.getInstances().get(0);
|
||||
CorrelationAttributeInstance instance = correlationAttributeInstance;
|
||||
|
||||
// Store the original comment
|
||||
if (instance.getComment() != null) {
|
||||
currentComment = instance.getComment();
|
||||
}
|
||||
|
||||
pathLabel.setText(instance.getFilePath());
|
||||
commentTextArea.setText(instance.getComment());
|
||||
|
||||
this.correlationAttribute = correlationAttribute;
|
||||
this.correlationAttributeInstance = correlationAttributeInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,8 +101,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
commentTextArea = new javax.swing.JTextArea();
|
||||
okButton = new javax.swing.JButton();
|
||||
cancelButton = new javax.swing.JButton();
|
||||
fileLabel = new javax.swing.JLabel();
|
||||
pathLabel = new javax.swing.JLabel();
|
||||
commentLabel = new javax.swing.JLabel();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
@@ -131,10 +127,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(fileLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.fileLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.pathLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.commentLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
@@ -146,12 +138,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(fileLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(pathLabel))
|
||||
.addComponent(commentLabel))
|
||||
.addComponent(commentLabel)
|
||||
.addGap(0, 451, Short.MAX_VALUE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(0, 0, Short.MAX_VALUE)
|
||||
@@ -164,17 +151,13 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(fileLabel)
|
||||
.addComponent(pathLabel))
|
||||
.addGap(19, 19, 19)
|
||||
.addComponent(commentLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jScrollPane1)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(okButton)
|
||||
.addComponent(cancelButton))
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(cancelButton)
|
||||
.addComponent(okButton))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
@@ -187,7 +170,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
|
||||
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
|
||||
currentComment = commentTextArea.getText();
|
||||
correlationAttribute.getInstances().get(0).setComment(currentComment);
|
||||
correlationAttributeInstance.setComment(currentComment);
|
||||
commentUpdated = true;
|
||||
|
||||
dispose();
|
||||
@@ -197,9 +180,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JLabel commentLabel;
|
||||
private javax.swing.JTextArea commentTextArea;
|
||||
private javax.swing.JLabel fileLabel;
|
||||
private javax.swing.JScrollPane jScrollPane1;
|
||||
private javax.swing.JButton okButton;
|
||||
private javax.swing.JLabel pathLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
|
||||
+8
-20
@@ -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>
|
||||
|
||||
+143
-97
@@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.centralrepository.contentviewer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.BufferedWriter;
|
||||
@@ -57,10 +58,10 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@@ -85,10 +86,13 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
|
||||
private static final long serialVersionUID = -1L;
|
||||
|
||||
private final static Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName());
|
||||
|
||||
private static final int DEFAULT_MIN_CELL_WIDTH = 15;
|
||||
private static final int CELL_TEXT_WIDTH_PADDING = 5;
|
||||
|
||||
private final DataContentViewerOtherCasesTableModel tableModel;
|
||||
private final Collection<CorrelationAttribute> correlationAttributes;
|
||||
private final Collection<CorrelationAttributeInstance> correlationAttributes;
|
||||
/**
|
||||
* Could be null.
|
||||
*/
|
||||
@@ -125,8 +129,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
showCommonalityDetails();
|
||||
} else if (jmi.equals(addCommentMenuItem)) {
|
||||
try {
|
||||
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(otherCasesTable.getSelectedRow());
|
||||
AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute());
|
||||
OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow());
|
||||
AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.getCorrelationAttribute());
|
||||
action.actionPerformed(null);
|
||||
String currentComment = action.getComment();
|
||||
if (currentComment != null) {
|
||||
@@ -149,7 +153,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
// Set background of every nth row as light grey.
|
||||
TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer();
|
||||
otherCasesTable.setDefaultRenderer(Object.class, renderer);
|
||||
tableStatusPanelLabel.setVisible(false);
|
||||
|
||||
}
|
||||
|
||||
@@ -175,7 +178,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
int percentage;
|
||||
try {
|
||||
EamDb dbManager = EamDb.getInstance();
|
||||
for (CorrelationAttribute eamArtifact : correlationAttributes) {
|
||||
for (CorrelationAttributeInstance eamArtifact : correlationAttributes) {
|
||||
percentage = dbManager.getFrequencyPercentage(eamArtifact);
|
||||
msg.append(Bundle.DataContentViewerOtherCases_correlatedArtifacts_byType(percentage,
|
||||
eamArtifact.getCorrelationType().getDisplayName(),
|
||||
@@ -207,7 +210,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
if (-1 != selectedRowViewIdx) {
|
||||
EamDb dbManager = EamDb.getInstance();
|
||||
int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx);
|
||||
OtherOccurrenceNodeData nodeData = (OtherOccurrenceNodeData) tableModel.getRow(selectedRowModelIdx);
|
||||
OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) tableModel.getRow(selectedRowModelIdx);
|
||||
CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase();
|
||||
if (eamCasePartial == null) {
|
||||
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
|
||||
@@ -416,32 +419,40 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
*
|
||||
* @return A list of attributes that can be used for correlation
|
||||
*/
|
||||
private Collection<CorrelationAttribute> getCorrelationAttributesFromNode(Node node) {
|
||||
Collection<CorrelationAttribute> ret = new ArrayList<>();
|
||||
private Collection<CorrelationAttributeInstance> getCorrelationAttributesFromNode(Node node) {
|
||||
Collection<CorrelationAttributeInstance> ret = new ArrayList<>();
|
||||
|
||||
// correlate on blackboard artifact attributes if they exist and supported
|
||||
BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node);
|
||||
if (bbArtifact != null && EamDb.isEnabled()) {
|
||||
ret.addAll(EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbArtifact, false, false));
|
||||
ret.addAll(EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, false));
|
||||
}
|
||||
|
||||
|
||||
// we can correlate based on the MD5 if it is enabled
|
||||
if (this.file != null && EamDb.isEnabled()) {
|
||||
if (this.file != null && EamDb.isEnabled()) {
|
||||
try {
|
||||
|
||||
List<CorrelationAttribute.Type> artifactTypes = EamDb.getInstance().getDefinedCorrelationTypes();
|
||||
List<CorrelationAttributeInstance.Type> artifactTypes = EamDb.getInstance().getDefinedCorrelationTypes();
|
||||
String md5 = this.file.getMd5Hash();
|
||||
if (md5 != null && !md5.isEmpty() && null != artifactTypes && !artifactTypes.isEmpty()) {
|
||||
for (CorrelationAttribute.Type aType : artifactTypes) {
|
||||
if (aType.getId() == CorrelationAttribute.FILES_TYPE_ID) {
|
||||
ret.add(new CorrelationAttribute(aType, md5));
|
||||
for (CorrelationAttributeInstance.Type aType : artifactTypes) {
|
||||
if (aType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
|
||||
CorrelationCase corCase = EamDb.getInstance().getCase(Case.getCurrentCase());
|
||||
ret.add(new CorrelationAttributeInstance(
|
||||
md5,
|
||||
aType,
|
||||
corCase,
|
||||
CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()),
|
||||
file.getParentPath() + file.getName(),
|
||||
"",
|
||||
file.getKnown()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
} catch (EamDbException | TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
@@ -449,7 +460,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
if (this.file != null) {
|
||||
String md5 = this.file.getMd5Hash();
|
||||
if (md5 != null && !md5.isEmpty()) {
|
||||
ret.add(new CorrelationAttribute(CorrelationAttribute.getDefaultCorrelationTypes().get(0), md5));
|
||||
ret.add(new CorrelationAttributeInstance(CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0), md5));
|
||||
}
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
@@ -462,12 +473,12 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
|
||||
@Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."})
|
||||
/**
|
||||
* Gets the list of Eam Cases and determines the earliest case creation date.
|
||||
* Sets the label to display the earliest date string to the user.
|
||||
* Gets the list of Eam Cases and determines the earliest case creation
|
||||
* date. Sets the label to display the earliest date string to the user.
|
||||
*/
|
||||
private void setEarliestCaseDate() {
|
||||
String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable();
|
||||
|
||||
private void setEarliestCaseDate() {
|
||||
String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable();
|
||||
|
||||
if (EamDb.isEnabled()) {
|
||||
LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC);
|
||||
DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US);
|
||||
@@ -475,15 +486,15 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
EamDb dbManager = EamDb.getInstance();
|
||||
List<CorrelationCase> cases = dbManager.getCases();
|
||||
for (CorrelationCase aCase : cases) {
|
||||
LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate()));
|
||||
|
||||
if (caseDate.isBefore(earliestDate)) {
|
||||
LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate()));
|
||||
|
||||
if (caseDate.isBefore(earliestDate)) {
|
||||
earliestDate = caseDate;
|
||||
dateStringDisplay = aCase.getCreationDate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS
|
||||
} catch (ParseException ex) {
|
||||
@@ -495,10 +506,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the central repo database (if enabled) and the case database to find all
|
||||
* artifact instances correlated to the given central repository artifact. If the
|
||||
* central repo is not enabled, this will only return files from the current case
|
||||
* with matching MD5 hashes.
|
||||
* Query the central repo database (if enabled) and the case database to
|
||||
* find all artifact instances correlated to the given central repository
|
||||
* artifact. If the central repo is not enabled, this will only return files
|
||||
* from the current case with matching MD5 hashes.
|
||||
*
|
||||
* @param corAttr CorrelationAttribute to query for
|
||||
* @param dataSourceName Data source to filter results
|
||||
@@ -506,19 +517,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
*
|
||||
* @return A collection of correlated artifact instances
|
||||
*/
|
||||
private Map<UniquePathKey,OtherOccurrenceNodeData> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) {
|
||||
private Map<UniquePathKey, OtherOccurrenceNodeInstanceData> getCorrelatedInstances(CorrelationAttributeInstance 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:
|
||||
// - the case UUID is different
|
||||
@@ -530,14 +541,14 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
|| !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)
|
||||
|| !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) {
|
||||
|
||||
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
|
||||
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
|
||||
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
|
||||
nodeDataMap.put(uniquePathKey, newNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (corAttr.getCorrelationType().getDisplayName().equals("Files")) {
|
||||
if (corAttr.getCorrelationType().getDisplayName().equals("Files")) {
|
||||
List<AbstractFile> caseDbFiles = getCaseDbMatches(corAttr, openCase);
|
||||
|
||||
for (AbstractFile caseDbFile : caseDbFiles) {
|
||||
@@ -560,15 +571,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all other abstract files in the current case with the same MD5 as the selected node.
|
||||
* Get all other abstract files in the current case with the same MD5 as the
|
||||
* selected node.
|
||||
*
|
||||
* @param corAttr The CorrelationAttribute containing the MD5 to search for
|
||||
* @param openCase The current case
|
||||
*
|
||||
* @return List of matching AbstractFile objects
|
||||
*
|
||||
* @throws NoCurrentCaseException
|
||||
* @throws TskCoreException
|
||||
* @throws EamDbException
|
||||
* @throws EamDbException
|
||||
*/
|
||||
private List<AbstractFile> getCaseDbMatches(CorrelationAttribute corAttr, Case openCase) throws NoCurrentCaseException, TskCoreException, EamDbException {
|
||||
private List<AbstractFile> getCaseDbMatches(CorrelationAttributeInstance corAttr, Case openCase) throws NoCurrentCaseException, TskCoreException, EamDbException {
|
||||
String md5 = corAttr.getCorrelationValue();
|
||||
SleuthkitCase tsk = openCase.getSleuthkitCase();
|
||||
List<AbstractFile> matches = tsk.findAllFilesWhere(String.format("md5 = '%s'", new Object[]{md5}));
|
||||
@@ -586,18 +601,18 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
|
||||
/**
|
||||
* Adds the file to the nodeDataMap map if it does not already exist
|
||||
*
|
||||
* @param autopsyCase
|
||||
*
|
||||
* @param autopsyCase
|
||||
* @param nodeDataMap
|
||||
* @param newFile
|
||||
*
|
||||
* @throws TskCoreException
|
||||
* @throws EamDbException
|
||||
*/
|
||||
private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException {
|
||||
|
||||
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(newFile, autopsyCase);
|
||||
|
||||
private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException {
|
||||
|
||||
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase);
|
||||
|
||||
// If the caseDB object has a notable tag associated with it, update
|
||||
// the known status to BAD
|
||||
if (newNode.getKnown() != TskData.FileKnown.BAD) {
|
||||
@@ -613,13 +628,13 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
|
||||
// Make a key to see if the file is already in the map
|
||||
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
|
||||
|
||||
|
||||
// If this node is already in the list, the only thing we need to do is
|
||||
// update the known status to BAD if the caseDB version had known status BAD.
|
||||
// Otherwise this is a new node so add the new node to the map.
|
||||
if (nodeDataMap.containsKey(uniquePathKey)) {
|
||||
if (newNode.getKnown() == TskData.FileKnown.BAD) {
|
||||
OtherOccurrenceNodeData prevInstance = nodeDataMap.get(uniquePathKey);
|
||||
OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey);
|
||||
prevInstance.updateKnown(newNode.getKnown());
|
||||
}
|
||||
} else {
|
||||
@@ -642,7 +657,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
} else {
|
||||
return this.file != null
|
||||
&& this.file.getSize() > 0
|
||||
&& ((this.file.getMd5Hash() != null) && ( ! this.file.getMd5Hash().isEmpty()));
|
||||
&& ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,8 +680,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
*
|
||||
* @param node The node being viewed.
|
||||
*/
|
||||
@Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other occurrences to display.",
|
||||
"DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file."})
|
||||
@Messages({
|
||||
"DataContentViewerOtherCases.table.noArtifacts=Item has no attributes with which to search.",
|
||||
"DataContentViewerOtherCases.table.noResultsFound=No results found."
|
||||
})
|
||||
private void populateTable(Node node) {
|
||||
String dataSourceName = "";
|
||||
String deviceId = "";
|
||||
@@ -683,8 +700,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
|
||||
// get the attributes we can correlate on
|
||||
correlationAttributes.addAll(getCorrelationAttributesFromNode(node));
|
||||
for (CorrelationAttribute corAttr : correlationAttributes) {
|
||||
Map<UniquePathKey,OtherOccurrenceNodeData> correlatedNodeDataMap = new HashMap<>(0);
|
||||
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
|
||||
Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
|
||||
|
||||
// get correlation and reference set instances from DB
|
||||
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
|
||||
@@ -696,36 +713,45 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
}
|
||||
|
||||
if (correlationAttributes.isEmpty()) {
|
||||
// @@@ BC: We should have a more descriptive message than this. Mention that the file didn't have a MD5, etc.
|
||||
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_noArtifacts());
|
||||
tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noArtifacts()));
|
||||
setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noArtifacts());
|
||||
} else if (0 == tableModel.getRowCount()) {
|
||||
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_isempty());
|
||||
tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noResultsFound()));
|
||||
setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noResultsFound());
|
||||
} else {
|
||||
clearMessageOnTableStatusPanel();
|
||||
setColumnWidths();
|
||||
}
|
||||
setEarliestCaseDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust a given column for the text provided.
|
||||
*
|
||||
* @param columnIndex The index of the column to adjust.
|
||||
* @param text The text whose length will be used to adjust the
|
||||
* column width.
|
||||
*/
|
||||
private void setColumnWidthToText(int columnIndex, String text) {
|
||||
TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex);
|
||||
FontMetrics fontMetrics = otherCasesTable.getFontMetrics(otherCasesTable.getFont());
|
||||
int stringWidth = fontMetrics.stringWidth(text);
|
||||
column.setMinWidth(stringWidth + CELL_TEXT_WIDTH_PADDING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust column widths to their preferred values.
|
||||
*/
|
||||
private void setColumnWidths() {
|
||||
for (int idx = 0; idx < tableModel.getColumnCount(); idx++) {
|
||||
TableColumn column = otherCasesTable.getColumnModel().getColumn(idx);
|
||||
int colWidth = tableModel.getColumnPreferredWidth(idx);
|
||||
if (0 < colWidth) {
|
||||
column.setPreferredWidth(colWidth);
|
||||
column.setMinWidth(DEFAULT_MIN_CELL_WIDTH);
|
||||
int columnWidth = tableModel.getColumnPreferredWidth(idx);
|
||||
if (columnWidth > 0) {
|
||||
column.setPreferredWidth(columnWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void displayMessageOnTableStatusPanel(String message) {
|
||||
tableStatusPanelLabel.setText(message);
|
||||
tableStatusPanelLabel.setVisible(true);
|
||||
}
|
||||
|
||||
private void clearMessageOnTableStatusPanel() {
|
||||
tableStatusPanelLabel.setVisible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
@@ -749,7 +775,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
earliestCaseLabel = new javax.swing.JLabel();
|
||||
earliestCaseDate = new javax.swing.JLabel();
|
||||
tableStatusPanel = new javax.swing.JPanel();
|
||||
tableStatusPanelLabel = new javax.swing.JLabel();
|
||||
|
||||
rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
|
||||
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
|
||||
@@ -811,8 +836,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
.addGap(0, 16, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51));
|
||||
|
||||
javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel);
|
||||
tableContainerPanel.setLayout(tableContainerPanelLayout);
|
||||
tableContainerPanelLayout.setHorizontalGroup(
|
||||
@@ -825,20 +848,16 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
.addComponent(earliestCaseLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(earliestCaseDate)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
tableContainerPanelLayout.setVerticalGroup(
|
||||
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
|
||||
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 176, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0)
|
||||
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(earliestCaseLabel)
|
||||
.addComponent(earliestCaseDate))
|
||||
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
|
||||
.addGap(2, 2, 2)
|
||||
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(earliestCaseLabel)
|
||||
.addComponent(earliestCaseDate))
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, 0))
|
||||
@@ -857,7 +876,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
.addGap(0, 483, Short.MAX_VALUE)
|
||||
.addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(otherCasesPanelLayout.createSequentialGroup()
|
||||
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE)
|
||||
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0)))
|
||||
);
|
||||
|
||||
@@ -869,7 +888,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE)
|
||||
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@@ -879,8 +898,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) {
|
||||
int rowIndex = otherCasesTable.getSelectedRow();
|
||||
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex);
|
||||
if (selectedNode.isCentralRepoNode()) {
|
||||
enableCentralRepoActions = true;
|
||||
if (selectedNode instanceof OtherOccurrenceNodeInstanceData) {
|
||||
OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedNode;
|
||||
enableCentralRepoActions = instanceData.isCentralRepoNode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -904,20 +924,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
private javax.swing.JPanel tableContainerPanel;
|
||||
private javax.swing.JScrollPane tableScrollPane;
|
||||
private javax.swing.JPanel tableStatusPanel;
|
||||
private javax.swing.JLabel tableStatusPanelLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
* Used as a key to ensure we eliminate duplicates from the result set by
|
||||
* not overwriting CR correlation instances.
|
||||
*/
|
||||
static final class UniquePathKey {
|
||||
private static final class UniquePathKey {
|
||||
|
||||
private final String dataSourceID;
|
||||
private final String filePath;
|
||||
private final String type;
|
||||
|
||||
UniquePathKey(OtherOccurrenceNodeData nodeData) {
|
||||
UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) {
|
||||
super();
|
||||
dataSourceID = nodeData.getDeviceID();
|
||||
if (nodeData.getFilePath() != null) {
|
||||
@@ -931,10 +950,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof UniquePathKey) {
|
||||
UniquePathKey otherKey = (UniquePathKey)(other);
|
||||
return ( Objects.equals(otherKey.dataSourceID, this.dataSourceID)
|
||||
&& Objects.equals(otherKey.filePath, this.filePath)
|
||||
&& Objects.equals(otherKey.type, this.type));
|
||||
UniquePathKey otherKey = (UniquePathKey) (other);
|
||||
return (Objects.equals(otherKey.getDataSourceID(), this.getDataSourceID())
|
||||
&& Objects.equals(otherKey.getFilePath(), this.getFilePath())
|
||||
&& Objects.equals(otherKey.getType(), this.getType()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -944,7 +963,34 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
//int hash = 7;
|
||||
//hash = 67 * hash + this.dataSourceID.hashCode();
|
||||
//hash = 67 * hash + this.filePath.hashCode();
|
||||
return Objects.hash(dataSourceID, filePath, type);
|
||||
return Objects.hash(getDataSourceID(), getFilePath(), getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of this UniquePathKey.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file path for the UniquePathKey.
|
||||
*
|
||||
* @return the filePath
|
||||
*/
|
||||
String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data source id for the UniquePathKey.
|
||||
*
|
||||
* @return the dataSourceID
|
||||
*/
|
||||
String getDataSourceID() {
|
||||
return dataSourceID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+44
-20
@@ -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();
|
||||
|
||||
+3
-211
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Central Repository
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
@@ -17,217 +17,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.centralrepository.contentviewer;
|
||||
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
|
||||
/**
|
||||
* Class for populating the Other Occurrences tab
|
||||
* Marker interface for Other Occurrences nodes.
|
||||
*/
|
||||
class OtherOccurrenceNodeData {
|
||||
interface OtherOccurrenceNodeData {
|
||||
|
||||
// For now hard code the string for the central repo files type, since
|
||||
// getting it dynamically can fail.
|
||||
private static final String FILE_TYPE_STR = "Files";
|
||||
|
||||
private final String caseName;
|
||||
private String deviceID;
|
||||
private String dataSourceName;
|
||||
private final String filePath;
|
||||
private final String typeStr;
|
||||
private final CorrelationAttribute.Type type;
|
||||
private final String value;
|
||||
private TskData.FileKnown known;
|
||||
private String comment;
|
||||
|
||||
private AbstractFile originalAbstractFile = null;
|
||||
private CorrelationAttributeInstance originalCorrelationInstance = null;
|
||||
|
||||
/**
|
||||
* Create a node from a central repo instance.
|
||||
* @param instance The central repo instance
|
||||
* @param type The type of the instance
|
||||
* @param value The value of the instance
|
||||
*/
|
||||
OtherOccurrenceNodeData(CorrelationAttributeInstance instance, CorrelationAttribute.Type type, String value) {
|
||||
caseName = instance.getCorrelationCase().getDisplayName();
|
||||
deviceID = instance.getCorrelationDataSource().getDeviceID();
|
||||
dataSourceName = instance.getCorrelationDataSource().getName();
|
||||
filePath = instance.getFilePath();
|
||||
this.typeStr = type.getDisplayName();
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
known = instance.getKnownStatus();
|
||||
comment = instance.getComment();
|
||||
|
||||
originalCorrelationInstance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a node from an abstract file.
|
||||
* @param newFile The abstract file
|
||||
* @param autopsyCase The current case
|
||||
* @throws EamDbException
|
||||
*/
|
||||
OtherOccurrenceNodeData(AbstractFile newFile, Case autopsyCase) throws EamDbException {
|
||||
caseName = autopsyCase.getDisplayName();
|
||||
try {
|
||||
DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId());
|
||||
deviceID = dataSource.getDeviceId();
|
||||
dataSourceName = dataSource.getName();
|
||||
} catch (TskDataException | TskCoreException ex) {
|
||||
throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex);
|
||||
}
|
||||
|
||||
filePath = newFile.getParentPath() + newFile.getName();
|
||||
typeStr = FILE_TYPE_STR;
|
||||
this.type = null;
|
||||
value = newFile.getMd5Hash();
|
||||
known = newFile.getKnown();
|
||||
comment = "";
|
||||
|
||||
originalAbstractFile = newFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this node is a "file" type
|
||||
* @return true if it is a file type
|
||||
*/
|
||||
boolean isFileType() {
|
||||
return FILE_TYPE_STR.equals(typeStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the known status for this node
|
||||
* @param newKnownStatus The new known status
|
||||
*/
|
||||
void updateKnown(TskData.FileKnown newKnownStatus) {
|
||||
known = newKnownStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the comment for this node
|
||||
* @param newComment The new comment
|
||||
*/
|
||||
void updateComment(String newComment) {
|
||||
comment = newComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a central repo node.
|
||||
* @return true if this node was created from a central repo instance, false otherwise
|
||||
*/
|
||||
boolean isCentralRepoNode() {
|
||||
return (originalCorrelationInstance != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the saved instance plus type and value to make a new CorrelationAttribute.
|
||||
* Should only be called if isCentralRepoNode() is true.
|
||||
* @return the newly created CorrelationAttribute
|
||||
*/
|
||||
CorrelationAttribute createCorrelationAttribute() throws EamDbException {
|
||||
if (! isCentralRepoNode() ) {
|
||||
throw new EamDbException("Can not create CorrelationAttribute for non central repo node");
|
||||
}
|
||||
CorrelationAttribute attr = new CorrelationAttribute(type, value);
|
||||
attr.addInstance(originalCorrelationInstance);
|
||||
return attr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the case name
|
||||
* @return the case name
|
||||
*/
|
||||
String getCaseName() {
|
||||
return caseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the device ID
|
||||
* @return the device ID
|
||||
*/
|
||||
String getDeviceID() {
|
||||
return deviceID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data source name
|
||||
* @return the data source name
|
||||
*/
|
||||
String getDataSourceName() {
|
||||
return dataSourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file path
|
||||
* @return the file path
|
||||
*/
|
||||
String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type (as a string)
|
||||
* @return the type
|
||||
*/
|
||||
String getType() {
|
||||
return typeStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value (MD5 hash for files)
|
||||
* @return the value
|
||||
*/
|
||||
String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the known status
|
||||
* @return the known status
|
||||
*/
|
||||
TskData.FileKnown getKnown() {
|
||||
return known;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the comment
|
||||
* @return the comment
|
||||
*/
|
||||
String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backing abstract file.
|
||||
* Should only be called if isCentralRepoNode() is false
|
||||
* @return the original abstract file
|
||||
*/
|
||||
AbstractFile getAbstractFile() throws EamDbException {
|
||||
if (originalCorrelationInstance == null) {
|
||||
throw new EamDbException("AbstractFile is null");
|
||||
}
|
||||
return originalAbstractFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backing CorrelationAttributeInstance.
|
||||
* Should only be called if isCentralRepoNode() is true
|
||||
* @return the original CorrelationAttributeInstance
|
||||
* @throws EamDbException
|
||||
*/
|
||||
CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException {
|
||||
if (originalCorrelationInstance == null) {
|
||||
throw new EamDbException("CorrelationAttributeInstance is null");
|
||||
}
|
||||
return originalCorrelationInstance;
|
||||
}
|
||||
}
|
||||
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
|
||||
/**
|
||||
* Class for populating the Other Occurrences tab
|
||||
*/
|
||||
class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
|
||||
|
||||
// For now hard code the string for the central repo files type, since
|
||||
// getting it dynamically can fail.
|
||||
private static final String FILE_TYPE_STR = "Files";
|
||||
|
||||
private final String caseName;
|
||||
private String deviceID;
|
||||
private String dataSourceName;
|
||||
private final String filePath;
|
||||
private final String typeStr;
|
||||
private final String value;
|
||||
private TskData.FileKnown known;
|
||||
private String comment;
|
||||
|
||||
private AbstractFile originalAbstractFile = null;
|
||||
private CorrelationAttributeInstance originalCorrelationInstance = null;
|
||||
|
||||
/**
|
||||
* Create a node from a central repo instance.
|
||||
* @param instance The central repo instance
|
||||
* @param type The type of the instance
|
||||
* @param value The value of the instance
|
||||
*/
|
||||
OtherOccurrenceNodeInstanceData(CorrelationAttributeInstance instance, CorrelationAttributeInstance.Type type, String value) {
|
||||
caseName = instance.getCorrelationCase().getDisplayName();
|
||||
deviceID = instance.getCorrelationDataSource().getDeviceID();
|
||||
dataSourceName = instance.getCorrelationDataSource().getName();
|
||||
filePath = instance.getFilePath();
|
||||
this.typeStr = type.getDisplayName();
|
||||
this.value = value;
|
||||
known = instance.getKnownStatus();
|
||||
comment = instance.getComment();
|
||||
|
||||
originalCorrelationInstance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a node from an abstract file.
|
||||
* @param newFile The abstract file
|
||||
* @param autopsyCase The current case
|
||||
* @throws EamDbException
|
||||
*/
|
||||
OtherOccurrenceNodeInstanceData(AbstractFile newFile, Case autopsyCase) throws EamDbException {
|
||||
caseName = autopsyCase.getDisplayName();
|
||||
try {
|
||||
DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId());
|
||||
deviceID = dataSource.getDeviceId();
|
||||
dataSourceName = dataSource.getName();
|
||||
} catch (TskDataException | TskCoreException ex) {
|
||||
throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex);
|
||||
}
|
||||
|
||||
filePath = newFile.getParentPath() + newFile.getName();
|
||||
typeStr = FILE_TYPE_STR;
|
||||
value = newFile.getMd5Hash();
|
||||
known = newFile.getKnown();
|
||||
comment = "";
|
||||
|
||||
originalAbstractFile = newFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this node is a "file" type
|
||||
* @return true if it is a file type
|
||||
*/
|
||||
boolean isFileType() {
|
||||
return FILE_TYPE_STR.equals(typeStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the known status for this node
|
||||
* @param newKnownStatus The new known status
|
||||
*/
|
||||
void updateKnown(TskData.FileKnown newKnownStatus) {
|
||||
known = newKnownStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the comment for this node
|
||||
* @param newComment The new comment
|
||||
*/
|
||||
void updateComment(String newComment) {
|
||||
comment = newComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a central repo node.
|
||||
* @return true if this node was created from a central repo instance, false otherwise
|
||||
*/
|
||||
boolean isCentralRepoNode() {
|
||||
return (originalCorrelationInstance != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the saved instance plus type and value to make a new CorrelationAttribute.
|
||||
* Should only be called if isCentralRepoNode() is true.
|
||||
* @return the newly created CorrelationAttribute
|
||||
*/
|
||||
CorrelationAttributeInstance getCorrelationAttribute() throws EamDbException {
|
||||
if (! isCentralRepoNode() ) {
|
||||
throw new EamDbException("Can not create CorrelationAttribute for non central repo node");
|
||||
}
|
||||
return originalCorrelationInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the case name
|
||||
* @return the case name
|
||||
*/
|
||||
String getCaseName() {
|
||||
return caseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the device ID
|
||||
* @return the device ID
|
||||
*/
|
||||
String getDeviceID() {
|
||||
return deviceID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data source name
|
||||
* @return the data source name
|
||||
*/
|
||||
String getDataSourceName() {
|
||||
return dataSourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file path
|
||||
* @return the file path
|
||||
*/
|
||||
String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type (as a string)
|
||||
* @return the type
|
||||
*/
|
||||
String getType() {
|
||||
return typeStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value (MD5 hash for files)
|
||||
* @return the value
|
||||
*/
|
||||
String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the known status
|
||||
* @return the known status
|
||||
*/
|
||||
TskData.FileKnown getKnown() {
|
||||
return known;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the comment
|
||||
* @return the comment
|
||||
*/
|
||||
String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backing abstract file.
|
||||
* Should only be called if isCentralRepoNode() is false
|
||||
* @return the original abstract file
|
||||
*/
|
||||
AbstractFile getAbstractFile() throws EamDbException {
|
||||
if (originalCorrelationInstance == null) {
|
||||
throw new EamDbException("AbstractFile is null");
|
||||
}
|
||||
return originalAbstractFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backing CorrelationAttributeInstance.
|
||||
* Should only be called if isCentralRepoNode() is true
|
||||
* @return the original CorrelationAttributeInstance
|
||||
* @throws EamDbException
|
||||
*/
|
||||
CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException {
|
||||
if (originalCorrelationInstance == null) {
|
||||
throw new EamDbException("CorrelationAttributeInstance is null");
|
||||
}
|
||||
return originalCorrelationInstance;
|
||||
}
|
||||
}
|
||||
Executable
+34
@@ -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;
|
||||
}
|
||||
}
|
||||
+306
-245
File diff suppressed because it is too large
Load Diff
@@ -1,380 +0,0 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-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.centralrepository.datamodel;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
|
||||
/**
|
||||
* Represents a type and value pair that can be used for correlation.
|
||||
* CorrelationAttributeInstances store information about the actual
|
||||
* occurrences of the attribute.
|
||||
*/
|
||||
public class CorrelationAttribute implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String ID;
|
||||
private String correlationValue;
|
||||
private Type correlationType;
|
||||
private final List<CorrelationAttributeInstance> artifactInstances;
|
||||
|
||||
// Type ID's for Default Correlation Types
|
||||
public static final int FILES_TYPE_ID = 0;
|
||||
public static final int DOMAIN_TYPE_ID = 1;
|
||||
public static final int EMAIL_TYPE_ID = 2;
|
||||
public static final int PHONE_TYPE_ID = 3;
|
||||
public static final int USBID_TYPE_ID = 4;
|
||||
|
||||
/**
|
||||
* Load the default correlation types
|
||||
*
|
||||
* @throws EamDbException if the Type's dbTableName has invalid characters/format
|
||||
*/
|
||||
@Messages({"CorrelationType.FILES.displayName=Files",
|
||||
"CorrelationType.DOMAIN.displayName=Domains",
|
||||
"CorrelationType.EMAIL.displayName=Email Addresses",
|
||||
"CorrelationType.PHONE.displayName=Phone Numbers",
|
||||
"CorrelationType.USBID.displayName=USB Devices"})
|
||||
public static List<CorrelationAttribute.Type> getDefaultCorrelationTypes() throws EamDbException {
|
||||
List<CorrelationAttribute.Type> DEFAULT_CORRELATION_TYPES = new ArrayList<>();
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttribute.Type(FILES_TYPE_ID, Bundle.CorrelationType_FILES_displayName(), "file", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttribute.Type(DOMAIN_TYPE_ID, Bundle.CorrelationType_DOMAIN_displayName(), "domain", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttribute.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttribute.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttribute.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS
|
||||
return DEFAULT_CORRELATION_TYPES;
|
||||
}
|
||||
|
||||
public CorrelationAttribute(Type correlationType, String correlationValue) throws EamDbException {
|
||||
if(correlationValue == null) {
|
||||
throw new EamDbException ("Correlation value is null");
|
||||
}
|
||||
this.ID = "";
|
||||
this.correlationType = correlationType;
|
||||
// Lower-case all values to normalize and improve correlation hits, going forward make sure this makes sense for all correlation types
|
||||
this.correlationValue = correlationValue.toLowerCase();
|
||||
this.artifactInstances = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Boolean equals(CorrelationAttribute otherArtifact) {
|
||||
return ((this.getID().equals(otherArtifact.getID()))
|
||||
&& (this.getCorrelationType().equals(otherArtifact.getCorrelationType()))
|
||||
&& (this.getCorrelationValue().equals(otherArtifact.getCorrelationValue()))
|
||||
&& (this.getInstances().equals(otherArtifact.getInstances())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// NOTE: This string is currently being used in IngestEventsListener to detect if we have already seen
|
||||
// the value and type pair. Be careful if this method is changed.
|
||||
String result = this.getID()
|
||||
+ this.getCorrelationType().toString()
|
||||
+ this.getCorrelationValue();
|
||||
result = this.getInstances().stream().map((inst) -> inst.toString()).reduce(result, String::concat);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the ID
|
||||
*/
|
||||
public String getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ID the ID to set
|
||||
*/
|
||||
public void setID(String ID) {
|
||||
this.ID = ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the correlationValue
|
||||
*/
|
||||
public String getCorrelationValue() {
|
||||
return correlationValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param correlationValue the correlationValue to set
|
||||
*/
|
||||
public void setCorrelationValue(String correlationValue) {
|
||||
// Lower-case all values to normalize and improve correlation hits, going forward make sure this makes sense for all correlation types
|
||||
this.correlationValue = correlationValue.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the correlation Type
|
||||
*/
|
||||
public Type getCorrelationType() {
|
||||
return correlationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param correlationType the correlation Type to set
|
||||
*/
|
||||
public void setCorrelationType(Type correlationType) {
|
||||
this.correlationType = correlationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the List of artifactInstances; empty list of none have been
|
||||
* added.
|
||||
*/
|
||||
public List<CorrelationAttributeInstance> getInstances() {
|
||||
return new ArrayList<>(artifactInstances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of artifact instances
|
||||
*
|
||||
* @param artifactInstances the List of artifactInstances to set.
|
||||
*/
|
||||
public void setInstances(List<CorrelationAttributeInstance> artifactInstances) {
|
||||
this.artifactInstances.clear();
|
||||
if (null != artifactInstances) {
|
||||
this.artifactInstances.addAll(artifactInstances);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an artifact instance to the list
|
||||
*
|
||||
* @param artifactInstance the instance to add
|
||||
*/
|
||||
public void addInstance(CorrelationAttributeInstance artifactInstance) {
|
||||
this.artifactInstances.add(artifactInstance);
|
||||
}
|
||||
|
||||
public static class Type implements Serializable {
|
||||
|
||||
private int id;
|
||||
private String displayName;
|
||||
private String dbTableName;
|
||||
private Boolean supported;
|
||||
private Boolean enabled;
|
||||
private final String DB_NAMES_REGEX = "[a-z][a-z0-9_]*";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id Unique ID for this Correlation Type
|
||||
* @param displayName Name of this type displayed in the UI.
|
||||
* @param dbTableName Central repository db table where data of this type is stored.
|
||||
* Must start with a lowercase letter and only contain
|
||||
* lowercase letters, numbers, and '_' characters.
|
||||
* @param supported Is this Type currently supported
|
||||
* @param enabled Is this Type currently enabled.
|
||||
*/
|
||||
public Type(int id, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException {
|
||||
if(dbTableName == null) {
|
||||
throw new EamDbException("dbTableName is null");
|
||||
}
|
||||
this.id = id;
|
||||
this.displayName = displayName;
|
||||
this.dbTableName = dbTableName;
|
||||
this.supported = supported;
|
||||
this.enabled = enabled;
|
||||
if (!Pattern.matches(DB_NAMES_REGEX, dbTableName)) {
|
||||
throw new EamDbException("Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for custom types where we do not know the Type ID until
|
||||
* the row has been entered into the correlation_types table
|
||||
* in the central repository.
|
||||
*
|
||||
* @param displayName Name of this type displayed in the UI.
|
||||
* @param dbTableName Central repository db table where data of this type is stored
|
||||
* Must start with a lowercase letter and only contain
|
||||
* lowercase letters, numbers, and '_' characters.
|
||||
* @param supported Is this Type currently supported
|
||||
* @param enabled Is this Type currently enabled.
|
||||
*/
|
||||
public Type(String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException {
|
||||
this(-1, displayName, dbTableName, supported, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if 2 Type objects are equal
|
||||
*
|
||||
* @param that Type object for comparison.
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (this == that) {
|
||||
return true;
|
||||
} else if (!(that instanceof CorrelationAttribute.Type)) {
|
||||
return false;
|
||||
} else {
|
||||
return ((CorrelationAttribute.Type) that).sameType(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the content of this artifact type object is equivalent
|
||||
* to the content of another artifact type object.
|
||||
*
|
||||
* @param that the other type
|
||||
*
|
||||
* @return true if it is the same type
|
||||
*/
|
||||
private boolean sameType(CorrelationAttribute.Type that) {
|
||||
return this.id == that.getId()
|
||||
&& Objects.equals(this.supported, that.isSupported())
|
||||
&& Objects.equals(this.enabled, that.isEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 67 * hash + Objects.hashCode(this.id);
|
||||
hash = 67 * hash + Objects.hashCode(this.supported);
|
||||
hash = 67 * hash + Objects.hashCode(this.enabled);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append("(id=").append(getId());
|
||||
str.append(", displayName=").append(getDisplayName());
|
||||
str.append(", dbTableName=").append(getDbTableName());
|
||||
str.append(", supported=").append(isSupported().toString());
|
||||
str.append(", enabled=").append(isEnabled().toString());
|
||||
str.append(")");
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the id
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id the id to set
|
||||
*/
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this Artifact Type is supported.
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public Boolean isSupported() {
|
||||
return supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this Artifact Type as supported or not supported.
|
||||
*
|
||||
* @param supported the supported to set
|
||||
*/
|
||||
public void setSupported(Boolean supported) {
|
||||
this.supported = supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this Artifact Type is enabled.
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public Boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this Artifact Type as enabled or not enabled.
|
||||
*
|
||||
* @param enabled the enabled to set
|
||||
*/
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the displayName
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param displayName the displayName to set
|
||||
*/
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* To support having different database tables for each Type,
|
||||
* this field provides the prefix/suffix of the table name,
|
||||
* which allows us to automatically compute the table names
|
||||
* and index names.
|
||||
*
|
||||
* It is the prefix for the instances tables *_instances. (i.e. file_instances)
|
||||
* It is the suffix for the reference tables reference_*. (i.e. reference_file)
|
||||
*
|
||||
* When custom Types are added in the future, they are already supported
|
||||
* by just giving the desired value for the table name for each custom
|
||||
* Type. Possibly having all custom Types use a common table name.
|
||||
*
|
||||
* @return the dbTableName
|
||||
*/
|
||||
public String getDbTableName() {
|
||||
return dbTableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* To support having different database tables for each Type,
|
||||
* this field provides the prefix/suffix of the table name,
|
||||
* which allows us to automatically compute the table names
|
||||
* and index names.
|
||||
*
|
||||
* It is the prefix for the instances tables *_instances. (i.e. file_instances)
|
||||
* It is the suffix for the reference tables reference_*. (i.e. reference_file)
|
||||
*
|
||||
* When custom Types are added in the future, they are already supported
|
||||
* by just giving the desired value for the table name for each custom
|
||||
* Type. Possibly having all custom Types use a common table name. (i.e. custom_instances)
|
||||
*
|
||||
* @param dbTableName the dbTableName to set. Must start with lowercase letter
|
||||
* and can only contain lowercase letters, numbers, and '_' characters.
|
||||
*
|
||||
* @throws EamDbException if dbTableName contains invalid characters
|
||||
*/
|
||||
public void setDbTableName(String dbTableName) throws EamDbException {
|
||||
if (!Pattern.matches(DB_NAMES_REGEX, dbTableName)) {
|
||||
throw new EamDbException("Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); // NON-NLS
|
||||
}
|
||||
this.dbTableName = dbTableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
+323
-8
@@ -19,6 +19,10 @@
|
||||
package org.sleuthkit.autopsy.centralrepository.datamodel;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
@@ -37,6 +41,8 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int ID;
|
||||
private String correlationValue;
|
||||
private CorrelationAttributeInstance.Type correlationType;
|
||||
private CorrelationCase correlationCase;
|
||||
private CorrelationDataSource correlationDataSource;
|
||||
private String filePath;
|
||||
@@ -44,26 +50,50 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
private TskData.FileKnown knownStatus;
|
||||
|
||||
public CorrelationAttributeInstance(
|
||||
String correlationValue,
|
||||
CorrelationAttributeInstance.Type correlationType,
|
||||
CorrelationCase eamCase,
|
||||
CorrelationDataSource eamDataSource,
|
||||
String filePath
|
||||
) throws EamDbException {
|
||||
this(-1, eamCase, eamDataSource, filePath, null, TskData.FileKnown.UNKNOWN);
|
||||
this(correlationType, correlationValue, -1, eamCase, eamDataSource, filePath, null, TskData.FileKnown.UNKNOWN);
|
||||
}
|
||||
|
||||
|
||||
public CorrelationAttributeInstance(
|
||||
String correlationValue,
|
||||
CorrelationAttributeInstance.Type correlationType,
|
||||
CorrelationCase eamCase,
|
||||
CorrelationDataSource eamDataSource,
|
||||
String filePath,
|
||||
String comment,
|
||||
TskData.FileKnown knownStatus
|
||||
) throws EamDbException {
|
||||
this(-1, eamCase, eamDataSource, filePath, comment, knownStatus);
|
||||
this(correlationType, correlationValue, -1, eamCase, eamDataSource, filePath, comment, knownStatus);
|
||||
}
|
||||
|
||||
public CorrelationAttributeInstance(
|
||||
Type correlationType,
|
||||
String correlationValue,
|
||||
CorrelationCase correlationCase,
|
||||
CorrelationDataSource fromTSKDataSource,
|
||||
String string) throws EamDbException {
|
||||
this(correlationType, correlationValue, -1, correlationCase, fromTSKDataSource, string, "", TskData.FileKnown.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: Only used for when EamDB is NOT enabled.
|
||||
*
|
||||
* @param aType CorrelationAttributeInstance.Type
|
||||
* @param value correlation value
|
||||
*/
|
||||
public CorrelationAttributeInstance(Type aType, String value) throws EamDbException {
|
||||
this(aType, value, -1, null, null, "", "", TskData.FileKnown.UNKNOWN);
|
||||
}
|
||||
|
||||
CorrelationAttributeInstance(
|
||||
int ID,
|
||||
Type type,
|
||||
String value,
|
||||
int instanceId,
|
||||
CorrelationCase eamCase,
|
||||
CorrelationDataSource eamDataSource,
|
||||
String filePath,
|
||||
@@ -74,7 +104,13 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
throw new EamDbException("file path is null");
|
||||
}
|
||||
|
||||
this.ID = ID;
|
||||
if (value == null) {
|
||||
throw new EamDbException("correlation value is null");
|
||||
}
|
||||
|
||||
this.correlationType = type;
|
||||
this.correlationValue = value;
|
||||
this.ID = instanceId;
|
||||
this.correlationCase = eamCase;
|
||||
this.correlationDataSource = eamDataSource;
|
||||
// Lower case paths to normalize paths and improve correlation results, if this causes significant issues on case-sensitive file systems, remove
|
||||
@@ -102,11 +138,40 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
+ this.getComment();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the correlationValue
|
||||
*/
|
||||
public String getCorrelationValue() {
|
||||
return correlationValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param correlationValue the correlationValue to set
|
||||
*/
|
||||
public void setCorrelationValue(String correlationValue) {
|
||||
// Lower-case all values to normalize and improve correlation hits, going forward make sure this makes sense for all correlation types
|
||||
this.correlationValue = correlationValue.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the correlation Type
|
||||
*/
|
||||
public Type getCorrelationType() {
|
||||
return correlationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param correlationType the correlation Type to set
|
||||
*/
|
||||
public void setCorrelationType(Type correlationType) {
|
||||
this.correlationType = correlationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a database instance?
|
||||
*
|
||||
* @return True if the instance ID is greater or equal to zero; otherwise
|
||||
* false.
|
||||
* false.
|
||||
*/
|
||||
public boolean isDatabaseInstance() {
|
||||
return (ID >= 0);
|
||||
@@ -115,7 +180,7 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
/**
|
||||
* @return the database ID
|
||||
*/
|
||||
int getID() {
|
||||
public int getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@@ -169,9 +234,259 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
* as notable and should never be set to KNOWN.
|
||||
*
|
||||
* @param knownStatus Should be BAD if the item is tagged as notable,
|
||||
* UNKNOWN otherwise
|
||||
* UNKNOWN otherwise
|
||||
*/
|
||||
public void setKnownStatus(TskData.FileKnown knownStatus) {
|
||||
this.knownStatus = knownStatus;
|
||||
}
|
||||
|
||||
// Type ID's for Default Correlation Types
|
||||
public static final int FILES_TYPE_ID = 0;
|
||||
public static final int DOMAIN_TYPE_ID = 1;
|
||||
public static final int EMAIL_TYPE_ID = 2;
|
||||
public static final int PHONE_TYPE_ID = 3;
|
||||
public static final int USBID_TYPE_ID = 4;
|
||||
|
||||
/**
|
||||
* Load the default correlation types
|
||||
*
|
||||
* @throws EamDbException if the Type's dbTableName has invalid
|
||||
* characters/format
|
||||
*/
|
||||
@Messages({"CorrelationType.FILES.displayName=Files",
|
||||
"CorrelationType.DOMAIN.displayName=Domains",
|
||||
"CorrelationType.EMAIL.displayName=Email Addresses",
|
||||
"CorrelationType.PHONE.displayName=Phone Numbers",
|
||||
"CorrelationType.USBID.displayName=USB Devices"})
|
||||
public static List<CorrelationAttributeInstance.Type> getDefaultCorrelationTypes() throws EamDbException {
|
||||
List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = new ArrayList<>();
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(FILES_TYPE_ID, Bundle.CorrelationType_FILES_displayName(), "file", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(DOMAIN_TYPE_ID, Bundle.CorrelationType_DOMAIN_displayName(), "domain", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS
|
||||
return DEFAULT_CORRELATION_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correlation Types which determine which table to query in the CR
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static class Type implements Serializable { // NOPMD Avoid short class names like Type
|
||||
|
||||
private int typeId;
|
||||
private String displayName;
|
||||
private String dbTableName;
|
||||
private Boolean supported;
|
||||
private Boolean enabled;
|
||||
private final static String DB_NAMES_REGEX = "[a-z][a-z0-9_]*";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id Unique ID for this Correlation Type
|
||||
* @param displayName Name of this type displayed in the UI.
|
||||
* @param dbTableName Central repository db table where data of this
|
||||
* type is stored. Must start with a lowercase letter and only contain
|
||||
* lowercase letters, numbers, and '_' characters.
|
||||
* @param supported Is this Type currently supported
|
||||
* @param enabled Is this Type currently enabled.
|
||||
*/
|
||||
public Type(int typeId, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException {
|
||||
if (dbTableName == null) {
|
||||
throw new EamDbException("dbTableName is null");
|
||||
}
|
||||
this.typeId = typeId;
|
||||
this.displayName = displayName;
|
||||
this.dbTableName = dbTableName;
|
||||
this.supported = supported;
|
||||
this.enabled = enabled;
|
||||
if (!Pattern.matches(DB_NAMES_REGEX, dbTableName)) {
|
||||
throw new EamDbException("Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for custom types where we do not know the Type ID until
|
||||
* the row has been entered into the correlation_types table in the
|
||||
* central repository.
|
||||
*
|
||||
* @param displayName Name of this type displayed in the UI.
|
||||
* @param dbTableName Central repository db table where data of this
|
||||
* type is stored Must start with a lowercase letter and only contain
|
||||
* lowercase letters, numbers, and '_' characters.
|
||||
* @param supported Is this Type currently supported
|
||||
* @param enabled Is this Type currently enabled.
|
||||
*/
|
||||
public Type(String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException {
|
||||
this(-1, displayName, dbTableName, supported, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if 2 Type objects are equal
|
||||
*
|
||||
* @param that Type object for comparison.
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (this == that) {
|
||||
return true;
|
||||
} else if (!(that instanceof CorrelationAttributeInstance.Type)) {
|
||||
return false;
|
||||
} else {
|
||||
return ((CorrelationAttributeInstance.Type) that).sameType(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the content of this artifact type object is equivalent
|
||||
* to the content of another artifact type object.
|
||||
*
|
||||
* @param that the other type
|
||||
*
|
||||
* @return true if it is the same type
|
||||
*/
|
||||
private boolean sameType(CorrelationAttributeInstance.Type that) {
|
||||
return this.typeId == that.getId()
|
||||
&& Objects.equals(this.supported, that.isSupported())
|
||||
&& Objects.equals(this.enabled, that.isEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 67 * hash + Objects.hashCode(this.typeId);
|
||||
hash = 67 * hash + Objects.hashCode(this.supported);
|
||||
hash = 67 * hash + Objects.hashCode(this.enabled);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder(55);
|
||||
str.append("(id=")
|
||||
.append(getId())
|
||||
.append(", displayName=")
|
||||
.append(getDisplayName())
|
||||
.append(", dbTableName=")
|
||||
.append(getDbTableName())
|
||||
.append(", supported=")
|
||||
.append(isSupported().toString())
|
||||
.append(", enabled=")
|
||||
.append(isEnabled().toString())
|
||||
.append(')');
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the typeId
|
||||
*/
|
||||
public int getId() {
|
||||
return typeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id the typeId to set
|
||||
*/
|
||||
public void setId(int typeId) {
|
||||
this.typeId = typeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this Artifact Type is supported.
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public Boolean isSupported() {
|
||||
return supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this Artifact Type as supported or not supported.
|
||||
*
|
||||
* @param supported the supported to set
|
||||
*/
|
||||
public void setSupported(Boolean supported) {
|
||||
this.supported = supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this Artifact Type is enabled.
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public Boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this Artifact Type as enabled or not enabled.
|
||||
*
|
||||
* @param enabled the enabled to set
|
||||
*/
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the displayName
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param displayName the displayName to set
|
||||
*/
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* To support having different database tables for each Type, this field
|
||||
* provides the prefix/suffix of the table name, which allows us to
|
||||
* automatically compute the table names and index names.
|
||||
*
|
||||
* It is the prefix for the instances tables *_instances. (i.e.
|
||||
* file_instances) It is the suffix for the reference tables
|
||||
* reference_*. (i.e. reference_file)
|
||||
*
|
||||
* When custom Types are added in the future, they are already supported
|
||||
* by just giving the desired value for the table name for each custom
|
||||
* Type. Possibly having all custom Types use a common table name.
|
||||
*
|
||||
* @return the dbTableName
|
||||
*/
|
||||
public String getDbTableName() {
|
||||
return dbTableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* To support having different database tables for each Type, this field
|
||||
* provides the prefix/suffix of the table name, which allows us to
|
||||
* automatically compute the table names and index names.
|
||||
*
|
||||
* It is the prefix for the instances tables *_instances. (i.e.
|
||||
* file_instances) It is the suffix for the reference tables
|
||||
* reference_*. (i.e. reference_file)
|
||||
*
|
||||
* When custom Types are added in the future, they are already supported
|
||||
* by just giving the desired value for the table name for each custom
|
||||
* Type. Possibly having all custom Types use a common table name. (i.e.
|
||||
* custom_instances)
|
||||
*
|
||||
* @param dbTableName the dbTableName to set. Must start with lowercase
|
||||
* letter and can only contain lowercase letters, numbers, and '_'
|
||||
* characters.
|
||||
*
|
||||
* @throws EamDbException if dbTableName contains invalid characters
|
||||
*/
|
||||
public void setDbTableName(String dbTableName) throws EamDbException {
|
||||
if (!Pattern.matches(DB_NAMES_REGEX, dbTableName)) {
|
||||
throw new EamDbException("Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); // NON-NLS
|
||||
}
|
||||
this.dbTableName = dbTableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.sleuthkit.datamodel.TskData;
|
||||
*/
|
||||
public class EamArtifactUtil {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(EamArtifactUtil.class.getName());
|
||||
|
||||
public EamArtifactUtil() {
|
||||
@@ -55,18 +54,18 @@ public class EamArtifactUtil {
|
||||
* EamArtifact with a single EamArtifactInstance within. If not, return
|
||||
* null.
|
||||
*
|
||||
* @param bbArtifact BlackboardArtifact to examine
|
||||
* @param bbArtifact BlackboardArtifact to examine
|
||||
* @param addInstanceDetails If true, add instance details from bbArtifact
|
||||
* into the returned structure
|
||||
* @param checkEnabled If true, only create a CorrelationAttribute if
|
||||
* it is enabled
|
||||
* into the returned structure
|
||||
* @param checkEnabled If true, only create a CorrelationAttribute if it is
|
||||
* enabled
|
||||
*
|
||||
* @return List of EamArtifacts
|
||||
*/
|
||||
public static List<CorrelationAttribute> getCorrelationAttributeFromBlackboardArtifact(BlackboardArtifact bbArtifact,
|
||||
boolean addInstanceDetails, boolean checkEnabled) {
|
||||
public static List<CorrelationAttributeInstance> makeInstancesFromBlackboardArtifact(BlackboardArtifact bbArtifact,
|
||||
boolean checkEnabled) {
|
||||
|
||||
List<CorrelationAttribute> eamArtifacts = new ArrayList<>();
|
||||
List<CorrelationAttributeInstance> eamArtifacts = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// Cycle through the types and see if there is a correlation attribute that works
|
||||
@@ -74,9 +73,10 @@ public class EamArtifactUtil {
|
||||
//
|
||||
// @@@ This seems ineffecient. Instead of cycling based on correlation type, we should just
|
||||
// have switch based on artifact type
|
||||
for (CorrelationAttribute.Type aType : EamDb.getInstance().getDefinedCorrelationTypes()) {
|
||||
for (CorrelationAttributeInstance.Type aType : EamDb.getInstance().getDefinedCorrelationTypes()) {
|
||||
if ((checkEnabled && aType.isEnabled()) || !checkEnabled) {
|
||||
CorrelationAttribute correlationAttribute = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(aType, bbArtifact);
|
||||
// Now always adds the instance details associated with this occurance.
|
||||
CorrelationAttributeInstance correlationAttribute = EamArtifactUtil.makeInstanceFromBlackboardArtifact(aType, bbArtifact);
|
||||
if (correlationAttribute != null) {
|
||||
eamArtifacts.add(correlationAttribute);
|
||||
}
|
||||
@@ -87,42 +87,6 @@ public class EamArtifactUtil {
|
||||
return eamArtifacts;
|
||||
}
|
||||
|
||||
// if they asked for it, add the instance details associated with this occurance.
|
||||
if (!eamArtifacts.isEmpty() && addInstanceDetails) {
|
||||
try {
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
AbstractFile bbSourceFile = currentCase.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID());
|
||||
if (null == bbSourceFile) {
|
||||
//@@@ Log this
|
||||
return eamArtifacts;
|
||||
}
|
||||
|
||||
// make an instance for the BB source file
|
||||
CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows());
|
||||
if (null == correlationCase) {
|
||||
correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows());
|
||||
}
|
||||
CorrelationAttributeInstance eamInstance = new CorrelationAttributeInstance(
|
||||
correlationCase,
|
||||
CorrelationDataSource.fromTSKDataSource(correlationCase, bbSourceFile.getDataSource()),
|
||||
bbSourceFile.getParentPath() + bbSourceFile.getName(),
|
||||
"",
|
||||
TskData.FileKnown.UNKNOWN
|
||||
);
|
||||
|
||||
// add the instance details
|
||||
for (CorrelationAttribute eamArtifact : eamArtifacts) {
|
||||
eamArtifact.addInstance(eamInstance);
|
||||
}
|
||||
} catch (TskCoreException | EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS
|
||||
return eamArtifacts;
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Case is closed.", ex); // NON-NLS
|
||||
return eamArtifacts;
|
||||
}
|
||||
}
|
||||
|
||||
return eamArtifacts;
|
||||
}
|
||||
|
||||
@@ -131,12 +95,12 @@ public class EamArtifactUtil {
|
||||
* based on the data in the blackboard artifact.
|
||||
*
|
||||
* @param correlationType The Central Repository artifact type to create
|
||||
* @param bbArtifact The blackboard artifact to pull data from
|
||||
* @param bbArtifact The blackboard artifact to pull data from
|
||||
*
|
||||
* @return the new EamArtifact, or null if one was not created because
|
||||
* bbArtifact did not contain the needed data
|
||||
* bbArtifact did not contain the needed data
|
||||
*/
|
||||
private static CorrelationAttribute getCorrelationAttributeFromBlackboardArtifact(CorrelationAttribute.Type correlationType,
|
||||
private static CorrelationAttributeInstance makeInstanceFromBlackboardArtifact(CorrelationAttributeInstance.Type correlationType,
|
||||
BlackboardArtifact bbArtifact) throws EamDbException {
|
||||
String value = null;
|
||||
int artifactTypeID = bbArtifact.getArtifactTypeID();
|
||||
@@ -147,10 +111,10 @@ public class EamArtifactUtil {
|
||||
BlackboardAttribute attribute = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
|
||||
if (attribute != null) {
|
||||
BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
|
||||
return EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(correlationType, associatedArtifact);
|
||||
return EamArtifactUtil.makeInstanceFromBlackboardArtifact(correlationType, associatedArtifact);
|
||||
}
|
||||
|
||||
} else if (correlationType.getId() == CorrelationAttribute.EMAIL_TYPE_ID
|
||||
} else if (correlationType.getId() == CorrelationAttributeInstance.EMAIL_TYPE_ID
|
||||
&& BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() == artifactTypeID) {
|
||||
|
||||
BlackboardAttribute setNameAttr = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
|
||||
@@ -158,7 +122,7 @@ public class EamArtifactUtil {
|
||||
&& EamArtifactUtil.getEmailAddressAttrString().equals(setNameAttr.getValueString())) {
|
||||
value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD)).getValueString();
|
||||
}
|
||||
} else if (correlationType.getId() == CorrelationAttribute.DOMAIN_TYPE_ID
|
||||
} else if (correlationType.getId() == CorrelationAttributeInstance.DOMAIN_TYPE_ID
|
||||
&& (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID() == artifactTypeID
|
||||
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID() == artifactTypeID
|
||||
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == artifactTypeID
|
||||
@@ -166,7 +130,7 @@ public class EamArtifactUtil {
|
||||
|
||||
// Lower-case this to normalize domains
|
||||
value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN)).getValueString();
|
||||
} else if (correlationType.getId() == CorrelationAttribute.PHONE_TYPE_ID
|
||||
} else if (correlationType.getId() == CorrelationAttributeInstance.PHONE_TYPE_ID
|
||||
&& (BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID() == artifactTypeID
|
||||
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() == artifactTypeID
|
||||
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == artifactTypeID)) {
|
||||
@@ -195,7 +159,7 @@ public class EamArtifactUtil {
|
||||
}
|
||||
}
|
||||
|
||||
} else if (correlationType.getId() == CorrelationAttribute.USBID_TYPE_ID
|
||||
} else if (correlationType.getId() == CorrelationAttributeInstance.USBID_TYPE_ID
|
||||
&& BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID() == artifactTypeID) {
|
||||
|
||||
value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID)).getValueString();
|
||||
@@ -210,12 +174,54 @@ public class EamArtifactUtil {
|
||||
}
|
||||
|
||||
if (null != value) {
|
||||
return new CorrelationAttribute(correlationType, value);
|
||||
return makeCorrelationAttributeInstanceUsingTypeValue(bbArtifact, correlationType, value);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the determined type and vallue, then looks up instance details to
|
||||
* create proper CorrelationAttributeInstance.
|
||||
*
|
||||
* @param bbArtifact the blackboard artifatc
|
||||
* @param correlationType the given type
|
||||
* @param value the artifact value
|
||||
* @return CorrelationAttributeInstance from details
|
||||
*/
|
||||
private static CorrelationAttributeInstance makeCorrelationAttributeInstanceUsingTypeValue(BlackboardArtifact bbArtifact, CorrelationAttributeInstance.Type correlationType, String value) {
|
||||
try {
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
AbstractFile bbSourceFile = currentCase.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID());
|
||||
if (null == bbSourceFile) {
|
||||
logger.log(Level.SEVERE, "Error creating artifact instance. Abstract File was null."); // NON-NLS
|
||||
return null;
|
||||
}
|
||||
|
||||
// make an instance for the BB source file
|
||||
CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows());
|
||||
if (null == correlationCase) {
|
||||
correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows());
|
||||
}
|
||||
return new CorrelationAttributeInstance(
|
||||
value,
|
||||
correlationType,
|
||||
correlationCase,
|
||||
CorrelationDataSource.fromTSKDataSource(correlationCase, bbSourceFile.getDataSource()),
|
||||
bbSourceFile.getParentPath() + bbSourceFile.getName(),
|
||||
"",
|
||||
TskData.FileKnown.UNKNOWN
|
||||
);
|
||||
|
||||
} catch (TskCoreException | EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS
|
||||
return null;
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Case is closed.", ex); // NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve CorrelationAttribute from the given Content.
|
||||
*
|
||||
@@ -223,7 +229,7 @@ public class EamArtifactUtil {
|
||||
*
|
||||
* @return The new CorrelationAttribute, or null if retrieval failed.
|
||||
*/
|
||||
public static CorrelationAttribute getCorrelationAttributeFromContent(Content content) {
|
||||
public static CorrelationAttributeInstance getInstanceFromContent(Content content) {
|
||||
|
||||
if (!(content instanceof AbstractFile)) {
|
||||
return null;
|
||||
@@ -235,15 +241,14 @@ public class EamArtifactUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
CorrelationAttribute correlationAttribute;
|
||||
CorrelationAttribute.Type type;
|
||||
CorrelationAttributeInstance.Type type;
|
||||
CorrelationCase correlationCase;
|
||||
CorrelationDataSource correlationDataSource;
|
||||
String value;
|
||||
String filePath;
|
||||
|
||||
|
||||
try {
|
||||
type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
|
||||
type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
|
||||
correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows());
|
||||
if (null == correlationCase) {
|
||||
correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows());
|
||||
@@ -258,9 +263,10 @@ public class EamArtifactUtil {
|
||||
logger.log(Level.SEVERE, "Case is closed.", ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
CorrelationAttributeInstance correlationAttributeInstance;
|
||||
try {
|
||||
correlationAttribute = EamDb.getInstance().getCorrelationAttribute(type, correlationCase, correlationDataSource, value, filePath);
|
||||
correlationAttributeInstance = EamDb.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, value, filePath);
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.WARNING, String.format(
|
||||
"Correlation attribute could not be retrieved for '%s' (id=%d): %s",
|
||||
@@ -268,7 +274,7 @@ public class EamArtifactUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
return correlationAttribute;
|
||||
return correlationAttributeInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,7 +290,7 @@ public class EamArtifactUtil {
|
||||
*
|
||||
* @return The new EamArtifact or null if creation failed
|
||||
*/
|
||||
public static CorrelationAttribute makeCorrelationAttributeFromContent(Content content) {
|
||||
public static CorrelationAttributeInstance makeInstanceFromContent(Content content) {
|
||||
|
||||
if (!(content instanceof AbstractFile)) {
|
||||
return null;
|
||||
@@ -302,20 +308,20 @@ public class EamArtifactUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
CorrelationAttribute eamArtifact;
|
||||
try {
|
||||
CorrelationAttribute.Type filesType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
|
||||
eamArtifact = new CorrelationAttribute(filesType, af.getMd5Hash());
|
||||
CorrelationAttributeInstance.Type filesType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
|
||||
|
||||
CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows());
|
||||
if (null == correlationCase) {
|
||||
correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows());
|
||||
}
|
||||
CorrelationAttributeInstance cei = new CorrelationAttributeInstance(
|
||||
return new CorrelationAttributeInstance(
|
||||
filesType,
|
||||
af.getMd5Hash(),
|
||||
correlationCase,
|
||||
CorrelationDataSource.fromTSKDataSource(correlationCase, af.getDataSource()),
|
||||
af.getParentPath() + af.getName());
|
||||
eamArtifact.addInstance(cei);
|
||||
return eamArtifact;
|
||||
|
||||
} catch (TskCoreException | EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error making correlation attribute.", ex);
|
||||
return null;
|
||||
@@ -332,7 +338,7 @@ public class EamArtifactUtil {
|
||||
* @param file The file to test
|
||||
*
|
||||
* @return true if the file should be added to the central repo, false
|
||||
* otherwise
|
||||
* otherwise
|
||||
*/
|
||||
public static boolean isSupportedAbstractFileType(AbstractFile file) {
|
||||
if (file == null) {
|
||||
|
||||
@@ -233,7 +233,7 @@ public interface EamDb {
|
||||
*
|
||||
* @param eamArtifact The artifact to add
|
||||
*/
|
||||
void addArtifact(CorrelationAttribute eamArtifact) throws EamDbException;
|
||||
void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Retrieves eamArtifact instances from the database that are associated
|
||||
@@ -244,7 +244,7 @@ public interface EamDb {
|
||||
*
|
||||
* @return List of artifact instances for a given type/value
|
||||
*/
|
||||
List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException;
|
||||
List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Retrieves eamArtifact instances from the database that are associated
|
||||
@@ -257,7 +257,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
List<CorrelationAttributeInstance> getArtifactInstancesByPath(CorrelationAttribute.Type aType, String filePath) throws EamDbException;
|
||||
List<CorrelationAttributeInstance> getArtifactInstancesByPath(CorrelationAttributeInstance.Type aType, String filePath) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Retrieves number of artifact instances in the database that are
|
||||
@@ -269,7 +269,7 @@ public interface EamDb {
|
||||
* @return Number of artifact instances having ArtifactType and
|
||||
* ArtifactValue.
|
||||
*/
|
||||
Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException;
|
||||
Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Calculate the percentage of data sources that have this attribute value.
|
||||
@@ -278,7 +278,7 @@ public interface EamDb {
|
||||
*
|
||||
* @return Int between 0 and 100
|
||||
*/
|
||||
int getFrequencyPercentage(CorrelationAttribute corAttr) throws EamDbException;
|
||||
int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Retrieves number of unique caseDisplayName / dataSource tuples in the
|
||||
@@ -290,7 +290,7 @@ public interface EamDb {
|
||||
*
|
||||
* @return Number of unique tuples
|
||||
*/
|
||||
Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException;
|
||||
Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Retrieves number of data sources in the database.
|
||||
@@ -314,18 +314,18 @@ public interface EamDb {
|
||||
|
||||
/**
|
||||
* Adds an eamArtifact to an internal list to be later added to DB. Artifact
|
||||
* can have 1 or more Artifact Instances. Insert will be triggered by a
|
||||
* threshold or a call to bulkInsertArtifacts().
|
||||
can have 1 or more Artifact Instances. Insert will be triggered by a
|
||||
threshold or a call to commitAttributeInstancesBulk().
|
||||
*
|
||||
* @param eamArtifact The artifact to add
|
||||
*/
|
||||
void prepareBulkArtifact(CorrelationAttribute eamArtifact) throws EamDbException;
|
||||
void addAttributeInstanceBulk(CorrelationAttributeInstance eamArtifact) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Executes a bulk insert of the eamArtifacts added from the
|
||||
* prepareBulkArtifact() method
|
||||
addAttributeInstanceBulk() method
|
||||
*/
|
||||
void bulkInsertArtifacts() throws EamDbException;
|
||||
void commitAttributeInstancesBulk() throws EamDbException;
|
||||
|
||||
/**
|
||||
* Executes a bulk insert of the cases
|
||||
@@ -341,7 +341,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
void updateAttributeInstanceComment(CorrelationAttribute eamArtifact) throws EamDbException;
|
||||
void updateAttributeInstanceComment(CorrelationAttributeInstance eamArtifact) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Find a correlation attribute in the Central Repository database given the
|
||||
@@ -357,7 +357,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
CorrelationAttribute getCorrelationAttribute(CorrelationAttribute.Type type, CorrelationCase correlationCase,
|
||||
CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase,
|
||||
CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException;
|
||||
|
||||
/**
|
||||
@@ -367,7 +367,7 @@ public interface EamDb {
|
||||
* @param eamArtifact Artifact containing exactly one (1) ArtifactInstance.
|
||||
* @param knownStatus The status to change the artifact to
|
||||
*/
|
||||
void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException;
|
||||
void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Gets list of matching eamArtifact instances that have knownStatus =
|
||||
@@ -378,7 +378,7 @@ public interface EamDb {
|
||||
*
|
||||
* @return List with 0 or more matching eamArtifact instances.
|
||||
*/
|
||||
List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException;
|
||||
List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Gets list of matching eamArtifact instances that have knownStatus =
|
||||
@@ -388,7 +388,7 @@ public interface EamDb {
|
||||
* @return List with 0 or more matching eamArtifact instances.
|
||||
* @throws EamDbException
|
||||
*/
|
||||
List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException;
|
||||
List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType) throws EamDbException;
|
||||
/**
|
||||
* Count matching eamArtifacts instances that have knownStatus = "Bad".
|
||||
*
|
||||
@@ -397,7 +397,7 @@ public interface EamDb {
|
||||
*
|
||||
* @return Number of matching eamArtifacts
|
||||
*/
|
||||
Long getCountArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException;
|
||||
Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Gets list of distinct case display names, where each case has 1+ Artifact
|
||||
@@ -411,7 +411,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
List<String> getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException;
|
||||
List<String> getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Remove a reference set and all values contained in it.
|
||||
@@ -483,7 +483,7 @@ public interface EamDb {
|
||||
*
|
||||
* @return Global known status of the artifact
|
||||
*/
|
||||
boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException;
|
||||
boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Add a new organization
|
||||
@@ -577,7 +577,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
List<EamGlobalSet> getAllReferenceSets(CorrelationAttribute.Type correlationType) throws EamDbException;
|
||||
List<EamGlobalSet> getAllReferenceSets(CorrelationAttributeInstance.Type correlationType) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Add a new reference instance
|
||||
@@ -588,7 +588,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttribute.Type correlationType) throws EamDbException;
|
||||
void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttributeInstance.Type correlationType) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Insert the bulk collection of Global File Instances
|
||||
@@ -599,7 +599,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
void bulkInsertReferenceTypeEntries(Set<EamGlobalFileInstance> globalInstances, CorrelationAttribute.Type contentType) throws EamDbException;
|
||||
void bulkInsertReferenceTypeEntries(Set<EamGlobalFileInstance> globalInstances, CorrelationAttributeInstance.Type contentType) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Get all reference entries having a given correlation type and value
|
||||
@@ -611,7 +611,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
List<EamGlobalFileInstance> getReferenceInstancesByTypeValue(CorrelationAttribute.Type aType, String aValue) throws EamDbException;
|
||||
List<EamGlobalFileInstance> getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Add a new EamArtifact.Type to the db.
|
||||
@@ -622,7 +622,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
public int newCorrelationType(CorrelationAttribute.Type newType) throws EamDbException;
|
||||
int newCorrelationType(CorrelationAttributeInstance.Type newType) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Get the list of EamArtifact.Type's that are defined in the DB and can be
|
||||
@@ -633,7 +633,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
public List<CorrelationAttribute.Type> getDefinedCorrelationTypes() throws EamDbException;
|
||||
List<CorrelationAttributeInstance.Type> getDefinedCorrelationTypes() throws EamDbException;
|
||||
|
||||
/**
|
||||
* Get the list of enabled EamArtifact.Type's that will be used to correlate
|
||||
@@ -644,7 +644,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
public List<CorrelationAttribute.Type> getEnabledCorrelationTypes() throws EamDbException;
|
||||
List<CorrelationAttributeInstance.Type> getEnabledCorrelationTypes() throws EamDbException;
|
||||
|
||||
/**
|
||||
* Get the list of supported EamArtifact.Type's that can be used to
|
||||
@@ -655,7 +655,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
public List<CorrelationAttribute.Type> getSupportedCorrelationTypes() throws EamDbException;
|
||||
List<CorrelationAttributeInstance.Type> getSupportedCorrelationTypes() throws EamDbException;
|
||||
|
||||
/**
|
||||
* Update a EamArtifact.Type.
|
||||
@@ -664,7 +664,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
public void updateCorrelationType(CorrelationAttribute.Type aType) throws EamDbException;
|
||||
void updateCorrelationType(CorrelationAttributeInstance.Type aType) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Get the EamArtifact.Type that has the given Type.Id.
|
||||
@@ -675,7 +675,7 @@ public interface EamDb {
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
public CorrelationAttribute.Type getCorrelationTypeById(int typeId) throws EamDbException;
|
||||
CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Upgrade the schema of the database (if needed)
|
||||
@@ -704,7 +704,7 @@ public interface EamDb {
|
||||
* @param instanceTableCallback callback to process the instance
|
||||
* @throws EamDbException
|
||||
*/
|
||||
void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException;
|
||||
void processInstanceTable(CorrelationAttributeInstance.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException;
|
||||
|
||||
/**
|
||||
* Process the Artifact instance in the EamDb
|
||||
@@ -714,6 +714,6 @@ public interface EamDb {
|
||||
* @param whereClause query string to execute
|
||||
* @throws EamDbException
|
||||
*/
|
||||
void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException;
|
||||
void processInstanceTableWhere(CorrelationAttributeInstance.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException;
|
||||
|
||||
}
|
||||
|
||||
@@ -104,9 +104,9 @@ public class EamDbUtil {
|
||||
String sql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?)";
|
||||
|
||||
try {
|
||||
List<CorrelationAttribute.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttribute.getDefaultCorrelationTypes();
|
||||
List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes();
|
||||
preparedStatement = conn.prepareStatement(sql);
|
||||
for (CorrelationAttribute.Type newType : DEFAULT_CORRELATION_TYPES) {
|
||||
for (CorrelationAttributeInstance.Type newType : DEFAULT_CORRELATION_TYPES) {
|
||||
preparedStatement.setInt(1, newType.getId());
|
||||
preparedStatement.setString(2, newType.getDisplayName());
|
||||
preparedStatement.setString(3, newType.getDbTableName());
|
||||
@@ -346,7 +346,7 @@ public class EamDbUtil {
|
||||
*
|
||||
* @return Instance table name for this Type.
|
||||
*/
|
||||
public static String correlationTypeToInstanceTableName(CorrelationAttribute.Type type) {
|
||||
public static String correlationTypeToInstanceTableName(CorrelationAttributeInstance.Type type) {
|
||||
return type.getDbTableName() + "_instances";
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ public class EamDbUtil {
|
||||
*
|
||||
* @return Reference table name for this Type.
|
||||
*/
|
||||
public static String correlationTypeToReferenceTableName(CorrelationAttribute.Type type) {
|
||||
public static String correlationTypeToReferenceTableName(CorrelationAttributeInstance.Type type) {
|
||||
return "reference_" + type.getDbTableName();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public class EamGlobalSet {
|
||||
private String version;
|
||||
private TskData.FileKnown fileKnownStatus;
|
||||
private boolean isReadOnly;
|
||||
private CorrelationAttribute.Type type;
|
||||
private CorrelationAttributeInstance.Type type;
|
||||
private LocalDate importDate;
|
||||
|
||||
public EamGlobalSet(
|
||||
@@ -42,7 +42,7 @@ public class EamGlobalSet {
|
||||
String version,
|
||||
TskData.FileKnown knownStatus,
|
||||
boolean isReadOnly,
|
||||
CorrelationAttribute.Type type,
|
||||
CorrelationAttributeInstance.Type type,
|
||||
LocalDate importDate) {
|
||||
this.globalSetID = globalSetID;
|
||||
this.orgID = orgID;
|
||||
@@ -60,7 +60,7 @@ public class EamGlobalSet {
|
||||
String version,
|
||||
TskData.FileKnown knownStatus,
|
||||
boolean isReadOnly,
|
||||
CorrelationAttribute.Type type,
|
||||
CorrelationAttributeInstance.Type type,
|
||||
LocalDate importDate) {
|
||||
this(-1, orgID, setName, version, knownStatus, isReadOnly, type, importDate);
|
||||
}
|
||||
@@ -83,7 +83,7 @@ public class EamGlobalSet {
|
||||
String version,
|
||||
TskData.FileKnown knownStatus,
|
||||
boolean isReadOnly,
|
||||
CorrelationAttribute.Type type) {
|
||||
CorrelationAttributeInstance.Type type) {
|
||||
this(-1, orgID, setName, version, knownStatus, isReadOnly, type, LocalDate.now());
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ public class EamGlobalSet {
|
||||
*
|
||||
* @return the type (files, phone numbers, etc)
|
||||
*/
|
||||
public CorrelationAttribute.Type getType() {
|
||||
public CorrelationAttributeInstance.Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ public class EamGlobalSet {
|
||||
*
|
||||
* @param type
|
||||
*/
|
||||
void setType(CorrelationAttribute.Type type) {
|
||||
void setType(CorrelationAttributeInstance.Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.centralrepository.datamodel;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
@@ -115,10 +114,10 @@ final class PostgresEamDb extends AbstractSqlEamDb {
|
||||
|
||||
String instancesTemplate = "TRUNCATE TABLE %s_instances RESTART IDENTITY CASCADE";
|
||||
String referencesTemplate = "TRUNCATE TABLE reference_%s RESTART IDENTITY CASCADE";
|
||||
for (CorrelationAttribute.Type type : defaultCorrelationTypes) {
|
||||
for (CorrelationAttributeInstance.Type type : defaultCorrelationTypes) {
|
||||
dropContent.executeUpdate(String.format(instancesTemplate, type.getDbTableName()));
|
||||
// FUTURE: support other reference types
|
||||
if (type.getId() == CorrelationAttribute.FILES_TYPE_ID) {
|
||||
if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
|
||||
dropContent.executeUpdate(String.format(referencesTemplate, type.getDbTableName()));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -446,11 +446,11 @@ public final class PostgresEamDbSettings {
|
||||
stmt.execute(createDbInfoTable.toString());
|
||||
|
||||
// Create a separate instance and reference table for each correlation type
|
||||
List<CorrelationAttribute.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttribute.getDefaultCorrelationTypes();
|
||||
List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes();
|
||||
|
||||
String reference_type_dbname;
|
||||
String instance_type_dbname;
|
||||
for (CorrelationAttribute.Type type : DEFAULT_CORRELATION_TYPES) {
|
||||
for (CorrelationAttributeInstance.Type type : DEFAULT_CORRELATION_TYPES) {
|
||||
reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type);
|
||||
instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type);
|
||||
|
||||
@@ -461,7 +461,7 @@ public final class PostgresEamDbSettings {
|
||||
stmt.execute(String.format(instancesIdx4, instance_type_dbname, instance_type_dbname));
|
||||
|
||||
// FUTURE: allow more than the FILES type
|
||||
if (type.getId() == CorrelationAttribute.FILES_TYPE_ID) {
|
||||
if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
|
||||
stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname));
|
||||
stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname));
|
||||
stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname));
|
||||
|
||||
@@ -125,10 +125,10 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
|
||||
String instancesTemplate = "DELETE FROM %s_instances";
|
||||
String referencesTemplate = "DELETE FROM global_files";
|
||||
for (CorrelationAttribute.Type type : defaultCorrelationTypes) {
|
||||
for (CorrelationAttributeInstance.Type type : defaultCorrelationTypes) {
|
||||
dropContent.executeUpdate(String.format(instancesTemplate, type.getDbTableName()));
|
||||
// FUTURE: support other reference types
|
||||
if (type.getId() == CorrelationAttribute.FILES_TYPE_ID) {
|
||||
if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
|
||||
dropContent.executeUpdate(String.format(referencesTemplate, type.getDbTableName()));
|
||||
}
|
||||
}
|
||||
@@ -428,10 +428,10 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @param eamArtifact The artifact to add
|
||||
*/
|
||||
@Override
|
||||
public void addArtifact(CorrelationAttribute eamArtifact) throws EamDbException {
|
||||
public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws EamDbException {
|
||||
try {
|
||||
acquireExclusiveLock();
|
||||
super.addArtifact(eamArtifact);
|
||||
super.addArtifactInstance(eamArtifact);
|
||||
} finally {
|
||||
releaseExclusiveLock();
|
||||
}
|
||||
@@ -447,7 +447,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @return List of artifact instances for a given type/value
|
||||
*/
|
||||
@Override
|
||||
public List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException {
|
||||
public List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getArtifactInstancesByTypeValue(aType, value);
|
||||
@@ -468,7 +468,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public List<CorrelationAttributeInstance> getArtifactInstancesByPath(CorrelationAttribute.Type aType, String filePath) throws EamDbException {
|
||||
public List<CorrelationAttributeInstance> getArtifactInstancesByPath(CorrelationAttributeInstance.Type aType, String filePath) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getArtifactInstancesByPath(aType, filePath);
|
||||
@@ -489,7 +489,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException {
|
||||
public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getCountArtifactInstancesByTypeValue(aType, value);
|
||||
@@ -499,7 +499,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFrequencyPercentage(CorrelationAttribute corAttr) throws EamDbException {
|
||||
public int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getFrequencyPercentage(corAttr);
|
||||
@@ -520,7 +520,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException {
|
||||
public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getCountUniqueCaseDataSourceTuplesHavingTypeValue(aType, value);
|
||||
@@ -562,13 +562,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
|
||||
/**
|
||||
* Executes a bulk insert of the eamArtifacts added from the
|
||||
* prepareBulkArtifact() method
|
||||
addAttributeInstanceBulk() method
|
||||
*/
|
||||
@Override
|
||||
public void bulkInsertArtifacts() throws EamDbException {
|
||||
public void commitAttributeInstancesBulk() throws EamDbException {
|
||||
try {
|
||||
acquireExclusiveLock();
|
||||
super.bulkInsertArtifacts();
|
||||
super.commitAttributeInstancesBulk();
|
||||
} finally {
|
||||
releaseExclusiveLock();
|
||||
}
|
||||
@@ -598,10 +598,10 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* KNOWN
|
||||
*/
|
||||
@Override
|
||||
public void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException {
|
||||
public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException {
|
||||
try {
|
||||
acquireExclusiveLock();
|
||||
super.setArtifactInstanceKnownStatus(eamArtifact, knownStatus);
|
||||
super.setAttributeInstanceKnownStatus(eamArtifact, knownStatus);
|
||||
} finally {
|
||||
releaseExclusiveLock();
|
||||
}
|
||||
@@ -617,7 +617,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @return List with 0 or more matching eamArtifact instances.
|
||||
*/
|
||||
@Override
|
||||
public List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException {
|
||||
public List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getArtifactInstancesKnownBad(aType, value);
|
||||
@@ -636,7 +636,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException {
|
||||
public List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getArtifactInstancesKnownBad(aType);
|
||||
@@ -654,7 +654,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @return Number of matching eamArtifacts
|
||||
*/
|
||||
@Override
|
||||
public Long getCountArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException {
|
||||
public Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getCountArtifactInstancesKnownBad(aType, value);
|
||||
@@ -676,7 +676,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public List<String> getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException {
|
||||
public List<String> getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getListCasesHavingArtifactInstancesKnownBad(aType, value);
|
||||
@@ -727,7 +727,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException {
|
||||
public void processInstanceTable(CorrelationAttributeInstance.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
super.processInstanceTable(type, instanceTableCallback);
|
||||
@@ -744,7 +744,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException {
|
||||
public void processInstanceTableWhere(CorrelationAttributeInstance.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
super.processInstanceTableWhere(type, whereClause, instanceTableCallback);
|
||||
@@ -782,7 +782,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @return Global known status of the artifact
|
||||
*/
|
||||
@Override
|
||||
public boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException {
|
||||
public boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.isArtifactKnownBadByReference(aType, value);
|
||||
@@ -914,7 +914,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public List<EamGlobalSet> getAllReferenceSets(CorrelationAttribute.Type correlationType) throws EamDbException {
|
||||
public List<EamGlobalSet> getAllReferenceSets(CorrelationAttributeInstance.Type correlationType) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getAllReferenceSets(correlationType);
|
||||
@@ -932,7 +932,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttribute.Type correlationType) throws EamDbException {
|
||||
public void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttributeInstance.Type correlationType) throws EamDbException {
|
||||
try {
|
||||
acquireExclusiveLock();
|
||||
super.addReferenceInstance(eamGlobalFileInstance, correlationType);
|
||||
@@ -947,7 +947,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public void bulkInsertReferenceTypeEntries(Set<EamGlobalFileInstance> globalInstances, CorrelationAttribute.Type contentType) throws EamDbException {
|
||||
public void bulkInsertReferenceTypeEntries(Set<EamGlobalFileInstance> globalInstances, CorrelationAttributeInstance.Type contentType) throws EamDbException {
|
||||
try {
|
||||
acquireExclusiveLock();
|
||||
super.bulkInsertReferenceTypeEntries(globalInstances, contentType);
|
||||
@@ -967,7 +967,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public List<EamGlobalFileInstance> getReferenceInstancesByTypeValue(CorrelationAttribute.Type aType, String aValue) throws EamDbException {
|
||||
public List<EamGlobalFileInstance> getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getReferenceInstancesByTypeValue(aType, aValue);
|
||||
@@ -986,7 +986,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public int newCorrelationType(CorrelationAttribute.Type newType) throws EamDbException {
|
||||
public int newCorrelationType(CorrelationAttributeInstance.Type newType) throws EamDbException {
|
||||
try {
|
||||
acquireExclusiveLock();
|
||||
return super.newCorrelationType(newType);
|
||||
@@ -1005,7 +1005,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public List<CorrelationAttribute.Type> getDefinedCorrelationTypes() throws EamDbException {
|
||||
public List<CorrelationAttributeInstance.Type> getDefinedCorrelationTypes() throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getDefinedCorrelationTypes();
|
||||
@@ -1024,7 +1024,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public List<CorrelationAttribute.Type> getEnabledCorrelationTypes() throws EamDbException {
|
||||
public List<CorrelationAttributeInstance.Type> getEnabledCorrelationTypes() throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getEnabledCorrelationTypes();
|
||||
@@ -1043,7 +1043,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public List<CorrelationAttribute.Type> getSupportedCorrelationTypes() throws EamDbException {
|
||||
public List<CorrelationAttributeInstance.Type> getSupportedCorrelationTypes() throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getSupportedCorrelationTypes();
|
||||
@@ -1060,7 +1060,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public void updateCorrelationType(CorrelationAttribute.Type aType) throws EamDbException {
|
||||
public void updateCorrelationType(CorrelationAttributeInstance.Type aType) throws EamDbException {
|
||||
try {
|
||||
acquireExclusiveLock();
|
||||
super.updateCorrelationType(aType);
|
||||
@@ -1079,7 +1079,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
||||
* @throws EamDbException
|
||||
*/
|
||||
@Override
|
||||
public CorrelationAttribute.Type getCorrelationTypeById(int typeId) throws EamDbException {
|
||||
public CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws EamDbException {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getCorrelationTypeById(typeId);
|
||||
|
||||
@@ -394,11 +394,11 @@ public final class SqliteEamDbSettings {
|
||||
stmt.execute(createDbInfoTable.toString());
|
||||
|
||||
// Create a separate instance and reference table for each artifact type
|
||||
List<CorrelationAttribute.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttribute.getDefaultCorrelationTypes();
|
||||
List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes();
|
||||
|
||||
String reference_type_dbname;
|
||||
String instance_type_dbname;
|
||||
for (CorrelationAttribute.Type type : DEFAULT_CORRELATION_TYPES) {
|
||||
for (CorrelationAttributeInstance.Type type : DEFAULT_CORRELATION_TYPES) {
|
||||
reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type);
|
||||
instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type);
|
||||
|
||||
@@ -409,7 +409,7 @@ public final class SqliteEamDbSettings {
|
||||
stmt.execute(String.format(instancesIdx4, instance_type_dbname, instance_type_dbname));
|
||||
|
||||
// FUTURE: allow more than the FILES type
|
||||
if (type.getId() == CorrelationAttribute.FILES_TYPE_ID) {
|
||||
if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
|
||||
stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname));
|
||||
stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname));
|
||||
stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname));
|
||||
|
||||
+12
-13
@@ -36,13 +36,12 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@@ -192,12 +191,12 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
final CorrelationAttribute eamArtifact = EamArtifactUtil.makeCorrelationAttributeFromContent(af);
|
||||
final CorrelationAttributeInstance eamArtifact = EamArtifactUtil.makeInstanceFromContent(af);
|
||||
|
||||
if (eamArtifact != null) {
|
||||
// send update to Central Repository db
|
||||
try {
|
||||
dbManager.setArtifactInstanceKnownStatus(eamArtifact, knownStatus);
|
||||
dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus);
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS
|
||||
}
|
||||
@@ -292,11 +291,11 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
return;
|
||||
}
|
||||
|
||||
List<CorrelationAttribute> convertedArtifacts = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbArtifact, true, true);
|
||||
for (CorrelationAttribute eamArtifact : convertedArtifacts) {
|
||||
eamArtifact.getInstances().get(0).setComment(comment);
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, true);
|
||||
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
|
||||
eamArtifact.setComment(comment);
|
||||
try {
|
||||
dbManager.setArtifactInstanceKnownStatus(eamArtifact, knownStatus);
|
||||
dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus);
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS
|
||||
}
|
||||
@@ -365,9 +364,9 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
if (!hasTagWithConflictingKnownStatus) {
|
||||
//Get the correlation atttributes that correspond to the current BlackboardArtifactTag if their status should be changed
|
||||
//with the initial set of correlation attributes this should be a single correlation attribute
|
||||
List<CorrelationAttribute> convertedArtifacts = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbTag.getArtifact(), true, true);
|
||||
for (CorrelationAttribute eamArtifact : convertedArtifacts) {
|
||||
EamDb.getInstance().setArtifactInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbTag.getArtifact(), true);
|
||||
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
|
||||
EamDb.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,9 +400,9 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
}
|
||||
//if the file will have no tags with a status which would prevent the current status from being changed
|
||||
if (!hasTagWithConflictingKnownStatus) {
|
||||
final CorrelationAttribute eamArtifact = EamArtifactUtil.makeCorrelationAttributeFromContent(contentTag.getContent());
|
||||
final CorrelationAttributeInstance eamArtifact = EamArtifactUtil.makeInstanceFromContent(contentTag.getContent());
|
||||
if (eamArtifact != null) {
|
||||
EamDb.getInstance().setArtifactInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
|
||||
EamDb.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -38,7 +38,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
@@ -54,7 +54,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadUtils;
|
||||
*/
|
||||
public class IngestEventsListener {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CorrelationAttribute.class.getName());
|
||||
private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName());
|
||||
|
||||
final Collection<String> recentlyAddedCeArtifacts = new LinkedHashSet<>();
|
||||
private static int correlationModuleInstanceCount;
|
||||
@@ -248,12 +248,12 @@ public class IngestEventsListener {
|
||||
if (null == bbArtifacts) { //the ModuleDataEvents don't always have a collection of artifacts set
|
||||
return;
|
||||
}
|
||||
List<CorrelationAttribute> eamArtifacts = new ArrayList<>();
|
||||
List<CorrelationAttributeInstance> eamArtifacts = new ArrayList<>();
|
||||
|
||||
for (BlackboardArtifact bbArtifact : bbArtifacts) {
|
||||
// eamArtifact will be null OR a EamArtifact containing one EamArtifactInstance.
|
||||
List<CorrelationAttribute> convertedArtifacts = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbArtifact, true, true);
|
||||
for (CorrelationAttribute eamArtifact : convertedArtifacts) {
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, true);
|
||||
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
|
||||
try {
|
||||
// Only do something with this artifact if it's unique within the job
|
||||
if (recentlyAddedCeArtifacts.add(eamArtifact.toString())) {
|
||||
@@ -276,9 +276,9 @@ public class IngestEventsListener {
|
||||
}
|
||||
}
|
||||
if (FALSE == eamArtifacts.isEmpty()) {
|
||||
for (CorrelationAttribute eamArtifact : eamArtifacts) {
|
||||
for (CorrelationAttributeInstance eamArtifact : eamArtifacts) {
|
||||
try {
|
||||
dbManager.addArtifact(eamArtifact);
|
||||
dbManager.addArtifactInstance(eamArtifact);
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding artifact to database.", ex); //NON-NLS
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.sleuthkit.autopsy.ingest.IngestMessage;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
@@ -70,7 +69,7 @@ final class IngestModule implements FileIngestModule {
|
||||
private CorrelationCase eamCase;
|
||||
private CorrelationDataSource eamDataSource;
|
||||
private Blackboard blackboard;
|
||||
private CorrelationAttribute.Type filesType;
|
||||
private CorrelationAttributeInstance.Type filesType;
|
||||
|
||||
private final boolean flagTaggedNotableItems;
|
||||
|
||||
@@ -149,16 +148,16 @@ final class IngestModule implements FileIngestModule {
|
||||
|
||||
// insert this file into the central repository
|
||||
try {
|
||||
CorrelationAttribute eamArtifact = new CorrelationAttribute(filesType, md5);
|
||||
CorrelationAttributeInstance cefi = new CorrelationAttributeInstance(
|
||||
md5,
|
||||
filesType,
|
||||
eamCase,
|
||||
eamDataSource,
|
||||
abstractFile.getParentPath() + abstractFile.getName(),
|
||||
null,
|
||||
TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database.
|
||||
);
|
||||
eamArtifact.addInstance(cefi);
|
||||
dbManager.prepareBulkArtifact(eamArtifact);
|
||||
dbManager.addAttributeInstanceBulk(cefi);
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
@@ -182,7 +181,7 @@ final class IngestModule implements FileIngestModule {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dbManager.bulkInsertArtifacts();
|
||||
dbManager.commitAttributeInstancesBulk();
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS
|
||||
}
|
||||
@@ -264,7 +263,7 @@ final class IngestModule implements FileIngestModule {
|
||||
}
|
||||
|
||||
try {
|
||||
filesType = centralRepoDb.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
|
||||
filesType = centralRepoDb.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
|
||||
throw new IngestModuleException("Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
|
||||
@@ -43,7 +43,7 @@ final class ManageCorrelationPropertiesDialog extends javax.swing.JDialog {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ManageCorrelationPropertiesDialog.class.getName());
|
||||
|
||||
private final List<CorrelationAttribute.Type> correlationTypes;
|
||||
private final List<CorrelationAttributeInstance.Type> correlationTypes;
|
||||
|
||||
/**
|
||||
* Displays a dialog that allows a user to select which Type(s) should be
|
||||
|
||||
+2
-4
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
@@ -146,13 +145,12 @@ public abstract class AbstractCommonAttributeInstance {
|
||||
* @return the appropriate leaf node for the results tree
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
static DisplayableItemNode createNode(CorrelationAttribute attribute, AbstractFile abstractFile, String currentCaseName) throws TskCoreException {
|
||||
static DisplayableItemNode createNode(CorrelationAttributeInstance attribute, AbstractFile abstractFile, String currentCaseName) throws TskCoreException {
|
||||
|
||||
DisplayableItemNode leafNode;
|
||||
CorrelationAttributeInstance attributeInstance = attribute.getInstances().get(0);
|
||||
|
||||
if (abstractFile == null) {
|
||||
leafNode = new CentralRepoCommonAttributeInstanceNode(attributeInstance);
|
||||
leafNode = new CentralRepoCommonAttributeInstanceNode(attribute);
|
||||
} else {
|
||||
final String abstractFileDataSourceName = abstractFile.getDataSource().getName();
|
||||
leafNode = new CaseDBCommonAttributeInstanceNode(abstractFile, currentCaseName, abstractFileDataSourceName);
|
||||
|
||||
+3
-1
@@ -42,11 +42,13 @@ public abstract class AbstractCommonAttributeSearcher {
|
||||
private final Map<Long, String> dataSourceIdToNameMap;
|
||||
private boolean filterByMedia;
|
||||
private boolean filterByDoc;
|
||||
final int frequencyPercentageThreshold;
|
||||
|
||||
AbstractCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMedia, boolean filterByDoc){
|
||||
AbstractCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMedia, boolean filterByDoc, int percentageThreshold){
|
||||
this.filterByDoc = filterByDoc;
|
||||
this.filterByMedia = filterByMedia;
|
||||
this.dataSourceIdToNameMap = dataSourceIdMap;
|
||||
this.frequencyPercentageThreshold = percentageThreshold;
|
||||
}
|
||||
|
||||
Map<Long, String> getDataSourceIdToNameMap(){
|
||||
|
||||
+3
-3
@@ -41,15 +41,15 @@ public class AllInterCaseCommonAttributeSearcher extends InterCaseCommonAttribut
|
||||
* broadly categorized as document types
|
||||
* @throws EamDbException
|
||||
*/
|
||||
public AllInterCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException {
|
||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
|
||||
public AllInterCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException {
|
||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
|
||||
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap());
|
||||
Map<Integer, CommonAttributeValueList> interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase());
|
||||
return new CommonAttributeSearchResults(interCaseCommonFiles);
|
||||
return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -37,8 +37,8 @@ final public class AllIntraCaseCommonAttributeSearcher extends IntraCaseCommonAt
|
||||
* @param filterByMediaMimeType match only on files whose mime types can be broadly categorized as media types
|
||||
* @param filterByDocMimeType match only on files whose mime types can be broadly categorized as document types
|
||||
*/
|
||||
public AllIntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
|
||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
|
||||
public AllIntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) {
|
||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,20 @@ CommonFilesPanel.intraCaseRadio.label=Correlate within current case only
|
||||
CommonFilesPanel.interCaseRadio.label=Correlate amongst all known cases (uses Central Repo)
|
||||
IntraCasePanel.allDataSourcesRadioButton.text=Matches may be from any data source
|
||||
IntraCasePanel.withinDataSourceRadioButton.text=At least one match must appear in the data source selected below:
|
||||
IntraCasePanel.selectDataSourceComboBox.actionCommand=
|
||||
InterCasePanel.specificCentralRepoCaseRadio.text=Matches must be from the following Central Repo case:
|
||||
InterCasePanel.anyCentralRepoCaseRadio.text=Matches may be from any Central Repo case
|
||||
CommonAttributePanel.jCheckBox1.text=Hide files found in over
|
||||
CommonAttributePanel.jLabel1.text=% of data sources in central repository.
|
||||
CommonAttributePanel.percentageThresholdTextTwo.text_1=% of data sources in central repository.
|
||||
CommonAttributePanel.percentageThresholdTextOne.text=20
|
||||
CommonAttributePanel.percentageThresholdCheck.text_1=Hide files found in over
|
||||
CommonAttributePanel.intraCaseRadio.text=Within current case
|
||||
CommonAttributePanel.commonFilesSearchLabel1.text=<html>Find common files to correlate data soures or cases.</html>
|
||||
CommonAttributePanel.errorText.text=<html>In order to search, you must select a file category.</html>
|
||||
CommonAttributePanel.categoriesLabel.text=File Types To Include:
|
||||
CommonAttributePanel.documentsCheckbox.text=Documents
|
||||
CommonAttributePanel.pictureVideoCheckbox.text=Pictures and Videos
|
||||
CommonAttributePanel.selectedFileCategoriesButton.toolTipText=Select from the options below...
|
||||
CommonAttributePanel.selectedFileCategoriesButton.text=Only the selected file types:
|
||||
CommonAttributePanel.allFileCategoriesRadioButton.toolTipText=No filtering applied to results...
|
||||
@@ -16,9 +28,3 @@ CommonAttributePanel.cancelButton.actionCommand=Cancel
|
||||
CommonAttributePanel.cancelButton.text=Cancel
|
||||
CommonAttributePanel.searchButton.text=Search
|
||||
CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search
|
||||
CommonAttributePanel.intraCaseRadio.text=Within current case
|
||||
CommonAttributePanel.commonFilesSearchLabel1.text=<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
|
||||
|
||||
@@ -63,9 +63,7 @@ final public class CaseDBCommonAttributeInstance extends AbstractCommonAttribute
|
||||
|
||||
SleuthkitCase tskDb = currentCase.getSleuthkitCase();
|
||||
|
||||
AbstractFile abstractFile = tskDb.findAllFilesWhere(String.format("obj_id in (%s)", this.getAbstractFileObjectId())).get(0);
|
||||
|
||||
return abstractFile;
|
||||
return tskDb.findAllFilesWhere(String.format("obj_id in (%s)", this.getAbstractFileObjectId())).get(0);
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Unable to find AbstractFile for record with obj_id: %s. Node not created.", new Object[]{this.getAbstractFileObjectId()}), ex);
|
||||
|
||||
@@ -82,6 +82,7 @@ public class CaseDBCommonAttributeInstanceNode extends FileNode {
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsCsvList(this.getContent())));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource()));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType())));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName));
|
||||
|
||||
this.addTagProperty(sheetSet);
|
||||
|
||||
|
||||
+4
-5
@@ -27,7 +27,6 @@ import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
||||
@@ -44,7 +43,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CentralRepoCommonAttributeInstance.class.getName());
|
||||
private final Integer crFileId;
|
||||
private CorrelationAttribute currentAttribute;
|
||||
private CorrelationAttributeInstance currentAttribute;
|
||||
private final Map<String, Long> dataSourceNameToIdMap;
|
||||
|
||||
CentralRepoCommonAttributeInstance(Integer attrInstId, Map<Long, String> dataSourceIdToNameMap) {
|
||||
@@ -53,7 +52,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr
|
||||
this.dataSourceNameToIdMap = invertMap(dataSourceIdToNameMap);
|
||||
}
|
||||
|
||||
void setCurrentAttributeInst(CorrelationAttribute attribute) {
|
||||
void setCurrentAttributeInst(CorrelationAttributeInstance attribute) {
|
||||
this.currentAttribute = attribute;
|
||||
}
|
||||
|
||||
@@ -63,7 +62,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr
|
||||
Case currentCase;
|
||||
if (this.currentAttribute != null) {
|
||||
|
||||
final CorrelationAttributeInstance currentAttributeInstance = this.currentAttribute.getInstances().get(0);
|
||||
final CorrelationAttributeInstance currentAttributeInstance = this.currentAttribute;
|
||||
|
||||
String currentFullPath = currentAttributeInstance.getFilePath();
|
||||
String currentDataSource = currentAttributeInstance.getCorrelationDataSource().getName();
|
||||
@@ -109,7 +108,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr
|
||||
|
||||
// @@@ We should be doing more of this work in teh generateKeys method. We want to do as little as possible in generateNodes
|
||||
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor();
|
||||
CorrelationAttribute corrAttr = eamDbAttrInst.findSingleCorrelationAttribute(crFileId);
|
||||
CorrelationAttributeInstance corrAttr = eamDbAttrInst.findSingleCorrelationAttribute(crFileId);
|
||||
List<DisplayableItemNode> attrInstNodeList = new ArrayList<>(0);
|
||||
String currCaseDbName = Case.getCurrentCase().getDisplayName();
|
||||
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
</Component>
|
||||
</NonVisualComponents>
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[450, 375]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[412, 350]"/>
|
||||
<Dimension value="[450, 375]"/>
|
||||
</Property>
|
||||
<Property name="resizable" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
@@ -29,16 +32,23 @@
|
||||
<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"/>
|
||||
<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,-81,0,0,2,102"/>
|
||||
</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 name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[450, 375]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[450, 375]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[450, 375]"/>
|
||||
</Property>
|
||||
<Property name="requestFocusEnabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
@@ -49,46 +59,56 @@
|
||||
<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" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="1" 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 type="102" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
<Component id="filler1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="80" max="-2" attributes="0"/>
|
||||
<Component id="filler2" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="errorText" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="35" max="-2" attributes="0"/>
|
||||
<Group type="102" 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 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 type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="29" 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>
|
||||
<Component id="allFileCategoriesRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
|
||||
<Component id="layoutPanel" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="percentageThresholdCheck" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="percentageThresholdTextOne" min="-2" pref="40" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="percentageThresholdTextTwo" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace pref="9" max="32767" attributes="0"/>
|
||||
</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>
|
||||
@@ -104,7 +124,9 @@
|
||||
<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"/>
|
||||
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
|
||||
<Component id="layoutPanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace 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"/>
|
||||
@@ -116,18 +138,23 @@
|
||||
<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"/>
|
||||
<Component id="percentageThresholdCheck" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="percentageThresholdTextOne" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="percentageThresholdTextTwo" 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 type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="filler2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="filler1" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<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>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@@ -238,6 +265,7 @@
|
||||
<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, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="verticalAlignment" type="int" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="commonFilesSearchLabel1">
|
||||
@@ -295,6 +323,59 @@
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JCheckBox" name="percentageThresholdCheck">
|
||||
<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.percentageThresholdCheck.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="percentageThresholdCheckActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="percentageThresholdTextOne">
|
||||
<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.percentageThresholdTextOne.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[40, 24]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[40, 24]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[40, 24]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="percentageThresholdTextTwo">
|
||||
<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.percentageThresholdTextTwo.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.Box$Filler" name="filler1">
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 32767]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalGlue"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.Box$Filler" name="filler2">
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[32767, 32767]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.Glue"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -29,6 +30,8 @@ import java.util.logging.Level;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.util.NbBundle;
|
||||
@@ -52,16 +55,21 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* logic. Nested within CommonFilesDialog.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Long NO_DATA_SOURCE_SELECTED = -1L;
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName());
|
||||
private final UserInputErrorManager errorManager;
|
||||
|
||||
private boolean pictureViewCheckboxState;
|
||||
|
||||
private boolean documentsCheckboxState;
|
||||
|
||||
private int percentageThresholdValue = 20;
|
||||
|
||||
/**
|
||||
* Creates new form CommonFilesPanel
|
||||
*/
|
||||
@@ -74,21 +82,71 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
super(new JFrame(Bundle.CommonFilesPanel_frame_title()),
|
||||
Bundle.CommonFilesPanel_frame_msg(), true);
|
||||
initComponents();
|
||||
|
||||
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
||||
this.errorText.setVisible(false);
|
||||
this.setupDataSources();
|
||||
|
||||
if (CommonAttributePanel.isEamDbAvailable()) {
|
||||
if (CommonAttributePanel.isEamDbAvailableForIntercaseSearch()) {
|
||||
this.setupCases();
|
||||
} else {
|
||||
this.disableIntercaseSearch();
|
||||
}
|
||||
|
||||
if(CommonAttributePanel.isEamDbAvailableForPercentageFrequencyCalculations()){
|
||||
this.enablePercentageOptions();
|
||||
} else {
|
||||
this.disablePercentageOptions();
|
||||
}
|
||||
|
||||
this.errorManager = new UserInputErrorManager();
|
||||
|
||||
this.percentageThresholdTextOne.getDocument().addDocumentListener(new DocumentListener(){
|
||||
|
||||
private Dimension preferredSize = CommonAttributePanel.this.percentageThresholdTextOne.getPreferredSize();
|
||||
|
||||
private void maintainSize(){
|
||||
CommonAttributePanel.this.percentageThresholdTextOne.setSize(preferredSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent event) {
|
||||
this.maintainSize();
|
||||
CommonAttributePanel.this.percentageThresholdChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent event) {
|
||||
this.maintainSize();
|
||||
CommonAttributePanel.this.percentageThresholdChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent event) {
|
||||
this.maintainSize();
|
||||
CommonAttributePanel.this.percentageThresholdChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isEamDbAvailable() {
|
||||
private static boolean isEamDbAvailableForIntercaseSearch() {
|
||||
try {
|
||||
EamDb DbManager = EamDb.getInstance();
|
||||
return DbManager != null;
|
||||
return EamDb.isEnabled()
|
||||
&& EamDb.getInstance() != null
|
||||
&& EamDb.getInstance().getCases().size() > 1
|
||||
&& Case.isCaseOpen()
|
||||
&& Case.getCurrentCase() != null
|
||||
&& EamDb.getInstance().getCase(Case.getCurrentCase()) != null;
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unexpected exception while checking for EamDB enabled.", ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isEamDbAvailableForPercentageFrequencyCalculations(){
|
||||
try {
|
||||
return EamDb.isEnabled()
|
||||
&& EamDb.getInstance() != null
|
||||
&& EamDb.getInstance().getCases().size() > 0;
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unexpected exception while checking for EamDB enabled.", ex);
|
||||
}
|
||||
@@ -132,7 +190,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"BoxedValueEquality", "NumberEquality"})
|
||||
protected CommonAttributeSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException, Exception {
|
||||
protected CommonAttributeSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
|
||||
progress = ProgressHandle.createHandle(Bundle.CommonFilesPanel_search_done_searchProgressGathering());
|
||||
progress.start();
|
||||
progress.switchToIndeterminate();
|
||||
@@ -154,20 +212,27 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
}
|
||||
}
|
||||
|
||||
int percentageThreshold = CommonAttributePanel.this.percentageThresholdValue;
|
||||
|
||||
if (!CommonAttributePanel.this.percentageThresholdCheck.isSelected()) {
|
||||
//0 has the effect of disabling the feature
|
||||
percentageThreshold = 0;
|
||||
}
|
||||
|
||||
if (CommonAttributePanel.this.interCaseRadio.isSelected()) {
|
||||
|
||||
if (caseId == InterCasePanel.NO_CASE_SELECTED) {
|
||||
builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
|
||||
builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold);
|
||||
} else {
|
||||
builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
|
||||
builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold);
|
||||
}
|
||||
} else {
|
||||
if (dataSourceId == CommonAttributePanel.NO_DATA_SOURCE_SELECTED) {
|
||||
builder = new AllIntraCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
|
||||
builder = new AllIntraCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold);
|
||||
|
||||
setTitleForAllDataSources();
|
||||
} else {
|
||||
builder = new SingleIntraCaseCommonAttributeSearcher(dataSourceId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
|
||||
builder = new SingleIntraCaseCommonAttributeSearcher(dataSourceId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold);
|
||||
|
||||
setTitleForSingleSource(dataSourceId);
|
||||
}
|
||||
@@ -226,7 +291,6 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
}.execute();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up the data sources dropdown and returns the data sources map for
|
||||
* future usage.
|
||||
@@ -259,8 +323,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
boolean multipleDataSources = this.caseHasMultipleSources();
|
||||
CommonAttributePanel.this.intraCasePanel.rigForMultipleDataSources(multipleDataSources);
|
||||
|
||||
//TODO this should be attached to the intra/inter radio buttons
|
||||
CommonAttributePanel.this.setSearchButtonEnabled(true);
|
||||
CommonAttributePanel.this.updateErrorTextAndSearchBox();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +394,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Integer, String> mapDataSources(List<CorrelationCase> cases) throws Exception {
|
||||
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) {
|
||||
@@ -344,7 +407,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<Integer, String> doInBackground() throws Exception {
|
||||
protected Map<Integer, String> doInBackground() throws EamDbException {
|
||||
|
||||
List<CorrelationCase> dataSources = EamDb.getInstance().getCases();
|
||||
Map<Integer, String> caseMap = mapDataSources(dataSources);
|
||||
@@ -401,9 +464,14 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
layoutPanel = new java.awt.Panel();
|
||||
intraCasePanel = new org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel();
|
||||
interCasePanel = new org.sleuthkit.autopsy.commonfilesearch.InterCasePanel();
|
||||
percentageThresholdCheck = new javax.swing.JCheckBox();
|
||||
percentageThresholdTextOne = new javax.swing.JTextField();
|
||||
percentageThresholdTextTwo = new javax.swing.JLabel();
|
||||
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
|
||||
filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 32767));
|
||||
|
||||
setMinimumSize(new java.awt.Dimension(412, 350));
|
||||
setPreferredSize(new java.awt.Dimension(412, 350));
|
||||
setMaximumSize(new java.awt.Dimension(450, 375));
|
||||
setMinimumSize(new java.awt.Dimension(450, 375));
|
||||
setResizable(false);
|
||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
public void windowClosed(java.awt.event.WindowEvent evt) {
|
||||
@@ -411,7 +479,10 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
}
|
||||
});
|
||||
|
||||
jPanel1.setPreferredSize(new java.awt.Dimension(412, 350));
|
||||
jPanel1.setMaximumSize(new java.awt.Dimension(450, 375));
|
||||
jPanel1.setMinimumSize(new java.awt.Dimension(450, 375));
|
||||
jPanel1.setPreferredSize(new java.awt.Dimension(450, 375));
|
||||
jPanel1.setRequestFocusEnabled(false);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel2, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel2.text")); // NOI18N
|
||||
commonFilesSearchLabel2.setFocusable(false);
|
||||
@@ -474,6 +545,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
|
||||
errorText.setForeground(new java.awt.Color(255, 0, 0));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.errorText.text")); // NOI18N
|
||||
errorText.setVerticalAlignment(javax.swing.SwingConstants.TOP);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel1.text")); // NOI18N
|
||||
commonFilesSearchLabel1.setFocusable(false);
|
||||
@@ -499,42 +571,64 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
layoutPanel.add(intraCasePanel, "card3");
|
||||
layoutPanel.add(interCasePanel, "card2");
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(percentageThresholdCheck, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdCheck.text_1")); // NOI18N
|
||||
percentageThresholdCheck.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
percentageThresholdCheckActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
percentageThresholdTextOne.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdTextOne.text")); // NOI18N
|
||||
percentageThresholdTextOne.setMaximumSize(new java.awt.Dimension(40, 24));
|
||||
percentageThresholdTextOne.setMinimumSize(new java.awt.Dimension(40, 24));
|
||||
percentageThresholdTextOne.setPreferredSize(new java.awt.Dimension(40, 24));
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(percentageThresholdTextTwo, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdTextTwo.text_1")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(searchButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(cancelButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(errorText))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(commonFilesSearchLabel2)
|
||||
.addComponent(intraCaseRadio)
|
||||
.addComponent(interCaseRadio)
|
||||
.addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(categoriesLabel)
|
||||
.addComponent(selectedFileCategoriesButton)))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGap(0, 0, Short.MAX_VALUE)
|
||||
.addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(80, 80, 80)
|
||||
.addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(errorText)))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGap(35, 35, 35)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(documentsCheckbox)
|
||||
.addComponent(pictureVideoCheckbox)))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(allFileCategoriesRadioButton)))
|
||||
.addContainerGap())
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGap(20, 20, 20)
|
||||
.addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(10, 10, 10)))
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(commonFilesSearchLabel2)
|
||||
.addComponent(intraCaseRadio)
|
||||
.addComponent(interCaseRadio)
|
||||
.addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(categoriesLabel)
|
||||
.addComponent(selectedFileCategoriesButton)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGap(29, 29, 29)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(documentsCheckbox)
|
||||
.addComponent(pictureVideoCheckbox)))
|
||||
.addComponent(allFileCategoriesRadioButton)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
|
||||
.addGap(10, 10, 10)
|
||||
.addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(percentageThresholdCheck)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(percentageThresholdTextTwo)))
|
||||
.addContainerGap(9, Short.MAX_VALUE))))
|
||||
);
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
@@ -547,7 +641,9 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
.addComponent(intraCaseRadio)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(interCaseRadio)
|
||||
.addGap(79, 79, 79)
|
||||
.addGap(2, 2, 2)
|
||||
.addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(categoriesLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(selectedFileCategoriesButton)
|
||||
@@ -559,85 +655,90 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
.addComponent(allFileCategoriesRadioButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(searchButton)
|
||||
.addComponent(cancelButton)
|
||||
.addComponent(errorText))
|
||||
.addComponent(percentageThresholdCheck)
|
||||
.addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(percentageThresholdTextTwo))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(searchButton)
|
||||
.addComponent(cancelButton)
|
||||
.addComponent(errorText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addContainerGap())
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
|
||||
.addGap(98, 98, 98)
|
||||
.addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(180, 180, 180)))
|
||||
);
|
||||
|
||||
getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);
|
||||
}// </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
|
||||
|
||||
private void percentageThresholdCheckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdCheckActionPerformed
|
||||
if (this.percentageThresholdCheck.isSelected()) {
|
||||
this.percentageThresholdTextOne.setEnabled(true);
|
||||
} else {
|
||||
this.percentageThresholdTextOne.setEnabled(false);
|
||||
}
|
||||
|
||||
this.handleFrequencyPercentageState();
|
||||
}//GEN-LAST:event_percentageThresholdCheckActionPerformed
|
||||
|
||||
private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed
|
||||
((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel);
|
||||
}//GEN-LAST:event_interCaseRadioActionPerformed
|
||||
|
||||
private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed
|
||||
((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel);
|
||||
}//GEN-LAST:event_intraCaseRadioActionPerformed
|
||||
|
||||
private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed
|
||||
this.handleFileTypeCheckBoxState();
|
||||
}//GEN-LAST:event_documentsCheckboxActionPerformed
|
||||
|
||||
private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed
|
||||
this.handleFileTypeCheckBoxState();
|
||||
}//GEN-LAST:event_pictureVideoCheckboxActionPerformed
|
||||
|
||||
private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed
|
||||
this.handleFileTypeCheckBoxState();
|
||||
}//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed
|
||||
|
||||
private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed
|
||||
this.handleFileTypeCheckBoxState();
|
||||
}//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed
|
||||
|
||||
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
||||
SwingUtilities.windowForComponent(this).dispose();
|
||||
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||
|
||||
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
|
||||
search();
|
||||
SwingUtilities.windowForComponent(this).dispose();
|
||||
}//GEN-LAST:event_searchButtonActionPerformed
|
||||
|
||||
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
||||
SwingUtilities.windowForComponent(this).dispose();
|
||||
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||
private void percentageThresholdChanged(){
|
||||
String percentageString = this.percentageThresholdTextOne.getText();
|
||||
|
||||
private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed
|
||||
this.manageCheckBoxState();
|
||||
this.toggleErrorTextAndSearchBox();
|
||||
}//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed
|
||||
|
||||
private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed
|
||||
this.manageCheckBoxState();
|
||||
}//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed
|
||||
|
||||
private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed
|
||||
this.toggleErrorTextAndSearchBox();
|
||||
}//GEN-LAST:event_pictureVideoCheckboxActionPerformed
|
||||
|
||||
private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed
|
||||
this.toggleErrorTextAndSearchBox();
|
||||
}//GEN-LAST:event_documentsCheckboxActionPerformed
|
||||
|
||||
private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed
|
||||
((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel);
|
||||
handleIntraCaseSearchCriteriaChanged();
|
||||
}//GEN-LAST:event_intraCaseRadioActionPerformed
|
||||
|
||||
public void handleIntraCaseSearchCriteriaChanged() {
|
||||
if (this.areIntraCaseSearchCriteriaMet()) {
|
||||
this.searchButton.setEnabled(true);
|
||||
this.hideErrorMessages();
|
||||
} else {
|
||||
this.searchButton.setEnabled(false);
|
||||
this.hideErrorMessages();
|
||||
this.showIntraCaseErrorMessage();
|
||||
try {
|
||||
this.percentageThresholdValue = Integer.parseInt(percentageString);
|
||||
|
||||
} catch (NumberFormatException exception) {
|
||||
this.percentageThresholdValue = -1;
|
||||
}
|
||||
|
||||
this.handleFrequencyPercentageState();
|
||||
}
|
||||
|
||||
private void updateErrorTextAndSearchBox() {
|
||||
|
||||
private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed
|
||||
((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel);
|
||||
handleInterCaseSearchCriteriaChanged();
|
||||
}//GEN-LAST:event_interCaseRadioActionPerformed
|
||||
|
||||
private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed
|
||||
SwingUtilities.windowForComponent(this).dispose();
|
||||
}//GEN-LAST:event_formWindowClosed
|
||||
|
||||
public void handleInterCaseSearchCriteriaChanged() {
|
||||
if (this.areInterCaseSearchCriteriaMet()) {
|
||||
this.searchButton.setEnabled(true);
|
||||
this.hideErrorMessages();
|
||||
} else {
|
||||
this.searchButton.setEnabled(false);
|
||||
this.hideErrorMessages();
|
||||
this.showInterCaseErrorMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleErrorTextAndSearchBox() {
|
||||
if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) {
|
||||
if (this.errorManager.anyErrors()) {
|
||||
this.searchButton.setEnabled(false);
|
||||
//grab the first error error and show it
|
||||
this.errorText.setText(this.errorManager.getErrors().get(0));
|
||||
this.errorText.setVisible(true);
|
||||
} else {
|
||||
this.searchButton.setEnabled(true);
|
||||
@@ -645,7 +746,21 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
}
|
||||
}
|
||||
|
||||
private void manageCheckBoxState() {
|
||||
private void enablePercentageOptions() {
|
||||
this.percentageThresholdTextOne.setEnabled(true);
|
||||
this.percentageThresholdCheck.setEnabled(true);
|
||||
this.percentageThresholdCheck.setSelected(true);
|
||||
this.percentageThresholdTextTwo.setEnabled(true);
|
||||
}
|
||||
|
||||
private void disablePercentageOptions() {
|
||||
this.percentageThresholdTextOne.setEnabled(false);
|
||||
this.percentageThresholdCheck.setEnabled(false);
|
||||
this.percentageThresholdCheck.setSelected(false);
|
||||
this.percentageThresholdTextTwo.setEnabled(false);
|
||||
}
|
||||
|
||||
private void handleFileTypeCheckBoxState() {
|
||||
|
||||
this.pictureViewCheckboxState = this.pictureVideoCheckbox.isSelected();
|
||||
this.documentsCheckboxState = this.documentsCheckbox.isSelected();
|
||||
@@ -653,6 +768,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
if (this.allFileCategoriesRadioButton.isSelected()) {
|
||||
this.pictureVideoCheckbox.setEnabled(false);
|
||||
this.documentsCheckbox.setEnabled(false);
|
||||
|
||||
this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, false);
|
||||
}
|
||||
|
||||
if (this.selectedFileCategoriesButton.isSelected()) {
|
||||
@@ -663,8 +780,24 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
this.pictureVideoCheckbox.setEnabled(true);
|
||||
this.documentsCheckbox.setEnabled(true);
|
||||
|
||||
this.toggleErrorTextAndSearchBox();
|
||||
if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) {
|
||||
this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, true);
|
||||
} else {
|
||||
this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, false);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateErrorTextAndSearchBox();
|
||||
}
|
||||
|
||||
private void handleFrequencyPercentageState() {
|
||||
if (this.percentageThresholdValue > 0 && this.percentageThresholdValue <= 100) {
|
||||
this.errorManager.setError(UserInputErrorManager.FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, false);
|
||||
} else {
|
||||
this.errorManager.setError(UserInputErrorManager.FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, true);
|
||||
}
|
||||
|
||||
this.updateErrorTextAndSearchBox();
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
@@ -676,6 +809,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
private javax.swing.JCheckBox documentsCheckbox;
|
||||
private javax.swing.JLabel errorText;
|
||||
private javax.swing.ButtonGroup fileTypeFilterButtonGroup;
|
||||
private javax.swing.Box.Filler filler1;
|
||||
private javax.swing.Box.Filler filler2;
|
||||
private org.sleuthkit.autopsy.commonfilesearch.InterCasePanel interCasePanel;
|
||||
private javax.swing.JRadioButton interCaseRadio;
|
||||
private javax.swing.ButtonGroup interIntraButtonGroup;
|
||||
@@ -683,34 +818,11 @@ public final class CommonAttributePanel extends javax.swing.JDialog {
|
||||
private javax.swing.JRadioButton intraCaseRadio;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private java.awt.Panel layoutPanel;
|
||||
private javax.swing.JCheckBox percentageThresholdCheck;
|
||||
private javax.swing.JTextField percentageThresholdTextOne;
|
||||
private javax.swing.JLabel percentageThresholdTextTwo;
|
||||
private javax.swing.JCheckBox pictureVideoCheckbox;
|
||||
private javax.swing.JButton searchButton;
|
||||
private javax.swing.JRadioButton selectedFileCategoriesButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
void setSearchButtonEnabled(boolean enabled) {
|
||||
this.searchButton.setEnabled(enabled);
|
||||
}
|
||||
|
||||
private boolean areIntraCaseSearchCriteriaMet() {
|
||||
return this.intraCasePanel.areSearchCriteriaMet();
|
||||
}
|
||||
|
||||
private boolean areInterCaseSearchCriteriaMet() {
|
||||
return this.interCasePanel.areSearchCriteriaMet();
|
||||
}
|
||||
|
||||
private void hideErrorMessages() {
|
||||
this.errorText.setVisible(false);
|
||||
}
|
||||
|
||||
private void showIntraCaseErrorMessage() {
|
||||
this.errorText.setText(this.intraCasePanel.getErrorMessage());
|
||||
this.errorText.setVisible(true);
|
||||
}
|
||||
|
||||
private void showInterCaseErrorMessage() {
|
||||
this.errorText.setText(this.interCasePanel.getErrorMessage());
|
||||
this.errorText.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -19,10 +19,13 @@
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
|
||||
|
||||
@@ -65,6 +68,8 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo
|
||||
*/
|
||||
static class InstanceCountNodeFactory extends ChildFactory<Integer>{
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(InstanceCountNodeFactory.class.getName());
|
||||
|
||||
private final CommonAttributeSearchResults searchResults;
|
||||
|
||||
/**
|
||||
@@ -78,7 +83,11 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<Integer> list) {
|
||||
list.addAll(this.searchResults.getMetadata().keySet());
|
||||
try {
|
||||
list.addAll(this.searchResults.getMetadata().keySet());
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to create keys.", ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,27 +19,37 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
|
||||
/**
|
||||
* Stores the results from the various types of common attribute searching
|
||||
* Stores results based on how they are currently displayed in the UI
|
||||
*/
|
||||
final public class CommonAttributeSearchResults {
|
||||
|
||||
|
||||
// maps instance count to list of attribute values.
|
||||
private final Map<Integer, CommonAttributeValueList> instanceCountToAttributeValues;
|
||||
|
||||
private final int percentageThreshold;
|
||||
|
||||
/**
|
||||
* Create a values object which can be handed off to the node factories.
|
||||
*
|
||||
* @param values list of CommonAttributeValue indexed by size of
|
||||
*
|
||||
* @param values list of CommonAttributeValue indexed by size of
|
||||
* CommonAttributeValue
|
||||
*/
|
||||
CommonAttributeSearchResults(Map<Integer, CommonAttributeValueList> metadata){
|
||||
this.instanceCountToAttributeValues = metadata;
|
||||
CommonAttributeSearchResults(Map<Integer, CommonAttributeValueList> metadata, int percentageThreshold) {
|
||||
//wrap in a new object in case any client code has used an unmodifiable collection
|
||||
this.instanceCountToAttributeValues = new HashMap<>(metadata);
|
||||
this.percentageThreshold = percentageThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,29 +58,100 @@ final public class CommonAttributeSearchResults {
|
||||
* This is a convenience method - you can also iterate over
|
||||
* <code>getValues()</code>.
|
||||
*
|
||||
* @param isntanceCound key
|
||||
* @param instanceCount key
|
||||
* @return list of values which represent matches
|
||||
*/
|
||||
CommonAttributeValueList getAttributeValuesForInstanceCount(Integer instanceCount) {
|
||||
return this.instanceCountToAttributeValues.get(instanceCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmodifiable collection of values, indexed by number of
|
||||
* grandchildren, which represents the common attributes found in the
|
||||
/**
|
||||
* Get an unmodifiable collection of values, indexed by number of
|
||||
* grandchildren, which represents the common attributes found in the
|
||||
* search.
|
||||
*
|
||||
* @return map of sizes of children to list of matches
|
||||
*/
|
||||
public Map<Integer, CommonAttributeValueList> getMetadata() {
|
||||
*/
|
||||
public Map<Integer, CommonAttributeValueList> getMetadata() throws EamDbException {
|
||||
if(this.percentageThreshold == 0){
|
||||
return Collections.unmodifiableMap(this.instanceCountToAttributeValues);
|
||||
} else {
|
||||
return this.getMetadata(this.percentageThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmodifiable collection of values, indexed by number of
|
||||
* grandchildren, which represents the common attributes found in the
|
||||
* search.
|
||||
*
|
||||
* Remove results which are not found in the portion of available data
|
||||
sources described by maximumPercentageThreshold.
|
||||
*
|
||||
* @return metadata
|
||||
*/
|
||||
private Map<Integer, CommonAttributeValueList> getMetadata(int maximumPercentageThreshold) throws EamDbException {
|
||||
|
||||
if(maximumPercentageThreshold == 0){
|
||||
return Collections.unmodifiableMap(this.instanceCountToAttributeValues);
|
||||
}
|
||||
|
||||
CorrelationAttributeInstance.Type fileAttributeType = CorrelationAttributeInstance
|
||||
.getDefaultCorrelationTypes()
|
||||
.stream()
|
||||
.filter(filterType -> filterType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID)
|
||||
.findFirst().get();
|
||||
|
||||
EamDb eamDb = EamDb.getInstance();
|
||||
|
||||
Map<Integer, List<CommonAttributeValue>> itemsToRemove = new HashMap<>();
|
||||
|
||||
for(Entry<Integer, CommonAttributeValueList> listOfValues : Collections.unmodifiableMap(this.instanceCountToAttributeValues).entrySet()){
|
||||
|
||||
final Integer key = listOfValues.getKey();
|
||||
final CommonAttributeValueList values = listOfValues.getValue();
|
||||
|
||||
for(CommonAttributeValue value : values.getDelayedMetadataList()){ // Need the real metadata
|
||||
|
||||
int frequencyPercentage = eamDb.getFrequencyPercentage(new CorrelationAttributeInstance(fileAttributeType, value.getValue()));
|
||||
|
||||
if(frequencyPercentage > maximumPercentageThreshold){
|
||||
if(itemsToRemove.containsKey(key)){
|
||||
itemsToRemove.get(key).add(value);
|
||||
} else {
|
||||
List<CommonAttributeValue> toRemove = new ArrayList<>();
|
||||
toRemove.add(value);
|
||||
itemsToRemove.put(key, toRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(Entry<Integer, List<CommonAttributeValue>> valuesToRemove : itemsToRemove.entrySet()){
|
||||
|
||||
final Integer key = valuesToRemove.getKey();
|
||||
final List<CommonAttributeValue> values = valuesToRemove.getValue();
|
||||
|
||||
for (CommonAttributeValue value : values){
|
||||
final CommonAttributeValueList instanceCountValue = this.instanceCountToAttributeValues.get(key);
|
||||
instanceCountValue.removeMetaData(value);
|
||||
|
||||
if(instanceCountValue.getDelayedMetadataList().isEmpty()){ // Check the real metadata
|
||||
this.instanceCountToAttributeValues.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(this.instanceCountToAttributeValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* How many distinct common files exist for this search results?
|
||||
*
|
||||
* @return number of common files
|
||||
*/
|
||||
public int size() {
|
||||
|
||||
|
||||
int count = 0;
|
||||
for (CommonAttributeValueList data : this.instanceCountToAttributeValues.values()) {
|
||||
for(CommonAttributeValue md5 : data.getMetadataList()){
|
||||
|
||||
@@ -57,8 +57,7 @@ final public class CommonAttributeValue {
|
||||
* @return
|
||||
*/
|
||||
public String getCases() {
|
||||
final String cases = this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", "));
|
||||
return cases;
|
||||
return this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
public String getDataSources() {
|
||||
@@ -67,8 +66,7 @@ final public class CommonAttributeValue {
|
||||
sources.add(data.getDataSource());
|
||||
}
|
||||
|
||||
final String dataSources = String.join(", ", sources);
|
||||
return dataSources;
|
||||
return String.join(", ", sources);
|
||||
}
|
||||
|
||||
void addInstance(AbstractCommonAttributeInstance metadata) {
|
||||
|
||||
@@ -67,6 +67,20 @@ final public class CommonAttributeValueList {
|
||||
return Collections.unmodifiableList(this.metadataList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the delayed list of value nodes. Only use for
|
||||
* determining how many CommonAttributeValues
|
||||
* actually exist in the list.
|
||||
* @return metadataList the list of nodes
|
||||
*/
|
||||
List<CommonAttributeValue> getDelayedMetadataList() {
|
||||
return Collections.unmodifiableList(this.delayedMetadataList);
|
||||
}
|
||||
|
||||
void removeMetaData(CommonAttributeValue commonVal) {
|
||||
this.delayedMetadataList.remove(commonVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size of the backing list, in case
|
||||
* displayDelayedMetadata() has not be called yet.
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
@@ -36,8 +35,6 @@ import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
*/
|
||||
public class CommonAttributeValueNode extends DisplayableItemNode {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CommonAttributeValueNode.class.getName());
|
||||
|
||||
private final String value;
|
||||
private final int commonFileCount;
|
||||
private final String cases;
|
||||
|
||||
@@ -48,9 +48,17 @@ final public class CommonFilesSearchAction extends CallableSystemAction {
|
||||
public boolean isEnabled(){
|
||||
boolean shouldBeEnabled = false;
|
||||
try {
|
||||
shouldBeEnabled = Case.isCaseOpen()
|
||||
&& Case.getCurrentCase().getDataSources().size() > 1
|
||||
|| (EamDb.isEnabled() && EamDb.getInstance().getCases().size() > 1);
|
||||
//dont refactor any of this to pull out common expressions - order of evaluation of each expression is significant
|
||||
shouldBeEnabled =
|
||||
(Case.isCaseOpen() &&
|
||||
Case.getCurrentCase().getDataSources().size() > 1)
|
||||
||
|
||||
(EamDb.isEnabled() &&
|
||||
EamDb.getInstance() != null &&
|
||||
EamDb.getInstance().getCases().size() > 1 &&
|
||||
Case.isCaseOpen() &&
|
||||
Case.getCurrentCase() != null &&
|
||||
EamDb.getInstance().getCase(Case.getCurrentCase()) != null);
|
||||
|
||||
} catch(TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error getting data sources for action enabled check", ex);
|
||||
|
||||
+2
-2
@@ -43,8 +43,8 @@ abstract class InterCaseCommonAttributeSearcher extends AbstractCommonAttributeS
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
InterCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException {
|
||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
|
||||
InterCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException {
|
||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold);
|
||||
dbManager = EamDb.getInstance();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import javax.swing.ComboBoxModel;
|
||||
import org.openide.util.NbBundle;
|
||||
|
||||
/**
|
||||
* UI controls for Common Files Search scenario where the user intends to find
|
||||
@@ -39,16 +38,18 @@ public class InterCasePanel extends javax.swing.JPanel {
|
||||
private ComboBoxModel<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) {
|
||||
@@ -59,10 +60,6 @@ public class InterCasePanel extends javax.swing.JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
String getErrorMessage(){
|
||||
return this.errorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
@@ -127,10 +124,12 @@ public class InterCasePanel extends javax.swing.JPanel {
|
||||
if(this.caseComboBox.isEnabled() && this.caseComboBox.getSelectedItem() == null){
|
||||
this.caseComboBox.setSelectedIndex(0);
|
||||
}
|
||||
this.anyCase = false;
|
||||
}//GEN-LAST:event_specificCentralRepoCaseRadioActionPerformed
|
||||
|
||||
private void anyCentralRepoCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_anyCentralRepoCaseRadioActionPerformed
|
||||
this.caseComboBox.setEnabled(false);
|
||||
this.anyCase = true;
|
||||
}//GEN-LAST:event_anyCentralRepoCaseRadioActionPerformed
|
||||
|
||||
|
||||
@@ -170,6 +169,10 @@ public class InterCasePanel extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
Integer getSelectedCaseId(){
|
||||
if(this.anyCase){
|
||||
return InterCasePanel.NO_CASE_SELECTED;
|
||||
}
|
||||
|
||||
for(Entry<Integer, String> entry : this.caseMap.entrySet()){
|
||||
if(entry.getValue().equals(this.caseComboBox.getSelectedItem())){
|
||||
return entry.getKey();
|
||||
@@ -178,16 +181,4 @@ public class InterCasePanel extends javax.swing.JPanel {
|
||||
|
||||
return InterCasePanel.NO_CASE_SELECTED;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"InterCasePanel.showInterCaseErrorMessage.message=Cannot run intercase correlation search: no cases in Central Repository."
|
||||
})
|
||||
boolean areSearchCriteriaMet() {
|
||||
if(this.caseMap.isEmpty()){
|
||||
this.errorMessage = Bundle.InterCasePanel_showInterCaseErrorMessage_message();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-11
@@ -25,7 +25,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
@@ -55,11 +55,20 @@ final class InterCaseSearchResultsProcessor {
|
||||
+ "WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value) "
|
||||
+ "AND (case_id=%s OR case_id=%s) GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value";
|
||||
|
||||
/**
|
||||
* Used in the InterCaseCommonAttributeSearchers to find common attribute instances and generate nodes at the UI level.
|
||||
* @param dataSources
|
||||
*/
|
||||
InterCaseSearchResultsProcessor(Map<Long, String> dataSources){
|
||||
this.dataSources = dataSources;
|
||||
}
|
||||
|
||||
InterCaseSearchResultsProcessor(){}
|
||||
/**
|
||||
* Used in the CentralRepoCommonAttributeInstance to find common attribute instances and generate nodes at the UI level.
|
||||
*/
|
||||
InterCaseSearchResultsProcessor(){
|
||||
//intentionally emtpy - we need a constructor which does not set the data sources field
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single CorrelationAttribute given an id.
|
||||
@@ -67,11 +76,11 @@ final class InterCaseSearchResultsProcessor {
|
||||
* @param attrbuteId Row of CorrelationAttribute to retrieve from the EamDb
|
||||
* @return CorrelationAttribute object representation of retrieved match
|
||||
*/
|
||||
CorrelationAttribute findSingleCorrelationAttribute(int attrbuteId) {
|
||||
CorrelationAttributeInstance findSingleCorrelationAttribute(int attrbuteId) {
|
||||
try {
|
||||
InterCaseCommonAttributeRowCallback instancetableCallback = new InterCaseCommonAttributeRowCallback();
|
||||
EamDb DbManager = EamDb.getInstance();
|
||||
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
|
||||
CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
|
||||
DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attrbuteId), instancetableCallback);
|
||||
|
||||
return instancetableCallback.getCorrelationAttribute();
|
||||
@@ -93,7 +102,7 @@ final class InterCaseSearchResultsProcessor {
|
||||
try {
|
||||
InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback();
|
||||
EamDb DbManager = EamDb.getInstance();
|
||||
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
|
||||
CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
|
||||
int caseId = DbManager.getCase(currentCase).getID();
|
||||
|
||||
DbManager.processInstanceTableWhere(fileType, String.format(interCaseWhereClause, caseId,
|
||||
@@ -120,7 +129,7 @@ final class InterCaseSearchResultsProcessor {
|
||||
try {
|
||||
InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback();
|
||||
EamDb DbManager = EamDb.getInstance();
|
||||
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
|
||||
CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
|
||||
int caseId = DbManager.getCase(currentCase).getID();
|
||||
int targetCaseId = singleCase.getID();
|
||||
DbManager.processInstanceTableWhere(fileType, String.format(singleInterCaseWhereClause, caseId,
|
||||
@@ -200,18 +209,18 @@ final class InterCaseSearchResultsProcessor {
|
||||
*/
|
||||
private class InterCaseCommonAttributeRowCallback implements InstanceTableCallback {
|
||||
|
||||
CorrelationAttribute correlationAttribute = null;
|
||||
CorrelationAttributeInstance correlationAttributeInstance = null;
|
||||
|
||||
@Override
|
||||
public void process(ResultSet resultSet) {
|
||||
try {
|
||||
EamDb DbManager = EamDb.getInstance();
|
||||
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
|
||||
CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
|
||||
|
||||
while (resultSet.next()) {
|
||||
CorrelationCase correlationCase = DbManager.getCaseById(InstanceTableCallback.getCaseId(resultSet));
|
||||
CorrelationDataSource dataSource = DbManager.getDataSourceById(correlationCase, InstanceTableCallback.getDataSourceId(resultSet));
|
||||
correlationAttribute = DbManager.getCorrelationAttribute(fileType,
|
||||
correlationAttributeInstance = DbManager.getCorrelationAttributeInstance(fileType,
|
||||
correlationCase,
|
||||
dataSource,
|
||||
InstanceTableCallback.getValue(resultSet),
|
||||
@@ -223,8 +232,8 @@ final class InterCaseSearchResultsProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
CorrelationAttribute getCorrelationAttribute() {
|
||||
return correlationAttribute;
|
||||
CorrelationAttributeInstance getCorrelationAttribute() {
|
||||
return correlationAttributeInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -55,8 +55,8 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt
|
||||
* @param filterByDocMimeType match only on files whose mime types can be
|
||||
* broadly categorized as document types
|
||||
*/
|
||||
IntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
|
||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
|
||||
IntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) {
|
||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +130,7 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt
|
||||
|
||||
Map<Integer, CommonAttributeValueList> instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles);
|
||||
|
||||
return new CommonAttributeSearchResults(instanceCollatedCommonFiles);
|
||||
return new CommonAttributeSearchResults(instanceCollatedCommonFiles, this.frequencyPercentageThreshold);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,11 +156,13 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt
|
||||
StringBuilder mimeTypeFilter = new StringBuilder(mimeTypesToFilterOn.size());
|
||||
if (!mimeTypesToFilterOn.isEmpty()) {
|
||||
for (String mimeType : mimeTypesToFilterOn) {
|
||||
mimeTypeFilter.append("'").append(mimeType).append("',");
|
||||
mimeTypeFilter.append(SINGLE_QUOTE).append(mimeType).append(SINGLE_QUTOE_COMMA);
|
||||
}
|
||||
mimeTypeString = mimeTypeFilter.toString().substring(0, mimeTypeFilter.length() - 1);
|
||||
mimeTypeString = String.format(FILTER_BY_MIME_TYPES_WHERE_CLAUSE, new Object[]{mimeTypeString});
|
||||
}
|
||||
return mimeTypeString;
|
||||
}
|
||||
static final String SINGLE_QUTOE_COMMA = "',";
|
||||
static final String SINGLE_QUOTE = "'";
|
||||
}
|
||||
|
||||
@@ -24,9 +24,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import javax.swing.ComboBoxModel;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* UI controls for Common Files Search scenario where the user intends to find
|
||||
* common files between datasources. It is an inner panel which provides the ability
|
||||
@@ -38,34 +35,17 @@ public class IntraCasePanel extends javax.swing.JPanel {
|
||||
private static final long serialVersionUID = 1L;
|
||||
static final long NO_DATA_SOURCE_SELECTED = -1;
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName());
|
||||
|
||||
private boolean singleDataSource;
|
||||
private String selectedDataSource;
|
||||
private ComboBoxModel<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<>();
|
||||
}
|
||||
|
||||
public boolean isSingleDataSource(){
|
||||
return this.singleDataSource;
|
||||
}
|
||||
|
||||
public String getSelectedDataSource(){
|
||||
if(this.singleDataSource && this.selectedDataSource != null){
|
||||
return selectedDataSource;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
this.singleDataSource = true;
|
||||
}
|
||||
|
||||
public Map<Long, String> getDataSourceMap(){
|
||||
@@ -73,6 +53,10 @@ public class IntraCasePanel extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
Long getSelectedDataSourceId(){
|
||||
if(!this.singleDataSource){
|
||||
return IntraCasePanel.NO_DATA_SOURCE_SELECTED;
|
||||
}
|
||||
|
||||
for(Entry<Long, String> entry : this.dataSourceMap.entrySet()){
|
||||
if(entry.getValue().equals(this.selectDataSourceComboBox.getSelectedItem())){
|
||||
return entry.getKey();
|
||||
@@ -115,12 +99,8 @@ public class IntraCasePanel extends javax.swing.JPanel {
|
||||
});
|
||||
|
||||
selectDataSourceComboBox.setModel(dataSourcesList);
|
||||
selectDataSourceComboBox.setActionCommand(org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.selectDataSourceComboBox.actionCommand")); // NOI18N
|
||||
selectDataSourceComboBox.setEnabled(false);
|
||||
selectDataSourceComboBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
selectDataSourceComboBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
@@ -158,15 +138,6 @@ public class IntraCasePanel extends javax.swing.JPanel {
|
||||
withinDataSourceSelected(withinDataSourceRadioButton.isSelected());
|
||||
}//GEN-LAST:event_withinDataSourceRadioButtonActionPerformed
|
||||
|
||||
private void selectDataSourceComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectDataSourceComboBoxActionPerformed
|
||||
final Object selectedItem = selectDataSourceComboBox.getSelectedItem();
|
||||
if (selectedItem != null) {
|
||||
selectedDataSource = selectedItem.toString();
|
||||
} else {
|
||||
selectedDataSource = "";
|
||||
}
|
||||
}//GEN-LAST:event_selectDataSourceComboBoxActionPerformed
|
||||
|
||||
private void withinDataSourceSelected(boolean selected) {
|
||||
selectDataSourceComboBox.setEnabled(selected);
|
||||
if (selectDataSourceComboBox.isEnabled()) {
|
||||
@@ -199,20 +170,4 @@ public class IntraCasePanel extends javax.swing.JPanel {
|
||||
this.dataSourceMap.clear();
|
||||
this.dataSourceMap.putAll(dataSourceMap);
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"IntraCasePanel.areSearchCriteriaMet.message=Cannot run intra-case correlation search."
|
||||
})
|
||||
boolean areSearchCriteriaMet() {
|
||||
if(this.dataSourceMap.isEmpty()){
|
||||
this.errorMessage = Bundle.IntraCasePanel_areSearchCriteriaMet_message();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
String getErrorMessage() {
|
||||
return this.errorMessage;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -44,8 +44,8 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri
|
||||
* @param filterByDocMimeType
|
||||
* @throws EamDbException
|
||||
*/
|
||||
public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException {
|
||||
super(dataSourceIdMap,filterByMediaMimeType, filterByDocMimeType);
|
||||
public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException {
|
||||
super(dataSourceIdMap,filterByMediaMimeType, filterByDocMimeType, percentageThreshold);
|
||||
|
||||
this.corrleationCaseId = correlationCaseId;
|
||||
this.correlationCaseName = "";
|
||||
@@ -74,7 +74,7 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri
|
||||
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap());
|
||||
Map<Integer, CommonAttributeValueList> interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase);
|
||||
|
||||
return new CommonAttributeSearchResults(interCaseCommonFiles);
|
||||
return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -41,8 +41,8 @@ final public class SingleIntraCaseCommonAttributeSearcher extends IntraCaseCommo
|
||||
* @param filterByDocMimeType match only on files whose mime types can be
|
||||
* broadly categorized as document types
|
||||
*/
|
||||
public SingleIntraCaseCommonAttributeSearcher(Long dataSourceId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
|
||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
|
||||
public SingleIntraCaseCommonAttributeSearcher(Long dataSourceId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) {
|
||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold);
|
||||
this.selectedDataSourceId = dataSourceId;
|
||||
this.dataSourceName = dataSourceIdMap.get(this.selectedDataSourceId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
*
|
||||
* 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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import org.openide.util.NbBundle;
|
||||
|
||||
/**
|
||||
* Manager for present state of errors on the Common Files Search.
|
||||
*/
|
||||
class UserInputErrorManager {
|
||||
|
||||
static final int FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY = 1;
|
||||
static final int NO_FILE_CATEGORIES_SELECTED_KEY = 2;
|
||||
|
||||
private final Map<Integer, ErrorMessage> currentErrors;
|
||||
|
||||
/**
|
||||
* Construct a new ErrorManager which can be used to track the status
|
||||
* of all known error states, retrieve error messages, and determine if
|
||||
* anything is in an error state.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"UserInputErrorManager.frequency=Invalid Frequency Percentage: 0 < % < 100.",
|
||||
"UserInputErrorManager.categories=No file categories are included in the search."})
|
||||
UserInputErrorManager (){
|
||||
|
||||
//when new errors are needed for the dialog, define a key and a value
|
||||
// and add them to the map.
|
||||
|
||||
this.currentErrors = new HashMap<>();
|
||||
this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage(Bundle.UserInputErrorManager_frequency()));
|
||||
this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage(Bundle.UserInputErrorManager_categories()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the given error message on, or off
|
||||
* @param errorId the error to toggle
|
||||
* @param errorState true for on, false for off
|
||||
*/
|
||||
void setError(int errorId, boolean errorState){
|
||||
if(this.currentErrors.containsKey(errorId)){
|
||||
this.currentErrors.get(errorId).setStatus(errorState);
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("The given errorId is not mapped to an ErrorMessage: %s.", errorId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Are any user settings presently in an error state?
|
||||
* @return true for yes, else false
|
||||
*/
|
||||
boolean anyErrors(){
|
||||
return this.currentErrors.values().stream().anyMatch(errorMessage -> errorMessage.isErrorSet() == true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of distinct string messages describing the various error states.
|
||||
*/
|
||||
List<String> getErrors(){
|
||||
return this.currentErrors.values().stream()
|
||||
.filter(errorMessage -> errorMessage.isErrorSet() == true)
|
||||
.map(ErrorMessage::getMessage)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an error message for the CommonFilesSearch panel, it's
|
||||
* uniqueId, and it's status.
|
||||
*/
|
||||
private class ErrorMessage {
|
||||
|
||||
private final String message;
|
||||
private boolean status;
|
||||
|
||||
/**
|
||||
* Create a message with a unique uniqueId. Default status is false (off).
|
||||
* @param uniqueId unique uniqueId
|
||||
* @param message message to display
|
||||
*/
|
||||
ErrorMessage(String message){
|
||||
this.message = message;
|
||||
this.status = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the status of this message
|
||||
* @param status
|
||||
*/
|
||||
void setStatus(boolean status){
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the message
|
||||
* @return
|
||||
*/
|
||||
String getMessage(){
|
||||
return this.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the status (true for error status, false for no error)
|
||||
* @return
|
||||
*/
|
||||
boolean isErrorSet(){
|
||||
return this.status;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
@@ -46,7 +46,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
final class RelationshipNode extends BlackboardArtifactNode {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName());
|
||||
|
||||
|
||||
RelationshipNode(BlackboardArtifact artifact) {
|
||||
super(artifact);
|
||||
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s");
|
||||
@@ -115,7 +115,7 @@ final class RelationshipNode extends BlackboardArtifactNode {
|
||||
}
|
||||
|
||||
addTagProperty(sheetSet);
|
||||
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,23 +22,16 @@ import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JOptionPane;
|
||||
@@ -48,14 +41,13 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||
import org.sleuthkit.autopsy.casemodule.services.Services;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.tabulardatareader.AbstractReader;
|
||||
import org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException;
|
||||
import org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException;
|
||||
import org.sleuthkit.autopsy.tabulardatareader.FileReaderFactory;
|
||||
|
||||
/**
|
||||
* A file content viewer for SQLite database files.
|
||||
@@ -70,7 +62,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
private final SQLiteTableView selectedTableView = new SQLiteTableView();
|
||||
private AbstractFile sqliteDbFile;
|
||||
private File tmpDbFile;
|
||||
private Connection connection;
|
||||
private AbstractReader sqliteReader;
|
||||
private int numRows; // num of rows in the selected table
|
||||
private int currPage = 0; // curr page of rows being displayed
|
||||
|
||||
@@ -347,13 +339,9 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
numEntriesField.setText("");
|
||||
|
||||
// close DB connection to file
|
||||
if (null != connection) {
|
||||
try {
|
||||
connection.close();
|
||||
connection = null;
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS
|
||||
}
|
||||
if (null != sqliteReader) {
|
||||
sqliteReader.close();
|
||||
sqliteReader = null;
|
||||
}
|
||||
|
||||
sqliteDbFile = null;
|
||||
@@ -370,41 +358,16 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
"SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.",
|
||||
"SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.",
|
||||
"# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",})
|
||||
private void processSQLiteFile() {
|
||||
|
||||
private void processSQLiteFile() {
|
||||
tablesDropdownList.removeAllItems();
|
||||
|
||||
// Copy the file to temp folder
|
||||
String tmpDBPathName;
|
||||
try {
|
||||
tmpDBPathName = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + sqliteDbFile.getName();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase());
|
||||
return;
|
||||
}
|
||||
|
||||
tmpDbFile = new File(tmpDBPathName);
|
||||
if (! tmpDbFile.exists()) {
|
||||
try {
|
||||
ContentUtils.writeToFile(sqliteDbFile, tmpDbFile);
|
||||
|
||||
// Look for any meta files associated with this DB - WAL, SHM, etc.
|
||||
findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal");
|
||||
findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm");
|
||||
} catch (IOException | NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Load the SQLite JDBC driver, if necessary.
|
||||
Class.forName("org.sqlite.JDBC"); //NON-NLS
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS
|
||||
|
||||
Map<String, String> dbTablesMap = getTables();
|
||||
String localDiskPath = Case.getCurrentCaseThrows().getTempDirectory() +
|
||||
File.separator + sqliteDbFile.getName();
|
||||
|
||||
sqliteReader = FileReaderFactory.createReader(SUPPORTED_MIMETYPES[0], sqliteDbFile, localDiskPath);
|
||||
|
||||
Map<String, String> dbTablesMap = sqliteReader.getTableSchemas();
|
||||
|
||||
if (dbTablesMap.isEmpty()) {
|
||||
tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry());
|
||||
tablesDropdownList.setEnabled(false);
|
||||
@@ -413,78 +376,28 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
tablesDropdownList.addItem(tableName);
|
||||
});
|
||||
}
|
||||
} catch (ClassNotFoundException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver());
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to get tables from DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase());
|
||||
} catch (FileReaderException ex) {
|
||||
logger.log(Level.SEVERE, String.format(
|
||||
"Failed to get tables from DB file '%s' (objId=%d)", //NON-NLS
|
||||
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
|
||||
MessageNotifyUtil.Message.error(
|
||||
Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
|
||||
} catch (FileReaderInitException ex) {
|
||||
logger.log(Level.SEVERE, String.format(
|
||||
"Failed to create a SQLiteReader '%s' (objId=%d)", //NON-NLS
|
||||
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a meta file associated with the give SQLite db If found,
|
||||
* copies the file to the temp folder
|
||||
*
|
||||
* @param sqliteFile - SQLIte db file being processed
|
||||
* @param metaFileName name of meta file to look for
|
||||
*/
|
||||
private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
|
||||
Case openCase = Case.getCurrentCaseThrows();
|
||||
SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
|
||||
Services services = new Services(sleuthkitCase);
|
||||
FileManager fileManager = services.getFileManager();
|
||||
List<AbstractFile> metaFiles = fileManager.findFiles(sqliteFile.getDataSource(), metaFileName, sqliteFile.getParent().getName());
|
||||
if (metaFiles != null) {
|
||||
for (AbstractFile metaFile : metaFiles) {
|
||||
String tmpMetafilePathName = openCase.getTempDirectory() + File.separator + metaFile.getName();
|
||||
File tmpMetafile = new File(tmpMetafilePathName);
|
||||
ContentUtils.writeToFile(metaFile, tmpMetafile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the table names and schemas from the SQLite database file.
|
||||
*
|
||||
* @return A mapping of table names to SQL CREATE TABLE statements.
|
||||
*/
|
||||
private Map<String, String> getTables() throws SQLException {
|
||||
Map<String, String> dbTablesMap = new TreeMap<>();
|
||||
Statement statement = null;
|
||||
ResultSet resultSet = null;
|
||||
try {
|
||||
statement = connection.createStatement();
|
||||
resultSet = statement.executeQuery(
|
||||
"SELECT name, sql FROM sqlite_master "
|
||||
+ " WHERE type= 'table' "
|
||||
+ " ORDER BY name;"); //NON-NLS
|
||||
while (resultSet.next()) {
|
||||
String tableName = resultSet.getString("name"); //NON-NLS
|
||||
String tableSQL = resultSet.getString("sql"); //NON-NLS
|
||||
dbTablesMap.put(tableName, tableSQL);
|
||||
}
|
||||
} finally {
|
||||
if (null != resultSet) {
|
||||
resultSet.close();
|
||||
}
|
||||
if (null != statement) {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
return dbTablesMap;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"# {0} - tableName",
|
||||
"SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}"
|
||||
})
|
||||
private void selectTable(String tableName) {
|
||||
|
||||
try (Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery(
|
||||
"SELECT count (*) as count FROM " + tableName)) { //NON-NLS{
|
||||
|
||||
numRows = resultSet.getInt("count");
|
||||
try {
|
||||
numRows = sqliteReader.getRowCountFromTable(tableName);
|
||||
numEntriesField.setText(numRows + " entries");
|
||||
|
||||
currPage = 1;
|
||||
@@ -503,9 +416,12 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
selectedTableView.setupTable(Collections.emptyList());
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName));
|
||||
} catch (FileReaderException ex) {
|
||||
logger.log(Level.SEVERE, String.format(
|
||||
"Failed to load table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS
|
||||
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
|
||||
MessageNotifyUtil.Message.error(
|
||||
Bundle.SQLiteViewer_selectTable_errorText(tableName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,109 +429,108 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
"SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"})
|
||||
private void readTable(String tableName, int startRow, int numRowsToRead) {
|
||||
|
||||
try (
|
||||
Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery(
|
||||
"SELECT * FROM " + tableName
|
||||
+ " LIMIT " + Integer.toString(numRowsToRead)
|
||||
+ " OFFSET " + Integer.toString(startRow - 1))) {
|
||||
|
||||
ArrayList<Map<String, Object>> rows = resultSetToArrayList(resultSet);
|
||||
try {
|
||||
List<Map<String, Object>> rows = sqliteReader.getRowsFromTable(
|
||||
tableName, startRow, numRowsToRead);
|
||||
if (Objects.nonNull(rows)) {
|
||||
selectedTableView.setupTable(rows);
|
||||
} else {
|
||||
selectedTableView.setupTable(Collections.emptyList());
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
|
||||
} catch (FileReaderException ex) {
|
||||
logger.log(Level.SEVERE, String.format(
|
||||
"Failed to read table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS
|
||||
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
|
||||
MessageNotifyUtil.Message.error(
|
||||
Bundle.SQLiteViewer_readTable_errorText(tableName));
|
||||
}
|
||||
}
|
||||
|
||||
@NbBundle.Messages("SQLiteViewer.BlobNotShown.message=BLOB Data not shown")
|
||||
private ArrayList<Map<String, Object>> resultSetToArrayList(ResultSet rs) throws SQLException {
|
||||
ResultSetMetaData metaData = rs.getMetaData();
|
||||
int columns = metaData.getColumnCount();
|
||||
ArrayList<Map<String, Object>> rowlist = new ArrayList<>();
|
||||
while (rs.next()) {
|
||||
Map<String, Object> row = new LinkedHashMap<>(columns);
|
||||
for (int i = 1; i <= columns; ++i) {
|
||||
if (rs.getObject(i) == null) {
|
||||
row.put(metaData.getColumnName(i), "");
|
||||
} else {
|
||||
if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) {
|
||||
row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message());
|
||||
} else {
|
||||
row.put(metaData.getColumnName(i), rs.getObject(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
rowlist.add(row);
|
||||
}
|
||||
|
||||
return rowlist;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
|
||||
"SQLiteViewer.exportTableToCsv.FileName=File name: ",
|
||||
"SQLiteViewer.exportTableToCsv.TableName=Table name: "
|
||||
/**
|
||||
* Converts a sqlite table into a CSV file.
|
||||
*
|
||||
* @param file
|
||||
* @param tableName
|
||||
* @param rowMap A list of rows in the table, where each row is represented as a column-value
|
||||
* map.
|
||||
* @throws FileNotFoundException
|
||||
* @throws IOException
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"SQLiteViewer.exportTableToCsv.FileName=File name: ",
|
||||
"SQLiteViewer.exportTableToCsv.TableName=Table name: "
|
||||
})
|
||||
public void exportTableToCSV(File file, String tableName,
|
||||
List<Map<String, Object>> rowMap) throws FileNotFoundException, IOException{
|
||||
|
||||
File csvFile;
|
||||
String fileName = file.getName();
|
||||
if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
|
||||
csvFile = file;
|
||||
} else {
|
||||
csvFile = new File(file.toString() + ".csv");
|
||||
}
|
||||
|
||||
try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
|
||||
|
||||
out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
|
||||
out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
|
||||
|
||||
String header = createColumnHeader(rowMap.get(0)).concat("\n");
|
||||
out.write(header.getBytes());
|
||||
|
||||
for (Map<String, Object> maps : rowMap) {
|
||||
String row = maps.values()
|
||||
.stream()
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.joining(","))
|
||||
.concat("\n");
|
||||
out.write(row.getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
|
||||
})
|
||||
private void exportTableToCsv(File file) {
|
||||
String tableName = (String) this.tablesDropdownList.getSelectedItem();
|
||||
try (
|
||||
Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery("SELECT * FROM " + tableName)) {
|
||||
List<Map<String, Object>> currentTableRows = resultSetToArrayList(resultSet);
|
||||
try {
|
||||
List<Map<String, Object>> currentTableRows =
|
||||
sqliteReader.getRowsFromTable(tableName);
|
||||
|
||||
if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) {
|
||||
logger.log(Level.INFO, String.format("The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId())); //NON-NLS
|
||||
logger.log(Level.INFO, String.format(
|
||||
"The table %s is empty. (objId=%d)", tableName, //NON-NLS
|
||||
sqliteDbFile.getId()));
|
||||
} else {
|
||||
File csvFile;
|
||||
String fileName = file.getName();
|
||||
if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
|
||||
csvFile = file;
|
||||
} else {
|
||||
csvFile = new File(file.toString() + ".csv");
|
||||
}
|
||||
|
||||
try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
|
||||
|
||||
out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
|
||||
out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
|
||||
// Set up the column names
|
||||
Map<String, Object> row = currentTableRows.get(0);
|
||||
StringBuffer header = new StringBuffer();
|
||||
for (Map.Entry<String, Object> col : row.entrySet()) {
|
||||
String colName = col.getKey();
|
||||
if (header.length() > 0) {
|
||||
header.append(',').append(colName);
|
||||
} else {
|
||||
header.append(colName);
|
||||
}
|
||||
}
|
||||
out.write(header.append('\n').toString().getBytes());
|
||||
|
||||
for (Map<String, Object> maps : currentTableRows) {
|
||||
StringBuffer valueLine = new StringBuffer();
|
||||
maps.values().forEach((value) -> {
|
||||
if (valueLine.length() > 0) {
|
||||
valueLine.append(',').append(value.toString());
|
||||
} else {
|
||||
valueLine.append(value.toString());
|
||||
}
|
||||
});
|
||||
out.write(valueLine.append('\n').toString().getBytes());
|
||||
}
|
||||
}
|
||||
exportTableToCSV(file, tableName, currentTableRows);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
|
||||
} catch (FileReaderException ex) {
|
||||
logger.log(Level.SEVERE, String.format(
|
||||
"Failed to read table %s from DB file '%s' (objId=%d)", //NON-NLS
|
||||
tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
|
||||
MessageNotifyUtil.Message.error(
|
||||
Bundle.SQLiteViewer_readTable_errorText(tableName));
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText());
|
||||
logger.log(Level.SEVERE, String.format(
|
||||
"Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(
|
||||
Bundle.SQLiteViewer_exportTableToCsv_write_errText());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a comma seperated header string from the keys of the column
|
||||
* row map.
|
||||
*
|
||||
* @param row column header row map
|
||||
* @return comma seperated header string
|
||||
*/
|
||||
private String createColumnHeader(Map<String, Object> row) {
|
||||
return row.entrySet()
|
||||
.stream()
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.optionalArgument('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);
|
||||
String[] dir= values.get(liveAutopsyOption);
|
||||
String directory = dir == null ? PlatformUtil.getUserDirectory().toString() : dir[0];
|
||||
ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_BASECASE, directory);
|
||||
} catch (RuntimeProperties.RuntimePropertiesException ex) {
|
||||
logger.log(Level.SEVERE, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -44,6 +46,33 @@ public class RuntimeProperties {
|
||||
throw new RuntimePropertiesException("The runningWithGUI flag has already been set and cannot be changed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -70,7 +70,8 @@ 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,21 +401,25 @@ public final class UserPreferences {
|
||||
|
||||
/**
|
||||
* get the maximum number of log files to save
|
||||
*
|
||||
* @return Number of log files
|
||||
*/
|
||||
public static int getLogFileCount() {
|
||||
return preferences.getInt(MAX_NUM_OF_LOG_FILE, LOG_FILE_NUM_INT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
@@ -370,6 +370,13 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
|
||||
this.currentRootNode = rootNode;
|
||||
if (this.currentRootNode != null) {
|
||||
/*
|
||||
* The only place we reset the rootNodeListener allowing the
|
||||
* contents of the results tab represented by this node to be
|
||||
* changed a single time before it is necessary to reset it again.
|
||||
* Necessary when transitioning from "Please wait..." node to having
|
||||
* contents.
|
||||
*/
|
||||
rootNodeListener.reset();
|
||||
this.currentRootNode.addNodeListener(rootNodeListener);
|
||||
}
|
||||
@@ -518,7 +525,6 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
|
||||
/**
|
||||
* Closes down the component. Intended to be called by the parent top
|
||||
>>>>>>> custom-release-may-2018
|
||||
* component when it is closed.
|
||||
*/
|
||||
void close() {
|
||||
@@ -529,7 +535,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
|
||||
this.resultViewers.forEach((viewer) -> viewer.setNode(null));
|
||||
|
||||
if (!this.isMain) { // RJCTODO: What?
|
||||
if (!this.isMain) {
|
||||
this.resultViewers.forEach(DataResultViewer::clearComponent);
|
||||
this.descriptionLabel.removeAll();
|
||||
this.numberOfChildNodesLabel.removeAll();
|
||||
@@ -581,11 +587,13 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
|
||||
/**
|
||||
* Responds to changes in the root node due to asynchronous child node
|
||||
* creation.
|
||||
* creation. This listener allows for the tabs of the result viewer to be
|
||||
* set up again after the "Please wait..." node has ended and actual content
|
||||
* should be displayed in the table.
|
||||
*/
|
||||
// RJCTODO: Why do we need this?
|
||||
private class RootNodeListener implements NodeListener {
|
||||
|
||||
//it is assumed we are still waiting for data when the node is initially constructed
|
||||
private volatile boolean waitingForData = true;
|
||||
|
||||
public void reset() {
|
||||
@@ -598,10 +606,12 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
updateMatches();
|
||||
|
||||
/*
|
||||
* There is a known issue in this code whereby we will only call
|
||||
* setupTabs() once even though childrenAdded could be called
|
||||
* multiple times. That means that each panel may not have access to
|
||||
* all of the children when they decide if they support the content
|
||||
* Ensures that after the initial call to setupTabs in the
|
||||
* DataResultPanel.setNode method that we only call setupTabs one
|
||||
* additional time. This is to account for the transition that is
|
||||
* possible from a "Please wait..." node or a tab with no results in
|
||||
* it and a tab containing data and thereby having all of it's
|
||||
* columns.
|
||||
*/
|
||||
if (waitingForData && containsReal(delta)) {
|
||||
waitingForData = false;
|
||||
|
||||
@@ -205,6 +205,18 @@ public class ImageUtils {
|
||||
}
|
||||
AbstractFile file = (AbstractFile) content;
|
||||
|
||||
/**
|
||||
* Before taking on the potentially costly task of calculating the MIME
|
||||
* type that can happen in isMediaThumbnailSupported() below, let's
|
||||
* first see if the file extension is in the set of supported media file
|
||||
* extensions.
|
||||
*/
|
||||
List<String> supportedExtensions = new ArrayList<>(SUPPORTED_IMAGE_EXTENSIONS);
|
||||
supportedExtensions.addAll(VideoUtils.getSupportedVideoExtensions());
|
||||
if (isSupportedMediaExtension(file, supportedExtensions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return VideoUtils.isVideoThumbnailSupported(file)
|
||||
|| isImageThumbnailSupported(file);
|
||||
}
|
||||
@@ -258,9 +270,7 @@ public class ImageUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
String extension = file.getNameExtension();
|
||||
|
||||
if (StringUtils.isNotBlank(extension) && supportedExtension.contains(extension)) {
|
||||
if (isSupportedMediaExtension(file, supportedExtension)) {
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
@@ -276,6 +286,21 @@ public class ImageUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the given file have an extension in the given list of supported
|
||||
* extensions.
|
||||
*
|
||||
* @param file
|
||||
* @param supportedExtensions
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static boolean isSupportedMediaExtension(final AbstractFile file, final List<String> supportedExtensions) {
|
||||
String extension = file.getNameExtension();
|
||||
|
||||
return (StringUtils.isNotBlank(extension) && supportedExtensions.contains(extension));
|
||||
}
|
||||
|
||||
/**
|
||||
* //TODO: AUT-2057 this FileTypeDetector needs to be recreated when the
|
||||
* user adds new user defined file types.
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/*
|
||||
* 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");
|
||||
* 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.
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.*;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -456,10 +454,10 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
dataSourceStr));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If EXIF, add props for file size and path
|
||||
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
|
||||
|
||||
|
||||
long size = 0;
|
||||
String path = ""; //NON-NLS
|
||||
if (associated instanceof AbstractFile) {
|
||||
|
||||
@@ -116,7 +116,7 @@ public interface DisplayableItemNodeVisitor<T> {
|
||||
|
||||
T visit(InterestingHits.SetNameNode ihsn);
|
||||
|
||||
T visit(CommonAttributeValueNode mn);
|
||||
T visit(CommonAttributeValueNode cavn);
|
||||
|
||||
T visit(CommonAttributeSearchResultRootNode cfn);
|
||||
|
||||
@@ -200,8 +200,8 @@ public interface DisplayableItemNodeVisitor<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(CommonAttributeValueNode mn) {
|
||||
return defaultVisit(mn);
|
||||
public T visit(CommonAttributeValueNode cavn) {
|
||||
return defaultVisit(cavn);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/*
|
||||
* 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");
|
||||
* 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.
|
||||
@@ -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(
|
||||
@@ -126,19 +125,18 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
actionsList.addAll(ContextMenuExtensionPoint.getActions());
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
@@ -40,7 +40,7 @@ public class LocalDirectoryNode extends SpecialDirectoryNode {
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/Folder-icon.png"); //NON-NLS
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({
|
||||
"LocalDirectoryNode.createSheet.name.name=Name",
|
||||
|
||||
@@ -96,7 +96,7 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
|
||||
public Action[] getActions(boolean context) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
actionsList.addAll(Arrays.asList(super.getActions(true)));
|
||||
|
||||
|
||||
actionsList.add(new ViewContextAction(NbBundle.getMessage(this.getClass(), "LocalFileNode.viewFileInDir.text"), this.content));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(new NewWindowViewAction(
|
||||
@@ -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
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
*
|
||||
* Copyright 2011-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.
|
||||
@@ -55,23 +55,33 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
@@ -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,12 +142,20 @@ 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 {
|
||||
|
||||
private final long datasourceObjId;
|
||||
|
||||
|
||||
private final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
|
||||
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
|
||||
Case.Events.CONTENT_TAG_ADDED,
|
||||
@@ -197,13 +213,14 @@ public class Tags implements AutopsyVisitableItem {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param objId data source object id
|
||||
*/
|
||||
TagNameNodeFactory(long objId) {
|
||||
this.datasourceObjId = objId;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -59,7 +57,7 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode {
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/folder-icon-virtual.png"); //TODO NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({"VirtualDirectoryNode.createSheet.size.name=Size (Bytes)",
|
||||
"VirtualDirectoryNode.createSheet.size.displayName=Size (Bytes)",
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -124,4 +124,5 @@ GroupDataSourcesDialog.dataSourceCountLabel.text=jLabel1
|
||||
GroupDataSourcesDialog.queryLabel.text=Would you like to group by data source for faster loading?
|
||||
GroupDataSourcesDialog.yesButton.text=Yes
|
||||
GroupDataSourcesDialog.noButton.text=No
|
||||
GroupDataSourcesDialog.title=Group by Data Source?
|
||||
GroupDataSourcesDialog.title=Group by Data Source?
|
||||
DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text=Hide Other User's Tags
|
||||
|
||||
@@ -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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showOnlyCurrentUserTagsCheckboxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -53,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).
|
||||
@@ -110,10 +112,12 @@ public final class ExtractAction extends AbstractAction {
|
||||
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) event.getSource()) == JFileChooser.APPROVE_OPTION) {
|
||||
updateExportDirectory(fileChooser.getSelectedFile().getParent(), openCase);
|
||||
|
||||
ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>();
|
||||
fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile()));
|
||||
runExtractionTasks(event, fileExtractionTasks);
|
||||
@@ -137,7 +141,7 @@ public final class ExtractAction extends AbstractAction {
|
||||
}
|
||||
JFileChooser folderChooser = new JFileChooser();
|
||||
folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
folderChooser.setCurrentDirectory(new File(openCase.getExportDirectory()));
|
||||
folderChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
|
||||
if (folderChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {
|
||||
File destinationFolder = folderChooser.getSelectedFile();
|
||||
if (!destinationFolder.exists()) {
|
||||
@@ -150,6 +154,7 @@ public final class ExtractAction extends AbstractAction {
|
||||
return;
|
||||
}
|
||||
}
|
||||
updateExportDirectory(destinationFolder.getPath(), openCase);
|
||||
|
||||
/*
|
||||
* get the unique set of files from the list. A user once reported
|
||||
@@ -169,6 +174,45 @@ public final class ExtractAction extends AbstractAction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the export directory path.
|
||||
*
|
||||
* @param openCase The current case.
|
||||
*
|
||||
* @return The export directory path.
|
||||
*/
|
||||
private String getExportDirectory(Case openCase) {
|
||||
String caseExportPath = openCase.getExportDirectory();
|
||||
|
||||
if (userDefinedExportPath == null) {
|
||||
return caseExportPath;
|
||||
}
|
||||
|
||||
File file = new File(userDefinedExportPath);
|
||||
if (file.exists() == false || file.isDirectory() == false) {
|
||||
return caseExportPath;
|
||||
}
|
||||
|
||||
return userDefinedExportPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the default export directory. If the directory path matches the
|
||||
* case export directory, then the directory used will always match the
|
||||
* export directory of any given case. Otherwise, the path last used will be
|
||||
* saved.
|
||||
*
|
||||
* @param exportPath The export path.
|
||||
* @param openCase The current case.
|
||||
*/
|
||||
private void updateExportDirectory(String exportPath, Case openCase) {
|
||||
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
|
||||
userDefinedExportPath = null;
|
||||
} else {
|
||||
userDefinedExportPath = exportPath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a series of file extraction tasks.
|
||||
*
|
||||
|
||||
@@ -67,6 +67,7 @@ final class ExtractUnallocAction extends AbstractAction {
|
||||
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;
|
||||
|
||||
@@ -159,7 +160,7 @@ 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);
|
||||
@@ -167,6 +168,9 @@ final class ExtractUnallocAction extends AbstractAction {
|
||||
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);
|
||||
|
||||
@@ -228,7 +232,45 @@ final class ExtractUnallocAction extends AbstractAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the export directory path.
|
||||
*
|
||||
* @param openCase The current case.
|
||||
*
|
||||
* @return The export directory path.
|
||||
*/
|
||||
private String getExportDirectory(Case openCase) {
|
||||
String caseExportPath = openCase.getExportDirectory();
|
||||
|
||||
if (userDefinedExportPath == null) {
|
||||
return caseExportPath;
|
||||
}
|
||||
|
||||
File file = new File(userDefinedExportPath);
|
||||
if (file.exists() == false || file.isDirectory() == false) {
|
||||
return caseExportPath;
|
||||
}
|
||||
|
||||
return userDefinedExportPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the default export directory. If the directory path matches the
|
||||
* case export directory, then the directory used will always match the
|
||||
* export directory of any given case. Otherwise, the path last used will be
|
||||
* saved.
|
||||
*
|
||||
* @param exportPath The export path.
|
||||
* @param openCase The current case.
|
||||
*/
|
||||
private void updateExportDirectory(String exportPath, Case openCase) {
|
||||
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
|
||||
userDefinedExportPath = null;
|
||||
} else {
|
||||
userDefinedExportPath = exportPath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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("");
|
||||
}
|
||||
@@ -69,7 +83,7 @@ public class StatusIconCellRenderer extends GrayableCellRenderer {
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public enum Status {
|
||||
OK,
|
||||
WARNING,
|
||||
|
||||
@@ -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"
|
||||
|
||||
+292
-109
@@ -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;
|
||||
|
||||
@@ -29,7 +29,7 @@ import javax.swing.JOptionPane;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
|
||||
@@ -532,7 +532,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
|
||||
|
||||
try{
|
||||
int referenceSetID = EamDb.getInstance().newReferenceSet(new EamGlobalSet(selectedOrg.getOrgID(), hashSetNameTextField.getText(),
|
||||
"", fileKnown, false, EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID)));
|
||||
"", fileKnown, false, EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID)));
|
||||
newHashDb = HashDbManager.getInstance().addExistingCentralRepoHashSet(hashSetNameTextField.getText(),
|
||||
"", referenceSetID,
|
||||
true, sendIngestMessagesCheckbox.isSelected(), type, false);
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamGlobalFileInstance;
|
||||
@@ -491,7 +491,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
List<HashDbInfo> crHashSets = new ArrayList<>();
|
||||
if(EamDb.isEnabled()){
|
||||
try{
|
||||
List<EamGlobalSet> crSets = EamDb.getInstance().getAllReferenceSets(EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID));
|
||||
List<EamGlobalSet> crSets = EamDb.getInstance().getAllReferenceSets(EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID));
|
||||
for(EamGlobalSet globalSet:crSets){
|
||||
|
||||
// Defaults for fields not stored in the central repository:
|
||||
@@ -1237,7 +1237,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
try{
|
||||
EamGlobalFileInstance fileInstance = new EamGlobalFileInstance(referenceSetID, file.getMd5Hash(),
|
||||
type, comment);
|
||||
EamDb.getInstance().addReferenceInstance(fileInstance,EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID));
|
||||
EamDb.getInstance().addReferenceInstance(fileInstance,EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID));
|
||||
} catch (EamDbException ex){
|
||||
throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex);
|
||||
}
|
||||
@@ -1271,7 +1271,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
|
||||
try{
|
||||
EamDb.getInstance().bulkInsertReferenceTypeEntries(globalFileInstances,
|
||||
EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID));
|
||||
EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID));
|
||||
} catch (EamDbException ex){
|
||||
throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex);
|
||||
}
|
||||
|
||||
+5
-3
@@ -33,7 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.Executors;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamGlobalFileInstance;
|
||||
@@ -48,6 +48,8 @@ import org.sleuthkit.datamodel.TskData;
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements PropertyChangeListener {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private CentralRepoImportWorker worker; // Swing worker that will import the file and send updates to the dialog
|
||||
|
||||
@NbBundle.Messages({"ImportCentralRepoDbProgressDialog.title.text=Central Repository Import Progress",})
|
||||
@@ -239,11 +241,11 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P
|
||||
// Create an empty hashset in the central repository
|
||||
EamDb dbManager = EamDb.getInstance();
|
||||
referenceSetID.set(dbManager.newReferenceSet(new EamGlobalSet(orgId, hashSetName, version, knownStatus,
|
||||
readOnly, EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID))));
|
||||
readOnly, EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID))));
|
||||
|
||||
// Get the "FILES" content type. This is a database lookup so we
|
||||
// only want to do it once.
|
||||
CorrelationAttribute.Type contentType = dbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
|
||||
CorrelationAttributeInstance.Type contentType = dbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
|
||||
|
||||
// Holds the current batch of hashes that need to be written to the central repo
|
||||
Set<EamGlobalFileInstance> globalInstances = new HashSet<>();
|
||||
|
||||
@@ -97,8 +97,7 @@ public final class JythonModuleLoader {
|
||||
if (file.isDirectory()) {
|
||||
File[] pythonScripts = file.listFiles(new PythonScriptFileFilter());
|
||||
for (File script : pythonScripts) {
|
||||
try {
|
||||
Scanner fileScanner = new Scanner(script);
|
||||
try (Scanner fileScanner = new Scanner(script)) {
|
||||
while (fileScanner.hasNextLine()) {
|
||||
String line = fileScanner.nextLine();
|
||||
if (line.startsWith("class ") && filter.accept(line)) { //NON-NLS
|
||||
|
||||
@@ -784,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) {
|
||||
@@ -1294,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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-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.tabulardatareader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* An abstract reader interface for retrieving contents from files via a common
|
||||
* API.
|
||||
*/
|
||||
public abstract class AbstractReader implements AutoCloseable {
|
||||
|
||||
public AbstractReader(AbstractFile file, String localDiskPath)
|
||||
throws FileReaderInitException {
|
||||
|
||||
writeDataSourceToLocalDisk(file, localDiskPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the data source file contents to local drive for processing.
|
||||
* This function is common to all readers.
|
||||
*
|
||||
* @param file AbstractFile from the data source
|
||||
* @param localDiskPath Local drive path to copy AbstractFile contents
|
||||
* @throws IOException Exception writing file contents
|
||||
* @throws NoCurrentCaseException Current case closed during file copying
|
||||
* @throws TskCoreException Exception finding files from abstract file
|
||||
*/
|
||||
private void writeDataSourceToLocalDisk(AbstractFile file, String localDiskPath)
|
||||
throws FileReaderInitException {
|
||||
|
||||
try {
|
||||
File localDatabaseFile = new File(localDiskPath);
|
||||
if (!localDatabaseFile.exists()) {
|
||||
ContentUtils.writeToFile(file, localDatabaseFile);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new FileReaderInitException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the a mapping of table names to table schemas (may be in the form of
|
||||
* headers or create table statements for databases).
|
||||
*
|
||||
* @return Mapping of table names to schemas
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
public abstract Map<String, String> getTableSchemas() throws FileReaderException;
|
||||
|
||||
/**
|
||||
* Returns the row count fo the given table name.
|
||||
*
|
||||
* @param tableName
|
||||
* @return number of rows in the current table
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
public abstract Integer getRowCountFromTable(String tableName) throws FileReaderException;
|
||||
|
||||
/**
|
||||
* Returns a collection view of the rows in a table.
|
||||
*
|
||||
* @param tableName
|
||||
* @return List view of the rows in the table
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
public abstract List<Map<String, Object>> getRowsFromTable(String tableName) throws FileReaderException;
|
||||
|
||||
/**
|
||||
* Returns a window of rows starting at the offset and ending when the number of rows read
|
||||
* equals the 'numRowsToRead' parameter or there is nothing left to read.
|
||||
*
|
||||
* @param tableName table name to be read from
|
||||
* @param offset start index to begin reading
|
||||
* @param numRowsToRead number of rows to read past offset
|
||||
* @return List view of the rows in the table
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
public abstract List<Map<String, Object>> getRowsFromTable(String tableName,
|
||||
int offset, int numRowsToRead) throws FileReaderException;
|
||||
|
||||
@Override
|
||||
public abstract void close();
|
||||
|
||||
/**
|
||||
* Checked exceptions are specific to a given implementation, so this custom
|
||||
* exception allows for a common interface to accommodate all of them. Init
|
||||
* exception allows for more flexibility in logging.
|
||||
*/
|
||||
public static class FileReaderInitException extends Exception {
|
||||
public FileReaderInitException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public FileReaderInitException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public FileReaderInitException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checked exceptions are specific to a given implementation, so this custom
|
||||
* exception allows for a common interface to accommodate all of them.
|
||||
*/
|
||||
public class FileReaderException extends Exception {
|
||||
public FileReaderException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public FileReaderException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public FileReaderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-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.tabulardatareader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import com.monitorjbl.xlsx.StreamingReader;
|
||||
import org.apache.poi.hssf.OldExcelFormatException;
|
||||
|
||||
|
||||
/**
|
||||
* Reads excel files and implements the abstract reader api for interfacing with the
|
||||
* content. Supports .xls and .xlsx files.
|
||||
*/
|
||||
public final class ExcelReader extends AbstractReader {
|
||||
/* Boilerplate code */
|
||||
private final static IngestServices services = IngestServices.getInstance();
|
||||
private final static Logger logger = services.getLogger(ExcelReader.class.getName());
|
||||
|
||||
private Workbook workbook;
|
||||
private final static String XLSX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
private final static String XLS_MIME_TYPE = "application/vnd.ms-excel";
|
||||
private final static String EMPTY_CELL_STRING = "";
|
||||
private Map<String, Row> headerCache;
|
||||
|
||||
public ExcelReader(AbstractFile file, String localDiskPath, String mimeType)
|
||||
throws FileReaderInitException {
|
||||
super(file, localDiskPath);
|
||||
try {
|
||||
this.workbook = createWorkbook(localDiskPath, mimeType);
|
||||
headerCache = new HashMap<>();
|
||||
} catch (IOException ex) {
|
||||
throw new FileReaderInitException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal factory for creating the correct workbook given the mime type. The
|
||||
* file reader factory in this module passes both the XLSMimeType and XLSXMimeType
|
||||
* into this constructor for the reader to handle. This avoided the need for creating
|
||||
* an AbstractExcelReader class and two sub classes overriding the workbook field.
|
||||
* Additionally, I don't forsee needing to support more than these two mime types.
|
||||
*
|
||||
* @param localDiskPath To open an input stream for poi to read from
|
||||
* @param mimeType The mimeType passed to the constructor
|
||||
* @return The corrent workbook instance
|
||||
* @throws IOException Issue with input stream and opening file location at
|
||||
* localDiskPath
|
||||
* @throws FileReaderInitException mimetype unsupported
|
||||
*/
|
||||
private Workbook createWorkbook(String localDiskPath, String mimeType) throws
|
||||
IOException, FileReaderInitException {
|
||||
switch (mimeType) {
|
||||
case XLS_MIME_TYPE:
|
||||
try {
|
||||
//Apache POI only supports BIFF8 format, anything below is considered
|
||||
//old excel format and is not a concern for us.
|
||||
return new HSSFWorkbook(new FileInputStream(new File(localDiskPath)));
|
||||
} catch (OldExcelFormatException e) {
|
||||
throw new FileReaderInitException(e);
|
||||
}
|
||||
case XLSX_MIME_TYPE:
|
||||
//StreamingReader is part of the xlsx streamer dependency that creates
|
||||
//a streaming version of XSSFWorkbook for reading (SXSSFWorkbook is only for writing
|
||||
//large workbooks, not reading). This libary provides a workbook interface
|
||||
//that is mostly identical to the poi workbook api, hence both the HSSFWorkbook
|
||||
//and this can use the same functions below.
|
||||
return StreamingReader.builder().rowCacheSize(500).open(new File(localDiskPath));
|
||||
default:
|
||||
throw new FileReaderInitException(String.format("Excel reader for mime " +
|
||||
"type [%s] is not supported", mimeType));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows in a given excel table (aka sheet).
|
||||
*
|
||||
* @param tableName Name of table to count total rows from
|
||||
* @return row count for requested table name
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
@Override
|
||||
public Integer getRowCountFromTable(String tableName) throws FileReaderException {
|
||||
return workbook.getSheet(tableName).getLastRowNum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of all the rows from a given table in an excel document.
|
||||
*
|
||||
* @param tableName Current sheet name being read
|
||||
* @return A collection of row maps
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
@Override
|
||||
public List<Map<String, Object>> getRowsFromTable(String tableName) throws FileReaderException {
|
||||
//Pad with + 1 because rows are zero index, thus a LastRowNum() (in getRowCountFromTable()) of 1
|
||||
//indicates that there are records in 0 and 1 and so a total row count of
|
||||
//2. This also implies there is no way to determine if a workbook is empty,
|
||||
//since a last row num of 0 doesnt differentiate between a record in 0 or
|
||||
//nothing in the workbook. Such a HSSF.
|
||||
return getRowsFromTable(tableName, 0, getRowCountFromTable(tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a window of rows starting at the offset and ending when the number of rows read
|
||||
* equals the 'numRowsToRead' parameter or the iterator has nothing left to read.
|
||||
*
|
||||
* For instance: offset 1, numRowsToRead 5 would return 5 results (1-5).
|
||||
* offset 0, numRowsToRead 5 would return 5 results (0-4).
|
||||
*
|
||||
* @param tableName Current name of sheet to be read
|
||||
* @param offset start index to begin reading (documents are 0 indexed)
|
||||
* @param numRowsToRead number of rows to read
|
||||
* @return
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
@Override
|
||||
public List<Map<String, Object>> getRowsFromTable(String tableName,
|
||||
int offset, int numRowsToRead) throws FileReaderException {
|
||||
//StreamingReader maintains the same pointer to a sheet rowIterator, so this
|
||||
//call returns an iterator that could have already been iterated on instead
|
||||
//of a fresh copy. We must cache the header value from the call to
|
||||
//getTableSchemas as important information in the first row could have been
|
||||
//missed.
|
||||
Iterator<Row> sheetIter = workbook.getSheet(tableName).rowIterator();
|
||||
List<Map<String, Object>> rowList = new ArrayList<>();
|
||||
|
||||
//Read the header value as the header may be a row of data in the
|
||||
//excel sheet
|
||||
if(headerCache.containsKey(tableName)) {
|
||||
Row header = headerCache.get(tableName);
|
||||
if(header.getRowNum() >= offset
|
||||
&& header.getRowNum() < (offset + numRowsToRead)) {
|
||||
rowList.add(getRowMap(tableName, header));
|
||||
}
|
||||
}
|
||||
|
||||
while(sheetIter.hasNext()) {
|
||||
Row currRow = sheetIter.next();
|
||||
//If the current row number is within the window of our row capture
|
||||
if(currRow.getRowNum() >= offset
|
||||
&& currRow.getRowNum() < (offset + numRowsToRead)) {
|
||||
rowList.add(getRowMap(tableName, currRow));
|
||||
}
|
||||
|
||||
//if current row number is equal to our upper bound
|
||||
//of rows requested to be read.
|
||||
if(currRow.getRowNum() >= (offset + numRowsToRead)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rowList;
|
||||
}
|
||||
|
||||
private Map<String, Object> getRowMap(String tableName, Row row) {
|
||||
Map<String, Object> rowMap = new HashMap<>();
|
||||
for(Cell cell : row) {
|
||||
String columnName = getColumnName(cell, tableName);
|
||||
Object value = getCellValue(cell);
|
||||
rowMap.put(columnName, value);
|
||||
}
|
||||
return rowMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a given cell. The correct value function must be
|
||||
* called on a cell depending on its type, hence the switch.
|
||||
*
|
||||
* @param cell Cell object containing a getter function for its value type
|
||||
* @return A generic object pointer to the cell's value
|
||||
*/
|
||||
private Object getCellValue(Cell cell){
|
||||
switch (cell.getCellTypeEnum()) {
|
||||
case BOOLEAN:
|
||||
return cell.getBooleanCellValue();
|
||||
case STRING:
|
||||
return cell.getRichStringCellValue().getString();
|
||||
case NUMERIC:
|
||||
if (DateUtil.isCellDateFormatted(cell)) {
|
||||
return cell.getDateCellValue();
|
||||
} else {
|
||||
return cell.getNumericCellValue();
|
||||
}
|
||||
case FORMULA:
|
||||
return cell.getCellFormula();
|
||||
default:
|
||||
//Cell must be empty at this branch
|
||||
return EMPTY_CELL_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the column that the cell currently lives in
|
||||
* Cell Value: 6784022342 -> Header name: Phone Number
|
||||
*
|
||||
* @param cell current cell being read
|
||||
* @param tableName current sheet name being read
|
||||
* @return the name of the column the current cell lives in
|
||||
*/
|
||||
private String getColumnName(Cell cell, String tableName) {
|
||||
if(headerCache.containsKey(tableName)) {
|
||||
Row header = headerCache.get(tableName);
|
||||
Cell columnHeaderCell = header.getCell(cell.getRowIndex());
|
||||
if(columnHeaderCell == null) {
|
||||
return EMPTY_CELL_STRING;
|
||||
}
|
||||
Object columnHeaderValue = getCellValue(columnHeaderCell);
|
||||
return columnHeaderValue.toString();
|
||||
}
|
||||
//No header present
|
||||
return EMPTY_CELL_STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of sheet names to headers (header is in a comma-seperated string).
|
||||
* Warning: Only call this ONCE per excel file.
|
||||
*
|
||||
* @return A map of sheet names to header strings.
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getTableSchemas() throws FileReaderException {
|
||||
Map<String, String> tableSchemas = new HashMap<>();
|
||||
for(Sheet sheet : workbook) {
|
||||
Iterator<Row> iterator = sheet.rowIterator();
|
||||
if(iterator.hasNext()) {
|
||||
//Consume header
|
||||
Row header = iterator.next();
|
||||
headerCache.put(sheet.getSheetName(), header);
|
||||
String headerStringFormat = StringUtils.join(header.cellIterator(), ", ");
|
||||
tableSchemas.put(sheet.getSheetName(), headerStringFormat);
|
||||
}
|
||||
}
|
||||
|
||||
return tableSchemas;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
workbook.close();
|
||||
} catch (IOException ex) {
|
||||
//Non-essential exception, user has no need for the connection
|
||||
//object at this stage so closing details are not important
|
||||
logger.log(Level.WARNING, "Could not close excel file input stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-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.tabulardatareader;
|
||||
|
||||
import org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
|
||||
/**
|
||||
* Factory for creating the correct reader given the mime type of a file.
|
||||
*/
|
||||
public final class FileReaderFactory {
|
||||
|
||||
private FileReaderFactory() {
|
||||
}
|
||||
/**
|
||||
* Instantiates the appropriate reader given the mimeType argument. Currently
|
||||
* supports SQLite files and Excel files (.xls and .xlsx). BIFF5 format of .xls
|
||||
* is not supported.
|
||||
*
|
||||
* @param mimeType mimeType passed in from the ingest module
|
||||
g * @param file current file under inspection
|
||||
* @param localDiskPath path for abstract file contents to be written
|
||||
* @return The correct reader class needed to read the file contents
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException
|
||||
*/
|
||||
public static AbstractReader createReader(String mimeType, AbstractFile file,
|
||||
String localDiskPath) throws FileReaderInitException {
|
||||
switch (mimeType) {
|
||||
case "application/x-sqlite3":
|
||||
return new SQLiteReader(file, localDiskPath);
|
||||
case "application/vnd.ms-excel":
|
||||
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
|
||||
try {
|
||||
return new ExcelReader(file, localDiskPath, mimeType);
|
||||
//Catches runtime exceptions being emitted from Apache
|
||||
//POI (such as EncryptedDocumentException) and wraps them
|
||||
//into FileReaderInitException to be caught and logged
|
||||
//in the ingest module.
|
||||
} catch(Exception poiInitException) {
|
||||
throw new FileReaderInitException(poiInitException);
|
||||
}
|
||||
default:
|
||||
throw new FileReaderInitException(String.format("Reader for mime "
|
||||
+ "type [%s] is not supported", mimeType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-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.tabulardatareader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||
import org.sleuthkit.autopsy.casemodule.services.Services;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Reads sqlite databases and returns results in a list collection.
|
||||
*/
|
||||
public final class SQLiteReader extends AbstractReader {
|
||||
|
||||
private final Connection connection;
|
||||
private final static IngestServices services = IngestServices.getInstance();
|
||||
private final static Logger logger = services.getLogger(SQLiteReader.class.getName());
|
||||
|
||||
/**
|
||||
* Writes data source file contents to local disk and opens a sqlite JDBC
|
||||
* connection.
|
||||
*
|
||||
* @param sqliteDbFile Data source abstract file
|
||||
* @param localDiskPath Location for database contents to be copied to
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException
|
||||
*/
|
||||
public SQLiteReader(AbstractFile sqliteDbFile, String localDiskPath) throws FileReaderInitException {
|
||||
super(sqliteDbFile, localDiskPath);
|
||||
try {
|
||||
// Look for any meta files associated with this DB - WAL, SHM, etc.
|
||||
findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal");
|
||||
findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm");
|
||||
|
||||
connection = getDatabaseConnection(localDiskPath);
|
||||
} catch (ClassNotFoundException | SQLException |IOException |
|
||||
NoCurrentCaseException | TskCoreException ex) {
|
||||
throw new FileReaderInitException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a meta file associated with the give SQLite database. If found,
|
||||
* copies the file to the local disk folder
|
||||
*
|
||||
* @param sqliteFile file being processed
|
||||
* @param metaFileName name of meta file to look for
|
||||
* @throws NoCurrentCaseException Case has been closed.
|
||||
* @throws TskCoreException fileManager cannot find AbstractFile files.
|
||||
* @throws IOException Issue during writing to file.
|
||||
*/
|
||||
private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile,
|
||||
String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
|
||||
|
||||
Case openCase = Case.getCurrentCaseThrows();
|
||||
SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
|
||||
Services services = new Services(sleuthkitCase);
|
||||
FileManager fileManager = services.getFileManager();
|
||||
|
||||
List<AbstractFile> metaFiles = fileManager.findFiles(
|
||||
sqliteFile.getDataSource(), metaFileName,
|
||||
sqliteFile.getParent().getName());
|
||||
|
||||
if (metaFiles != null) {
|
||||
for (AbstractFile metaFile : metaFiles) {
|
||||
String tmpMetafilePathName = openCase.getTempDirectory() +
|
||||
File.separator + metaFile.getName();
|
||||
File tmpMetafile = new File(tmpMetafilePathName);
|
||||
ContentUtils.writeToFile(metaFile, tmpMetafile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a JDBC connection to the sqlite database specified by the path
|
||||
* parameter.
|
||||
*
|
||||
* @param databasePath Local path of sqlite database
|
||||
* @return Connection JDBC connection, to be maintained and closed by the reader
|
||||
* @throws ClassNotFoundException missing SQLite JDBC class
|
||||
* @throws SQLException Exception during opening database connection
|
||||
*/
|
||||
private Connection getDatabaseConnection(String databasePath)
|
||||
throws ClassNotFoundException, SQLException {
|
||||
|
||||
// Load the SQLite JDBC driver, if necessary.
|
||||
Class.forName("org.sqlite.JDBC"); //NON-NLS
|
||||
return DriverManager.getConnection(
|
||||
"jdbc:sqlite:" + databasePath); //NON-NLS
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a map view of table names to table schemas (in the form of
|
||||
* CREATE TABLE statments).
|
||||
*
|
||||
* @return A map of table names to table schemas
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getTableSchemas() throws FileReaderException {
|
||||
|
||||
Map<String, String> dbTablesMap = new TreeMap<>();
|
||||
|
||||
try (Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery(
|
||||
"SELECT name, sql FROM sqlite_master " //NON-NLS
|
||||
+ " WHERE type= 'table' " //NON-NLS
|
||||
+ " ORDER BY name;")){ //NON-NLS
|
||||
|
||||
while (resultSet.next()) {
|
||||
String tableName = resultSet.getString("name"); //NON-NLS
|
||||
String tableSQL = resultSet.getString("sql"); //NON-NLS
|
||||
dbTablesMap.put(tableName, tableSQL);
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
throw new FileReaderException(ex);
|
||||
}
|
||||
|
||||
return dbTablesMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the total number of rows from a table in the SQLite database.
|
||||
*
|
||||
* @param tableName
|
||||
* @return Row count from tableName
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
@Override
|
||||
public Integer getRowCountFromTable(String tableName)
|
||||
throws FileReaderException {
|
||||
String quotedTableName = wrapTableNameStringWithQuotes(tableName);
|
||||
try (Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery(
|
||||
"SELECT count (*) as count FROM " + quotedTableName)){ //NON-NLS
|
||||
return resultSet.getInt("count"); //NON-NLS
|
||||
} catch (SQLException ex) {
|
||||
throw new FileReaderException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all rows from a given table in the SQLite database. If only a
|
||||
* subset of rows are desired, see the overloaded function below.
|
||||
*
|
||||
* @param tableName
|
||||
* @return List of rows, where each row is
|
||||
* represented as a column-value map.
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
@Override
|
||||
public List<Map<String, Object>> getRowsFromTable(String tableName)
|
||||
throws FileReaderException {
|
||||
//This method does not directly call its overloaded counterpart
|
||||
//since the second parameter would need to be retreived from a call to
|
||||
//getTableRowCount().
|
||||
String quotedTableName = wrapTableNameStringWithQuotes(tableName);
|
||||
try(Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery(
|
||||
"SELECT * FROM " + quotedTableName)) { //NON-NLS
|
||||
return resultSetToList(resultSet);
|
||||
} catch (SQLException ex) {
|
||||
throw new FileReaderException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a subset of the rows from a given table in the SQLite database.
|
||||
*
|
||||
* @param tableName
|
||||
* @param offset Desired start index (rows begin at 1)
|
||||
* @param numRowsToRead Number of rows past the start index
|
||||
* @return List of rows, where each row is
|
||||
* represented as a column-value map.
|
||||
* @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException
|
||||
*/
|
||||
@Override
|
||||
public List<Map<String, Object>> getRowsFromTable(String tableName,
|
||||
int offset, int numRowsToRead) throws FileReaderException{
|
||||
String quotedTableName = wrapTableNameStringWithQuotes(tableName);
|
||||
try(Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery(
|
||||
"SELECT * FROM " + quotedTableName //NON-NLS
|
||||
+ " LIMIT " + Integer.toString(numRowsToRead) //NON-NLS
|
||||
+ " OFFSET " + Integer.toString(offset - 1))) { //NON-NLS
|
||||
return resultSetToList(resultSet);
|
||||
} catch (SQLException ex) {
|
||||
throw new FileReaderException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps table name with quotation marks in case table name contains spaces.
|
||||
* sqliteJDBC cannot read table names with spaces in them unless surrounded
|
||||
* by quotation marks.
|
||||
*
|
||||
* @param tableName
|
||||
* @return Input name: Result Table -> "Result Table"
|
||||
*/
|
||||
private String wrapTableNameStringWithQuotes(String tableName) {
|
||||
return "\"" + tableName +"\"";
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a ResultSet (row results from a table read) into a list.
|
||||
*
|
||||
* @param resultSet row results from a table read
|
||||
* @return List of rows, where each row is
|
||||
* represented as a column-value map.
|
||||
* @throws SQLException occurs if ResultSet is closed while attempting to
|
||||
* access it's data.
|
||||
*/
|
||||
@NbBundle.Messages("SQLiteReader.BlobNotShown.message=BLOB Data not shown")
|
||||
private List<Map<String, Object>> resultSetToList(ResultSet resultSet) throws SQLException {
|
||||
|
||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||
int columns = metaData.getColumnCount();
|
||||
List<Map<String, Object>> rowMap = new ArrayList<>();
|
||||
while (resultSet.next()) {
|
||||
Map<String, Object> row = new LinkedHashMap<>(columns);
|
||||
for (int i = 1; i <= columns; ++i) {
|
||||
if (resultSet.getObject(i) == null) {
|
||||
row.put(metaData.getColumnName(i), "");
|
||||
} else {
|
||||
if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) {
|
||||
row.put(metaData.getColumnName(i), Bundle.SQLiteReader_BlobNotShown_message());
|
||||
} else {
|
||||
row.put(metaData.getColumnName(i), resultSet.getObject(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
rowMap.add(row);
|
||||
}
|
||||
|
||||
return rowMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Closes underlying JDBC connection.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException ex) {
|
||||
//Non-essential exception, user has no need for the connection
|
||||
//object at this stage so closing details are not important
|
||||
logger.log(Level.WARNING, "Could not close JDBC connection", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.testutils;
|
||||
|
||||
|
||||
//import junit.framework.Test;
|
||||
//import org.netbeans.junit.NbModuleSuite;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dsmyda
|
||||
*/
|
||||
public final class SuiteUtils {
|
||||
|
||||
/*
|
||||
public static Test createSuite(Class cls) {
|
||||
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(cls).
|
||||
clusters(".*").
|
||||
enableModules(".*");
|
||||
return conf.suite();
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+360
-378
File diff suppressed because it is too large
Load Diff
+65
-9
@@ -19,17 +19,21 @@
|
||||
*/
|
||||
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 junit.framework.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.
|
||||
@@ -62,9 +66,9 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase {
|
||||
try {
|
||||
this.utils.enableCentralRepo();
|
||||
this.utils.createCases(this.utils.getIngestSettingsForHashAndFileType(), InterCaseTestUtils.CASE3);
|
||||
} catch (Exception ex) {
|
||||
} catch (TskCoreException | EamDbException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex);
|
||||
Assert.fail(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +86,7 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase {
|
||||
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);
|
||||
AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, 0);
|
||||
|
||||
CommonAttributeSearchResults metadata = builder.findFiles();
|
||||
|
||||
@@ -120,9 +124,9 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase {
|
||||
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1));
|
||||
|
||||
|
||||
} catch (Exception ex) {
|
||||
} catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex);
|
||||
Assert.fail(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +139,7 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase {
|
||||
|
||||
int matchesMustAlsoBeFoundInThisCase = this.utils.getCaseMap().get(CASE2);
|
||||
|
||||
AbstractCommonAttributeSearcher builder = new SingleInterCaseCommonAttributeSearcher(matchesMustAlsoBeFoundInThisCase, dataSources, false, false);
|
||||
AbstractCommonAttributeSearcher builder = new SingleInterCaseCommonAttributeSearcher(matchesMustAlsoBeFoundInThisCase, dataSources, false, false, 0);
|
||||
|
||||
CommonAttributeSearchResults metadata = builder.findFiles();
|
||||
|
||||
@@ -173,9 +177,61 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase {
|
||||
assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1));
|
||||
|
||||
|
||||
} catch (Exception ex) {
|
||||
} catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex);
|
||||
Assert.fail(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We should be able to observe that certain files o no longer returned
|
||||
* in the result set since they do not appear frequently enough.
|
||||
*/
|
||||
public void testThree(){
|
||||
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, 50);
|
||||
|
||||
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, 0));
|
||||
|
||||
//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, 0));
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user