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 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 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); } }