diff --git a/Core/ivy.xml b/Core/ivy.xml
index b6cfe4a568..eea67feb19 100755
--- a/Core/ivy.xml
+++ b/Core/ivy.xml
@@ -16,6 +16,7 @@
+
diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties
index 8868378570..7c36c61db7 100755
--- a/Core/nbproject/project.properties
+++ b/Core/nbproject/project.properties
@@ -1,10 +1,13 @@
file.reference.activemq-all-5.11.1.jar=release/modules/ext/activemq-all-5.11.1.jar
file.reference.c3p0-0.9.5.jar=release/modules/ext/c3p0-0.9.5.jar
+file.reference.commons-compress-1.12.jar=release/modules/ext/commons-compress-1.12.jar
+file.reference.commons-dbcp2-2.1.1.jar=release\\modules\\ext\\commons-dbcp2-2.1.1.jar
+file.reference.commons-pool2-2.4.2.jar=release\\modules\\ext\\commons-pool2-2.4.2.jar
file.reference.jdom-2.0.5-contrib.jar=release/modules/ext/jdom-2.0.5-contrib.jar
file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar
file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone-2.7.0.jar
file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar
-file.reference.metadata-extractor-2.8.1.jar=release/modules/ext/metadata-extractor-2.8.1.jar
+file.reference.metadata-extractor-2.9.1.jar=release/modules/ext/metadata-extractor-2.9.1.jar
file.reference.postgresql-9.4.1211.jre7.jar=release/modules/ext/postgresql-9.4.1211.jre7.jar
file.reference.opencv-248.jar=release/modules/ext/opencv-248.jar
file.reference.Rejistry-1.0-SNAPSHOT.jar=release/modules/ext/Rejistry-1.0-SNAPSHOT.jar
@@ -13,6 +16,7 @@ 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.StixLib.jar=release/modules/ext/StixLib.jar
file.reference.tika-core-1.14.jar=release/modules/ext/tika-core-1.14.jar
+file.reference.tika-parsers-1.14.jar=release/modules/ext/tika-parsers-1.14.jar
file.reference.Tsk_DataModel_PostgreSQL.jar=release/modules/ext/Tsk_DataModel_PostgreSQL.jar
file.reference.xmpcore-5.1.2.jar=release/modules/ext/xmpcore-5.1.2.jar
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
@@ -21,12 +25,10 @@ file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8
file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar
javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
-javadoc.reference.metadata-extractor-2.8.1.jar=release/modules/ext/metadata-extractor-2.8.1-src.zip
license.file=../LICENSE-2.0.txt
nbm.homepage=http://www.sleuthkit.org/
nbm.module.author=Brian Carrier
nbm.needs.restart=true
-source.reference.metadata-extractor-2.8.1.jar=release/modules/ext/metadata-extractor-2.8.1-src.zip!/Source/
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
spec.version.base=10.9
diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml
index 0e6b98892f..525e6da6a7 100755
--- a/Core/nbproject/project.xml
+++ b/Core/nbproject/project.xml
@@ -307,7 +307,6 @@
org.sleuthkit.autopsy.datasourceprocessors
org.sleuthkit.autopsy.directorytree
org.sleuthkit.autopsy.events
- org.sleuthkit.autopsy.externalresults
org.sleuthkit.autopsy.filesearch
org.sleuthkit.autopsy.guiutils
org.sleuthkit.autopsy.ingest
@@ -321,93 +320,25 @@
org.sleuthkit.autopsy.report
org.sleuthkit.datamodel
+
+ ext/zookeeper-3.4.6.jar
+ release/modules/ext/zookeeper-3.4.6.jar
+
ext/jdom-2.0.5.jar
release/modules/ext/jdom-2.0.5.jar
-
- ext/postgresql-9.4.1211.jre7.jar
- release/modules/ext/postgresql-9.4.1211.jre7.jar
-
-
- ext/mchange-commons-java-0.2.9.jar
- release/modules/ext/mchange-commons-java-0.2.9.jar
-
-
- ext/c3p0-0.9.5.jar
- release/modules/ext/c3p0-0.9.5.jar
-
-
- ext/xmpcore-5.1.2.jar
- release/modules/ext/xmpcore-5.1.2.jar
-
-
- ext/StixLib.jar
- release/modules/ext/StixLib.jar
-
-
- ext/sqlite-jdbc-3.8.11.jar
- release/modules/ext/sqlite-jdbc-3.8.11.jar
-
-
- ext/opencv-248.jar
- release/modules/ext/opencv-248.jar
-
-
- ext/Rejistry-1.0-SNAPSHOT.jar
- release/modules/ext/Rejistry-1.0-SNAPSHOT.jar
-
-
- ext/activemq-all-5.11.1.jar
- release/modules/ext/activemq-all-5.11.1.jar
-
-
- ext/Rejistry-1.0-SNAPSHOT.jar
- release/modules/ext/Rejistry-1.0-SNAPSHOT.jar
-
-
- ext/jython-standalone-2.7.0.jar
- release/modules/ext/jython-standalone-2.7.0.jar
-
-
- ext/sevenzipjbinding.jar
- release/modules/ext/sevenzipjbinding.jar
-
-
- ext/sevenzipjbinding-AllPlatforms.jar
- release/modules/ext/sevenzipjbinding-AllPlatforms.jar
-
ext/tika-core-1.14.jar
release/modules/ext/tika-core-1.14.jar
-
- ext/metadata-extractor-2.8.1.jar
- release/modules/ext/metadata-extractor-2.8.1.jar
-
-
- ext/metadata-extractor-2.8.1.jar
- release/modules/ext/metadata-extractor-2.8.1.jar
-
-
- ext/jdom-2.0.5-contrib.jar
- release/modules/ext/jdom-2.0.5-contrib.jar
-
ext/Tsk_DataModel_PostgreSQL.jar
release/modules/ext/Tsk_DataModel_PostgreSQL.jar
- ext/zookeeper-3.4.6.jar
- release/modules/ext/zookeeper-3.4.6.jar
-
-
- ext/curator-client-2.8.0.jar
- release/modules/ext/curator-client-2.8.0.jar
-
-
- ext/curator-recipes-2.8.0.jar
- release/modules/ext/curator-recipes-2.8.0.jar
+ ext/opencv-248.jar
+ release/modules/ext/opencv-248.jar
ext/curator-framework-2.8.0.jar
@@ -417,10 +348,78 @@
ext/commons-dbcp2-2.1.1.jar
release\modules\ext\commons-dbcp2-2.1.1.jar
+
+ ext/tika-parsers-1.14.jar
+ release/modules/ext/tika-parsers-1.14.jar
+
+
+ ext/jython-standalone-2.7.0.jar
+ release/modules/ext/jython-standalone-2.7.0.jar
+
+
+ ext/sevenzipjbinding.jar
+ release/modules/ext/sevenzipjbinding.jar
+
+
+ ext/mchange-commons-java-0.2.9.jar
+ release/modules/ext/mchange-commons-java-0.2.9.jar
+
+
+ ext/postgresql-9.4.1211.jre7.jar
+ release/modules/ext/postgresql-9.4.1211.jre7.jar
+
+
+ ext/curator-recipes-2.8.0.jar
+ release/modules/ext/curator-recipes-2.8.0.jar
+
+
+ ext/xmpcore-5.1.2.jar
+ release/modules/ext/xmpcore-5.1.2.jar
+
+
+ ext/StixLib.jar
+ release/modules/ext/StixLib.jar
+
+
+ ext/curator-client-2.8.0.jar
+ release/modules/ext/curator-client-2.8.0.jar
+
+
+ ext/sqlite-jdbc-3.8.11.jar
+ release/modules/ext/sqlite-jdbc-3.8.11.jar
+
+
+ ext/activemq-all-5.11.1.jar
+ release/modules/ext/activemq-all-5.11.1.jar
+
+
+ ext/Rejistry-1.0-SNAPSHOT.jar
+ release/modules/ext/Rejistry-1.0-SNAPSHOT.jar
+
+
+ ext/sevenzipjbinding-AllPlatforms.jar
+ release/modules/ext/sevenzipjbinding-AllPlatforms.jar
+
ext/commons-pool2-2.4.2.jar
release\modules\ext\commons-pool2-2.4.2.jar
+
+ ext/metadata-extractor-2.9.1.jar
+ release/modules/ext/metadata-extractor-2.9.1.jar
+
+
+ ext/commons-compress-1.12.jar
+ release/modules/ext/commons-compress-1.12.jar
+
+
+ ext/jdom-2.0.5-contrib.jar
+ release/modules/ext/jdom-2.0.5-contrib.jar
+
+
+ ext/c3p0-0.9.5.jar
+ release/modules/ext/c3p0-0.9.5.jar
+
diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java
index f665aeb7d5..95b68b87d6 100755
--- a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java
+++ b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2011-2016 Basis Technology Corp.
+ * Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,12 +26,14 @@ import javax.swing.AbstractAction;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import org.openide.util.NbBundle;
+import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.Presenter;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.TskData;
/**
* An abstract base class for Actions that allow users to tag SleuthKit data
@@ -107,7 +109,8 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
if (null != tagNamesMap && !tagNamesMap.isEmpty()) {
for (Map.Entry entry : tagNamesMap.entrySet()) {
String tagDisplayName = entry.getKey();
- JMenuItem tagNameItem = new JMenuItem(tagDisplayName);
+ String notableString = entry.getValue().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ JMenuItem tagNameItem = new JMenuItem(tagDisplayName + notableString);
// for the bookmark tag name only, added shortcut label
if (tagDisplayName.equals(NbBundle.getMessage(AddTagAction.class, "AddBookmarkTagAction.bookmark.text"))) {
tagNameItem.setAccelerator(AddBookmarkTagAction.BOOKMARK_SHORTCUT);
@@ -122,7 +125,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
JMenuItem empty = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.noTags"));
empty.setEnabled(false);
quickTagMenu.add(empty);
- }
+ }
quickTagMenu.addSeparator();
@@ -155,10 +158,10 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
/**
* Method to add to the action listener for each menu item. Allows a tag
* display name to be added to the menu with an action listener without
- * having to instantiate a TagName object for it.
- * When the method is called, the TagName object is created here if it
- * doesn't already exist.
- *
+ * having to instantiate a TagName object for it. When the method is
+ * called, the TagName object is created here if it doesn't already
+ * exist.
+ *
* @param tagDisplayName display name for the tag name
* @param tagName TagName object associated with the tag name,
* may be null
diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties
index 28d4e4006a..1cbc4b70b6 100755
--- a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties
@@ -4,7 +4,7 @@ GetTagNameDialog.okButton.text=OK
GetTagNameDialog.preexistingLabel.text=Pre-existing Tag Names:
GetTagNameDialog.newTagPanel.border.title=New Tag
GetTagNameDialog.tagNameLabel.text=Tag Name:
-GetTagNameAndCommentDialog.newTagButton.text=New Tag Name
+GetTagNameAndCommentDialog.newTagButton.text=New Tag
GetTagNameAndCommentDialog.okButton.text=OK
GetTagNameAndCommentDialog.commentText.toolTipText=Enter an optional tag comment or leave blank
GetTagNameAndCommentDialog.commentText.text=
@@ -12,7 +12,6 @@ GetTagNameAndCommentDialog.commentLabel.text=Comment:
# To change this template, choose Tools | Templates
# and open the template in the editor.
GetTagNameAndCommentDialog.cancelButton.text=Cancel
-GetTagNameAndCommentDialog.tagCombo.toolTipText=Select tag to use
GetTagNameAndCommentDialog.tagLabel.text=Tag:
AddTagAction.bookmarkFile=Bookmark file
AddTagAction.quickTag=Quick Tag
@@ -45,3 +44,4 @@ ShowIngestProgressSnapshotAction.actionName.text=Get Ingest Progress Snapshot
OpenPythonModulesFolderAction.actionName.text=Python Plugins
OpenPythonModulesFolderAction.errorMsg.folderNotFound=Python plugins folder not found: {0}
CTL_OpenPythonModulesFolderAction=Python Plugins
+GetTagNameAndCommentDialog.tagCombo.toolTipText=Select tag to use
diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties
index 195d12c695..d6f865ecf0 100755
--- a/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties
+++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties
@@ -8,7 +8,6 @@ GetTagNameAndCommentDialog.okButton.text=OK
GetTagNameAndCommentDialog.commentText.toolTipText=\u30bf\u30b0\u306e\u30aa\u30d7\u30b7\u30e7\u30ca\u30eb\u306e\u30b3\u30e1\u30f3\u30c8\u3092\u5165\u529b\u307e\u305f\u306f\u7a7a\u6b04\u306b\u3057\u3066\u304f\u3060\u3055\u3044
GetTagNameAndCommentDialog.commentLabel.text=\u30b3\u30e1\u30f3\u30c8\uff1a
GetTagNameAndCommentDialog.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb
-GetTagNameAndCommentDialog.tagCombo.toolTipText=\u4f7f\u7528\u3059\u308b\u30bf\u30b0\u3092\u9078\u629e
GetTagNameAndCommentDialog.tagLabel.text=\u30bf\u30b0\uff1a
AddBlackboardArtifactTagAction.singularTagResult=\u7d50\u679c\u306b\u30bf\u30b0\u3092\u8ffd\u52a0
AddBlackboardArtifactTagAction.pluralTagResult=\u7d50\u679c\u306b\u30bf\u30b0\u3092\u8ffd\u52a0
@@ -48,4 +47,5 @@ CTL_OpenOutputFolder=\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30d5\u30a9\u30eb\u30c
OpenOutputFolder.error1=\u6b21\u306e\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30d5\u30a9\u30eb\u30c0\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0}
OpenOutputFolder.noCaseOpen=\u30aa\u30fc\u30d7\u30f3\u30b1\u30fc\u30b9\u304c\u306a\u3044\u306e\u3067\u3001\u4f5c\u696d\u4e2d\u306e\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30d5\u30a9\u30eb\u30c0\u304c\u3042\u308a\u307e\u305b\u3093\u3002
GetTagNameDialog.illegalChars.msg=\u4f7f\u7528\u3067\u304d\u306a\u3044\u6587\u5b57\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002\n\u6b21\u306e\u6587\u5b57\u306f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\uff1a\\ \: * ? " < > |
-OpenOutputFolder.CouldNotOpenOutputFolder=\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30d5\u30a9\u30eb\u30c0\u304c\u304c\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f
\ No newline at end of file
+OpenOutputFolder.CouldNotOpenOutputFolder=\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30d5\u30a9\u30eb\u30c0\u304c\u304c\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f
+GetTagNameAndCommentDialog.tagCombo.toolTipText=\u4f7f\u7528\u3059\u308b\u30bf\u30b0\u3092\u9078\u629e
diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.java
index d9fd5d364f..86696b0c63 100755
--- a/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.java
+++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.java
@@ -1,15 +1,15 @@
/*
* Autopsy Forensic Browser
- *
+ *
* Copyright 2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -43,6 +43,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.TskData;
/**
* Instances of this Action allow users to delete tags applied to blackboard
@@ -52,7 +53,7 @@ import org.sleuthkit.datamodel.TskCoreException;
"DeleteFileBlackboardArtifactTagAction.deleteTag=Remove Result Tag"
})
public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implements Presenter.Popup {
-
+
private static final Logger LOGGER = Logger.getLogger(DeleteFileBlackboardArtifactTagAction.class.getName());
private static final long serialVersionUID = 1L;
@@ -89,27 +90,27 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem
}
@NbBundle.Messages({"# {0} - artifactID",
- "DeleteFileBlackboardArtifactTagAction.deleteTag.alert=Unable to untag artifact {0}."})
+ "DeleteFileBlackboardArtifactTagAction.deleteTag.alert=Unable to untag artifact {0}."})
protected void deleteTag(TagName tagName, BlackboardArtifactTag artifactTag, long artifactId) {
new SwingWorker() {
@Override
protected Void doInBackground() throws Exception {
TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager();
-
+
// Pull the from the global context to avoid unnecessary calls
// to the database.
- final Collection selectedFilesList =
- new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
+ final Collection selectedFilesList
+ = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
AbstractFile file = selectedFilesList.iterator().next();
-
+
try {
LOGGER.log(Level.INFO, "Removing tag {0} from {1}", new Object[]{tagName.getDisplayName(), file.getName()}); //NON-NLS
tagsManager.deleteBlackboardArtifactTag(artifactTag);
} catch (TskCoreException tskCoreException) {
LOGGER.log(Level.SEVERE, "Error untagging artifact", tskCoreException); //NON-NLS
- Platform.runLater(() ->
- new Alert(Alert.AlertType.ERROR, Bundle.DeleteFileBlackboardArtifactTagAction_deleteTag_alert(artifactId)).show()
+ Platform.runLater(()
+ -> new Alert(Alert.AlertType.ERROR, Bundle.DeleteFileBlackboardArtifactTagAction_deleteTag_alert(artifactId)).show()
);
}
return null;
@@ -133,21 +134,21 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem
* comment.
*/
@NbBundle.Messages({"# {0} - artifactID",
- "DeleteFileBlackboardArtifactTagAction.deleteTags.alert=Unable to untag artifact {0}."})
+ "DeleteFileBlackboardArtifactTagAction.deleteTags.alert=Unable to untag artifact {0}."})
private class TagMenu extends JMenu {
private static final long serialVersionUID = 1L;
TagMenu() {
super(getActionDisplayName());
-
- final Collection selectedBlackboardArtifactsList =
- new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class));
-
- if(!selectedBlackboardArtifactsList.isEmpty()) {
- BlackboardArtifact artifact =
- selectedBlackboardArtifactsList.iterator().next();
-
+
+ final Collection selectedBlackboardArtifactsList
+ = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class));
+
+ if (!selectedBlackboardArtifactsList.isEmpty()) {
+ BlackboardArtifact artifact
+ = selectedBlackboardArtifactsList.iterator().next();
+
// Get the current set of tag names.
TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager();
@@ -163,17 +164,18 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem
// a tag with the associated tag name.
if (null != tagNamesMap && !tagNamesMap.isEmpty()) {
try {
- List existingTagsList =
- Case.getCurrentCase().getServices().getTagsManager()
- .getBlackboardArtifactTagsByArtifact(artifact);
+ List existingTagsList
+ = Case.getCurrentCase().getServices().getTagsManager()
+ .getBlackboardArtifactTagsByArtifact(artifact);
for (Map.Entry entry : tagNamesMap.entrySet()) {
String tagDisplayName = entry.getKey();
TagName tagName = entry.getValue();
- for(BlackboardArtifactTag artifactTag : existingTagsList) {
- if(tagDisplayName.equals(artifactTag.getName().getDisplayName())) {
- JMenuItem tagNameItem = new JMenuItem(tagDisplayName);
+ for (BlackboardArtifactTag artifactTag : existingTagsList) {
+ if (tagDisplayName.equals(artifactTag.getName().getDisplayName())) {
+ String notableString = tagName.getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ JMenuItem tagNameItem = new JMenuItem(tagDisplayName + notableString);
tagNameItem.addActionListener((ActionEvent e) -> {
deleteTag(tagName, artifactTag, artifact.getArtifactID());
});
@@ -187,7 +189,7 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem
}
}
- if(getItemCount() == 0) {
+ if (getItemCount() == 0) {
setEnabled(false);
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteFileContentTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileContentTagAction.java
index e49e0f9170..5c8d4abb74 100755
--- a/Core/src/org/sleuthkit/autopsy/actions/DeleteFileContentTagAction.java
+++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileContentTagAction.java
@@ -42,6 +42,7 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.TskData;
/**
* Instances of this Action allow users to delete tags applied to content.
@@ -169,7 +170,8 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen
TagName tagName = entry.getValue();
for(ContentTag contentTag : existingTagsList) {
if(tagDisplayName.equals(contentTag.getName().getDisplayName())) {
- JMenuItem tagNameItem = new JMenuItem(tagDisplayName);
+ String notableString = tagName.getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ JMenuItem tagNameItem = new JMenuItem(tagDisplayName + notableString);
tagNameItem.addActionListener((ActionEvent e) -> {
deleteTag(tagName, contentTag, file.getId());
});
diff --git a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.form b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.form
index 17a9738dbd..57c43e964b 100755
--- a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.form
+++ b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.form
@@ -28,7 +28,7 @@
-
+
@@ -39,11 +39,10 @@
-
+
-
+
-
@@ -108,8 +107,8 @@
-
-
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java
index 647d851341..e01a887949 100755
--- a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java
+++ b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java
@@ -1,15 +1,15 @@
/*
* Autopsy Forensic Browser
- *
- * Copyright 2011-2016 Basis Technology Corp.
+ *
+ * Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,17 +18,20 @@
*/
package org.sleuthkit.autopsy.actions;
+import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
-import java.util.Map;
-import java.util.TreeMap;
import java.util.logging.Level;
+import java.util.HashSet;
+import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
+import javax.swing.DefaultListCellRenderer;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JDialog;
+import javax.swing.JList;
import javax.swing.KeyStroke;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
@@ -37,15 +40,14 @@ import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.TskData;
public class GetTagNameAndCommentDialog extends JDialog {
private static final long serialVersionUID = 1L;
- private static final String NO_TAG_NAMES_MESSAGE = NbBundle.getMessage(GetTagNameAndCommentDialog.class,
- "GetTagNameAndCommentDialog.noTags");
- private final Map tagNamesMap = new TreeMap<>();
+ private final Set tagNamesSet = new HashSet<>();
private TagNameAndComment tagNameAndComment = null;
-
+
public static class TagNameAndComment {
private final TagName tagName;
@@ -68,7 +70,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
/**
* Show the Tag Name and Comment Dialog and return the TagNameAndContent
* chosen by the user. The dialog will be centered with the main autopsy
- * window as its owner.
+ * window as its owner.
*
* @return a TagNameAndComment instance containing the TagName selected by
* the user and the entered comment, or null if the user canceled
@@ -102,21 +104,34 @@ 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 newValue = ((TagName) value).getDisplayName() + status;
+ return super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus);
+ }
+ });
// Set up the dialog to close when Esc is pressed.
String cancelName = NbBundle.getMessage(this.getClass(), "GetTagNameAndCommentDialog.cancelName");
InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName);
ActionMap actionMap = getRootPane().getActionMap();
+
actionMap.put(cancelName, new AbstractAction() {
private static final long serialVersionUID = 1L;
+
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
- });
+ }
+ );
// Populate the combo box with the available tag names and save the
// tag name DTOs to be enable to return the one the user selects.
@@ -124,23 +139,22 @@ public class GetTagNameAndCommentDialog extends JDialog {
// not exist in the database).
TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager();
try {
- tagNamesMap.putAll(tagsManager.getDisplayNamesToTagNamesMap());
+ tagNamesSet.addAll(tagsManager.getAllTagNames());
+
} catch (TskCoreException ex) {
- Logger.getLogger(GetTagNameAndCommentDialog.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
+ Logger.getLogger(GetTagNameAndCommentDialog.class
+ .getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
}
- if (null != tagNamesMap && tagNamesMap.isEmpty()) {
- tagCombo.addItem(NO_TAG_NAMES_MESSAGE);
- } else {
- for (String tagDisplayName : tagNamesMap.keySet()) {
- tagCombo.addItem(tagDisplayName);
- }
+ for (TagName tag : tagNamesSet) {
+
+ tagCombo.addItem(tag);
}
// Center and show the dialog box.
this.setLocationRelativeTo(this.getOwner());
- setVisible(true);
+ setVisible(true);
}
-
+
/**
* 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
@@ -152,7 +166,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton();
- tagCombo = new javax.swing.JComboBox();
+ tagCombo = new javax.swing.JComboBox();
tagLabel = new javax.swing.JLabel();
commentLabel = new javax.swing.JLabel();
commentText = new javax.swing.JTextField();
@@ -203,7 +217,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(newTagButton)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 48, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 165, Short.MAX_VALUE)
.addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton))
@@ -212,10 +226,9 @@ public class GetTagNameAndCommentDialog extends JDialog {
.addComponent(commentLabel)
.addComponent(tagLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(commentText)
- .addComponent(tagCombo, 0, 214, Short.MAX_VALUE))
- .addGap(0, 0, Short.MAX_VALUE)))
+ .addComponent(tagCombo, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
.addContainerGap())
);
@@ -246,21 +259,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
}// //GEN-END:initComponents
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
- String tagDisplayName = (String) tagCombo.getSelectedItem();
- TagName tagNameFromCombo = tagNamesMap.get(tagDisplayName);
- if (tagNameFromCombo == null) {
- try {
- tagNameFromCombo = Case.getCurrentCase().getServices().getTagsManager().addTagName(tagDisplayName);
- } catch (TagsManager.TagNameAlreadyExistsException ex) {
- try {
- tagNameFromCombo = Case.getCurrentCase().getServices().getTagsManager().getDisplayNamesToTagNamesMap().get(tagDisplayName);
- } catch (TskCoreException ex1) {
- Logger.getLogger(AddTagAction.class.getName()).log(Level.SEVERE, tagDisplayName + " already exists in database but an error occurred in retrieving it.", ex1); //NON-NLS
- }
- } catch (TskCoreException ex) {
- Logger.getLogger(AddTagAction.class.getName()).log(Level.SEVERE, "Error adding " + tagDisplayName + " tag name", ex); //NON-NLS
- }
- }
+ TagName tagNameFromCombo = (TagName) tagCombo.getSelectedItem();
tagNameAndComment = new TagNameAndComment(tagNameFromCombo, commentText.getText());
dispose();
}//GEN-LAST:event_okButtonActionPerformed
@@ -278,9 +277,9 @@ public class GetTagNameAndCommentDialog extends JDialog {
private void newTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newTagButtonActionPerformed
TagName newTagName = GetTagNameDialog.doDialog(this);
if (newTagName != null) {
- tagNamesMap.put(newTagName.getDisplayName(), newTagName);
- tagCombo.addItem(newTagName.getDisplayName());
- tagCombo.setSelectedItem(newTagName.getDisplayName());
+ tagNamesSet.add(newTagName);
+ tagCombo.addItem(newTagName);
+ tagCombo.setSelectedItem(newTagName);
}
}//GEN-LAST:event_newTagButtonActionPerformed
@@ -290,7 +289,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
private javax.swing.JTextField commentText;
private javax.swing.JButton newTagButton;
private javax.swing.JButton okButton;
- private javax.swing.JComboBox tagCombo;
+ private javax.swing.JComboBox tagCombo;
private javax.swing.JLabel tagLabel;
// End of variables declaration//GEN-END:variables
}
diff --git a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.form b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.form
index a281ea606e..bf17a398a7 100755
--- a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.form
+++ b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.form
@@ -142,11 +142,20 @@
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -154,12 +163,17 @@
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -182,6 +196,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java
index 4e5720a0fc..515db21dab 100755
--- a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java
+++ b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java
@@ -1,15 +1,15 @@
/*
* Autopsy Forensic Browser
- *
+ *
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier sleuthkit org
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -36,13 +36,17 @@ import javax.swing.KeyStroke;
import javax.swing.table.AbstractTableModel;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
+import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.TskData;
+@Messages({"GetTagNameDialog.descriptionLabel.text=Description:",
+ "GetTagNameDialog.notableCheckbox.text=Tag indicates item is notable."})
public class GetTagNameDialog extends JDialog {
private static final long serialVersionUID = 1L;
@@ -79,7 +83,7 @@ public class GetTagNameDialog extends JDialog {
}
private GetTagNameDialog(Window owner) {
- super(owner,
+ super(owner,
NbBundle.getMessage(GetTagNameDialog.class, "GetTagNameDialog.createTag"),
ModalityType.APPLICATION_MODAL);
}
@@ -95,7 +99,7 @@ public class GetTagNameDialog extends JDialog {
ActionMap actionMap = getRootPane().getActionMap();
actionMap.put(cancelName, new AbstractAction() {
private static final long serialVersionUID = 1L;
-
+
@Override
public void actionPerformed(ActionEvent e) {
cancelButtonActionPerformed(e);
@@ -120,9 +124,9 @@ public class GetTagNameDialog extends JDialog {
// Center and show the dialog box.
this.setLocationRelativeTo(this.getOwner());
- setVisible(true);
+ setVisible(true);
}
-
+
private class TagsTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
@@ -172,6 +176,10 @@ public class GetTagNameDialog extends JDialog {
newTagPanel = new javax.swing.JPanel();
tagNameLabel = new javax.swing.JLabel();
tagNameField = new javax.swing.JTextField();
+ descriptionLabel = new javax.swing.JLabel();
+ descriptionScrollPane = new javax.swing.JScrollPane();
+ descriptionTextArea = new javax.swing.JTextArea();
+ notableCheckbox = new javax.swing.JCheckBox();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
addKeyListener(new java.awt.event.KeyAdapter() {
@@ -223,25 +231,46 @@ public class GetTagNameDialog extends JDialog {
}
});
+ org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(GetTagNameDialog.class, "GetTagNameDialog.descriptionLabel.text")); // NOI18N
+
+ descriptionTextArea.setColumns(20);
+ descriptionTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N
+ descriptionTextArea.setRows(3);
+ descriptionScrollPane.setViewportView(descriptionTextArea);
+
+ org.openide.awt.Mnemonics.setLocalizedText(notableCheckbox, org.openide.util.NbBundle.getMessage(GetTagNameDialog.class, "GetTagNameDialog.notableCheckbox.text")); // NOI18N
+
javax.swing.GroupLayout newTagPanelLayout = new javax.swing.GroupLayout(newTagPanel);
newTagPanel.setLayout(newTagPanelLayout);
newTagPanelLayout.setHorizontalGroup(
newTagPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(newTagPanelLayout.createSequentialGroup()
.addContainerGap()
- .addComponent(tagNameLabel)
- .addGap(36, 36, 36)
- .addComponent(tagNameField, javax.swing.GroupLayout.DEFAULT_SIZE, 235, Short.MAX_VALUE)
+ .addGroup(newTagPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(descriptionScrollPane, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(tagNameField, javax.swing.GroupLayout.DEFAULT_SIZE, 323, Short.MAX_VALUE)
+ .addGroup(newTagPanelLayout.createSequentialGroup()
+ .addGroup(newTagPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(notableCheckbox)
+ .addComponent(descriptionLabel)
+ .addComponent(tagNameLabel))
+ .addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
newTagPanelLayout.setVerticalGroup(
newTagPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(newTagPanelLayout.createSequentialGroup()
- .addContainerGap()
- .addGroup(newTagPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(tagNameLabel)
- .addComponent(tagNameField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addContainerGap(164, Short.MAX_VALUE))
+ .addGap(6, 6, 6)
+ .addComponent(tagNameLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(tagNameField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(descriptionLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(descriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 57, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(notableCheckbox)
+ .addContainerGap(31, Short.MAX_VALUE))
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
@@ -288,8 +317,14 @@ public class GetTagNameDialog extends JDialog {
dispose();
}//GEN-LAST:event_cancelButtonActionPerformed
+ @NbBundle.Messages({"GetTagNameDialog.tagNameAlreadyExists.message=Tag name must be unique. A tag with this name already exists.",
+ "GetTagNameDialog.tagNameAlreadyExists.title=Duplicate Tag Name",
+ "GetTagNameDialog.tagDescriptionIllegalCharacters.message=Tag descriptions may not contain commas (,) or semicolons (;)",
+ "GetTagNameDialog.tagDescriptionIllegalCharacters.title=Invalid character in tag description"})
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
String tagDisplayName = tagNameField.getText();
+ String userTagDescription = descriptionTextArea.getText();
+ TskData.FileKnown status = notableCheckbox.isSelected() ? TskData.FileKnown.BAD : TskData.FileKnown.UNKNOWN;
if (tagDisplayName.isEmpty()) {
JOptionPane.showMessageDialog(null,
NbBundle.getMessage(this.getClass(),
@@ -301,11 +336,18 @@ public class GetTagNameDialog extends JDialog {
NbBundle.getMessage(this.getClass(), "GetTagNameDialog.illegalChars.msg"),
NbBundle.getMessage(this.getClass(), "GetTagNameDialog.illegalCharsErr"),
JOptionPane.ERROR_MESSAGE);
+ } else if (userTagDescription.contains(",")
+ || userTagDescription.contains(";")) {
+ JOptionPane.showMessageDialog(null,
+ NbBundle.getMessage(this.getClass(), "GetTagNameDialog.tagDescriptionIllegalCharacters.message"),
+ NbBundle.getMessage(this.getClass(), "GetTagNameDialog.tagDescriptionIllegalCharacters.title"),
+ JOptionPane.ERROR_MESSAGE);
} else {
tagName = tagNamesMap.get(tagDisplayName);
+
if (tagName == null) {
try {
- tagName = Case.getCurrentCase().getServices().getTagsManager().addTagName(tagDisplayName);
+ tagName = Case.getCurrentCase().getServices().getTagsManager().addTagName(tagDisplayName, userTagDescription, TagName.HTML_COLOR.NONE, status);
dispose();
} catch (TskCoreException ex) {
Logger.getLogger(AddTagAction.class.getName()).log(Level.SEVERE, "Error adding " + tagDisplayName + " tag name", ex); //NON-NLS
@@ -331,7 +373,10 @@ public class GetTagNameDialog extends JDialog {
}
}
} else {
- dispose();
+ JOptionPane.showMessageDialog(null,
+ NbBundle.getMessage(this.getClass(), "GetTagNameDialog.tagNameAlreadyExists.message"),
+ NbBundle.getMessage(this.getClass(), "GetTagNameDialog.tagNameAlreadyExists.title"),
+ JOptionPane.INFORMATION_MESSAGE);
}
}
}//GEN-LAST:event_okButtonActionPerformed
@@ -350,8 +395,12 @@ public class GetTagNameDialog extends JDialog {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton cancelButton;
+ private javax.swing.JLabel descriptionLabel;
+ private javax.swing.JScrollPane descriptionScrollPane;
+ private javax.swing.JTextArea descriptionTextArea;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JPanel newTagPanel;
+ private javax.swing.JCheckBox notableCheckbox;
private javax.swing.JButton okButton;
private javax.swing.JLabel preexistingLabel;
private javax.swing.JTextField tagNameField;
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java
index 983c9420b2..52a2b7fa97 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java
@@ -362,7 +362,14 @@ public class Case {
* case number, the examiner name, examiner phone, examiner email, and
* the case notes.
*/
- CASE_DETAILS;
+ CASE_DETAILS,
+ /**
+ * A tag definition has changed (e.g., description, known status). The
+ * old value of the PropertyChangeEvent is the display name of the tag
+ * definition that has changed.
+ */
+ TAG_DEFINITION_CHANGED;
+
};
/**
@@ -1472,6 +1479,18 @@ public class Case {
eventPublisher.publish(new ContentTagDeletedEvent(deletedTag));
}
+ /**
+ * Notifies case event subscribers that a tag definition has changed.
+ *
+ * This should not be called from the event dispatch thread (EDT)
+ *
+ * @param changedTagName the name of the tag definition which was changed
+ */
+ public void notifyTagDefinitionChanged(String changedTagName) {
+ //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
+ eventPublisher.publish(new AutopsyEvent(Events.TAG_DEFINITION_CHANGED.toString(), changedTagName, null));
+ }
+
/**
* Notifies case event subscribers that an artifact tag has been added.
*
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties
index 4cca7e78d6..e9c1b5307c 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties
@@ -9,15 +9,7 @@ TagNameDialog.JOptionPane.tagNameEmpty.title=Empty tag name
TagOptionsPanel.tagTypesListLabel.text=Tag Names:
TagOptionsPanel.deleteTagNameButton.text=Delete Tag
TagOptionsPanel.newTagNameButton.text=New Tag
-TagOptionsPanel.editTagNameButton.text=Edit Tag
-TagNameDialog.descriptionLabel.text=Description:
TagNameDialog.okButton.text=OK
TagNameDialog.cancelButton.text=Cancel
TagNameDialog.tagNameTextField.text=
TagNameDialog.newTagNameLabel.text=Name:
-TagNameDialog.notableCheckbox.text=Tag indicates item is notable.
-TagOptionsPanel.isNotableLabel.text=Tag indicates item is notable:
-TagOptionsPanel.notableYesOrNoLabel.text=
-TagOptionsPanel.descriptionLabel.text=Tag Description:
-TagOptionsPanel.jTextArea1.text=Create and manage tags, which can be applied to files and results in the case. Notable tags will cause items tagged with them to be flagged as notable when using a central repository.
-TagOptionsPanel.ingestRunningWarningLabel.text=Cannot make changes to existing tags when ingest is running!
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java
index 096f3cad69..e1d81d2b65 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java
@@ -66,7 +66,7 @@ final class TagNameDefinition implements Comparable {
* @param displayName The display name for the tag name.
* @param description The description for the tag name.
* @param color The color for the tag name.
- * @param knownStatus The status denoted by the tag name.
+ * @param status The status denoted by the tag name.
*/
TagNameDefinition(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown status) {
this.displayName = displayName;
@@ -107,13 +107,12 @@ final class TagNameDefinition implements Comparable {
}
/**
- * Whether or not the status that this tag implies Notable status
+ * The status which will be applied to items with this tag.
*
- * @return true if the Notable status is implied by this tag, false
- * otherwise.
+ * @return a value of TskData.FileKnown which is associated with this tag
*/
- boolean isNotable() {
- return knownStatus == TskData.FileKnown.BAD;
+ TskData.FileKnown getKnownStatus() {
+ return knownStatus;
}
/**
@@ -157,8 +156,9 @@ final class TagNameDefinition implements Comparable {
if (!(obj instanceof TagNameDefinition)) {
return false;
}
- TagNameDefinition thatTagName = (TagNameDefinition) obj;
- return this.getDisplayName().equals(thatTagName.getDisplayName());
+ boolean sameName = this.getDisplayName().equals(((TagNameDefinition) obj).getDisplayName());
+ boolean sameStatus = this.getKnownStatus().equals(((TagNameDefinition) obj).getKnownStatus());
+ return sameName && sameStatus;
}
/**
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.form b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.form
index e8162e6b3b..a88e25eb55 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.form
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.form
@@ -117,7 +117,7 @@
-
+
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.java
index 36ed5aeaaa..4949be2507 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.java
@@ -1,20 +1,20 @@
/*
-* Autopsy Forensic Browser
-*
-* Copyright 2011-2017 Basis Technology Corp.
-* Contact: carrier sleuthkit org
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011-2017 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule.services;
@@ -29,7 +29,10 @@ import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.datamodel.TskData;
+@Messages({"TagNameDialog.descriptionLabel.text=Description:",
+ "TagNameDialog.notableCheckbox.text=Tag indicates item is notable."})
final class TagNameDialog extends javax.swing.JDialog {
private static final long serialVersionUID = 1L;
@@ -59,7 +62,7 @@ final class TagNameDialog extends javax.swing.JDialog {
initComponents();
tagNameTextField.setText(tagNameToEdit.getDisplayName());
descriptionTextArea.setText(tagNameToEdit.getDescription());
- notableCheckbox.setSelected(tagNameToEdit.isNotable());
+ notableCheckbox.setSelected(tagNameToEdit.getKnownStatus() == TskData.FileKnown.BAD);
tagNameTextField.setEnabled(false);
this.display();
}
@@ -126,24 +129,35 @@ final class TagNameDialog extends javax.swing.JDialog {
*
* @param okPressed whether the OK button was pressed.
*/
+ @Messages({"TagNameDialog.JOptionPane.tagDescriptionIllegalCharacters.message=Tag descriptions may not contain commas (,) or semicolons (;)",
+ "TagNameDialog.JOptionPane.tagDescriptionIllegalCharacters.title=Invalid character in tag description"})
private void doButtonAction(boolean okPressed) {
if (okPressed) {
String newTagDisplayName = tagNameTextField.getText().trim();
+ String descriptionText = descriptionTextArea.getText();
if (newTagDisplayName.isEmpty()) {
JOptionPane.showMessageDialog(null,
NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.JOptionPane.tagNameEmpty.message"),
NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.JOptionPane.tagNameEmpty.title"),
JOptionPane.ERROR_MESSAGE);
return;
- }
+ }
//if a tag name contains illegal characters and is not the name of one of the standard tags
if (TagsManager.containsIllegalCharacters(newTagDisplayName) && !TagNameDefinition.getStandardTagNames().contains(newTagDisplayName)) {
JOptionPane.showMessageDialog(null,
- NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.JOptionPane.tagNameIllegalCharacters.message"),
- NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.JOptionPane.tagNameIllegalCharacters.title"),
+ NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.JOptionPane.tagDescriptionIllegalCharacters.message"),
+ NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.JOptionPane.tagDescriptionIllegalCharacters.title"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ } else if (descriptionText.contains(",")
+ || descriptionText.contains(";")) {
+ JOptionPane.showMessageDialog(null,
+ NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.JOptionPane.tagDescriptionIllegalCharacters.message"),
+ NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.JOptionPane.tagDescriptionIllegalCharacters.title"),
JOptionPane.ERROR_MESSAGE);
return;
}
+
userTagDescription = descriptionTextArea.getText();
userTagDisplayName = newTagDisplayName;
userTagIsNotable = notableCheckbox.isSelected();
@@ -230,7 +244,7 @@ final class TagNameDialog extends javax.swing.JDialog {
descriptionTextArea.setColumns(20);
descriptionTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N
- descriptionTextArea.setRows(5);
+ descriptionTextArea.setRows(3);
descriptionScrollPane.setViewportView(descriptionTextArea);
org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.descriptionLabel.text")); // NOI18N
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form
index 541bd90cda..8654d04598 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form
@@ -91,7 +91,7 @@
-
+
@@ -100,7 +100,7 @@
-
+
@@ -113,11 +113,11 @@
-
-
+
+
-
-
+
+
@@ -137,7 +137,7 @@
-
+
@@ -223,14 +223,14 @@
-
+
-
+
@@ -243,7 +243,7 @@
-
+
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java
index 61314e6f1f..c5458d2f76 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java
@@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.casemodule.services;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
+import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.DefaultListModel;
@@ -28,6 +29,8 @@ import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle;
+import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.TagName;
@@ -39,22 +42,33 @@ import org.sleuthkit.datamodel.TskData;
final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
private static final long serialVersionUID = 1L;
- private static final String DEFAULT_DESCRIPTION = "";
private static final TagName.HTML_COLOR DEFAULT_COLOR = TagName.HTML_COLOR.NONE;
private final DefaultListModel tagTypesListModel;
private Set tagTypes;
private IngestJobEventPropertyChangeListener ingestJobEventsListener;
+ private Set updatedStatusTags;
/**
- * Creates new form TagsManagerOptionsPanel
+ * Creates new form TagOptionsPanel
*/
TagOptionsPanel() {
tagTypesListModel = new DefaultListModel<>();
tagTypes = new TreeSet<>(TagNameDefinition.getTagNameDefinitions());
+ updatedStatusTags = new HashSet<>();
initComponents();
customizeComponents();
}
+ @Messages({"TagOptionsPanel.panelDescriptionTextArea.text=Create and manage tags. "
+ + "Tags can be applied to files and results in the case. Notable tags will cause "
+ + "items tagged with them to be flagged as notable when using a central repository. "
+ + "Changing the status of a tag will only effect items in the current case.",
+ "TagOptionsPanel.ingestRunningWarningLabel.text=Cannot make changes to existing tags when ingest is running!",
+ "TagOptionsPanel.descriptionLabel.text=Tag Description:",
+ "TagOptionsPanel.notableYesOrNoLabel.text=",
+ "TagOptionsPanel.isNotableLabel.text=Tag indicates item is notable: ",
+ "TagOptionsPanel.editTagNameButton.text=Edit Tag"})
+
private void customizeComponents() {
tagNamesList.setModel(tagTypesListModel);
tagNamesList.addListSelectionListener((ListSelectionEvent event) -> {
@@ -88,13 +102,13 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
jSplitPane1 = new javax.swing.JSplitPane();
modifyTagTypesListPanel = new javax.swing.JPanel();
tagTypesListLabel = new javax.swing.JLabel();
- jScrollPane1 = new javax.swing.JScrollPane();
+ TagNameScrollPane = new javax.swing.JScrollPane();
tagNamesList = new javax.swing.JList<>();
newTagNameButton = new javax.swing.JButton();
deleteTagNameButton = new javax.swing.JButton();
editTagNameButton = new javax.swing.JButton();
- jScrollPane3 = new javax.swing.JScrollPane();
- jTextArea1 = new javax.swing.JTextArea();
+ panelDescriptionScrollPane = new javax.swing.JScrollPane();
+ panelDescriptionTextArea = new javax.swing.JTextArea();
tagTypesAdditionalPanel = new javax.swing.JPanel();
descriptionLabel = new javax.swing.JLabel();
descriptionScrollPane = new javax.swing.JScrollPane();
@@ -114,7 +128,7 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
org.openide.awt.Mnemonics.setLocalizedText(tagTypesListLabel, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.tagTypesListLabel.text")); // NOI18N
tagNamesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
- jScrollPane1.setViewportView(tagNamesList);
+ TagNameScrollPane.setViewportView(tagNamesList);
newTagNameButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add-tag.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(newTagNameButton, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.newTagNameButton.text")); // NOI18N
@@ -149,16 +163,16 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
}
});
- jTextArea1.setEditable(false);
- jTextArea1.setBackground(new java.awt.Color(240, 240, 240));
- jTextArea1.setColumns(20);
- jTextArea1.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N
- jTextArea1.setLineWrap(true);
- jTextArea1.setRows(3);
- jTextArea1.setText(org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.jTextArea1.text")); // NOI18N
- jTextArea1.setWrapStyleWord(true);
- jTextArea1.setFocusable(false);
- jScrollPane3.setViewportView(jTextArea1);
+ panelDescriptionTextArea.setEditable(false);
+ panelDescriptionTextArea.setBackground(new java.awt.Color(240, 240, 240));
+ panelDescriptionTextArea.setColumns(20);
+ panelDescriptionTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N
+ panelDescriptionTextArea.setLineWrap(true);
+ panelDescriptionTextArea.setRows(3);
+ panelDescriptionTextArea.setText(org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.panelDescriptionTextArea.text")); // NOI18N
+ panelDescriptionTextArea.setWrapStyleWord(true);
+ panelDescriptionTextArea.setFocusable(false);
+ panelDescriptionScrollPane.setViewportView(panelDescriptionTextArea);
javax.swing.GroupLayout modifyTagTypesListPanelLayout = new javax.swing.GroupLayout(modifyTagTypesListPanel);
modifyTagTypesListPanel.setLayout(modifyTagTypesListPanelLayout);
@@ -171,14 +185,14 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
.addGroup(modifyTagTypesListPanelLayout.createSequentialGroup()
.addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
- .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(TagNameScrollPane, javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, modifyTagTypesListPanelLayout.createSequentialGroup()
.addComponent(newTagNameButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editTagNameButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(deleteTagNameButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
- .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 345, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(panelDescriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 345, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
@@ -189,11 +203,11 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(modifyTagTypesListPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
- .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(panelDescriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tagTypesListLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 355, Short.MAX_VALUE)
+ .addComponent(TagNameScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 338, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(newTagNameButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
@@ -294,6 +308,9 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
);
}// //GEN-END:initComponents
+ @Messages({"TagOptionsPanel.TagNameDialog.tagNameAlreadyExists.message=Tag name must be unique. A tag with this name already exists.",
+ "TagOptionsPanel.TagNameDialog.tagNameAlreadyExists.title=Duplicate Tag Name"})
+
private void newTagNameButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newTagNameButtonActionPerformed
TagNameDialog dialog = new TagNameDialog();
TagNameDialog.BUTTON_PRESSED result = dialog.getResult();
@@ -311,8 +328,8 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} else {
JOptionPane.showMessageDialog(null,
- NbBundle.getMessage(TagOptionsPanel.class, "TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.message"),
- NbBundle.getMessage(TagOptionsPanel.class, "TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.title"),
+ NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.TagNameDialog.tagNameAlreadyExists.message"),
+ NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.TagNameDialog.tagNameAlreadyExists.title"),
JOptionPane.INFORMATION_MESSAGE);
}
}
@@ -337,17 +354,20 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
/*
* If tag name already exists, don't add the tag name.
*/
-
tagTypes.remove(originalTagName);
tagTypes.add(newTagType);
updateTagNamesListModel();
tagNamesList.setSelectedValue(newTagType, true);
updatePanel();
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
+ if (originalTagName.getKnownStatus() != newTagType.getKnownStatus() && Case.isCaseOpen()) {
+ updatedStatusTags.add(newTagType.getDisplayName());
+ }
}
}//GEN-LAST:event_editTagNameButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JScrollPane TagNameScrollPane;
private javax.swing.JButton deleteTagNameButton;
private javax.swing.JLabel descriptionLabel;
private javax.swing.JScrollPane descriptionScrollPane;
@@ -356,14 +376,13 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
private javax.swing.JLabel ingestRunningWarningLabel;
private javax.swing.JLabel isNotableLabel;
private javax.swing.JPanel jPanel1;
- private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JScrollPane jScrollPane2;
- private javax.swing.JScrollPane jScrollPane3;
private javax.swing.JSplitPane jSplitPane1;
- private javax.swing.JTextArea jTextArea1;
private javax.swing.JPanel modifyTagTypesListPanel;
private javax.swing.JButton newTagNameButton;
private javax.swing.JLabel notableYesOrNoLabel;
+ private javax.swing.JScrollPane panelDescriptionScrollPane;
+ private javax.swing.JTextArea panelDescriptionTextArea;
private javax.swing.JList tagNamesList;
private javax.swing.JPanel tagTypesAdditionalPanel;
private javax.swing.JLabel tagTypesListLabel;
@@ -395,6 +414,21 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
@Override
public void store() {
TagNameDefinition.setTagNameDefinitions(tagTypes);
+ sendStatusChangedEvents();
+ }
+
+ void cancelChanges() {
+ updatedStatusTags.clear();
+ }
+
+ private void sendStatusChangedEvents() {
+ for (String modifiedTagDisplayName : updatedStatusTags) {
+ //if user closes their case after options have been changed but before application of them is complete don't notify
+ if (Case.isCaseOpen()) {
+ Case.getCurrentCase().notifyTagDefinitionChanged(modifiedTagDisplayName);
+ }
+ }
+ updatedStatusTags.clear();
}
/**
@@ -414,9 +448,8 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel {
boolean enableDelete = enableEdit && !TagNameDefinition.getStandardTagNames().contains(tagNamesList.getSelectedValue().getDisplayName());
deleteTagNameButton.setEnabled(enableDelete);
if (isSelected) {
-
descriptionTextArea.setText(tagNamesList.getSelectedValue().getDescription());
- if (tagNamesList.getSelectedValue().isNotable()) {
+ if (tagNamesList.getSelectedValue().getKnownStatus() == TskData.FileKnown.BAD) {
notableYesOrNoLabel.setText("Yes");
} else {
notableYesOrNoLabel.setText("No");
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java
index de16d9456f..f96fe08f23 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java
@@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
+import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
@@ -69,6 +70,15 @@ public class TagsManager implements Closeable {
|| tagDisplayName.contains(";"));
}
+ @NbBundle.Messages({"TagsManager.notableTagEnding.text= (Notable)"})
+ /**
+ * Get String of text which is used to label tags as notable to the user.
+ *
+ * @return Bundle message TagsManager.notableTagEnding.text
+ */
+ public static String getNotableTagLabel(){
+ return Bundle.TagsManager_notableTagEnding_text();
+ }
/**
* Gets the set of display names of the currently available tag types. This
@@ -103,7 +113,7 @@ public class TagsManager implements Closeable {
public static List getNotableTagDisplayNames() {
List tagDisplayNames = new ArrayList<>();
for (TagNameDefinition tagDef : TagNameDefinition.getTagNameDefinitions()) {
- if (tagDef.isNotable()) {
+ if (tagDef.getKnownStatus() == TskData.FileKnown.BAD) {
tagDisplayNames.add(tagDef.getDisplayName());
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsOptionsPanelController.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsOptionsPanelController.java
index c27835f83a..fcd2131f90 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsOptionsPanelController.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsOptionsPanelController.java
@@ -69,6 +69,7 @@ public final class TagsOptionsPanelController extends OptionsPanelController {
*/
@Override
public void cancel() {
+ getPanel().cancelChanges();
}
@Override
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java
index f132a468d5..b9d445f8b6 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java
@@ -2208,8 +2208,13 @@ public abstract class AbstractSqlEamDb implements EamDb {
return eamGlobalFileInstance;
}
-
- void updateSchema() {
+
+ /**
+ * Update the schema of the database (if needed)
+ * @throws EamDbException
+ */
+ @Override
+ public void updateSchema() throws EamDbException {
ResultSet resultSet = null;
Statement statement;
@@ -2244,10 +2249,13 @@ public abstract class AbstractSqlEamDb implements EamDb {
System.out.println("Current schema version: " + majorVersion + "." + minorVersion);
CaseDbSchemaVersionNumber dbSchemaVersion = new CaseDbSchemaVersionNumber(majorVersion, minorVersion);
+ // Update from 1.0 to 1.1
if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 1)) < 0) {
statement.execute("ALTER TABLE reference_sets ADD COLUMN known_status INTEGER;"); //NON-NLS
statement.execute("ALTER TABLE reference_sets ADD COLUMN read_only BOOLEAN;"); //NON-NLS
statement.execute("ALTER TABLE reference_sets ADD COLUMN type INTEGER;"); //NON-NLS
+
+ statement.execute("INSERT INTO organizations (name) VALUES (" + EamDbUtil.getDefaultOrgName() + ")");
}
if (!updateSchemaVersion(conn)) {
@@ -2268,11 +2276,6 @@ public abstract class AbstractSqlEamDb implements EamDb {
ex.printStackTrace();
} finally {
EamDbUtil.closeResultSet(resultSet);
- try {
- conn.setAutoCommit(true);
- } catch (SQLException ex) {
- ex.printStackTrace();
- }
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java
old mode 100755
new mode 100644
index eb66856673..d047eb278c
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java
@@ -38,6 +38,7 @@ public class EamDbUtil {
private final static Logger LOGGER = Logger.getLogger(EamDbUtil.class.getName());
private static final String CENTRAL_REPO_NAME = "CentralRepository";
private static final String CENTRAL_REPO_USE_KEY = "db.useCentralRepo";
+ private static final String DEFAULT_ORG_NAME = "Not Specified";
/**
* Close the prepared statement.
@@ -233,6 +234,54 @@ public class EamDbUtil {
}
}
}
+
+ /**
+ * Get the default organization name
+ * @return the default org name
+ */
+ static String getDefaultOrgName() {
+ return DEFAULT_ORG_NAME;
+ }
+
+ /**
+ * Check whether the given org is the default organization.
+ *
+ * @param org
+ * @return true if it is the default org, false otherwise
+ */
+ public static boolean isDefaultOrg(EamOrganization org) {
+ return DEFAULT_ORG_NAME.equals(org.getName());
+ }
+
+ /**
+ * Add the default organization to the database
+ *
+ * @param conn
+ * @return true if successful, false otherwise
+ */
+ static boolean insertDefaultOrganization(Connection conn) {
+ if (null == conn) {
+ return false;
+ }
+
+ PreparedStatement preparedStatement = null;
+ String sql = "INSERT INTO organizations(org_name, poc_name, poc_email, poc_phone) VALUES (?, ?, ?, ?)";
+ try {
+ preparedStatement = conn.prepareStatement(sql);
+ preparedStatement.setString(1, DEFAULT_ORG_NAME);
+ preparedStatement.setString(2, "");
+ preparedStatement.setString(3, "");
+ preparedStatement.setString(4, "");
+ preparedStatement.executeUpdate();
+ } catch (SQLException ex) {
+ LOGGER.log(Level.SEVERE, "Error adding default organization", ex);
+ return false;
+ } finally {
+ EamDbUtil.closePreparedStatement(preparedStatement);
+ }
+
+ return true;
+ }
/**
* If the Central Repos use has been enabled.
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java
old mode 100755
new mode 100644
index 22ade065e3..f58d313e47
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java
@@ -485,7 +485,8 @@ public final class PostgresEamDbSettings {
}
boolean result = EamDbUtil.insertDefaultCorrelationTypes(conn)
- && EamDbUtil.updateSchemaVersion(conn);
+ && EamDbUtil.updateSchemaVersion(conn)
+ && EamDbUtil.insertDefaultOrganization(conn);
EamDbUtil.closeConnection(conn);
return result;
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java
old mode 100755
new mode 100644
index 20ad88a7cc..9355dbacde
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java
@@ -85,11 +85,6 @@ public final class SqliteEamDbSettings {
} catch (NumberFormatException ex) {
this.bulkThreshold = DEFAULT_BULK_THRESHHOLD;
}
-
-
-
- System.out.println("\n#### UPDATING DATABASE!!!");
- EamDbUtil.updateSchema(getEphemeralConnection());
}
public void saveSettings() {
@@ -437,7 +432,8 @@ public final class SqliteEamDbSettings {
}
boolean result = EamDbUtil.insertDefaultCorrelationTypes(conn)
- && EamDbUtil.updateSchemaVersion(conn);
+ && EamDbUtil.updateSchemaVersion(conn)
+ && EamDbUtil.insertDefaultOrganization(conn);
EamDbUtil.closeConnection(conn);
return result;
}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java
index dbe17d6e6d..7252ca53cc 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java
@@ -48,6 +48,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
+import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskDataException;
@@ -97,7 +98,10 @@ final class CaseEventListener implements PropertyChangeListener {
jobProcessingExecutor.submit(new DataSourceAddedTask(dbManager, evt));
}
break;
-
+ case TAG_DEFINITION_CHANGED: {
+ jobProcessingExecutor.submit(new TagDefinitionChangeTask(evt));
+ }
+ break;
case CURRENT_CASE: {
jobProcessingExecutor.submit(new CurrentCaseTask(dbManager, evt));
}
@@ -294,6 +298,117 @@ final class CaseEventListener implements PropertyChangeListener {
}
+ private final class TagDefinitionChangeTask implements Runnable {
+
+ private final PropertyChangeEvent event;
+
+ private TagDefinitionChangeTask(PropertyChangeEvent evt) {
+ event = evt;
+ }
+
+ @Override
+ public void run() {
+ if (!EamDb.isEnabled()) {
+ return;
+ }
+ //get the display name of the tag that has had it's definition modified
+ String modifiedTagName = (String) event.getOldValue();
+
+ /*
+ * Set knownBad status for all files/artifacts in the given case
+ * that are tagged with the given tag name.
+ */
+ try {
+ TagName tagName = Case.getCurrentCase().getServices().getTagsManager().getDisplayNamesToTagNamesMap().get(modifiedTagName);
+ //First update the artifacts
+ //Get all BlackboardArtifactTags with this tag name
+ List artifactTags = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifactTagsByTagName(tagName);
+ for (BlackboardArtifactTag bbTag : artifactTags) {
+ //start with assumption that none of the other tags applied to this Correlation Attribute will prevent it's status from being changed
+ boolean hasTagWithConflictingKnownStatus = false;
+ // if the status of the tag has been changed to TskData.FileKnown.UNKNOWN
+ // we need to check the status of all other tags on this correlation attribute before changing
+ // the status of the correlation attribute in the central repository
+ if (tagName.getKnownStatus() == TskData.FileKnown.UNKNOWN) {
+ Content content = bbTag.getContent();
+ // If the content which this Blackboard Artifact Tag is linked to is an AbstractFile with KNOWN status then
+ // it's status in the central reporsitory should not be changed to UNKNOWN
+ if ((content instanceof AbstractFile) && (((AbstractFile) content).getKnown() == TskData.FileKnown.KNOWN)) {
+ continue;
+ }
+ //Get the BlackboardArtifact which this BlackboardArtifactTag has been applied to.
+ BlackboardArtifact bbArtifact = bbTag.getArtifact();
+ TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager();
+ List tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbArtifact);
+ //get all tags which are on this blackboard artifact
+ for (BlackboardArtifactTag t : tags) {
+ //All instances of the modified tag name will be changed, they can not conflict with each other
+ if (t.getName().equals(tagName)) {
+ continue;
+ }
+ //if any other tags on this artifact are Notable in status then this artifact can not have its status changed
+ if (TskData.FileKnown.BAD == t.getName().getKnownStatus()) {
+ //a tag with a conflicting status has been found, the status of this correlation attribute can not be modified
+ hasTagWithConflictingKnownStatus = true;
+ break;
+ }
+ }
+ }
+ //if the Correlation Attribute will have no tags with a status which would prevent the current status from being changed
+ if (!hasTagWithConflictingKnownStatus) {
+ //Get the correlation atttributes that correspond to the current BlackboardArtifactTag if their status should be changed
+ //with the initial set of correlation attributes this should be a single correlation attribute
+ List convertedArtifacts = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbTag.getArtifact(), true, true);
+ for (CorrelationAttribute eamArtifact : convertedArtifacts) {
+ EamDb.getInstance().setArtifactInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
+ }
+ }
+ }
+ // Next update the files
+
+ List fileTags = Case.getCurrentCase().getSleuthkitCase().getContentTagsByTagName(tagName);
+ //Get all ContentTags with this tag name
+ for (ContentTag contentTag : fileTags) {
+ //start with assumption that none of the other tags applied to this ContentTag will prevent it's status from being changed
+ boolean hasTagWithConflictingKnownStatus = false;
+ // if the status of the tag has been changed to TskData.FileKnown.UNKNOWN
+ // we need to check the status of all other tags on this file before changing
+ // the status of the file in the central repository
+ if (tagName.getKnownStatus() == TskData.FileKnown.UNKNOWN) {
+ Content content = contentTag.getContent();
+ TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager();
+ List tags = tagsManager.getContentTagsByContent(content);
+ //get all tags which are on this file
+ for (ContentTag t : tags) {
+ //All instances of the modified tag name will be changed, they can not conflict with each other
+ if (t.getName().equals(tagName)) {
+ continue;
+ }
+ //if any other tags on this file are Notable in status then this file can not have its status changed
+ if (TskData.FileKnown.BAD == t.getName().getKnownStatus()) {
+ //a tag with a conflicting status has been found, the status of this file can not be modified
+ hasTagWithConflictingKnownStatus = true;
+ break;
+ }
+ }
+ }
+ //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.getEamArtifactFromContent(contentTag.getContent(),
+ tagName.getKnownStatus(), "");
+ if (eamArtifact != null) {
+ EamDb.getInstance().setArtifactInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
+ }
+ }
+ }
+ } catch (TskCoreException ex) {
+ LOGGER.log(Level.SEVERE, "Cannot update known status in central repository for tag: " + modifiedTagName, ex); //NON-NLS
+ } catch (EamDbException ex) {
+ LOGGER.log(Level.SEVERE, "Cannot get central repository for tag: " + modifiedTagName, ex); //NON-NLS
+ }
+ } //TAG_STATUS_CHANGED
+ }
+
private final class DataSourceAddedTask implements Runnable {
private final EamDb dbManager;
@@ -350,7 +465,7 @@ final class CaseEventListener implements PropertyChangeListener {
if ((null == event.getOldValue()) && (event.getNewValue() instanceof Case)) {
Case curCase = (Case) event.getNewValue();
IngestEventsListener.resetCeModuleInstanceCount();
-
+
CorrelationCase curCeCase = new CorrelationCase(
-1,
curCase.getName(), // unique case ID
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java
index ad6c26e4d8..9055485e2f 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java
@@ -35,6 +35,7 @@ import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization;
import org.sleuthkit.autopsy.coreutils.Logger;
@@ -72,7 +73,7 @@ public final class ManageOrganizationsDialog extends JDialog {
organizationList.setModel(rulesListModel);
organizationList.addListSelectionListener(new OrganizationListSelectionListener());
populateList();
- setButtonsEnabled(organizationList.getSelectedValue() != null);
+ setButtonsEnabled(organizationList.getSelectedValue());
newOrg = null;
} catch (EamDbException ex) {
Exceptions.printStackTrace(ex);
@@ -421,9 +422,15 @@ public final class ManageOrganizationsDialog extends JDialog {
return newOrg;
}
- private void setButtonsEnabled(boolean isSelected) {
- editButton.setEnabled(isSelected);
- deleteButton.setEnabled(isSelected);
+ private void setButtonsEnabled(EamOrganization selectedOrg) {
+ boolean isSelected = (selectedOrg != null);
+ boolean isDefaultOrg = false;
+ if(selectedOrg != null){
+ isDefaultOrg = EamDbUtil.isDefaultOrg(selectedOrg);
+ }
+
+ editButton.setEnabled(isSelected && (! isDefaultOrg));
+ deleteButton.setEnabled(isSelected && (! isDefaultOrg));
}
/**
@@ -436,9 +443,8 @@ public final class ManageOrganizationsDialog extends JDialog {
if (e.getValueIsAdjusting()) {
return;
}
- EamOrganization selected = organizationList.getSelectedValue();
- boolean isSelected = (selected != null);
- setButtonsEnabled(isSelected);
+ EamOrganization selected = organizationList.getSelectedValue();
+ setButtonsEnabled(selected);
if (selected != null) {
orgNameTextField.setText(selected.getName());
pocNameTextField.setText(selected.getPocName());
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form
index 06607d7d9b..2e618379e4 100755
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form
@@ -22,12 +22,18 @@
-
+
+
+
+
-
+
+
+
+
@@ -46,247 +52,336 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java
index 4f95087f66..435bdc99b4 100755
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java
@@ -18,9 +18,18 @@
*/
package org.sleuthkit.autopsy.corecomponents;
+import java.awt.image.BufferedImage;
import java.io.File;
+import java.io.IOException;
+import java.util.logging.Level;
+import javax.imageio.ImageIO;
+import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
import org.netbeans.spi.options.OptionsPanelController;
+import org.openide.util.NbBundle;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
@@ -29,16 +38,23 @@ import org.sleuthkit.autopsy.report.ReportBranding;
/**
* Options panel that allow users to set application preferences.
*/
+@Messages({"AutopsyOptionsPanel.agencyLogoPreview.text=No logo
selected
",
+ "AutopsyOptionsPanel.logoPanel.border.title=Logo",
+ "AutopsyOptionsPanel.viewPanel.border.title=View",
+ "AutopsyOptionsPanel.invalidImageFile.msg=The selected file was not able to be used as an agency logo.",
+ "AutopsyOptionsPanel.invalidImageFile.title=Invalid Image File"})
final class AutopsyOptionsPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private final JFileChooser fc;
+ private static final Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName());
AutopsyOptionsPanel() {
initComponents();
fc = new JFileChooser();
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setMultiSelectionEnabled(false);
+ fc.setAcceptAllFileFilterUsed(false);
fc.setFileFilter(new GeneralFilter(GeneralFilter.GRAPHIC_IMAGE_EXTS, GeneralFilter.GRAPHIC_IMG_DECR));
}
@@ -53,7 +69,31 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
boolean useLocalTime = UserPreferences.displayTimesInLocalTime();
useLocalTimeRB.setSelected(useLocalTime);
useGMTTimeRB.setSelected(!useLocalTime);
- agencyLogoPathField.setText(ModuleSettings.getConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP));
+ String path = ModuleSettings.getConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP);
+ try {
+ updateAgencyLogo(path);
+ } catch (IOException ex) {
+ logger.log(Level.WARNING, "Error loading image from previously saved agency logo path", ex);
+ }
+ }
+
+ private void updateAgencyLogo(String path) throws IOException {
+ agencyLogoPathField.setText(path);
+ ImageIcon agencyLogoIcon = new ImageIcon();
+ agencyLogoPreview.setText(Bundle.AutopsyOptionsPanel_agencyLogoPreview_text());
+ if (!agencyLogoPathField.getText().isEmpty()) {
+ File file = new File(agencyLogoPathField.getText());
+ if (file.exists()) {
+ BufferedImage image = ImageIO.read(file); //create it as an image first to support BMP files
+ if (image == null) {
+ throw new IOException("Unable to read file as a BufferedImage for file " + file.toString());
+ }
+ agencyLogoIcon = new ImageIcon(image.getScaledInstance(64, 64, 4));
+ agencyLogoPreview.setText("");
+ }
+ }
+ agencyLogoPreview.setIcon(agencyLogoIcon);
+ agencyLogoPreview.repaint();
}
void store() {
@@ -64,8 +104,8 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
UserPreferences.setHideSlackFilesInViewsTree(viewsHideSlackCB.isSelected());
UserPreferences.setDisplayTimesInLocalTime(useLocalTimeRB.isSelected());
if (!agencyLogoPathField.getText().isEmpty()) {
- File image = new File(agencyLogoPathField.getText());
- if (image.exists()) {
+ File file = new File(agencyLogoPathField.getText());
+ if (file.exists()) {
ModuleSettings.setConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP, agencyLogoPathField.getText());
}
}
@@ -87,24 +127,87 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
buttonGroup3 = new javax.swing.ButtonGroup();
jScrollPane1 = new javax.swing.JScrollPane();
jPanel1 = new javax.swing.JPanel();
- useBestViewerRB = new javax.swing.JRadioButton();
- keepCurrentViewerRB = new javax.swing.JRadioButton();
- jLabelSelectFile = new javax.swing.JLabel();
- jLabelTimeDisplay = new javax.swing.JLabel();
- useLocalTimeRB = new javax.swing.JRadioButton();
- useGMTTimeRB = new javax.swing.JRadioButton();
- jLabelHideKnownFiles = new javax.swing.JLabel();
- dataSourcesHideKnownCB = new javax.swing.JCheckBox();
- viewsHideKnownCB = new javax.swing.JCheckBox();
- dataSourcesHideSlackCB = new javax.swing.JCheckBox();
- viewsHideSlackCB = new javax.swing.JCheckBox();
- jLabelHideSlackFiles = new javax.swing.JLabel();
+ logoPanel = new javax.swing.JPanel();
agencyLogoImageLabel = new javax.swing.JLabel();
agencyLogoPathField = new javax.swing.JTextField();
browseLogosButton = new javax.swing.JButton();
+ agencyLogoPreview = new javax.swing.JLabel();
+ viewPanel = new javax.swing.JPanel();
+ jLabelSelectFile = new javax.swing.JLabel();
+ useBestViewerRB = new javax.swing.JRadioButton();
+ keepCurrentViewerRB = new javax.swing.JRadioButton();
+ jLabelHideKnownFiles = new javax.swing.JLabel();
+ dataSourcesHideKnownCB = new javax.swing.JCheckBox();
+ viewsHideKnownCB = new javax.swing.JCheckBox();
+ jLabelHideSlackFiles = new javax.swing.JLabel();
+ dataSourcesHideSlackCB = new javax.swing.JCheckBox();
+ viewsHideSlackCB = new javax.swing.JCheckBox();
+ jLabelTimeDisplay = new javax.swing.JLabel();
+ useLocalTimeRB = new javax.swing.JRadioButton();
+ useGMTTimeRB = new javax.swing.JRadioButton();
jScrollPane1.setBorder(null);
+ logoPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.logoPanel.border.title"))); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(agencyLogoImageLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoImageLabel.text")); // NOI18N
+
+ agencyLogoPathField.setEditable(false);
+ agencyLogoPathField.setBackground(new java.awt.Color(255, 255, 255));
+ agencyLogoPathField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoPathField.text")); // NOI18N
+ agencyLogoPathField.setFocusable(false);
+ agencyLogoPathField.setRequestFocusEnabled(false);
+
+ org.openide.awt.Mnemonics.setLocalizedText(browseLogosButton, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.browseLogosButton.text")); // NOI18N
+ browseLogosButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ browseLogosButtonActionPerformed(evt);
+ }
+ });
+
+ agencyLogoPreview.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
+ org.openide.awt.Mnemonics.setLocalizedText(agencyLogoPreview, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoPreview.text")); // NOI18N
+ agencyLogoPreview.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+ agencyLogoPreview.setMaximumSize(new java.awt.Dimension(64, 64));
+ agencyLogoPreview.setMinimumSize(new java.awt.Dimension(64, 64));
+ agencyLogoPreview.setPreferredSize(new java.awt.Dimension(64, 64));
+
+ javax.swing.GroupLayout logoPanelLayout = new javax.swing.GroupLayout(logoPanel);
+ logoPanel.setLayout(logoPanelLayout);
+ logoPanelLayout.setHorizontalGroup(
+ logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, logoPanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(agencyLogoImageLabel)
+ .addGroup(logoPanelLayout.createSequentialGroup()
+ .addGap(10, 10, 10)
+ .addComponent(agencyLogoPathField, javax.swing.GroupLayout.PREFERRED_SIZE, 259, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(browseLogosButton)))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(agencyLogoPreview, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap(149, Short.MAX_VALUE))
+ );
+ logoPanelLayout.setVerticalGroup(
+ logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(logoPanelLayout.createSequentialGroup()
+ .addGroup(logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(agencyLogoPreview, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(logoPanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(agencyLogoImageLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(agencyLogoPathField)
+ .addComponent(browseLogosButton))))
+ .addGap(0, 0, 0))
+ );
+
+ viewPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.viewPanel.border.title"))); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectFile, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelSelectFile.text")); // NOI18N
+
buttonGroup1.add(useBestViewerRB);
org.openide.awt.Mnemonics.setLocalizedText(useBestViewerRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useBestViewerRB.text")); // NOI18N
useBestViewerRB.setToolTipText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useBestViewerRB.toolTipText")); // NOI18N
@@ -123,7 +226,37 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}
});
- org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectFile, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelSelectFile.text")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(jLabelHideKnownFiles, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelHideKnownFiles.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(dataSourcesHideKnownCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.dataSourcesHideKnownCB.text")); // NOI18N
+ dataSourcesHideKnownCB.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ dataSourcesHideKnownCBActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(viewsHideKnownCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.viewsHideKnownCB.text")); // NOI18N
+ viewsHideKnownCB.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ viewsHideKnownCBActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(jLabelHideSlackFiles, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelHideSlackFiles.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(dataSourcesHideSlackCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.dataSourcesHideSlackCB.text")); // NOI18N
+ dataSourcesHideSlackCB.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ dataSourcesHideSlackCBActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(viewsHideSlackCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.viewsHideSlackCB.text")); // NOI18N
+ viewsHideSlackCB.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ viewsHideSlackCBActionPerformed(evt);
+ }
+ });
org.openide.awt.Mnemonics.setLocalizedText(jLabelTimeDisplay, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelTimeDisplay.text")); // NOI18N
@@ -143,93 +276,34 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}
});
- org.openide.awt.Mnemonics.setLocalizedText(jLabelHideKnownFiles, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelHideKnownFiles.text")); // NOI18N
-
- org.openide.awt.Mnemonics.setLocalizedText(dataSourcesHideKnownCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.dataSourcesHideKnownCB.text")); // NOI18N
- dataSourcesHideKnownCB.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(java.awt.event.ActionEvent evt) {
- dataSourcesHideKnownCBActionPerformed(evt);
- }
- });
-
- org.openide.awt.Mnemonics.setLocalizedText(viewsHideKnownCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.viewsHideKnownCB.text")); // NOI18N
- viewsHideKnownCB.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(java.awt.event.ActionEvent evt) {
- viewsHideKnownCBActionPerformed(evt);
- }
- });
-
- org.openide.awt.Mnemonics.setLocalizedText(dataSourcesHideSlackCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.dataSourcesHideSlackCB.text")); // NOI18N
- dataSourcesHideSlackCB.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(java.awt.event.ActionEvent evt) {
- dataSourcesHideSlackCBActionPerformed(evt);
- }
- });
-
- org.openide.awt.Mnemonics.setLocalizedText(viewsHideSlackCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.viewsHideSlackCB.text")); // NOI18N
- viewsHideSlackCB.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(java.awt.event.ActionEvent evt) {
- viewsHideSlackCBActionPerformed(evt);
- }
- });
-
- org.openide.awt.Mnemonics.setLocalizedText(jLabelHideSlackFiles, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelHideSlackFiles.text")); // NOI18N
-
- org.openide.awt.Mnemonics.setLocalizedText(agencyLogoImageLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoImageLabel.text")); // NOI18N
-
- agencyLogoPathField.setEditable(false);
- agencyLogoPathField.setBackground(new java.awt.Color(255, 255, 255));
- agencyLogoPathField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoPathField.text")); // NOI18N
- agencyLogoPathField.setFocusable(false);
- agencyLogoPathField.setRequestFocusEnabled(false);
-
- org.openide.awt.Mnemonics.setLocalizedText(browseLogosButton, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.browseLogosButton.text")); // NOI18N
- browseLogosButton.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(java.awt.event.ActionEvent evt) {
- browseLogosButtonActionPerformed(evt);
- }
- });
-
- javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
- jPanel1.setLayout(jPanel1Layout);
- jPanel1Layout.setHorizontalGroup(
- jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(jPanel1Layout.createSequentialGroup()
+ javax.swing.GroupLayout viewPanelLayout = new javax.swing.GroupLayout(viewPanel);
+ viewPanel.setLayout(viewPanelLayout);
+ viewPanelLayout.setHorizontalGroup(
+ viewPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, viewPanelLayout.createSequentialGroup()
.addContainerGap()
- .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(jPanel1Layout.createSequentialGroup()
- .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(jLabelTimeDisplay)
- .addComponent(jLabelHideKnownFiles)
- .addComponent(jLabelSelectFile)
- .addGroup(jPanel1Layout.createSequentialGroup()
- .addGap(10, 10, 10)
- .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(useLocalTimeRB)
- .addComponent(useGMTTimeRB)
- .addComponent(keepCurrentViewerRB)
- .addComponent(useBestViewerRB)
- .addComponent(dataSourcesHideKnownCB)
- .addComponent(viewsHideKnownCB))))
- .addContainerGap(140, Short.MAX_VALUE))
- .addGroup(jPanel1Layout.createSequentialGroup()
- .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(jLabelHideSlackFiles)
- .addComponent(agencyLogoImageLabel)
- .addGroup(jPanel1Layout.createSequentialGroup()
- .addGap(10, 10, 10)
- .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(jPanel1Layout.createSequentialGroup()
- .addComponent(agencyLogoPathField, javax.swing.GroupLayout.PREFERRED_SIZE, 259, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(browseLogosButton))
- .addComponent(dataSourcesHideSlackCB)
- .addComponent(viewsHideSlackCB))))
- .addGap(0, 0, Short.MAX_VALUE))))
+ .addGroup(viewPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(viewPanelLayout.createSequentialGroup()
+ .addGap(10, 10, 10)
+ .addGroup(viewPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(useGMTTimeRB)
+ .addComponent(keepCurrentViewerRB)
+ .addComponent(useBestViewerRB)
+ .addGroup(viewPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(useLocalTimeRB)
+ .addComponent(dataSourcesHideSlackCB)
+ .addComponent(viewsHideSlackCB)
+ .addComponent(dataSourcesHideKnownCB)
+ .addComponent(viewsHideKnownCB))))
+ .addComponent(jLabelHideSlackFiles)
+ .addComponent(jLabelTimeDisplay)
+ .addComponent(jLabelHideKnownFiles)
+ .addComponent(jLabelSelectFile))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
- jPanel1Layout.setVerticalGroup(
- jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(jPanel1Layout.createSequentialGroup()
+ viewPanelLayout.setVerticalGroup(
+ viewPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, viewPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabelSelectFile)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@@ -253,14 +327,28 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(useLocalTimeRB)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(useGMTTimeRB)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(agencyLogoImageLabel)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(agencyLogoPathField)
- .addComponent(browseLogosButton))
- .addGap(35, 35, 35))
+ .addComponent(useGMTTimeRB))
+ );
+
+ javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1Layout.setHorizontalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(viewPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(logoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ jPanel1Layout.setVerticalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addGap(0, 0, 0)
+ .addComponent(viewPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(0, 0, 0)
+ .addComponent(logoPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(0, 0, 0))
);
jScrollPane1.setViewportView(jPanel1);
@@ -269,11 +357,15 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 672, Short.MAX_VALUE)
+ .addGap(0, 0, 0))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(jScrollPane1)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 489, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(0, 0, Short.MAX_VALUE))
);
}// //GEN-END:initComponents
@@ -310,17 +402,32 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}//GEN-LAST:event_viewsHideSlackCBActionPerformed
private void browseLogosButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseLogosButtonActionPerformed
+ String oldLogoPath = agencyLogoPathField.getText();
int returnState = fc.showOpenDialog(this);
if (returnState == JFileChooser.APPROVE_OPTION) {
String path = fc.getSelectedFile().getPath();
- agencyLogoPathField.setText(path);
- firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
+ try {
+ updateAgencyLogo(path);
+ firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
+ } catch (IOException | IndexOutOfBoundsException ex) {
+ JOptionPane.showMessageDialog(null,
+ NbBundle.getMessage(this.getClass(),
+ "AutopsyOptionsPanel.invalidImageFile.msg"),
+ NbBundle.getMessage(this.getClass(), "AutopsyOptionsPanel.invalidImageFile.title"),
+ JOptionPane.ERROR_MESSAGE);
+ try {
+ updateAgencyLogo(oldLogoPath); //restore previous setting if new one is invalid
+ } catch (IOException ex1) {
+ logger.log(Level.WARNING, "Error loading image from previously saved agency logo path", ex1);
+ }
+ }
}
}//GEN-LAST:event_browseLogosButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel agencyLogoImageLabel;
private javax.swing.JTextField agencyLogoPathField;
+ private javax.swing.JLabel agencyLogoPreview;
private javax.swing.JButton browseLogosButton;
private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.ButtonGroup buttonGroup3;
@@ -333,9 +440,11 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JRadioButton keepCurrentViewerRB;
+ private javax.swing.JPanel logoPanel;
private javax.swing.JRadioButton useBestViewerRB;
private javax.swing.JRadioButton useGMTTimeRB;
private javax.swing.JRadioButton useLocalTimeRB;
+ private javax.swing.JPanel viewPanel;
private javax.swing.JCheckBox viewsHideKnownCB;
private javax.swing.JCheckBox viewsHideSlackCB;
// End of variables declaration//GEN-END:variables
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
index 5ac6cbd626..26a2cc03e0 100755
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
@@ -1,7 +1,7 @@
CTL_DataContentAction=DataContent
CTL_DataContentTopComponent=Data Content
CTL_CustomAboutAction=About
-OptionsCategory_Name_General=View
+OptionsCategory_Name_General=Application
OptionsCategory_Keywords_General=Autopsy Options
HINT_DataContentTopComponent=This is a DataContent window
HINT_NodeTableTopComponent=This is a DataResult window
@@ -198,7 +198,6 @@ AutopsyOptionsPanel.agencyLogoPathField.text=
SortChooserDialog.label=remove
SortChooser.addCriteriaButton.text=Add Sort Criteria
DataResultViewerThumbnail.sortButton.text=Sort
-
CriterionChooser.ascendingRadio.text=\u25b2 Ascending\n
CriterionChooser.removeButton.text=Remove
CriterionChooser.descendingRadio.text=\u25bc Descending
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java
index 4195b33008..eafa8377b2 100755
--- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java
@@ -38,6 +38,7 @@ public class FileTypeExtensions {
private final static List WEB_EXTENSIONS = Arrays.asList(".html", ".htm", ".css", ".js", ".php", ".aspx"); //NON-NLS
private final static List PDF_EXTENSIONS = Arrays.asList(".pdf"); //NON-NLS
private final static List ARCHIVE_EXTENSIONS = Arrays.asList(".zip", ".rar", ".7zip", ".7z", ".arj", ".tar", ".gzip", ".bzip", ".bzip2", ".cab", ".jar", ".cpio", ".ar", ".gz", ".tgz", ".bz2"); //NON-NLS
+ private final static List DATABASE_EXTENSIONS = Arrays.asList(".db", ".db3", ".sqlite", ".sqlite3"); //NON-NLS
public static List getImageExtensions() {
return IMAGE_EXTENSIONS;
@@ -75,6 +76,10 @@ public class FileTypeExtensions {
return ARCHIVE_EXTENSIONS;
}
+ public static List getDatabaseExtensions() {
+ return DATABASE_EXTENSIONS;
+ }
+
private FileTypeExtensions() {
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java
index 305da8e953..2341fff12c 100755
--- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java
@@ -34,6 +34,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
+import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.UserPreferences;
@@ -423,6 +424,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
}
// root node filters
+ @Messages({"FileTypeExtensionFilters.tskDatabaseFilter.text=Databases"})
public static enum RootFilter implements AutopsyVisitableItem, SearchFilterInterface {
TSK_IMAGE_FILTER(0, "TSK_IMAGE_FILTER", //NON-NLS
@@ -437,10 +439,13 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
TSK_ARCHIVE_FILTER(3, "TSK_ARCHIVE_FILTER", //NON-NLS
NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskArchiveFilter.text"),
FileTypeExtensions.getArchiveExtensions()),
- TSK_DOCUMENT_FILTER(3, "TSK_DOCUMENT_FILTER", //NON-NLS
+ TSK_DATABASE_FILTER(4, "TSK_DATABASE_FILTER", //NON-NLS
+ NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskDatabaseFilter.text"),
+ FileTypeExtensions.getDatabaseExtensions()),
+ TSK_DOCUMENT_FILTER(5, "TSK_DOCUMENT_FILTER", //NON-NLS
NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskDocumentFilter.text"),
Arrays.asList(".htm", ".html", ".doc", ".docx", ".odt", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".rtf")), //NON-NLS
- TSK_EXECUTABLE_FILTER(3, "TSK_EXECUTABLE_FILTER", //NON-NLS
+ TSK_EXECUTABLE_FILTER(6, "TSK_EXECUTABLE_FILTER", //NON-NLS
NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskExecFilter.text"),
FileTypeExtensions.getExecutableExtensions()); //NON-NLS
diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java
index 7d4328da59..7a9d9b04ef 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2013-2017 Basis Technology Corp.
+ * Copyright 2015 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,7 +44,7 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // "iso"}; NON-NLS
private String moduleDirRelative;
private String moduleDirAbsolute;
- private ImageExtractor imageExtractor;
+ private MSOfficeEmbeddedContentExtractor officeExtractor;
private SevenZipExtractor archiveExtractor;
private FileTypeDetector fileTypeDetector;
@@ -98,10 +98,10 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
}
/*
- * Construct an embedded images extractor for processing Microsoft
+ * Construct an embedded content extractor for processing Microsoft
* Office documents.
*/
- this.imageExtractor = new ImageExtractor(context, fileTypeDetector, moduleDirRelative, moduleDirAbsolute);
+ this.officeExtractor = new MSOfficeEmbeddedContentExtractor(context, fileTypeDetector, moduleDirRelative, moduleDirAbsolute);
}
@Override
@@ -134,8 +134,8 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
*/
if (archiveExtractor.isSevenZipExtractionSupported(abstractFile)) {
archiveExtractor.unpack(abstractFile);
- } else if (imageExtractor.isImageExtractionSupported(abstractFile)) {
- imageExtractor.extractImage(abstractFile);
+ } else if (officeExtractor.isContentExtractionSupported(abstractFile)) {
+ officeExtractor.extractEmbeddedContent(abstractFile);
}
return ProcessResult.OK;
}
diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
similarity index 58%
rename from Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java
rename to Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
index d58d935e97..61376d31f1 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2011-2017 Basis Technology Corp.
+ * Copyright 2015 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,14 +21,16 @@ package org.sleuthkit.autopsy.modules.embeddedfileextractor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.lang.IllegalArgumentException;
-import java.lang.IndexOutOfBoundsException;
-import java.lang.NullPointerException;
+import java.io.InputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.logging.Level;
-import org.apache.poi.POIXMLException;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
import org.apache.poi.hwpf.usermodel.Picture;
import org.apache.poi.hslf.usermodel.HSLFPictureData;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
@@ -39,11 +41,18 @@ import org.apache.poi.hwpf.model.PicturesTable;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.RecordFormatException;
-import org.apache.poi.xslf.usermodel.XMLSlideShow;
-import org.apache.poi.xslf.usermodel.XSLFPictureData;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.apache.poi.xwpf.usermodel.XWPFDocument;
-import org.apache.poi.xwpf.usermodel.XWPFPictureData;
+import org.apache.tika.config.TikaConfig;
+import org.apache.tika.detect.Detector;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.extractor.EmbeddedDocumentExtractor;
+import org.apache.tika.extractor.ParsingEmbeddedDocumentExtractor;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.mime.MediaType;
+import org.apache.tika.mime.MimeTypeException;
+import org.apache.tika.parser.AutoDetectParser;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.Parser;
+import org.apache.tika.sax.BodyContentHandler;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
@@ -57,24 +66,34 @@ import org.sleuthkit.datamodel.EncodedFileOutputStream;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
-class ImageExtractor {
+/**
+ * Extracts embedded content (e.g. images, audio, video) from Microsoft Office
+ * documents (both original and OOXML forms).
+ */
+class MSOfficeEmbeddedContentExtractor {
private final FileManager fileManager;
private final IngestServices services;
- private static final Logger logger = Logger.getLogger(ImageExtractor.class.getName());
+ private static final Logger LOGGER = Logger.getLogger(MSOfficeEmbeddedContentExtractor.class.getName());
private final IngestJobContext context;
private String parentFileName;
- private final String UNKNOWN_NAME_PREFIX = "image_"; //NON-NLS
+ private final String UNKNOWN_IMAGE_NAME_PREFIX = "image_"; //NON-NLS
private final FileTypeDetector fileTypeDetector;
private String moduleDirRelative;
private String moduleDirAbsolute;
+ private AutoDetectParser parser = new AutoDetectParser();
+ private Detector detector = parser.getDetector();
+ private TikaConfig config = TikaConfig.getDefaultConfig();
+
/**
- * Enum of mimetypes which support image extraction
+ * Enum of mimetypes for which we can extract embedded content.
*/
- enum SupportedImageExtractionFormats {
+ enum SupportedExtractionFormats {
DOC("application/msword"), //NON-NLS
DOCX("application/vnd.openxmlformats-officedocument.wordprocessingml.document"), //NON-NLS
@@ -85,7 +104,7 @@ class ImageExtractor {
private final String mimeType;
- SupportedImageExtractionFormats(final String mimeType) {
+ SupportedExtractionFormats(final String mimeType) {
this.mimeType = mimeType;
}
@@ -93,11 +112,10 @@ class ImageExtractor {
public String toString() {
return this.mimeType;
}
- // TODO Expand to support more formats
}
- private SupportedImageExtractionFormats abstractFileExtractionFormat;
+ private SupportedExtractionFormats abstractFileExtractionFormat;
- ImageExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) {
+ MSOfficeEmbeddedContentExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) {
this.fileManager = Case.getCurrentCase().getServices().getFileManager();
this.services = IngestServices.getInstance();
@@ -111,15 +129,15 @@ class ImageExtractor {
* This method returns true if the file format is currently supported. Else
* it returns false. Performs only Apache Tika based detection.
*
- * @param abstractFile The AbstractFilw whose mimetype is to be determined.
+ * @param abstractFile The AbstractFile whose mimetype is to be determined.
*
* @return This method returns true if the file format is currently
* supported. Else it returns false.
*/
- boolean isImageExtractionSupported(AbstractFile abstractFile) {
+ boolean isContentExtractionSupported(AbstractFile abstractFile) {
try {
String abstractFileMimeType = fileTypeDetector.getFileType(abstractFile);
- for (SupportedImageExtractionFormats s : SupportedImageExtractionFormats.values()) {
+ for (SupportedExtractionFormats s : SupportedExtractionFormats.values()) {
if (s.toString().equals(abstractFileMimeType)) {
abstractFileExtractionFormat = s;
return true;
@@ -127,60 +145,55 @@ class ImageExtractor {
}
return false;
} catch (TskCoreException ex) {
- logger.log(Level.SEVERE, "Error executing FileTypeDetector.getFileType()", ex); // NON-NLS
+ LOGGER.log(Level.SEVERE, "Error executing FileTypeDetector.getFileType()", ex); // NON-NLS
return false;
}
}
/**
- * This method selects the appropriate process of extracting images from
- * files using POI classes. Once the images have been extracted, the method
- * adds them to the DB and fires a ModuleContentEvent. ModuleContent Event
- * is not fired if the no images were extracted from the processed file.
+ * This method selects the appropriate process of extracting embedded
+ * content from files using either Tika or POI classes. Once the content has
+ * been extracted as files, the method adds them to the DB and fires a
+ * ModuleContentEvent. ModuleContent Event is not fired if no content
+ * was extracted from the processed file.
*
- * @param format
* @param abstractFile The abstract file to be processed.
*/
- void extractImage(AbstractFile abstractFile) {
- //
- // switchcase for different supported formats
- // process abstractFile according to the format by calling appropriate methods.
-
- List listOfExtractedImages = null;
+ void extractEmbeddedContent(AbstractFile abstractFile) {
+ List listOfExtractedImages = null;
List listOfExtractedImageAbstractFiles = null;
this.parentFileName = EmbeddedFileExtractorIngestModule.getUniqueName(abstractFile);
- //check if already has derived files, skip
+
+ // Skip files that already have been unpacked.
try {
if (abstractFile.hasChildren()) {
//check if local unpacked dir exists
if (new File(getOutputFolderPath(parentFileName)).exists()) {
- logger.log(Level.INFO, "File already has been processed as it has children and local unpacked file, skipping: {0}", abstractFile.getName()); //NON-NLS
+ LOGGER.log(Level.INFO, "File already has been processed as it has children and local unpacked file, skipping: {0}", abstractFile.getName()); //NON-NLS
return;
}
}
} catch (TskCoreException e) {
- logger.log(Level.SEVERE, String.format("Error checking if file already has been processed, skipping: %s", parentFileName), e); //NON-NLS
+ LOGGER.log(Level.SEVERE, String.format("Error checking if file already has been processed, skipping: %s", parentFileName), e); //NON-NLS
return;
}
+
+ // Call the appropriate extraction method based on mime type
switch (abstractFileExtractionFormat) {
- case DOC:
- listOfExtractedImages = extractImagesFromDoc(abstractFile);
- break;
case DOCX:
- listOfExtractedImages = extractImagesFromDocx(abstractFile);
+ case PPTX:
+ case XLSX:
+ listOfExtractedImages = extractEmbeddedContentFromOOXML(abstractFile);
+ break;
+ case DOC:
+ listOfExtractedImages = extractEmbeddedImagesFromDoc(abstractFile);
break;
case PPT:
- listOfExtractedImages = extractImagesFromPpt(abstractFile);
- break;
- case PPTX:
- listOfExtractedImages = extractImagesFromPptx(abstractFile);
+ listOfExtractedImages = extractEmbeddedImagesFromPpt(abstractFile);
break;
case XLS:
listOfExtractedImages = extractImagesFromXls(abstractFile);
break;
- case XLSX:
- listOfExtractedImages = extractImagesFromXlsx(abstractFile);
- break;
default:
break;
}
@@ -190,13 +203,13 @@ class ImageExtractor {
}
// the common task of adding abstractFile to derivedfiles is performed.
listOfExtractedImageAbstractFiles = new ArrayList<>();
- for (ExtractedImage extractedImage : listOfExtractedImages) {
+ for (ExtractedFile extractedImage : listOfExtractedImages) {
try {
listOfExtractedImageAbstractFiles.add(fileManager.addDerivedFile(extractedImage.getFileName(), extractedImage.getLocalPath(), extractedImage.getSize(),
extractedImage.getCtime(), extractedImage.getCrtime(), extractedImage.getAtime(), extractedImage.getAtime(),
true, abstractFile, null, EmbeddedFileExtractorModuleFactory.getModuleName(), null, null, TskData.EncodingType.XOR1));
} catch (TskCoreException ex) {
- logger.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.extractImage.addToDB.exception.msg"), ex); //NON-NLS
+ LOGGER.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.extractImage.addToDB.exception.msg"), ex); //NON-NLS
}
}
if (!listOfExtractedImages.isEmpty()) {
@@ -206,43 +219,79 @@ class ImageExtractor {
}
/**
- * Extract images from doc format files.
+ * Extracts embedded content from OOXML documents (i.e. pptx, docx and xlsx)
+ * using Tika. This will extract images and other multimedia content
+ * embedded in the given file.
+ *
+ * @param abstractFile The file to extract content from.
+ *
+ * @return A list of extracted files.
+ */
+ private List extractEmbeddedContentFromOOXML(AbstractFile abstractFile) {
+ Metadata metadata = new Metadata();
+
+ ParseContext parseContext = new ParseContext();
+ parseContext.set(Parser.class, parser);
+
+ // Passing -1 to the BodyContentHandler constructor disables the Tika
+ // write limit (which defaults to 100,000 characters.
+ ContentHandler contentHandler = new BodyContentHandler(-1);
+
+ // TODO: this will be needed once we upgrade to Tika 1.16 or later.
+ // OfficeParserConfig officeParserConfig = new OfficeParserConfig();
+ // officeParserConfig.setUseSAXPptxExtractor(true);
+ // officeParserConfig.setUseSAXDocxExtractor(true);
+ // parseContext.set(OfficeParserConfig.class, officeParserConfig);
+ EmbeddedDocumentExtractor extractor = new EmbeddedContentExtractor(parseContext);
+ parseContext.set(EmbeddedDocumentExtractor.class, extractor);
+ ReadContentInputStream stream = new ReadContentInputStream(abstractFile);
+
+ try {
+ parser.parse(stream, contentHandler, metadata, parseContext);
+ } catch (IOException | SAXException | TikaException ex) {
+ LOGGER.log(Level.WARNING, "Error while parsing file, skipping: " + abstractFile.getName(), ex); //NON-NLS
+ return null;
+ }
+
+ return ((EmbeddedContentExtractor) extractor).getExtractedImages();
+ }
+
+ /**
+ * Extract embedded images from doc format files.
*
* @param af the file from which images are to be extracted.
*
* @return list of extracted images. Returns null in case no images were
* extracted.
*/
- private List extractImagesFromDoc(AbstractFile af) {
+ private List extractEmbeddedImagesFromDoc(AbstractFile af) {
List listOfAllPictures;
-
+
try {
HWPFDocument doc = new HWPFDocument(new ReadContentInputStream(af));
PicturesTable pictureTable = doc.getPicturesTable();
listOfAllPictures = pictureTable.getAllPictures();
- } catch (IOException | IllegalArgumentException |
- IndexOutOfBoundsException | NullPointerException ex) {
+ } catch (IOException | IllegalArgumentException
+ | IndexOutOfBoundsException | NullPointerException ex) {
// IOException:
// Thrown when the document has issues being read.
-
+
// IllegalArgumentException:
// This will catch OldFileFormatException, which is thrown when the
// document's format is Word 95 or older. Alternatively, this is
// thrown when attempting to load an RTF file as a DOC file.
// However, our code verifies the file format before ever running it
- // through the ImageExtractor. This exception gets thrown in the
+ // through the EmbeddedContentExtractor. This exception gets thrown in the
// "IN10-0137.E01" image regardless. The reason is unknown.
-
// IndexOutOfBoundsException:
// NullPointerException:
// These get thrown in certain images. The reason is unknown. It is
// likely due to problems with the file formats that POI is poorly
// handling.
-
return null;
} catch (Throwable ex) {
// instantiating POI containers throw RuntimeExceptions
- logger.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.docContainer.init.err", af.getName()), ex); //NON-NLS
+ LOGGER.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.docContainer.init.err", af.getName()), ex); //NON-NLS
return null;
}
@@ -255,7 +304,7 @@ class ImageExtractor {
if (outputFolderPath == null) {
return null;
}
- List listOfExtractedImages = new ArrayList<>();
+ List listOfExtractedImages = new ArrayList<>();
byte[] data = null;
for (Picture picture : listOfAllPictures) {
String fileName = picture.suggestFullFileName();
@@ -266,99 +315,43 @@ class ImageExtractor {
}
writeExtractedImage(Paths.get(outputFolderPath, fileName).toString(), data);
// TODO Extract more info from the Picture viz ctime, crtime, atime, mtime
- listOfExtractedImages.add(new ExtractedImage(fileName, getFileRelativePath(fileName), picture.getSize(), af));
+ listOfExtractedImages.add(new ExtractedFile(fileName, getFileRelativePath(fileName), picture.getSize()));
}
return listOfExtractedImages;
}
/**
- * Extract images from docx format files.
+ * Extract embedded images from ppt format files.
*
* @param af the file from which images are to be extracted.
*
* @return list of extracted images. Returns null in case no images were
* extracted.
*/
- private List extractImagesFromDocx(AbstractFile af) {
- List listOfAllPictures = null;
-
- try {
- XWPFDocument docx = new XWPFDocument(new ReadContentInputStream(af));
- listOfAllPictures = docx.getAllPictures();
- } catch (POIXMLException | IOException ex) {
- // POIXMLException:
- // Thrown when document fails to load
-
- // IOException:
- // Thrown when the document has issues being read.
-
- return null;
- } catch (Throwable ex) {
- // instantiating POI containers throw RuntimeExceptions
- logger.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.docxContainer.init.err", af.getName()), ex); //NON-NLS
- return null;
- }
-
- // if no images are extracted from the PPT, return null, else initialize
- // the output folder for image extraction.
- String outputFolderPath;
- if (listOfAllPictures.isEmpty()) {
- return null;
- } else {
- outputFolderPath = getOutputFolderPath(this.parentFileName);
- }
- if (outputFolderPath == null) {
- return null;
- }
- List listOfExtractedImages = new ArrayList<>();
- byte[] data = null;
- for (XWPFPictureData xwpfPicture : listOfAllPictures) {
- String fileName = xwpfPicture.getFileName();
- try {
- data = xwpfPicture.getData();
- } catch (Exception ex) {
- return null;
- }
- writeExtractedImage(Paths.get(outputFolderPath, fileName).toString(), data);
- listOfExtractedImages.add(new ExtractedImage(fileName, getFileRelativePath(fileName), xwpfPicture.getData().length, af));
- }
- return listOfExtractedImages;
- }
-
- /**
- * Extract images from ppt format files.
- *
- * @param af the file from which images are to be extracted.
- *
- * @return list of extracted images. Returns null in case no images were
- * extracted.
- */
- private List extractImagesFromPpt(AbstractFile af) {
+ private List extractEmbeddedImagesFromPpt(AbstractFile af) {
List listOfAllPictures = null;
-
+
try {
HSLFSlideShow ppt = new HSLFSlideShow(new ReadContentInputStream(af));
listOfAllPictures = ppt.getPictureData();
- } catch (IOException | IllegalArgumentException |
- IndexOutOfBoundsException ex) {
+ } catch (IOException | IllegalArgumentException
+ | IndexOutOfBoundsException ex) {
// IllegalArgumentException:
// This will catch OldFileFormatException, which is thrown when the
// document version is unsupported. The IllegalArgumentException may
// also get thrown for unknown reasons.
-
+
// IOException:
// Thrown when the document has issues being read.
-
// IndexOutOfBoundsException:
// This gets thrown in certain images. The reason is unknown. It is
// likely due to problems with the file formats that POI is poorly
// handling.
-
return null;
} catch (Throwable ex) {
// instantiating POI containers throw RuntimeExceptions
- logger.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.pptContainer.init.err", af.getName()), ex); //NON-NLS
+ LOGGER.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.pptContainer.init.err", af.getName()), ex); //NON-NLS
return null;
}
@@ -374,10 +367,10 @@ class ImageExtractor {
return null;
}
- // extract the images to the above initialized outputFolder.
+ // extract the content to the above initialized outputFolder.
// extraction path - outputFolder/image_number.ext
int i = 0;
- List listOfExtractedImages = new ArrayList<>();
+ List listOfExtractedImages = new ArrayList<>();
byte[] data = null;
for (HSLFPictureData pictureData : listOfAllPictures) {
@@ -404,80 +397,19 @@ class ImageExtractor {
default:
continue;
}
- String imageName = UNKNOWN_NAME_PREFIX + i + ext; //NON-NLS
+ String imageName = UNKNOWN_IMAGE_NAME_PREFIX + i + ext; //NON-NLS
try {
data = pictureData.getData();
} catch (Exception ex) {
return null;
}
writeExtractedImage(Paths.get(outputFolderPath, imageName).toString(), data);
- listOfExtractedImages.add(new ExtractedImage(imageName, getFileRelativePath(imageName), pictureData.getData().length, af));
+ listOfExtractedImages.add(new ExtractedFile(imageName, getFileRelativePath(imageName), pictureData.getData().length));
i++;
}
return listOfExtractedImages;
}
- /**
- * Extract images from pptx format files.
- *
- * @param af the file from which images are to be extracted.
- *
- * @return list of extracted images. Returns null in case no images were
- * extracted.
- */
- private List extractImagesFromPptx(AbstractFile af) {
- List listOfAllPictures = null;
-
- try {
- XMLSlideShow pptx = new XMLSlideShow(new ReadContentInputStream(af));
- listOfAllPictures = pptx.getPictureData();
- } catch (POIXMLException | IOException ex) {
- // POIXMLException:
- // Thrown when document fails to load.
-
- // IOException:
- // Thrown when the document has issues being read
-
- return null;
- } catch (Throwable ex) {
- // instantiating POI containers throw RuntimeExceptions
- logger.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.pptxContainer.init.err", af.getName()), ex); //NON-NLS
- return null;
- }
-
- // if no images are extracted from the PPT, return null, else initialize
- // the output folder for image extraction.
- String outputFolderPath;
- if (listOfAllPictures.isEmpty()) {
- return null;
- } else {
- outputFolderPath = getOutputFolderPath(this.parentFileName);
- }
- if (outputFolderPath == null) {
- return null;
- }
-
- List listOfExtractedImages = new ArrayList<>();
- byte[] data = null;
- for (XSLFPictureData xslsPicture : listOfAllPictures) {
-
- // get image file name, write it to the module outputFolder, and add
- // it to the listOfExtractedImageAbstractFiles.
- String fileName = xslsPicture.getFileName();
- try {
- data = xslsPicture.getData();
- } catch (Exception ex) {
- return null;
- }
- writeExtractedImage(Paths.get(outputFolderPath, fileName).toString(), data);
- listOfExtractedImages.add(new ExtractedImage(fileName, getFileRelativePath(fileName), xslsPicture.getData().length, af));
-
- }
-
- return listOfExtractedImages;
-
- }
-
/**
* Extract images from xls format files.
*
@@ -486,41 +418,37 @@ class ImageExtractor {
* @return list of extracted images. Returns null in case no images were
* extracted.
*/
- private List extractImagesFromXls(AbstractFile af) {
+ private List extractImagesFromXls(AbstractFile af) {
List extends org.apache.poi.ss.usermodel.PictureData> listOfAllPictures = null;
-
+
try {
Workbook xls = new HSSFWorkbook(new ReadContentInputStream(af));
listOfAllPictures = xls.getAllPictures();
- } catch (IOException | LeftoverDataException |
- RecordFormatException | IllegalArgumentException |
- IndexOutOfBoundsException ex) {
+ } catch (IOException | LeftoverDataException
+ | RecordFormatException | IllegalArgumentException
+ | IndexOutOfBoundsException ex) {
// IllegalArgumentException:
// This will catch OldFileFormatException, which is thrown when the
// document version is unsupported. The IllegalArgumentException may
// also get thrown for unknown reasons.
-
+
// IOException:
// Thrown when the document has issues being read.
-
// LeftoverDataException:
// This is thrown for poorly formatted files that have more data
// than expected.
-
// RecordFormatException:
// This is thrown for poorly formatted files that have less data
// that expected.
-
// IllegalArgumentException:
// IndexOutOfBoundsException:
// These get thrown in certain images. The reason is unknown. It is
// likely due to problems with the file formats that POI is poorly
// handling.
-
return null;
} catch (Throwable ex) {
// instantiating POI containers throw RuntimeExceptions
- logger.log(Level.SEVERE, String.format("%s%s", NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.xlsContainer.init.err", af.getName()), af.getName()), ex); //NON-NLS
+ LOGGER.log(Level.SEVERE, String.format("%s%s", NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.xlsContainer.init.err", af.getName()), af.getName()), ex); //NON-NLS
return null;
}
@@ -537,75 +465,17 @@ class ImageExtractor {
}
int i = 0;
- List listOfExtractedImages = new ArrayList<>();
+ List listOfExtractedImages = new ArrayList<>();
byte[] data = null;
for (org.apache.poi.ss.usermodel.PictureData pictureData : listOfAllPictures) {
- String imageName = UNKNOWN_NAME_PREFIX + i + "." + pictureData.suggestFileExtension(); //NON-NLS
+ String imageName = UNKNOWN_IMAGE_NAME_PREFIX + i + "." + pictureData.suggestFileExtension(); //NON-NLS
try {
data = pictureData.getData();
} catch (Exception ex) {
return null;
}
writeExtractedImage(Paths.get(outputFolderPath, imageName).toString(), data);
- listOfExtractedImages.add(new ExtractedImage(imageName, getFileRelativePath(imageName), pictureData.getData().length, af));
- i++;
- }
- return listOfExtractedImages;
-
- }
-
- /**
- * Extract images from xlsx format files.
- *
- * @param af the file from which images are to be extracted.
- *
- * @return list of extracted images. Returns null in case no images were
- * extracted.
- */
- private List extractImagesFromXlsx(AbstractFile af) {
- List extends org.apache.poi.ss.usermodel.PictureData> listOfAllPictures = null;
-
- try {
- Workbook xlsx = new XSSFWorkbook(new ReadContentInputStream(af));
- listOfAllPictures = xlsx.getAllPictures();
- } catch (POIXMLException | IOException ex) {
- // POIXMLException:
- // Thrown when document fails to load.
-
- // IOException:
- // Thrown when the document has issues being read
-
- return null;
- } catch (Throwable ex) {
- // instantiating POI containers throw RuntimeExceptions
- logger.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.xlsxContainer.init.err", af.getName()), ex); //NON-NLS
- return null;
- }
-
- // if no images are extracted from the PPT, return null, else initialize
- // the output folder for image extraction.
- String outputFolderPath;
- if (listOfAllPictures.isEmpty()) {
- return null;
- } else {
- outputFolderPath = getOutputFolderPath(this.parentFileName);
- }
- if (outputFolderPath == null) {
- return null;
- }
-
- int i = 0;
- List listOfExtractedImages = new ArrayList<>();
- byte[] data = null;
- for (org.apache.poi.ss.usermodel.PictureData pictureData : listOfAllPictures) {
- String imageName = UNKNOWN_NAME_PREFIX + i + "." + pictureData.suggestFileExtension();
- try {
- data = pictureData.getData();
- } catch (Exception ex) {
- return null;
- }
- writeExtractedImage(Paths.get(outputFolderPath, imageName).toString(), data);
- listOfExtractedImages.add(new ExtractedImage(imageName, getFileRelativePath(imageName), pictureData.getData().length, af));
+ listOfExtractedImages.add(new ExtractedFile(imageName, getFileRelativePath(imageName), pictureData.getData().length));
i++;
}
return listOfExtractedImages;
@@ -623,18 +493,17 @@ class ImageExtractor {
try (EncodedFileOutputStream fos = new EncodedFileOutputStream(new FileOutputStream(outputPath), TskData.EncodingType.XOR1)) {
fos.write(data);
} catch (IOException ex) {
- logger.log(Level.WARNING, "Could not write to the provided location: " + outputPath, ex); //NON-NLS
+ LOGGER.log(Level.WARNING, "Could not write to the provided location: " + outputPath, ex); //NON-NLS
}
}
/**
- * Gets path to the output folder for image extraction. If the path does not
+ * Gets path to the output folder for file extraction. If the path does not
* exist, it is created.
*
- * @param parentFileName name of the abstract file being processed for image
- * extraction.
+ * @param parentFileName name of the abstract file being processed
*
- * @return path to the image extraction folder for a given abstract file.
+ * @return path to the file extraction folder for a given abstract file.
*/
private String getOutputFolderPath(String parentFileName) {
String outputFolderPath = moduleDirAbsolute + File.separator + parentFileName;
@@ -643,7 +512,7 @@ class ImageExtractor {
try {
outputFilePath.mkdirs();
} catch (SecurityException ex) {
- logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.getOutputFolderPath.exception.msg", parentFileName), ex);
+ LOGGER.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.getOutputFolderPath.exception.msg", parentFileName), ex);
return null;
}
}
@@ -665,11 +534,11 @@ class ImageExtractor {
}
/**
- * Represents the image extracted using POI methods. Currently, POI is not
- * capable of extracting ctime, crtime, mtime, and atime; these values are
- * set to 0.
+ * Represents a file extracted using either Tika or POI methods. Currently,
+ * POI is not capable of extracting ctime, crtime, mtime, and atime; these
+ * values are set to 0.
*/
- private static class ExtractedImage {
+ private static class ExtractedFile {
//String fileName, String localPath, long size, long ctime, long crtime,
//long atime, long mtime, boolean isFile, AbstractFile parentFile, String rederiveDetails, String toolName, String toolVersion, String otherDetails
@@ -680,13 +549,12 @@ class ImageExtractor {
private final long crtime;
private final long atime;
private final long mtime;
- private final AbstractFile parentFile;
- ExtractedImage(String fileName, String localPath, long size, AbstractFile parentFile) {
- this(fileName, localPath, size, 0, 0, 0, 0, parentFile);
+ ExtractedFile(String fileName, String localPath, long size) {
+ this(fileName, localPath, size, 0, 0, 0, 0);
}
- ExtractedImage(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, AbstractFile parentFile) {
+ ExtractedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime) {
this.fileName = fileName;
this.localPath = localPath;
this.size = size;
@@ -694,7 +562,6 @@ class ImageExtractor {
this.crtime = crtime;
this.atime = atime;
this.mtime = mtime;
- this.parentFile = parentFile;
}
public String getFileName() {
@@ -724,9 +591,84 @@ class ImageExtractor {
public long getMtime() {
return mtime;
}
+ }
- public AbstractFile getParentFile() {
- return parentFile;
+ /**
+ * Our custom embedded content extractor for OOXML files. We pass an
+ * instance of this class to Tika and Tika calls the parseEmbedded() method
+ * when it encounters an embedded file.
+ */
+ private class EmbeddedContentExtractor extends ParsingEmbeddedDocumentExtractor {
+
+ private int fileCount = 0;
+ // Map of file name to ExtractedFile instance. This can revert to a
+ // plain old list after we upgrade to Tika 1.16 or above.
+ private final Map nameToExtractedFileMap = new HashMap<>();
+
+ public EmbeddedContentExtractor(ParseContext context) {
+ super(context);
+ }
+
+ @Override
+ public boolean shouldParseEmbedded(Metadata metadata) {
+ return true;
+ }
+
+ @Override
+ public void parseEmbedded(InputStream stream, ContentHandler handler,
+ Metadata metadata, boolean outputHtml) throws SAXException, IOException {
+
+ // Get the mime type for the embedded document
+ MediaType contentType = detector.detect(stream, metadata);
+
+ if (!contentType.getType().equalsIgnoreCase("image") //NON-NLS
+ && !contentType.getType().equalsIgnoreCase("video") //NON-NLS
+ && !contentType.getType().equalsIgnoreCase("application") //NON-NLS
+ && !contentType.getType().equalsIgnoreCase("audio")) { //NON-NLS
+ return;
+ }
+
+ // try to get the name of the embedded file from the metadata
+ String name = metadata.get(Metadata.RESOURCE_NAME_KEY);
+
+ // TODO: This can be removed after we upgrade to Tika 1.16 or
+ // above. The 1.16 version of Tika keeps track of files that
+ // have been seen before.
+ if (nameToExtractedFileMap.containsKey(name)) {
+ return;
+ }
+
+ if (name == null) {
+ name = UNKNOWN_IMAGE_NAME_PREFIX + fileCount++;
+ } else {
+ //make sure to select only the file name (not any directory paths
+ //that might be included in the name) and make sure
+ //to normalize the name
+ name = FilenameUtils.normalize(FilenameUtils.getName(name));
+ }
+
+ // Get the suggested extension based on mime type.
+ if (name.indexOf('.') == -1) {
+ try {
+ name += config.getMimeRepository().forName(contentType.toString()).getExtension();
+ } catch (MimeTypeException ex) {
+ LOGGER.log(Level.WARNING, "Failed to get suggested extension for the following type: " + contentType.toString(), ex); //NON-NLS
+ }
+ }
+
+ File extractedFile = new File(Paths.get(getOutputFolderPath(parentFileName), name).toString());
+ byte[] fileData = IOUtils.toByteArray(stream);
+ writeExtractedImage(extractedFile.getAbsolutePath(), fileData);
+ nameToExtractedFileMap.put(name, new ExtractedFile(name, getFileRelativePath(name), fileData.length));
+ }
+
+ /**
+ * Get list of extracted files.
+ *
+ * @return List of extracted files.
+ */
+ public List getExtractedImages() {
+ return new ArrayList<>(nameToExtractedFileMap.values());
}
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties
new file mode 100755
index 0000000000..fdc7d9ed0d
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties
@@ -0,0 +1,8 @@
+EncryptionDetectionIngestJobSettingsPanel.minimumEntropyLabel.text=Minimum Entropy:
+EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeLabel.text=Minimum File Size:
+EncryptionDetectionIngestJobSettingsPanel.fileSizeMultiplesEnforcedCheckbox.text=Consider only files with sizes that are multiples of 512.
+EncryptionDetectionIngestJobSettingsPanel.slackFilesAllowedCheckbox.text=Consider slack space files.
+EncryptionDetectionIngestJobSettingsPanel.minimumEntropyTextbox.text=
+EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeTextbox.text=
+EncryptionDetectionIngestJobSettingsPanel.mbLabel.text=MB
+EncryptionDetectionIngestJobSettingsPanel.detectionSettingsLabel.text=Detection Settings
diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
index f18911c016..15fccb6a74 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
@@ -45,8 +45,11 @@ import org.sleuthkit.datamodel.TskData;
*/
final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter {
- private static final double ENTROPY_THRESHOLD = 7.5;
- private static final int FILE_SIZE_THRESHOLD = 5242880; // 5MB
+ static final double DEFAULT_CONFIG_MINIMUM_ENTROPY = 7.5;
+ static final int DEFAULT_CONFIG_MINIMUM_FILE_SIZE = 5242880; // 5MB;
+ static final boolean DEFAULT_CONFIG_FILE_SIZE_MULTIPLE_ENFORCED = true;
+ static final boolean DEFAULT_CONFIG_SLACK_FILES_ALLOWED = true;
+
private static final int FILE_SIZE_MODULUS = 512;
private static final double ONE_OVER_LOG2 = 1.4426950408889634073599246810019; // (1 / log(2))
private static final int BYTE_OCCURENCES_BUFFER_SIZE = 256;
@@ -55,13 +58,24 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
private final Logger LOGGER = SERVICES.getLogger(EncryptionDetectionModuleFactory.getModuleName());
private FileTypeDetector fileTypeDetector;
private Blackboard blackboard;
- private double entropy;
+ private double calculatedEntropy;
+
+ private final double minimumEntropy;
+ private final int minimumFileSize;
+ private final boolean fileSizeMultipleEnforced;
+ private final boolean slackFilesAllowed;
/**
- * Create a EncryptionDetectionFileIngestModule object that will detect files
- * that are encrypted and create blackboard artifacts as appropriate.
+ * Create a EncryptionDetectionFileIngestModule object that will detect
+ * files that are encrypted and create blackboard artifacts as appropriate.
+ * The supplied EncryptionDetectionIngestJobSettings object is used to
+ * configure the module.
*/
- EncryptionDetectionFileIngestModule() {
+ EncryptionDetectionFileIngestModule(EncryptionDetectionIngestJobSettings settings) {
+ minimumEntropy = settings.getMinimumEntropy();
+ minimumFileSize = settings.getMinimumFileSize();
+ fileSizeMultipleEnforced = settings.isFileSizeMultipleEnforced();
+ slackFilesAllowed = settings.isSlackFilesAllowed();
}
@Override
@@ -120,7 +134,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
*/
StringBuilder detailsSb = new StringBuilder();
detailsSb.append("File: ").append(file.getParentPath()).append(file.getName()).append("
\n");
- detailsSb.append("Entropy: ").append(entropy);
+ detailsSb.append("Entropy: ").append(calculatedEntropy);
SERVICES.postMessage(IngestMessage.createDataMessage(EncryptionDetectionModuleFactory.getModuleName(),
"Encryption Detected Match: " + file.getName(),
@@ -159,7 +173,8 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
if (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
&& !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)
&& !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR)
- && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR)) {
+ && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR)
+ && (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) || slackFilesAllowed)) {
/*
* Qualify the file against hash databases.
*/
@@ -168,17 +183,19 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
* Qualify the size.
*/
long contentSize = file.getSize();
- if (contentSize >= FILE_SIZE_THRESHOLD && (contentSize % FILE_SIZE_MODULUS) == 0) {
- /*
- * Qualify the MIME type.
- */
- try {
- String mimeType = fileTypeDetector.getFileType(file);
- if (mimeType != null && mimeType.equals("application/octet-stream")) {
- possiblyEncrypted = true;
+ if (contentSize >= minimumFileSize) {
+ if (!fileSizeMultipleEnforced || (contentSize % FILE_SIZE_MODULUS) == 0) {
+ /*
+ * Qualify the MIME type.
+ */
+ try {
+ String mimeType = fileTypeDetector.getFileType(file);
+ if (mimeType != null && mimeType.equals("application/octet-stream")) {
+ possiblyEncrypted = true;
+ }
+ } catch (TskCoreException ex) {
+ throw new TskCoreException("Failed to detect the file type.", ex);
}
- } catch (TskCoreException ex) {
- throw new TskCoreException("Failed to detect the file type.", ex);
}
}
}
@@ -186,8 +203,8 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
if (possiblyEncrypted) {
try {
- entropy = calculateEntropy(file);
- if (entropy > ENTROPY_THRESHOLD) {
+ calculatedEntropy = calculateEntropy(file);
+ if (calculatedEntropy >= minimumEntropy) {
return true;
}
} catch (IOException ex) {
diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettings.java
new file mode 100755
index 0000000000..2aa6ad860d
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettings.java
@@ -0,0 +1,133 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2017 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.modules.encryptiondetection;
+
+import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
+
+/**
+ * Ingest job settings for the Encryption Detection module.
+ */
+final class EncryptionDetectionIngestJobSettings implements IngestModuleIngestJobSettings {
+
+ private static final long serialVersionUID = 1L;
+
+ private double minimumEntropy;
+ private int minimumFileSize;
+ private boolean fileSizeMultipleEnforced;
+ private boolean slackFilesAllowed;
+
+ /**
+ * Instantiate the ingest job settings with default values.
+ */
+ EncryptionDetectionIngestJobSettings() {
+ this.minimumEntropy = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_MINIMUM_ENTROPY;
+ this.minimumFileSize = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_MINIMUM_FILE_SIZE;
+ this.fileSizeMultipleEnforced = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_FILE_SIZE_MULTIPLE_ENFORCED;
+ this.slackFilesAllowed = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_SLACK_FILES_ALLOWED;
+ }
+
+ /**
+ * Instantiate the ingest job settings.
+ *
+ * @param minimumEntropy The minimum entropy.
+ * @param minimumFileSize The minimum file size.
+ * @param fileSizeMultipleEnforced Files must be a multiple of 512 to be
+ * processed.
+ * @param slackFilesAllowed Slack files can be processed.
+ */
+ EncryptionDetectionIngestJobSettings(double minimumEntropy, int minimumFileSize, boolean fileSizeMultipleEnforced, boolean slackFilesAllowed) {
+ this.minimumEntropy = minimumEntropy;
+ this.minimumFileSize = minimumFileSize;
+ this.fileSizeMultipleEnforced = fileSizeMultipleEnforced;
+ this.slackFilesAllowed = slackFilesAllowed;
+ }
+
+ @Override
+ public long getVersionNumber() {
+ return serialVersionUID;
+ }
+
+ /**
+ * Get the minimum entropy necessary for the creation of blackboard
+ * artifacts.
+ *
+ * @return The minimum entropy.
+ */
+ double getMinimumEntropy() {
+ return minimumEntropy;
+ }
+
+ /**
+ * Set the minimum entropy necessary for the creation of blackboard
+ * artifacts.
+ */
+ void setMinimumEntropy(double minimumEntropy) {
+ this.minimumEntropy = minimumEntropy;
+ }
+
+ /**
+ * Get the minimum file size necessary for the creation of blackboard
+ * artifacts.
+ *
+ * @return The minimum file size.
+ */
+ int getMinimumFileSize() {
+ return minimumFileSize;
+ }
+
+ /**
+ * Set the minimum file size necessary for the creation of blackboard
+ * artifacts.
+ */
+ void setMinimumFileSize(int minimumFileSize) {
+ this.minimumFileSize = minimumFileSize;
+ }
+
+ /**
+ * Is the file size multiple enforced?
+ *
+ * @return True if enforcement is enabled; otherwise false.
+ */
+ boolean isFileSizeMultipleEnforced() {
+ return fileSizeMultipleEnforced;
+ }
+
+ /**
+ * Enable or disable file size multiple enforcement.
+ */
+ void setFileSizeMultipleEnforced(boolean fileSizeMultipleEnforced) {
+ this.fileSizeMultipleEnforced = fileSizeMultipleEnforced;
+ }
+
+ /**
+ * Are slack files allowed for processing?
+ *
+ * @return True if slack files are allowed; otherwise false.
+ */
+ boolean isSlackFilesAllowed() {
+ return slackFilesAllowed;
+ }
+
+ /**
+ * Allow or disallow slack files for processing.
+ */
+ void setSlackFilesAllowed(boolean slackFilesAllowed) {
+ this.slackFilesAllowed = slackFilesAllowed;
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form
new file mode 100755
index 0000000000..26c859fe4d
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form
@@ -0,0 +1,132 @@
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java
new file mode 100755
index 0000000000..123a62ec85
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java
@@ -0,0 +1,201 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2017 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.modules.encryptiondetection;
+
+import org.openide.util.NbBundle;
+import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
+import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
+
+/**
+ * Ingest job settings panel for the Encryption Detection module.
+ */
+final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel {
+
+ private static final int MEGABYTE_SIZE = 1048576;
+ private static final double MINIMUM_ENTROPY_INPUT_RANGE_MIN = 6.0;
+ private static final double MINIMUM_ENTROPY_INPUT_RANGE_MAX = 8.0;
+ private static final int MINIMUM_FILE_SIZE_INPUT_RANGE_MIN = 1;
+
+ /**
+ * Instantiate the ingest job settings panel.
+ *
+ * @param settings The ingest job settings.
+ */
+ public EncryptionDetectionIngestJobSettingsPanel(EncryptionDetectionIngestJobSettings settings) {
+ initComponents();
+ customizeComponents(settings);
+ }
+
+ /**
+ * Update components with values from the ingest job settings.
+ *
+ * @param settings The ingest job settings.
+ */
+ private void customizeComponents(EncryptionDetectionIngestJobSettings settings) {
+ minimumEntropyTextbox.setText(String.valueOf(settings.getMinimumEntropy()));
+ minimumFileSizeTextbox.setText(String.valueOf(settings.getMinimumFileSize() / MEGABYTE_SIZE));
+ fileSizeMultiplesEnforcedCheckbox.setSelected(settings.isFileSizeMultipleEnforced());
+ slackFilesAllowedCheckbox.setSelected(settings.isSlackFilesAllowed());
+ }
+
+ @Override
+ public IngestModuleIngestJobSettings getSettings() {
+ validateMinimumEntropy();
+ validateMinimumFileSize();
+
+ return new EncryptionDetectionIngestJobSettings(
+ Double.valueOf(minimumEntropyTextbox.getText()),
+ Integer.valueOf(minimumFileSizeTextbox.getText()) * MEGABYTE_SIZE,
+ fileSizeMultiplesEnforcedCheckbox.isSelected(),
+ slackFilesAllowedCheckbox.isSelected());
+ }
+
+ /**
+ * Validate the minimum entropy input.
+ *
+ * @throws IllegalArgumentException If the input is empty, invalid, or out
+ * of range.
+ */
+ @Messages({
+ "EncryptionDetectionIngestJobSettingsPanel.minimumEntropyInput.validationError.text=Minimum entropy input must be a number between 6.0 and 8.0."
+ })
+ private void validateMinimumEntropy() throws IllegalArgumentException {
+ try {
+ double minimumEntropy = Double.valueOf(minimumEntropyTextbox.getText());
+ if (minimumEntropy < MINIMUM_ENTROPY_INPUT_RANGE_MIN || minimumEntropy > MINIMUM_ENTROPY_INPUT_RANGE_MAX) {
+ throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(), "EncryptionDetectionIngestJobSettingsPanel.minimumEntropyInput.validationError.text"));
+ }
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(), "EncryptionDetectionIngestJobSettingsPanel.minimumEntropyInput.validationError.text"));
+ }
+ }
+
+ /**
+ * Validate the minimum file size input.
+ *
+ * @throws IllegalArgumentException If the input is empty, invalid, or out
+ * of range.
+ */
+ @Messages({
+ "EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeInput.validationError.text=Minimum file size input must be an integer (in megabytes) of 1 or greater."
+ })
+ private void validateMinimumFileSize() throws IllegalArgumentException {
+ try {
+ int minimumFileSize = Integer.valueOf(minimumFileSizeTextbox.getText());
+ if (minimumFileSize < MINIMUM_FILE_SIZE_INPUT_RANGE_MIN) {
+ throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(), "EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeInput.validationError.text"));
+ }
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(), "EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeInput.validationError.text"));
+ }
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ minimumEntropyTextbox = new javax.swing.JTextField();
+ minimumFileSizeTextbox = new javax.swing.JTextField();
+ fileSizeMultiplesEnforcedCheckbox = new javax.swing.JCheckBox();
+ slackFilesAllowedCheckbox = new javax.swing.JCheckBox();
+ minimumEntropyLabel = new javax.swing.JLabel();
+ minimumFileSizeLabel = new javax.swing.JLabel();
+ mbLabel = new javax.swing.JLabel();
+ detectionSettingsLabel = new javax.swing.JLabel();
+
+ minimumEntropyTextbox.setText(org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.minimumEntropyTextbox.text")); // NOI18N
+
+ minimumFileSizeTextbox.setText(org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeTextbox.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(fileSizeMultiplesEnforcedCheckbox, org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.fileSizeMultiplesEnforcedCheckbox.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(slackFilesAllowedCheckbox, org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.slackFilesAllowedCheckbox.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(minimumEntropyLabel, org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.minimumEntropyLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(minimumFileSizeLabel, org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(mbLabel, org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.mbLabel.text")); // NOI18N
+
+ detectionSettingsLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(detectionSettingsLabel, org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.detectionSettingsLabel.text")); // NOI18N
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(slackFilesAllowedCheckbox)
+ .addComponent(detectionSettingsLabel)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addComponent(minimumFileSizeLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(minimumFileSizeTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addComponent(minimumEntropyLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(minimumEntropyTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(mbLabel))
+ .addComponent(fileSizeMultiplesEnforcedCheckbox))
+ .addContainerGap(15, Short.MAX_VALUE))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(detectionSettingsLabel)
+ .addGap(16, 16, 16)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(minimumEntropyTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(minimumEntropyLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(minimumFileSizeTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(mbLabel)
+ .addComponent(minimumFileSizeLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(fileSizeMultiplesEnforcedCheckbox)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(slackFilesAllowedCheckbox)
+ .addContainerGap(160, Short.MAX_VALUE))
+ );
+ }// //GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel detectionSettingsLabel;
+ private javax.swing.JCheckBox fileSizeMultiplesEnforcedCheckbox;
+ private javax.swing.JLabel mbLabel;
+ private javax.swing.JLabel minimumEntropyLabel;
+ private javax.swing.JTextField minimumEntropyTextbox;
+ private javax.swing.JLabel minimumFileSizeLabel;
+ private javax.swing.JTextField minimumFileSizeTextbox;
+ private javax.swing.JCheckBox slackFilesAllowedCheckbox;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java
index 53eca1aec6..27549f648f 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java
@@ -22,10 +22,12 @@ import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.coreutils.Version;
+import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.FileIngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
-import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
+import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
+import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/**
* A factory that creates file ingest modules that detect encryption.
@@ -33,9 +35,9 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
@ServiceProvider(service = IngestModuleFactory.class)
@Messages({
"EncryptionDetectionFileIngestModule.moduleName.text=Encryption Detection",
- "EncryptionDetectionFileIngestModule.getDesc.text=Looks for large files with high entropy."
+ "EncryptionDetectionFileIngestModule.getDesc.text=Looks for files with the specified minimum entropy."
})
-public class EncryptionDetectionModuleFactory extends IngestModuleFactoryAdapter {
+public class EncryptionDetectionModuleFactory implements IngestModuleFactory {
@Override
public String getModuleDisplayName() {
@@ -44,7 +46,7 @@ public class EncryptionDetectionModuleFactory extends IngestModuleFactoryAdapter
/**
* Get the name of the module.
- *
+ *
* @return The module name.
*/
static String getModuleName() {
@@ -67,7 +69,48 @@ public class EncryptionDetectionModuleFactory extends IngestModuleFactoryAdapter
}
@Override
- public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings ingestOptions) {
- return new EncryptionDetectionFileIngestModule();
+ public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) {
+ if (!(settings instanceof EncryptionDetectionIngestJobSettings)) {
+ throw new IllegalArgumentException("Expected settings argument to be an instance of EncryptionDetectionIngestJobSettings.");
+ }
+ return new EncryptionDetectionFileIngestModule((EncryptionDetectionIngestJobSettings) settings);
}
-}
\ No newline at end of file
+
+ @Override
+ public boolean hasGlobalSettingsPanel() {
+ return false;
+ }
+
+ @Override
+ public IngestModuleGlobalSettingsPanel getGlobalSettingsPanel() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public IngestModuleIngestJobSettings getDefaultIngestJobSettings() {
+ return new EncryptionDetectionIngestJobSettings();
+ }
+
+ @Override
+ public boolean hasIngestJobSettingsPanel() {
+ return true;
+ }
+
+ @Override
+ public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
+ if (!(settings instanceof EncryptionDetectionIngestJobSettings)) {
+ throw new IllegalArgumentException("Expected settings argument to be an instance of EncryptionDetectionIngestJobSettings");
+ }
+ return new EncryptionDetectionIngestJobSettingsPanel((EncryptionDetectionIngestJobSettings) settings);
+ }
+
+ @Override
+ public boolean isDataSourceIngestModuleFactory() {
+ return false;
+ }
+
+ @Override
+ public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java
index 4bfbb8b734..b1aff14a8f 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java
@@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.modules.filetypeid;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -27,7 +28,9 @@ import java.util.TreeSet;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.tika.Tika;
+import org.apache.tika.io.TikaInputStream;
import org.apache.tika.mime.MimeTypes;
+import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
@@ -36,6 +39,7 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
+import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@@ -50,8 +54,6 @@ public class FileTypeDetector {
private static final Logger logger = Logger.getLogger(FileTypeDetector.class.getName());
private static final Tika tika = new Tika();
- private static final int BUFFER_SIZE = 64 * 1024;
- private final byte buffer[] = new byte[BUFFER_SIZE];
private final List userDefinedFileTypes;
private final List autopsyDefinedFileTypes;
private static SortedSet tikaDetectedTypes;
@@ -270,17 +272,11 @@ public class FileTypeDetector {
* bytes to Tika.
*/
if (null == mimeType) {
- try {
- byte buf[];
- int len = file.read(buffer, 0, BUFFER_SIZE);
- if (len < BUFFER_SIZE) {
- buf = new byte[len];
- System.arraycopy(buffer, 0, buf, 0, len);
- } else {
- buf = buffer;
- }
- String tikaType = tika.detect(buf, file.getName());
-
+ ReadContentInputStream stream = new ReadContentInputStream(file);
+
+ try (TikaInputStream tikaInputStream = TikaInputStream.get(stream)) {
+ String tikaType = tika.detect(tikaInputStream, file.getName());
+
/*
* Remove the Tika suffix from the MIME type name.
*/
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties
index 3ec2961927..7e4b10fc5d 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties
@@ -17,7 +17,7 @@ HashDbSearchPanel.errorField.text=Error: Not all files have been hashed.
HashDbSearchPanel.saveBox.text=Remember Hashes
HashDbSearchPanel.cancelButton.text=Cancel
OpenIDE-Module-Short-Description=Hash Database Ingest Module and hash db tools
-HashDbImportDatabaseDialog.jLabel1.text=Hash Set Name:
+HashDbImportDatabaseDialog.jLabel1.text=Name:
HashDbImportDatabaseDialog.databasePathTextField.text=
HashDbImportDatabaseDialog.knownBadRadioButton.text=Notable
HashDbImportDatabaseDialog.jLabel2.text=Type of database\:
@@ -39,7 +39,7 @@ HashDbCreateDatabaseDialog.sendIngestMessagesCheckbox.text=Send ingest inbox mes
HashDbImportDatabaseDialog.sendIngestMessagesCheckbox.text=Send ingest inbox message for each hit
HashDbImportDatabaseDialog.hashSetNameTextField.text=
HashDbImportDatabaseDialog.openButton.text=Open...
-HashDbCreateDatabaseDialog.jLabel3.text=Hash Set Name:
+HashDbCreateDatabaseDialog.jLabel3.text=Name:
HashDbCreateDatabaseDialog.okButton.text=OK
HashDbCreateDatabaseDialog.databasePathTextField.text=
AddContentToHashDbAction.ContentMenu.noHashDbsConfigd=No hash databases configured
@@ -205,7 +205,7 @@ HashLookupSettingsPanel.typeLabel.text=Type:
HashLookupSettingsPanel.locationLabel.text=Database Path:
HashLookupSettingsPanel.hashDbLocationLabel.text=No database selected
HashLookupSettingsPanel.hashDbNameLabel.text=No database selected
-HashLookupSettingsPanel.nameLabel.text=Hash Set Name:
+HashLookupSettingsPanel.nameLabel.text=Name:
HashLookupSettingsPanel.hashDatabasesLabel.text=Hash Databases:
HashLookupSettingsPanel.importDatabaseButton.toolTipText=
HashLookupSettingsPanel.importDatabaseButton.text=Import database
@@ -229,13 +229,13 @@ HashDbImportDatabaseDialog.lbVersion.text=Version:
HashDbImportDatabaseDialog.lbOrg.text=Source Organization:
HashDbImportDatabaseDialog.readOnlyCheckbox.text=Make database read-only
HashDbImportDatabaseDialog.orgButton.text=Manage Organizations
-HashDbImportDatabaseDialog.versionTextField.text=
-HashDbImportDatabaseDialog.fileTypeRadioButton.text=File
-HashDbImportDatabaseDialog.centralRepoRadioButton.text=Central Repository
-HashDbImportDatabaseDialog.jLabel4.text=Location:
-HashDbCreateDatabaseDialog.jLabel4.text=Location:
-HashDbCreateDatabaseDialog.fileTypeRadioButton.text=File
-HashDbCreateDatabaseDialog.centralRepoRadioButton.text=Central Repository
+HashDbImportDatabaseDialog.versionTextField.text=1.0
+HashDbImportDatabaseDialog.fileTypeRadioButton.text=Local
+HashDbImportDatabaseDialog.centralRepoRadioButton.text=Remote (Central Repository)
+HashDbImportDatabaseDialog.jLabel4.text=Destination:
+HashDbCreateDatabaseDialog.jLabel4.text=Destination:
+HashDbCreateDatabaseDialog.fileTypeRadioButton.text=Local
+HashDbCreateDatabaseDialog.centralRepoRadioButton.text=Remote (Central Repository)
HashDbCreateDatabaseDialog.lbOrg.text=Source Organization:
HashDbCreateDatabaseDialog.orgButton.text=Manage Organizations
HashDbCreateDatabaseDialog.databasePathLabel.text=Database Path:
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java
index 17a69930a2..13dd9bcd63 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java
@@ -33,6 +33,7 @@ import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamGlobalSet;
import org.sleuthkit.autopsy.centralrepository.optionspanel.ManageOrganizationsDialog;
@@ -154,8 +155,12 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
orgs = dbManager.getOrganizations();
orgs.forEach((org) -> {
orgComboBox.addItem(org.getName());
+ if(EamDbUtil.isDefaultOrg(org)){
+ orgComboBox.setSelectedItem(org.getName());
+ selectedOrg = org;
+ }
});
- if (!orgs.isEmpty()) {
+ if ((selectedOrg == null) && (!orgs.isEmpty())) {
selectedOrg = orgs.get(0);
}
} catch (EamDbException ex) {
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.form b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.form
index 682ec13c34..dbb9f7b4e3 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.form
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.form
@@ -33,17 +33,37 @@
-
-
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
-
+
@@ -52,96 +72,87 @@
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+
-
-
-
+
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java
index db70d1114d..0c5d94277b 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java
@@ -34,6 +34,7 @@ import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization;
import org.sleuthkit.autopsy.centralrepository.optionspanel.ManageOrganizationsDialog;
import org.sleuthkit.autopsy.coreutils.Logger;
@@ -84,25 +85,12 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
private void initFileChooser() {
fileChooser.setDragEnabled(false);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
- updateFileChooserFilter();
+ String[] EXTENSION = new String[]{"txt", "kdb", "idx", "hash", "Hash", "hsh"}; //NON-NLS
+ FileNameExtensionFilter filter = new FileNameExtensionFilter(
+ NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.fileNameExtFilter.text"), EXTENSION);
+ fileChooser.setFileFilter(filter);
fileChooser.setMultiSelectionEnabled(false);
}
-
- @NbBundle.Messages({"HashDbImportDatabaseDialog.centralRepoExtFilter.text=Hash Database File (.kdb, .idx or .hash)"})
- private void updateFileChooserFilter() {
- fileChooser.resetChoosableFileFilters();
- if(centralRepoRadioButton.isSelected()){
- String[] EXTENSION = new String[]{"kdb", "idx", "hash", "Hash"}; //NON-NLS
- FileNameExtensionFilter filter = new FileNameExtensionFilter(
- NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.centralRepoExtFilter.text"), EXTENSION);
- fileChooser.setFileFilter(filter);
- } else {
- String[] EXTENSION = new String[]{"txt", "kdb", "idx", "hash", "Hash", "hsh"}; //NON-NLS
- FileNameExtensionFilter filter = new FileNameExtensionFilter(
- NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.fileNameExtFilter.text"), EXTENSION);
- fileChooser.setFileFilter(filter);
- }
- }
private void display() {
Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
@@ -148,8 +136,12 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
orgs = dbManager.getOrganizations();
orgs.forEach((org) -> {
orgComboBox.addItem(org.getName());
+ if(EamDbUtil.isDefaultOrg(org)){
+ orgComboBox.setSelectedItem(org.getName());
+ selectedOrg = org;
+ }
});
- if (!orgs.isEmpty()) {
+ if ((selectedOrg == null) && (!orgs.isEmpty())) {
selectedOrg = orgs.get(0);
}
} catch (EamDbException ex) {
@@ -303,95 +295,102 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
- .addGap(0, 325, Short.MAX_VALUE)
- .addComponent(okButton)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel3)
+ .addComponent(jLabel4))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(cancelButton))
- .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
+ .addComponent(fileTypeRadioButton)
+ .addGap(26, 26, 26)
+ .addComponent(centralRepoRadioButton)
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(databasePathTextField)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(openButton)
+ .addContainerGap())))
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addComponent(sendIngestMessagesCheckbox)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(okButton))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(lbOrg)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(orgComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(orgComboBox, 0, 121, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(orgButton))
- .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel1)
.addComponent(lbVersion))
- .addGap(2, 2, 2)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGap(40, 40, 40)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(versionTextField)
- .addComponent(hashSetNameTextField)))
- .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(jLabel3)
- .addComponent(jLabel4))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
- .addComponent(fileTypeRadioButton)
- .addGap(26, 26, 26)
- .addComponent(centralRepoRadioButton))
- .addComponent(databasePathTextField))))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(openButton))
+ .addComponent(hashSetNameTextField))))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(cancelButton)
+ .addContainerGap())
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel2)
+ .addComponent(readOnlyCheckbox)
.addGroup(layout.createSequentialGroup()
.addGap(19, 19, 19)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(knownRadioButton)
- .addComponent(knownBadRadioButton)))
- .addComponent(sendIngestMessagesCheckbox)
- .addComponent(readOnlyCheckbox))
- .addGap(0, 0, Short.MAX_VALUE)))
- .addContainerGap())
+ .addComponent(knownBadRadioButton))))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
);
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, okButton});
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(fileTypeRadioButton)
- .addComponent(centralRepoRadioButton)
- .addComponent(jLabel4))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(openButton)
.addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(jLabel3))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(jLabel1)
- .addComponent(hashSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(versionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(lbVersion))
- .addGap(9, 9, 9)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(orgButton)
- .addComponent(orgComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(lbOrg))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(jLabel2)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(knownRadioButton)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(knownBadRadioButton)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(readOnlyCheckbox)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(sendIngestMessagesCheckbox)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(okButton)
- .addComponent(cancelButton))
+ .addComponent(jLabel3)
+ .addComponent(openButton))
+ .addGap(18, 18, 18)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(fileTypeRadioButton)
+ .addComponent(centralRepoRadioButton)
+ .addComponent(jLabel4))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel1)
+ .addComponent(hashSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(lbVersion)
+ .addComponent(versionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(5, 5, 5)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(orgButton)
+ .addComponent(orgComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(lbOrg))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel2)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(knownRadioButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(knownBadRadioButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(readOnlyCheckbox)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(sendIngestMessagesCheckbox)
+ .addGap(0, 21, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(cancelButton)
+ .addComponent(okButton))))
.addContainerGap())
);
@@ -409,7 +408,6 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
hashDbFolder.mkdir();
}
fileChooser.setCurrentDirectory(hashDbFolder);
- updateFileChooserFilter();
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
File databaseFile = fileChooser.getSelectedFile();
try {
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.form
index 5be552d274..d32cbf57d7 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.form
@@ -105,28 +105,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -143,6 +121,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java
index b774ad83c8..1adb75594d 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java
@@ -787,24 +787,6 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(jPanel1Layout.createSequentialGroup()
- .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(locationLabel)
- .addComponent(typeLabel)
- .addComponent(versionLabel)
- .addComponent(orgLabel)
- .addComponent(readOnlyLabel))
- .addGap(55, 55, 55)
- .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(hashDbTypeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 225, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(hashDbLocationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 225, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(hashDbVersionLabel)
- .addComponent(hashDbOrgLabel)
- .addComponent(hashDbReadOnlyLabel)))
- .addGroup(jPanel1Layout.createSequentialGroup()
- .addComponent(nameLabel)
- .addGap(53, 53, 53)
- .addComponent(hashDbNameLabel))
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(indexLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE)
@@ -816,7 +798,23 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(indexButton, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(10, 10, 10)
- .addComponent(addHashesToDatabaseButton))))
+ .addComponent(addHashesToDatabaseButton))
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(locationLabel)
+ .addComponent(typeLabel)
+ .addComponent(versionLabel)
+ .addComponent(orgLabel)
+ .addComponent(readOnlyLabel)
+ .addComponent(nameLabel))
+ .addGap(55, 55, 55)
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(hashDbNameLabel)
+ .addComponent(hashDbTypeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 225, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(hashDbLocationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 225, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(hashDbVersionLabel)
+ .addComponent(hashDbOrgLabel)
+ .addComponent(hashDbReadOnlyLabel)))))
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(70, 70, 70)
.addComponent(informationSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 305, javax.swing.GroupLayout.PREFERRED_SIZE))
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashkeeperHashSetParser.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashkeeperHashSetParser.java
new file mode 100644
index 0000000000..e66af62eb3
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashkeeperHashSetParser.java
@@ -0,0 +1,133 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 - 2017 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.modules.hashdatabase;
+
+import java.io.File;
+import java.io.InputStreamReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.Iterator;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVParser;
+import org.apache.commons.csv.CSVRecord;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.datamodel.TskCoreException;
+
+/**
+ * Parser for Hashkeeper hash sets (*.hsh)
+ */
+public class HashkeeperHashSetParser implements HashSetParser {
+
+ private String filename;
+ private InputStreamReader inputStreamReader;
+ private CSVParser csvParser;
+ private final long expectedHashCount; // Number of hashes we expect to read from the file
+ private final Iterator recordIterator;
+ private final int hashColumnIndex; // The index of the hash column
+
+ HashkeeperHashSetParser(String filename) throws TskCoreException {
+ this.filename = filename;
+
+ try {
+ // Estimate the total number of hashes in the file
+ File importFile = new File(filename);
+ long fileSize = importFile.length();
+ expectedHashCount = fileSize / 75 + 1; // As a rough estimate, assume 75 bytes per line. We add one to prevent this from being zero
+
+ // Create the parser
+ inputStreamReader = new InputStreamReader(new FileInputStream(filename)); //NON-NLS
+ csvParser = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(inputStreamReader);
+ if (!csvParser.getHeaderMap().keySet().contains("hash")) {
+ close();
+ throw new TskCoreException("Hashkeeper file format invalid - does not contain 'hash' column");
+ }
+
+ // For efficiency, store the index of the hash column
+ hashColumnIndex = csvParser.getHeaderMap().get("hash");
+
+ // Make an iterator to loop over the entries
+ recordIterator = csvParser.getRecords().listIterator();
+
+ // We're ready to use recordIterator to get each hash
+ } catch (IOException ex) {
+ close();
+ throw new TskCoreException("Error reading " + filename, ex);
+ }
+ }
+
+ /**
+ * Get the next hash to import
+ *
+ * @return The hash as a string, or null if the end of file was reached
+ * without error
+ * @throws TskCoreException
+ */
+ @Override
+ public String getNextHash() throws TskCoreException {
+ if (recordIterator.hasNext()) {
+ CSVRecord record = recordIterator.next();
+ String hash = record.get(hashColumnIndex);
+
+ if (hash.length() != 32) {
+ throw new TskCoreException("Hash has incorrect length: " + hash);
+ }
+
+ return (hash);
+ }
+ return null;
+ }
+
+ /**
+ * Check if there are more hashes to read
+ *
+ * @return true if we've read all expected hash values, false otherwise
+ */
+ @Override
+ public boolean doneReading() {
+ return (!recordIterator.hasNext());
+ }
+
+ /**
+ * Get the expected number of hashes in the file. This number can be an
+ * estimate.
+ *
+ * @return The expected hash count
+ */
+ @Override
+ public long getExpectedHashCount() {
+ return expectedHashCount;
+ }
+
+ /**
+ * Closes the import file
+ */
+ @Override
+ public final void close() {
+ if (inputStreamReader != null) {
+ try {
+ inputStreamReader.close();
+ } catch (IOException ex) {
+ Logger.getLogger(HashkeeperHashSetParser.class.getName()).log(Level.SEVERE, "Error closing Hashkeeper hash set " + filename, ex);
+ } finally {
+ inputStreamReader = null;
+ }
+ }
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/IdxHashSetParser.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/IdxHashSetParser.java
index 0c1b694e1b..0db5442c0c 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/IdxHashSetParser.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/IdxHashSetParser.java
@@ -28,7 +28,8 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.TskCoreException;
/**
- * Parser for idx files (*.idx)
+ * Parser for idx files and md5sum files (*.idx or *.txt) This parsers lines
+ * that start with md5 hashes and ignores any others
*/
class IdxHashSetParser implements HashSetParser {
@@ -49,6 +50,7 @@ class IdxHashSetParser implements HashSetParser {
File importFile = new File(filename);
long fileSize = importFile.length();
totalHashes = fileSize / 0x33 + 1; // IDX file lines are generally 0x33 bytes long. We add one to prevent this from being zero
+ // MD5sum output lines should be close enough to that (0x20 byte hash + filename)
}
/**
@@ -65,14 +67,15 @@ class IdxHashSetParser implements HashSetParser {
try {
while ((line = reader.readLine()) != null) {
- String[] parts = line.split("\\|");
+ // idx files have a pipe after the hash, md5sum files should have a space
+ String[] parts = line.split("\\|| ");
- // Header lines start with a 41 character dummy hash, 1 character longer than a SHA-1 hash
- if (parts.length != 2 || parts[0].length() == 41) {
+ String hashStr = parts[0].toLowerCase();
+ if (!hashStr.matches("^[0-9a-f]{32}$")) {
continue;
}
- return parts[0].toLowerCase();
+ return hashStr;
}
} catch (IOException ex) {
throw new TskCoreException("Error reading file " + filename, ex);
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ImportCentralRepoDbProgressDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ImportCentralRepoDbProgressDialog.java
index a2e9522893..37d3a20009 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ImportCentralRepoDbProgressDialog.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ImportCentralRepoDbProgressDialog.java
@@ -213,12 +213,14 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P
// Create the hash set parser
HashSetParser hashSetParser;
- if (importFileName.toLowerCase().endsWith(".idx")) {
+ if (importFileName.toLowerCase().endsWith(".idx") || importFileName.toLowerCase().endsWith(".txt")) {
hashSetParser = new IdxHashSetParser(importFileName);
- } else if(importFileName.toLowerCase().endsWith(".hash")){
+ } else if (importFileName.toLowerCase().endsWith(".hash")) {
hashSetParser = new EncaseHashSetParser(importFileName);
- } else if(importFileName.toLowerCase().endsWith(".kdb")){
+ } else if (importFileName.toLowerCase().endsWith(".kdb")) {
hashSetParser = new KdbHashSetParser(importFileName);
+ } else if (importFileName.toLowerCase().endsWith(".hsh")) {
+ hashSetParser = new HashkeeperHashSetParser(importFileName);
} else {
// We've gotten here with a format that can't be processed
throw new TskCoreException("Hash set to import is an unknown format : " + importFileName);
diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java
index c03c37a09b..aab7eaae97 100755
--- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java
+++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java
@@ -63,6 +63,7 @@ import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
class ReportHTML implements TableReportModule {
@@ -688,7 +689,8 @@ class ReportHTML implements TableReportModule {
}
for (int i = 0; i < tags.size(); i++) {
ContentTag tag = tags.get(i);
- linkToThumbnail.append(tag.getName().getDisplayName());
+ String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ linkToThumbnail.append(tag.getName().getDisplayName() + notableString);
if (i != tags.size() - 1) {
linkToThumbnail.append(", ");
}
diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java b/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java
index 08ec3d0151..53422c8f0d 100755
--- a/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java
+++ b/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java
@@ -41,10 +41,12 @@ import javax.swing.event.ListDataListener;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
+import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.TskData;
final class ReportVisualPanel2 extends JPanel {
@@ -102,7 +104,8 @@ final class ReportVisualPanel2 extends JPanel {
}
for (TagName tagName : tagNamesInUse) {
- tagStates.put(tagName.getDisplayName(), Boolean.FALSE);
+ String notableString = tagName.getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ tagStates.put(tagName.getDisplayName() + notableString, Boolean.FALSE);
}
tags.addAll(tagStates.keySet());
diff --git a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java
index 3df94c1d1c..8d5ad932ca 100755
--- a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java
+++ b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java
@@ -39,6 +39,7 @@ import java.util.TreeSet;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
+import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
@@ -299,7 +300,8 @@ class TableReportGenerator {
// Give the modules the rows for the content tags.
for (ContentTag tag : tags) {
// skip tags that we are not reporting on
- if (passesTagNamesFilter(tag.getName().getDisplayName()) == false) {
+ String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ if (passesTagNamesFilter(tag.getName().getDisplayName() + notableString) == false) {
continue;
}
@@ -310,7 +312,7 @@ class TableReportGenerator {
fileName = tag.getContent().getName();
}
- ArrayList rowData = new ArrayList<>(Arrays.asList(tag.getName().getDisplayName(), fileName, tag.getComment()));
+ ArrayList rowData = new ArrayList<>(Arrays.asList(tag.getName().getDisplayName() + notableString, fileName, tag.getComment()));
Content content = tag.getContent();
if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content;
@@ -376,12 +378,13 @@ class TableReportGenerator {
// Give the modules the rows for the content tags.
for (BlackboardArtifactTag tag : tags) {
- if (passesTagNamesFilter(tag.getName().getDisplayName()) == false) {
+ String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ if (passesTagNamesFilter(tag.getName().getDisplayName() + notableString) == false) {
continue;
}
List row;
- row = new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName(), tag.getComment(), tag.getContent().getName()));
+ row = new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName() + notableString, tag.getComment(), tag.getContent().getName()));
tableReport.addRow(row);
// check if the tag is an image that we should later make a thumbnail for
@@ -963,7 +966,8 @@ class TableReportGenerator {
try {
List contentTags = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(content);
for (ContentTag ct : contentTags) {
- allTags.add(ct.getName().getDisplayName());
+ String notableString = ct.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ allTags.add(ct.getName().getDisplayName() + notableString);
}
} catch (TskCoreException ex) {
errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedGetContentTags"));
@@ -1000,7 +1004,8 @@ class TableReportGenerator {
List tags = Case.getCurrentCase().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact);
HashSet uniqueTagNames = new HashSet<>();
for (BlackboardArtifactTag tag : tags) {
- uniqueTagNames.add(tag.getName().getDisplayName());
+ String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ uniqueTagNames.add(tag.getName().getDisplayName() + notableString);
}
if (failsTagFilter(uniqueTagNames, tagNamesFilter)) {
continue;
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java
index dd4cb60377..40537d3b83 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java
@@ -38,10 +38,10 @@ import org.sleuthkit.autopsy.ingest.IngestJob;
* ingest service.
*/
@ThreadSafe
-public final class AutoIngestJob implements Comparable, Serializable {
+final class AutoIngestJob implements Comparable, Serializable {
private static final long serialVersionUID = 1L;
- private static final int CURRENT_VERSION = 1;
+ private static final int CURRENT_VERSION = 2;
private static final int DEFAULT_PRIORITY = 0;
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
@@ -82,6 +82,12 @@ public final class AutoIngestJob implements Comparable, Serializa
private int numberOfCrashes;
@GuardedBy("this")
private StageDetails stageDetails;
+
+ /*
+ * Version 2 fields.
+ */
+ @GuardedBy("this")
+ private long dataSourceSize;
/**
* Constructs a new automated ingest job. All job state not specified in the
@@ -114,6 +120,11 @@ public final class AutoIngestJob implements Comparable, Serializa
this.processingStatus = ProcessingStatus.PENDING;
this.numberOfCrashes = 0;
this.stageDetails = this.getProcessingStageDetails();
+
+ /*
+ * Version 2 fields.
+ */
+ this.dataSourceSize = 0;
} catch (Exception ex) {
throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex);
}
@@ -151,6 +162,11 @@ public final class AutoIngestJob implements Comparable, Serializa
this.processingStatus = nodeData.getProcessingStatus();
this.numberOfCrashes = nodeData.getNumberOfCrashes();
this.stageDetails = this.getProcessingStageDetails();
+
+ /*
+ * Version 2 fields.
+ */
+ this.dataSourceSize = nodeData.getDataSourceSize();
} catch (Exception ex) {
throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex);
}
@@ -462,6 +478,24 @@ public final class AutoIngestJob implements Comparable, Serializa
this.numberOfCrashes = numberOfCrashes;
}
+ /**
+ * Gets the total size of the data source.
+ *
+ * @return The data source size.
+ */
+ synchronized long getDataSourceSize() {
+ return dataSourceSize;
+ }
+
+ /**
+ * Sets the total size of the data source.
+ *
+ * @param dataSourceSize The data source size.
+ */
+ synchronized void setDataSourceSize(long dataSourceSize) {
+ this.dataSourceSize = dataSourceSize;
+ }
+
/**
* Indicates whether some other job is "equal to" this job. Two jobs are
* equal if they have the same manifest file path.
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeData.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeData.java
index e2b267fded..f367fdf553 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeData.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeData.java
@@ -31,7 +31,7 @@ import javax.lang.model.type.TypeKind;
*/
final class AutoIngestJobNodeData {
- private static final int CURRENT_VERSION = 1;
+ private static final int CURRENT_VERSION = 2;
private static final int DEFAULT_PRIORITY = 0;
/*
@@ -47,7 +47,7 @@ final class AutoIngestJobNodeData {
* data. This avoids the need to continuously enlarge the buffer. Once the
* buffer has all the necessary data, it will be resized as appropriate.
*/
- private static final int MAX_POSSIBLE_NODE_DATA_SIZE = 131629;
+ private static final int MAX_POSSIBLE_NODE_DATA_SIZE = 131637;
/*
* Version 0 fields.
@@ -73,6 +73,11 @@ final class AutoIngestJobNodeData {
private long processingStageStartDate;
private String processingStageDetailsDescription; // 'byte' length used in byte array
private long processingStageDetailsStartDate;
+
+ /*
+ * Version 2 fields.
+ */
+ private long dataSourceSize;
/**
* Gets the current version of the auto ingest job coordination service node
@@ -109,6 +114,7 @@ final class AutoIngestJobNodeData {
setProcessingStage(job.getProcessingStage());
setProcessingStageStartDate(job.getProcessingStageStartDate());
setProcessingStageDetails(job.getProcessingStageDetails());
+ setDataSourceSize(job.getDataSourceSize());
}
/**
@@ -143,6 +149,7 @@ final class AutoIngestJobNodeData {
this.processingStageStartDate = 0L;
this.processingStageDetailsDescription = "";
this.processingStageDetailsStartDate = 0L;
+ this.dataSourceSize = 0L;
/*
* Get fields from node data.
@@ -179,6 +186,13 @@ final class AutoIngestJobNodeData {
this.processingHostName = getStringFromBuffer(buffer, TypeKind.SHORT);
}
+ if (buffer.hasRemaining()) {
+ /*
+ * Get version 2 fields.
+ */
+ this.dataSourceSize = buffer.getLong();
+ }
+
} catch (BufferUnderflowException ex) {
throw new InvalidDataException("Node data is incomplete", ex);
}
@@ -498,6 +512,24 @@ final class AutoIngestJobNodeData {
void setProcessingHostName(String processingHost) {
this.processingHostName = processingHost;
}
+
+ /**
+ * Gets the total size of the data source.
+ *
+ * @return The data source size.
+ */
+ long getDataSourceSize() {
+ return this.dataSourceSize;
+ }
+
+ /**
+ * Sets the total size of the data source.
+ *
+ * @param dataSourceSize The data source size.
+ */
+ void setDataSourceSize(long dataSourceSize) {
+ this.dataSourceSize = dataSourceSize;
+ }
/**
* Gets the node data as a byte array that can be sent to the coordination
@@ -515,7 +547,7 @@ final class AutoIngestJobNodeData {
buffer.putLong(this.completedDate);
buffer.putInt(this.errorsOccurred ? 1 : 0);
- if (this.version > 0) {
+ if (this.version >= 1) {
// Write version
buffer.putInt(this.version);
@@ -531,6 +563,10 @@ final class AutoIngestJobNodeData {
putStringIntoBuffer(this.processingStageDetailsDescription, buffer, TypeKind.BYTE);
buffer.putLong(this.processingStageDetailsStartDate);
putStringIntoBuffer(processingHostName, buffer, TypeKind.SHORT);
+
+ if (this.version >= 2) {
+ buffer.putLong(this.dataSourceSize);
+ }
}
// Prepare the array
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java
index 35b563b961..83337c551b 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java
@@ -56,7 +56,6 @@ import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
-import java.util.stream.Collectors;
import javax.annotation.concurrent.GuardedBy;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
@@ -98,6 +97,9 @@ import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestJobStartResult;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleError;
+import org.sleuthkit.datamodel.Content;
+import org.sleuthkit.datamodel.DataSource;
+import org.sleuthkit.datamodel.SleuthkitCase;
/**
* An auto ingest manager is responsible for processing auto ingest jobs defined
@@ -2263,6 +2265,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
return;
}
+ collectMetrics(caseForJob.getSleuthkitCase(), dataSource);
exportFiles(dataSource);
}
@@ -2544,6 +2547,39 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
}
}
+ /**
+ * Gather metrics to store in auto ingest job nodes. A SleuthkitCase
+ * instance is used to get the content size.
+ *
+ * @param caseDb The SleuthkitCase instance.
+ * @param dataSource The auto ingest data source.
+ *
+ * @throws CoordinationServiceException If there's a problem retrieving
+ * data from the coordination
+ * service.
+ * @throws InterruptedException If the thread calling the
+ * coordination service is
+ * interrupted.
+ */
+ private void collectMetrics(SleuthkitCase caseDb, AutoIngestDataSource dataSource) throws CoordinationServiceException, InterruptedException {
+ /*
+ * Get the data source size and store it in the current job.
+ */
+ List contentList = dataSource.getContent();
+ long dataSourceSize = 0;
+ for (Content content : contentList) {
+ dataSourceSize += ((DataSource) content).getContentSize(caseDb);
+ }
+ currentJob.setDataSourceSize(dataSourceSize);
+
+ /*
+ * Create node data from the current job and store it.
+ */
+ AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(currentJob);
+ String manifestNodePath = currentJob.getManifest().getFilePath().toString();
+ coordinationService.setNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestNodePath, nodeData.toArray());
+ }
+
/**
* Exports any files from the data source for the current job that
* satisfy any user-defined file export rules.
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/AddTagAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/AddTagAction.java
index e116ff171c..cd40efdb55 100755
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/AddTagAction.java
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/AddTagAction.java
@@ -1,15 +1,15 @@
/*
* Autopsy Forensic Browser
- *
- * Copyright 2013-16 Basis Technology Corp.
+ *
+ * Copyright 2013-2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -36,10 +36,12 @@ import javax.swing.SwingWorker;
import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionUtils;
import org.openide.util.NbBundle;
+import org.openide.util.NbBundle.Messages;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog;
import org.sleuthkit.autopsy.actions.GetTagNameDialog;
+import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent;
@@ -48,6 +50,7 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableTagsManager;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TagName;
+import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskCoreException;
/**
@@ -67,7 +70,8 @@ public class AddTagAction extends Action {
this.selectedFileIDs = selectedFileIDs;
this.tagName = tagName;
setGraphic(controller.getTagsManager().getGraphic(tagName));
- setText(tagName.getDisplayName());
+ String notableString = tagName.getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ setText(tagName.getDisplayName() + notableString);
setEventHandler(actionEvent -> addTagWithComment(""));
}
@@ -78,7 +82,7 @@ public class AddTagAction extends Action {
private void addTagWithComment(String comment) {
addTagsToFiles(tagName, comment, selectedFileIDs);
}
-
+
@NbBundle.Messages({"# {0} - fileID",
"AddDrawableTagAction.addTagsToFiles.alert=Unable to tag file {0}."})
private void addTagsToFiles(TagName tagName, String comment, Set selectedFiles) {
@@ -108,8 +112,8 @@ public class AddTagAction extends Action {
} catch (TskCoreException tskCoreException) {
LOGGER.log(Level.SEVERE, "Error tagging file", tskCoreException); //NON-NLS
- Platform.runLater(() ->
- new Alert(Alert.AlertType.ERROR, Bundle.AddDrawableTagAction_addTagsToFiles_alert(fileID)).show()
+ Platform.runLater(()
+ -> new Alert(Alert.AlertType.ERROR, Bundle.AddDrawableTagAction_addTagsToFiles_alert(fileID)).show()
);
break;
}
@@ -172,8 +176,8 @@ public class AddTagAction extends Action {
* or select a tag name and adds a tag with the resulting name.
*/
MenuItem newTagMenuItem = new MenuItem(Bundle.AddTagAction_menuItem_newTag());
- newTagMenuItem.setOnAction(actionEvent ->
- SwingUtilities.invokeLater(() -> {
+ newTagMenuItem.setOnAction(actionEvent
+ -> SwingUtilities.invokeLater(() -> {
TagName tagName = GetTagNameDialog.doDialog(getIGWindow());
if (tagName != null) {
new AddTagAction(controller, tagName, selectedFileIDs).handle(actionEvent);
@@ -188,8 +192,8 @@ public class AddTagAction extends Action {
* name.
*/
MenuItem tagAndCommentItem = new MenuItem(Bundle.AddTagAction_menuItem_tagAndComment());
- tagAndCommentItem.setOnAction(actionEvent ->
- SwingUtilities.invokeLater(() -> {
+ tagAndCommentItem.setOnAction(actionEvent
+ -> SwingUtilities.invokeLater(() -> {
GetTagNameAndCommentDialog.TagNameAndComment tagNameAndComment = GetTagNameAndCommentDialog.doDialog(getIGWindow());
if (null != tagNameAndComment) {
new AddTagAction(controller, tagNameAndComment.getTagName(), selectedFileIDs).addTagWithComment(tagNameAndComment.getComment());
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java
index 53a033a2c9..a21d163fdc 100755
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java
@@ -1,15 +1,15 @@
/*
* Autopsy Forensic Browser
- *
+ *
* Copyright 2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,7 +18,6 @@
*/
package org.sleuthkit.autopsy.imagegallery.actions;
-import java.awt.Window;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
@@ -30,24 +29,22 @@ import javafx.scene.control.Alert;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.image.ImageView;
-import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionUtils;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
-import org.openide.windows.TopComponent;
-import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
+import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
-import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableTagsManager;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.TskData;
/**
* Instances of this Action allow users to remove tags from content.
@@ -68,7 +65,8 @@ public class DeleteTagAction extends Action {
this.tagName = tagName;
this.contentTag = contentTag;
setGraphic(controller.getTagsManager().getGraphic(tagName));
- setText(tagName.getDisplayName());
+ String notableString = tagName.getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ setText(tagName.getDisplayName() + notableString);
setEventHandler(actionEvent -> deleteTag());
}
@@ -84,20 +82,20 @@ public class DeleteTagAction extends Action {
@Override
protected Void doInBackground() throws Exception {
DrawableTagsManager tagsManager = controller.getTagsManager();
-
+
// Pull the from the global context to avoid unnecessary calls
// to the database.
- final Collection selectedFilesList =
- new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
+ final Collection selectedFilesList
+ = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
AbstractFile file = selectedFilesList.iterator().next();
-
+
try {
LOGGER.log(Level.INFO, "Removing tag {0} from {1}", new Object[]{tagName.getDisplayName(), file.getName()}); //NON-NLS
tagsManager.deleteContentTag(contentTag);
} catch (TskCoreException tskCoreException) {
LOGGER.log(Level.SEVERE, "Error untagging file", tskCoreException); //NON-NLS
- Platform.runLater(() ->
- new Alert(Alert.AlertType.ERROR, Bundle.DeleteDrawableTagAction_deleteTag_alert(fileId)).show()
+ Platform.runLater(()
+ -> new Alert(Alert.AlertType.ERROR, Bundle.DeleteDrawableTagAction_deleteTag_alert(fileId)).show()
);
}
return null;
@@ -121,25 +119,25 @@ public class DeleteTagAction extends Action {
TagMenu(ImageGalleryController controller) {
setGraphic(new ImageView(DrawableAttribute.TAGS.getIcon()));
setText(Bundle.DeleteDrawableTagAction_displayName());
-
+
// For this menu, we shouldn't have more than one file selected.
// Therefore, we will simply grab the first file and work with that.
- final Collection selectedFilesList =
- new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
+ final Collection selectedFilesList
+ = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
AbstractFile file = selectedFilesList.iterator().next();
-
- try {
- List existingTagsList =
- Case.getCurrentCase().getServices().getTagsManager()
- .getContentTagsByContent(file);
- Collection tagNamesList =
- controller.getTagsManager().getNonCategoryTagNames();
+ try {
+ List existingTagsList
+ = Case.getCurrentCase().getServices().getTagsManager()
+ .getContentTagsByContent(file);
+
+ Collection tagNamesList
+ = controller.getTagsManager().getNonCategoryTagNames();
Iterator tagNameIterator = tagNamesList.iterator();
- for(int i=0; tagNameIterator.hasNext(); i++) {
+ for (int i = 0; tagNameIterator.hasNext(); i++) {
TagName tagName = tagNameIterator.next();
- for(ContentTag contentTag : existingTagsList) {
- if(contentTag.getName().getId() == tagName.getId()) {
+ for (ContentTag contentTag : existingTagsList) {
+ if (contentTag.getName().getId() == tagName.getId()) {
DeleteTagAction deleteDrawableTagAction = new DeleteTagAction(controller, tagName, contentTag, file.getId());
MenuItem tagNameItem = ActionUtils.createMenuItem(deleteDrawableTagAction);
getItems().add(tagNameItem);
@@ -151,7 +149,7 @@ public class DeleteTagAction extends Action {
.log(Level.SEVERE, "Error retrieving tags for TagMenu", ex); //NON-NLS
}
- if(getItems().isEmpty()) {
+ if (getItems().isEmpty()) {
setDisable(true);
}
}