diff --git a/Core/ivy.xml b/Core/ivy.xml index c3afd23afa..35cb8de904 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 bf9045dea8..b6931ff315 100755 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -1,5 +1,6 @@ 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 @@ -7,7 +8,7 @@ file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar file.reference.jsoup-1.10.3.jar=release/modules/ext/jsoup-1.10.3.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 @@ -16,6 +17,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 @@ -24,12 +26,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 ec8c304f37..c4fea0885c 100755 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -304,16 +304,15 @@ org.sleuthkit.autopsy.corecomponents org.sleuthkit.autopsy.coreutils org.sleuthkit.autopsy.datamodel - org.sleuthkit.autopsy.datamodel.tags 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 org.sleuthkit.autopsy.keywordsearchservice org.sleuthkit.autopsy.menuactions + org.sleuthkit.autopsy.modules.encryptiondetection org.sleuthkit.autopsy.modules.filetypeid org.sleuthkit.autopsy.modules.hashdatabase org.sleuthkit.autopsy.modules.vmextractor @@ -337,25 +336,13 @@ ext/tika-core-1.14.jar release/modules/ext/tika-core-1.14.jar - - ext/StixLib.jar - release/modules/ext/StixLib.jar - ext/Tsk_DataModel_PostgreSQL.jar release/modules/ext/Tsk_DataModel_PostgreSQL.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/StixLib.jar + release/modules/ext/StixLib.jar ext/opencv-248.jar @@ -365,18 +352,30 @@ ext/curator-framework-2.8.0.jar release/modules/ext/curator-framework-2.8.0.jar + + ext/curator-client-2.8.0.jar + release/modules/ext/curator-client-2.8.0.jar + ext/commons-dbcp2-2.1.1.jar release\modules\ext\commons-dbcp2-2.1.1.jar - ext/Rejistry-1.0-SNAPSHOT.jar - release/modules/ext/Rejistry-1.0-SNAPSHOT.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/activemq-all-5.11.1.jar + release/modules/ext/activemq-all-5.11.1.jar + + + ext/opencv-248.jar + release/modules/ext/opencv-248.jar + ext/sevenzipjbinding.jar release/modules/ext/sevenzipjbinding.jar @@ -385,26 +384,6 @@ ext/mchange-commons-java-0.2.9.jar release/modules/ext/mchange-commons-java-0.2.9.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.8.1.jar - release/modules/ext/metadata-extractor-2.8.1.jar - - - ext/jsoup-1.10.3.jar - release/modules/ext/jsoup-1.10.3.jar - - - ext/jdom-2.0.5-contrib.jar - release/modules/ext/jdom-2.0.5-contrib.jar - ext/postgresql-9.4.1211.jre7.jar release/modules/ext/postgresql-9.4.1211.jre7.jar @@ -414,17 +393,53 @@ release/modules/ext/curator-recipes-2.8.0.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/commons-dbcp2-2.1.1.jar - release\modules\ext\commons-dbcp2-2.1.1.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/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/jsoup-1.10.3.jar + release/modules/ext/jsoup-1.10.3.jar + + + ext/Rejistry-1.0-SNAPSHOT.jar + release/modules/ext/Rejistry-1.0-SNAPSHOT.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 + + + 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/AddLocalFilesTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLocalFilesTask.java index b2d14db118..c610ff78c3 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLocalFilesTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLocalFilesTask.java @@ -89,7 +89,7 @@ class AddLocalFilesTask implements Runnable { progress.setIndeterminate(true); FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); LocalFilesDataSource newDataSource = fileManager.addLocalFilesDataSource(deviceId, rootVirtualDirectoryName, "", localFilePaths, new ProgressUpdater()); - newDataSources.add(newDataSource.getRootDirectory()); + newDataSources.add(newDataSource); } catch (TskDataException | TskCoreException ex) { errors.add(ex.getMessage()); LOGGER.log(Level.SEVERE, String.format("Failed to add datasource: %s", ex.getMessage()), ex); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 00ce2cca5d..e2e58c5b5a 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -363,7 +363,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; + }; /** @@ -1463,6 +1470,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/CasePropertiesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java index 23e9d0b1c8..101f8688dd 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java @@ -78,10 +78,9 @@ final class CasePropertiesPanel extends javax.swing.JPanel { try { EamDb dbManager = EamDb.getInstance(); if (dbManager != null) { - CorrelationCase correlationCase = dbManager.getCaseByUUID(Case.getCurrentCase().getName()); + CorrelationCase correlationCase = dbManager.getCase(Case.getCurrentCase()); if (null == correlationCase) { - dbManager.newCase(Case.getCurrentCase()); - correlationCase = dbManager.getCaseByUUID(Case.getCurrentCase().getName()); + correlationCase = dbManager.newCase(Case.getCurrentCase()); } currentOrg = correlationCase.getOrg(); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java index 7a4a1f7bf4..89d527fcc1 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java @@ -91,7 +91,7 @@ final class NewCaseWizardAction extends CallableSystemAction { if (EamDb.isEnabled()) { //if the eam is enabled we need to save the case organization information now EamDb dbManager = EamDb.getInstance(); if (dbManager != null) { - CorrelationCase cRCase = dbManager.getCaseByUUID(Case.getCurrentCase().getName()); + CorrelationCase cRCase = dbManager.getCase(Case.getCurrentCase()); if (cRCase == null) { cRCase = dbManager.newCase(Case.getCurrentCase()); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java index a2793ec295..0a9649c45d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java @@ -89,7 +89,7 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { if (currentCase != null) { try { EamDb dbManager = EamDb.getInstance(); - selectedOrg = dbManager.getCaseByUUID(currentCase.getName()).getOrg(); + selectedOrg = dbManager.getCase(currentCase).getOrg(); } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Unable to get Organization associated with the case from Central Repo", ex); } @@ -561,7 +561,7 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { if (EamDb.isEnabled()) { try { EamDb dbManager = EamDb.getInstance(); - CorrelationCase correlationCase = dbManager.getCaseByUUID(Case.getCurrentCase().getName()); + CorrelationCase correlationCase = dbManager.getCase(Case.getCurrentCase()); if (caseDisplayNameTextField.isVisible()) { correlationCase.setDisplayName(caseDisplayNameTextField.getText()); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties index 0dd66e8c59..e9c1b5307c 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties @@ -1,16 +1,15 @@ OptionsCategory_Name_TagNamesOptions=Tags OptionsCategory_TagNames=TagNames Blackboard.unableToIndexArtifact.error.msg=Unable to index blackboard artifact {0} -NewTagNameDialog.title.text=New Tag Name -NewTagNameDialog.JOptionPane.tagNameIllegalCharacters.message=Tag name may not contain any of the following symbols\: \\ \: * ? " < > | , ; -NewTagNameDialog.JOptionPane.tagNameIllegalCharacters.title=Invalid character in tag name -NewTagNameDialog.JOptionPane.tagNameEmpty.message=The tag name cannot be empty -NewTagNameDialog.JOptionPane.tagNameEmpty.title=Empty tag name +TagNameDialog.title.text=New Tag +TagNameDialog.JOptionPane.tagNameIllegalCharacters.message=Tag name may not contain any of the following symbols\: \\ \: * ? " < > | , ; +TagNameDialog.JOptionPane.tagNameIllegalCharacters.title=Invalid character in tag name +TagNameDialog.JOptionPane.tagNameEmpty.message=The tag name cannot be empty +TagNameDialog.JOptionPane.tagNameEmpty.title=Empty tag name TagOptionsPanel.tagTypesListLabel.text=Tag Names: -TagOptionsPanel.panelDescriptionLabel.text=Autopsy keeps a list of the tag names you have created in the past. Add more or delete them here. -NewTagNameDialog.okButton.text=OK -NewTagNameDialog.cancelButton.text=Cancel -NewTagNameDialog.tagNameTextField.text= -NewTagNameDialog.newTagNameLabel.text=New Tag Name: -TagOptionsPanel.deleteTagNameButton.text=Delete Tag Name -TagOptionsPanel.newTagNameButton.text=New Tag Name +TagOptionsPanel.deleteTagNameButton.text=Delete Tag +TagOptionsPanel.newTagNameButton.text=New Tag +TagNameDialog.okButton.text=OK +TagNameDialog.cancelButton.text=Cancel +TagNameDialog.tagNameTextField.text= +TagNameDialog.newTagNameLabel.text=Name: diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties index a27e3ca586..41e871691b 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties @@ -1,2 +1,3 @@ TagsManager.predefTagNames.bookmark.text=\u30d6\u30c3\u30af\u30de\u30fc\u30af -Blackboard.unableToIndexArtifact.error.msg=blackboard\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8{0}\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 \ No newline at end of file +Blackboard.unableToIndexArtifact.error.msg=blackboard\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8{0}\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +TagOptionsPanel.ingestRunningWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u4e2d\u306b\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093\uff01 diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java index f12c2f7e5d..817052619b 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java @@ -47,7 +47,6 @@ import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.TskDataException; import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.AbstractContent; import org.sleuthkit.datamodel.CarvingResult; import org.sleuthkit.datamodel.TskData; @@ -409,10 +408,9 @@ public class FileManager implements Closeable { */ trans = caseDb.beginTransaction(); LocalFilesDataSource dataSource = caseDb.addLocalFilesDataSource(deviceId, rootDirectoryName, timeZone, trans); - VirtualDirectory rootDirectory = dataSource.getRootDirectory(); List filesAdded = new ArrayList<>(); for (java.io.File localFile : localFiles) { - AbstractFile fileAdded = addLocalFile(trans, rootDirectory, localFile, TskData.EncodingType.NONE, progressUpdater); + AbstractFile fileAdded = addLocalFile(trans, dataSource, localFile, TskData.EncodingType.NONE, progressUpdater); if (null != fileAdded) { filesAdded.add(fileAdded); } else { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java new file mode 100755 index 0000000000..e1d81d2b65 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java @@ -0,0 +1,306 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Level; +import javax.annotation.concurrent.Immutable; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; + +/** + * A tag name definition consisting of a display name, description and color. + */ +@Immutable +final class TagNameDefinition implements Comparable { + + private static final Logger LOGGER = Logger.getLogger(TagNameDefinition.class.getName()); + @NbBundle.Messages({"TagNameDefinition.predefTagNames.bookmark.text=Bookmark", + "TagNameDefinition.predefTagNames.followUp.text=Follow Up", + "TagNameDefinition.predefTagNames.notableItem.text=Notable Item"}) + private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS + private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS + + private static final List STANDARD_NOTABLE_TAG_DISPLAY_NAMES = Arrays.asList(Bundle.TagNameDefinition_predefTagNames_notableItem_text(), DhsImageCategory.ONE.getDisplayName(), DhsImageCategory.TWO.getDisplayName(), DhsImageCategory.THREE.getDisplayName()); // NON-NLS + private static final List STANDARD_TAG_DISPLAY_NAMES = Arrays.asList(Bundle.TagNameDefinition_predefTagNames_bookmark_text(), Bundle.TagNameDefinition_predefTagNames_followUp_text(), + Bundle.TagNameDefinition_predefTagNames_notableItem_text(), DhsImageCategory.ONE.getDisplayName(), + DhsImageCategory.TWO.getDisplayName(), DhsImageCategory.THREE.getDisplayName(), + DhsImageCategory.FOUR.getDisplayName(), DhsImageCategory.FIVE.getDisplayName()); + private final String displayName; + private final String description; + private final TagName.HTML_COLOR color; + private final TskData.FileKnown knownStatus; + + /** + * Constructs a tag name definition consisting of a display name, + * description, color and knownStatus. + * + * @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 status The status denoted by the tag name. + */ + TagNameDefinition(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown status) { + this.displayName = displayName; + this.description = description; + this.color = color; + this.knownStatus = status; + } + + static List getStandardTagNames() { + return STANDARD_TAG_DISPLAY_NAMES; + } + + /** + * Gets the display name for the tag name. + * + * @return The display name. + */ + String getDisplayName() { + return displayName; + } + + /** + * Gets the description for the tag name. + * + * @return The description. + */ + String getDescription() { + return description; + } + + /** + * Gets the color for the tag name. + * + * @return The color. + */ + TagName.HTML_COLOR getColor() { + return color; + } + + /** + * The status which will be applied to items with this tag. + * + * @return a value of TskData.FileKnown which is associated with this tag + */ + TskData.FileKnown getKnownStatus() { + return knownStatus; + } + + /** + * Compares this tag name definition with the specified tag name definition + * for order. + * + * @param other The tag name definition to which to compare this tag name + * definition. + * + * @return Negative integer, zero, or a positive integer to indicate that + * this tag name definition is less than, equal to, or greater than + * the specified tag name definition. + */ + @Override + public int compareTo(TagNameDefinition other) { + return this.getDisplayName().toLowerCase().compareTo(other.getDisplayName().toLowerCase()); + } + + /** + * Returns a hash code value for this tag name definition. + * + * @return The has code. + */ + @Override + public int hashCode() { + int hash = 7; + hash = 83 * hash + Objects.hashCode(this.displayName); + return hash; + } + + /** + * Indicates whether some other object is "equal to" this tag name + * definition. + * + * @param obj The object to test for equality. + * + * @return True or false. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TagNameDefinition)) { + return false; + } + boolean sameName = this.getDisplayName().equals(((TagNameDefinition) obj).getDisplayName()); + boolean sameStatus = this.getKnownStatus().equals(((TagNameDefinition) obj).getKnownStatus()); + return sameName && sameStatus; + } + + /** + * A string representation of this tag name definition. + * + * @return The display name of the tag type. + */ + @Override + public String toString() { + return displayName; + } + + /** + * @return A string representation of the tag name definition in the format + * that is used by the tags settings file. + */ + private String toSettingsFormat() { + return displayName + "," + description + "," + color.name() + "," + knownStatus.toString(); + } + + private TagName saveToCase(SleuthkitCase caseDb) { + TagName tagName = null; + try { + tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatus); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error updating non-file object ", ex); + } + return tagName; + } + + /** + * Gets tag name definitions from the tag settings file as well as the + * default tag name definitions. + * + * @return A set of tag name definition objects. + */ + static synchronized Set getTagNameDefinitions() { + Set tagNames = new HashSet<>(); + //modifiable copy of default tags list for us to keep track of which default tags have already been created + Set standardTags = new HashSet<>(STANDARD_TAG_DISPLAY_NAMES); + String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY); + if (null != setting && !setting.isEmpty()) { + List tagNameTuples = Arrays.asList(setting.split(";")); + int numberOfAttributes = 0; + if (tagNameTuples.size() > 0) { + // Determine if Tags.properties file needs to be upgraded + numberOfAttributes = tagNameTuples.get(0).split(",").length; + } + if (numberOfAttributes == 3) { + // Upgrade Tags.Properties with the settings in Central Repository Settings if necessary + tagNames.addAll(upgradeTagPropertiesFile(tagNameTuples, standardTags)); + } else if (numberOfAttributes == 4) { + // if the Tags.Properties file is up to date parse it + tagNames.addAll(readCurrentTagPropertiesFile(tagNameTuples, standardTags)); + } + } + //create standard tags which should always exist which were not already created for whatever reason, such as upgrade + for (String standardTagName : standardTags) { + if (STANDARD_NOTABLE_TAG_DISPLAY_NAMES.contains(standardTagName)) { + tagNames.add(new TagNameDefinition(standardTagName, "", TagName.HTML_COLOR.NONE, TskData.FileKnown.BAD)); + } else { + tagNames.add(new TagNameDefinition(standardTagName, "", TagName.HTML_COLOR.NONE, TskData.FileKnown.UNKNOWN)); + } + } + return tagNames; + + } + + /** + * Read the central repository properties file to get any knownStatus + * related tag settings that may exist in it. + * + * @param tagProperties the list of comma seperated tags in the + * Tags.properties file + * @param standardTagsToBeCreated the list of standard tags which have yet + * to be created + * + * @return tagNames a list of TagNameDefinitions + */ + private static Set upgradeTagPropertiesFile(List tagProperties, Set standardTagsToBeCreated) { + Set tagNames = new HashSet<>(); + List legacyNotableTags = new ArrayList<>(); + String badTagsStr = ModuleSettings.getConfigSetting("CentralRepository", "db.badTags"); // NON-NLS + if (badTagsStr == null || badTagsStr.isEmpty()) { //if there were no bad tags in the central repo properties file use the default list + legacyNotableTags.addAll(STANDARD_NOTABLE_TAG_DISPLAY_NAMES); + } else { //otherwise use the list that was in the central repository properties file + legacyNotableTags.addAll(Arrays.asList(badTagsStr.split(","))); + } + for (String tagNameTuple : tagProperties) { + String[] tagNameAttributes = tagNameTuple.split(","); //get the attributes + standardTagsToBeCreated.remove(tagNameAttributes[0]); //remove the tag from the list of standard tags which have not been created + if (legacyNotableTags.contains(tagNameAttributes[0])) { //if tag should be notable mark create it as such + tagNames.add(new TagNameDefinition(tagNameAttributes[0], tagNameAttributes[1], + TagName.HTML_COLOR.valueOf(tagNameAttributes[2]), TskData.FileKnown.BAD)); + } else { //otherwise create it as unknown + tagNames.add(new TagNameDefinition(tagNameAttributes[0], tagNameAttributes[1], + TagName.HTML_COLOR.valueOf(tagNameAttributes[2]), TskData.FileKnown.UNKNOWN)); //add the default value for that tag + } + } + return tagNames; + } + + /** + * Read the Tags.properties file to get the TagNameDefinitions that are + * preserved accross cases. + * + * @param tagProperties the list of comma seperated tags in the + * Tags.properties file + * @param standardTagsToBeCreated the list of standard tags which have yet + * to be created + * + * @return tagNames a list of TagNameDefinitions + */ + private static Set readCurrentTagPropertiesFile(List tagProperties, Set standardTagsToBeCreated) { + Set tagNames = new HashSet<>(); + for (String tagNameTuple : tagProperties) { + String[] tagNameAttributes = tagNameTuple.split(","); //get the attributes + standardTagsToBeCreated.remove(tagNameAttributes[0]); //remove the tag from the list of standard tags which have not been created + tagNames.add(new TagNameDefinition(tagNameAttributes[0], tagNameAttributes[1], + TagName.HTML_COLOR.valueOf(tagNameAttributes[2]), TskData.FileKnown.valueOf(tagNameAttributes[3]))); + } + return tagNames; + } + + /** + * Sets the tag name definitions in the tag settings file. + * + * @param tagNames A set of tag name definition objects. + */ + static synchronized void setTagNameDefinitions(Set tagNames) { + StringBuilder setting = new StringBuilder(); + for (TagNameDefinition tagName : tagNames) { + if (setting.length() != 0) { + setting.append(";"); + } + setting.append(tagName.toSettingsFormat()); + if (Case.isCaseOpen()) { + SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); + tagName.saveToCase(caseDb); + } + } + ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting.toString()); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefiniton.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefiniton.java deleted file mode 100755 index 8c7d29baa3..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefiniton.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * 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; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import javax.annotation.concurrent.Immutable; -import org.openide.util.Exceptions; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.coreutils.ModuleSettings; -import org.sleuthkit.datamodel.TagName; -import org.sleuthkit.autopsy.datamodel.tags.Category; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; - -/** - * A tag name definition consisting of a display name, description and color. - */ -@Immutable -final class TagNameDefiniton implements Comparable { - - @NbBundle.Messages({"TagNameDefiniton.predefTagNames.bookmark.text=Bookmark", - "TagNameDefiniton.predefTagNames.followUp.text=Follow Up", - "TagNameDefiniton.predefTagNames.notableItem.text=Notable Item"}) - private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS - private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS - - private static final List STANDARD_NOTABLE_TAG_DISPLAY_NAMES = Arrays.asList(Bundle.TagNameDefiniton_predefTagNames_notableItem_text(), Category.ONE.getDisplayName(), Category.TWO.getDisplayName(), Category.THREE.getDisplayName()); // NON-NLS - private static final List STANDARD_TAG_DISPLAY_NAMES = Arrays.asList(Bundle.TagNameDefiniton_predefTagNames_bookmark_text(), Bundle.TagNameDefiniton_predefTagNames_followUp_text(), - Bundle.TagNameDefiniton_predefTagNames_notableItem_text(), Category.ONE.getDisplayName(), - Category.TWO.getDisplayName(), Category.THREE.getDisplayName(), - Category.FOUR.getDisplayName(), Category.FIVE.getDisplayName()); - private final String displayName; - private final String description; - private final TagName.HTML_COLOR color; - private final TskData.FileKnown knownStatusDenoted; - - /** - * Constructs a tag name definition consisting of a display name, - * description, color and knownStatus. - * - * @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. - */ - TagNameDefiniton(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown status) { - this.displayName = displayName; - this.description = description; - this.color = color; - this.knownStatusDenoted = status; - } - - static List getStandardTagNames() { - return STANDARD_TAG_DISPLAY_NAMES; - } - - /** - * Gets the display name for the tag name. - * - * @return The display name. - */ - String getDisplayName() { - return displayName; - } - - /** - * Gets the description for the tag name. - * - * @return The description. - */ - String getDescription() { - return description; - } - - /** - * Gets the color for the tag name. - * - * @return The color. - */ - TagName.HTML_COLOR getColor() { - return color; - } - - /** - * Whether or not the status that this tag implies is the Notable status - * - * @return true if the Notable status is implied by this tag, false - * otherwise. - */ - boolean isNotable() { - return knownStatusDenoted == TskData.FileKnown.BAD; - } - - /** - * Compares this tag name definition with the specified tag name definition - * for order. - * - * @param other The tag name definition to which to compare this tag name - * definition. - * - * @return Negative integer, zero, or a positive integer to indicate that - * this tag name definition is less than, equal to, or greater than - * the specified tag name definition. - */ - @Override - public int compareTo(TagNameDefiniton other) { - return this.getDisplayName().toLowerCase().compareTo(other.getDisplayName().toLowerCase()); - } - - /** - * Returns a hash code value for this tag name definition. - * - * @return The has code. - */ - @Override - public int hashCode() { - int hash = 7; - hash = 83 * hash + Objects.hashCode(this.displayName); - return hash; - } - - /** - * Indicates whether some other object is "equal to" this tag name - * definition. - * - * @param obj The object to test for equality. - * - * @return True or false. - */ - @Override - public boolean equals(Object obj) { - if (!(obj instanceof TagNameDefiniton)) { - return false; - } - TagNameDefiniton thatTagName = (TagNameDefiniton) obj; - return this.getDisplayName().equals(thatTagName.getDisplayName()); - } - - /** - * A string representation of this tag name definition. - * - * @return The display name of the tag type. - */ - @Override - public String toString() { - return displayName; - } - - /** - * @return A string representation of the tag name definition in the format - * that is used by the tags settings file. - */ - private String toSettingsFormat() { - return displayName + "," + description + "," + color.name() + "," + knownStatusDenoted.toString(); - } - - private TagName saveToCase(SleuthkitCase caseDb) { - TagName tagName = null; - try { - tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatusDenoted); - } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); - } - return tagName; - } - - /** - * Gets tag name definitions from the tag settings file as well as the - * default tag name definitions. - * - * @return A set of tag name definition objects. - */ - static synchronized Set getTagNameDefinitions() { - Set tagNames = new HashSet<>(); - List standardTags = new ArrayList<>(STANDARD_TAG_DISPLAY_NAMES); //modifiable copy of default tags list for us to keep track of which ones already exist - String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY); - if (null != setting && !setting.isEmpty()) { - List tagNameTuples = Arrays.asList(setting.split(";")); - List notableTags = new ArrayList<>(); - String badTagsStr = ModuleSettings.getConfigSetting("CentralRepository", "db.badTags"); // NON-NLS - if (badTagsStr == null || badTagsStr.isEmpty()) { //if there were no bad tags in the central repo properties file use the default list - notableTags.addAll(STANDARD_NOTABLE_TAG_DISPLAY_NAMES); - } else { //otherwise use the list that was in the central repository properties file - notableTags.addAll(Arrays.asList(badTagsStr.split(","))); - } - for (String tagNameTuple : tagNameTuples) { //for each tag listed in the tags properties file - String[] tagNameAttributes = tagNameTuple.split(","); //get the attributes - if (tagNameAttributes.length == 3) { //if there are only 3 attributes so Tags.properties does not contain any tag definitions with knownStatus - standardTags.remove(tagNameAttributes[0]); //remove tag from default tags we need to create still - if (notableTags.contains(tagNameAttributes[0])) { //if tag should be notable mark create it as such - tagNames.add(new TagNameDefiniton(tagNameAttributes[0], tagNameAttributes[1], TagName.HTML_COLOR.valueOf(tagNameAttributes[2]), TskData.FileKnown.BAD)); - } else { //otherwise create it as unknown - tagNames.add(new TagNameDefiniton(tagNameAttributes[0], tagNameAttributes[1], TagName.HTML_COLOR.valueOf(tagNameAttributes[2]), TskData.FileKnown.UNKNOWN)); //add the default value for that tag - } - } else if (tagNameAttributes.length == 4) { //if there are 4 attributes its a current list we can use the values present - standardTags.remove(tagNameAttributes[0]); //remove tag from default tags we need to create still - tagNames.add(new TagNameDefiniton(tagNameAttributes[0], tagNameAttributes[1], TagName.HTML_COLOR.valueOf(tagNameAttributes[2]), TskData.FileKnown.valueOf(tagNameAttributes[3]))); - } - } - } - for (String standardTagName : standardTags) { //create standard tags which should always exist which were not already created for whatever reason, such as upgrade - if (STANDARD_NOTABLE_TAG_DISPLAY_NAMES.contains(standardTagName)) { - tagNames.add(new TagNameDefiniton(standardTagName, "", TagName.HTML_COLOR.NONE, TskData.FileKnown.BAD)); - } else { - tagNames.add(new TagNameDefiniton(standardTagName, "", TagName.HTML_COLOR.NONE, TskData.FileKnown.UNKNOWN)); - } - } - return tagNames; - } - - /** - * Sets the tag name definitions in the tag settings file. - * - * @param tagNames A set of tag name definition objects. - */ - static synchronized void setTagNameDefinitions(Set tagNames) { - StringBuilder setting = new StringBuilder(); - for (TagNameDefiniton tagName : tagNames) { - if (setting.length() != 0) { - setting.append(";"); - } - setting.append(tagName.toSettingsFormat()); - if (Case.isCaseOpen()) { - SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); - tagName.saveToCase(caseDb); - } - } - ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting.toString()); - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.form b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.form similarity index 57% rename from Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.form rename to Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.form index b6400b7c31..a88e25eb55 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.form @@ -23,10 +23,10 @@ - + - + @@ -34,6 +34,14 @@ + + + + + + + + @@ -41,20 +49,22 @@ - + - - - - + + + + + + + - @@ -63,21 +73,21 @@ - + - + - + @@ -87,12 +97,44 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.java similarity index 54% rename from Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.java rename to Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.java index b7b2e66ebc..4949be2507 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/NewTagNameDialog.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; @@ -28,11 +28,17 @@ import javax.swing.JOptionPane; 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; -final class NewTagNameDialog extends javax.swing.JDialog { +@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; private String userTagDisplayName; + private String userTagDescription; + private boolean userTagIsNotable; private BUTTON_PRESSED result; enum BUTTON_PRESSED { @@ -42,13 +48,25 @@ final class NewTagNameDialog extends javax.swing.JDialog { /** * Creates a new NewUserTagNameDialog dialog. */ - NewTagNameDialog() { - super(new JFrame(NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.title.text")), - NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.title.text"), true); + TagNameDialog() { + super(new JFrame(NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.title.text")), + NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.title.text"), true); initComponents(); this.display(); } + @Messages({"TagNameDialog.editTitle.text=Edit Tag"}) + TagNameDialog(TagNameDefinition tagNameToEdit) { + super(new JFrame(NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.editTitle.text")), + NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.editTitle.text"), true); + initComponents(); + tagNameTextField.setText(tagNameToEdit.getDisplayName()); + descriptionTextArea.setText(tagNameToEdit.getDescription()); + notableCheckbox.setSelected(tagNameToEdit.getKnownStatus() == TskData.FileKnown.BAD); + tagNameTextField.setEnabled(false); + this.display(); + } + /** * Sets display settings for the dialog and adds appropriate listeners. */ @@ -56,7 +74,7 @@ final class NewTagNameDialog extends javax.swing.JDialog { setLayout(new BorderLayout()); /* - * Center the dialog + * Center the dialog */ Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); int width = this.getSize().width; @@ -81,14 +99,17 @@ final class NewTagNameDialog extends javax.swing.JDialog { public void changedUpdate(DocumentEvent e) { fire(); } + @Override public void removeUpdate(DocumentEvent e) { fire(); } + @Override public void insertUpdate(DocumentEvent e) { fire(); } + private void fire() { enableOkButton(); } @@ -105,47 +126,68 @@ final class NewTagNameDialog extends javax.swing.JDialog { /** * Called when a button is pressed or when the dialog is closed. + * * @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(NewTagNameDialog.class, "NewTagNameDialog.JOptionPane.tagNameEmpty.message"), - NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.JOptionPane.tagNameEmpty.title"), + 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) && !TagNameDefiniton.getStandardTagNames().contains(newTagDisplayName)) { + if (TagsManager.containsIllegalCharacters(newTagDisplayName) && !TagNameDefinition.getStandardTagNames().contains(newTagDisplayName)) { JOptionPane.showMessageDialog(null, - NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.JOptionPane.tagNameIllegalCharacters.message"), - NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.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(); result = BUTTON_PRESSED.OK; } else { result = BUTTON_PRESSED.CANCEL; } - setVisible(false); + setVisible(false); } /** * Returns the tag name entered by the user. - * - * @return a new user tag name + * + * @return a new user tag name */ String getTagName() { return userTagDisplayName; } + String getTagDesciption() { + return userTagDescription; + } + + boolean isTagNotable() { + return userTagIsNotable; + } + /** * Returns information about which button was pressed. - * + * * @return BUTTON_PRESSED (OK, CANCEL) */ BUTTON_PRESSED getResult() { @@ -153,9 +195,9 @@ final class NewTagNameDialog extends javax.swing.JDialog { } /** - * Enable the OK button if the tag name text field is not empty. - * Sets the enter button as default, so user can press enter to activate - * an okButton press and add the tag name. + * Enable the OK button if the tag name text field is not empty. Sets the + * enter button as default, so user can press enter to activate an okButton + * press and add the tag name. */ private void enableOkButton() { okButton.setEnabled(!tagNameTextField.getText().isEmpty()); @@ -175,27 +217,40 @@ final class NewTagNameDialog extends javax.swing.JDialog { tagNameTextField = new javax.swing.JTextField(); cancelButton = new javax.swing.JButton(); okButton = new javax.swing.JButton(); + descriptionScrollPane = new javax.swing.JScrollPane(); + descriptionTextArea = new javax.swing.JTextArea(); + descriptionLabel = new javax.swing.JLabel(); + notableCheckbox = new javax.swing.JCheckBox(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - org.openide.awt.Mnemonics.setLocalizedText(newTagNameLabel, org.openide.util.NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.newTagNameLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(newTagNameLabel, org.openide.util.NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.newTagNameLabel.text")); // NOI18N - tagNameTextField.setText(org.openide.util.NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.tagNameTextField.text")); // NOI18N + tagNameTextField.setText(org.openide.util.NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.tagNameTextField.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.cancelButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.cancelButton.text")); // NOI18N cancelButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { cancelButtonActionPerformed(evt); } }); - org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(NewTagNameDialog.class, "NewTagNameDialog.okButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.okButton.text")); // NOI18N okButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { okButtonActionPerformed(evt); } }); + descriptionTextArea.setColumns(20); + descriptionTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N + descriptionTextArea.setRows(3); + descriptionScrollPane.setViewportView(descriptionTextArea); + + org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.descriptionLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(notableCheckbox, org.openide.util.NbBundle.getMessage(TagNameDialog.class, "TagNameDialog.notableCheckbox.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( @@ -203,13 +258,19 @@ final class NewTagNameDialog extends javax.swing.JDialog { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(tagNameTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 220, Short.MAX_VALUE) + .addComponent(tagNameTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 284, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) .addComponent(okButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cancelButton)) - .addComponent(newTagNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(newTagNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(descriptionScrollPane, javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(notableCheckbox) + .addComponent(descriptionLabel)) + .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); layout.setVerticalGroup( @@ -219,13 +280,16 @@ final class NewTagNameDialog extends javax.swing.JDialog { .addComponent(newTagNameLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(tagNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(50, Short.MAX_VALUE)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .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) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 42, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(cancelButton) - .addComponent(okButton)) - .addContainerGap()) + .addComponent(okButton))) ); pack(); @@ -242,7 +306,11 @@ final class NewTagNameDialog extends javax.swing.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.JLabel newTagNameLabel; + private javax.swing.JCheckBox notableCheckbox; private javax.swing.JButton okButton; private javax.swing.JTextField tagNameTextField; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form index c58201621a..8654d04598 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form @@ -16,7 +16,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -37,43 +37,39 @@ - - - - - - + + + - - - + - + - + - - + + - - - + + + + @@ -89,34 +85,46 @@ - + - - + - - - - + + + + + + + + + + + + + + - + - + + + - - + + - - + + + - + @@ -129,7 +137,7 @@ - + @@ -144,7 +152,7 @@ - + @@ -157,6 +165,15 @@ + + + + + + + + + @@ -170,11 +187,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -187,15 +263,109 @@ - + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java index 681cc95472..c5458d2f76 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java @@ -18,6 +18,10 @@ */ 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; @@ -25,7 +29,10 @@ 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; import org.sleuthkit.datamodel.TskData; @@ -35,26 +42,50 @@ 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 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<>(TagNameDefiniton.getTagNameDefinitions()); + 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) -> { - enableButtons(); + updatePanel(); }); + addIngestJobEventsListener(); + } + + /** + * Add a property change listener that listens to ingest job events to + * disable the buttons on the panel if ingest is running. This is done to + * prevent changes to user-defined types while the type definitions are in + * use. + */ + private void addIngestJobEventsListener() { + ingestJobEventsListener = new IngestJobEventPropertyChangeListener(); + IngestManager.getInstance().addIngestJobEventListener(ingestJobEventsListener); } /** @@ -67,31 +98,43 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { private void initComponents() { jPanel1 = new javax.swing.JPanel(); - panelDescriptionLabel = new javax.swing.JLabel(); jScrollPane2 = new javax.swing.JScrollPane(); 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(); + 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(); + descriptionTextArea = new javax.swing.JTextArea(); + isNotableLabel = new javax.swing.JLabel(); + notableYesOrNoLabel = new javax.swing.JLabel(); + ingestRunningWarningLabel = new javax.swing.JLabel(); - jPanel1.setPreferredSize(new java.awt.Dimension(750, 500)); + jPanel1.setPreferredSize(new java.awt.Dimension(750, 490)); - org.openide.awt.Mnemonics.setLocalizedText(panelDescriptionLabel, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.panelDescriptionLabel.text")); // NOI18N + jScrollPane2.setPreferredSize(new java.awt.Dimension(750, 490)); - jSplitPane1.setDividerLocation(400); + jSplitPane1.setDividerLocation(365); jSplitPane1.setDividerSize(1); + jSplitPane1.setPreferredSize(new java.awt.Dimension(748, 488)); 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 + newTagNameButton.setMaximumSize(new java.awt.Dimension(111, 25)); + newTagNameButton.setMinimumSize(new java.awt.Dimension(111, 25)); + newTagNameButton.setPreferredSize(new java.awt.Dimension(111, 25)); newTagNameButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { newTagNameButtonActionPerformed(evt); @@ -100,12 +143,37 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { deleteTagNameButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/delete-tag.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(deleteTagNameButton, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.deleteTagNameButton.text")); // NOI18N + deleteTagNameButton.setMaximumSize(new java.awt.Dimension(111, 25)); + deleteTagNameButton.setMinimumSize(new java.awt.Dimension(111, 25)); + deleteTagNameButton.setPreferredSize(new java.awt.Dimension(111, 25)); deleteTagNameButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { deleteTagNameButtonActionPerformed(evt); } }); + editTagNameButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/edit-tag.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(editTagNameButton, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.editTagNameButton.text")); // NOI18N + editTagNameButton.setMaximumSize(new java.awt.Dimension(111, 25)); + editTagNameButton.setMinimumSize(new java.awt.Dimension(111, 25)); + editTagNameButton.setPreferredSize(new java.awt.Dimension(111, 25)); + editTagNameButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + editTagNameButtonActionPerformed(evt); + } + }); + + 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); modifyTagTypesListPanelLayout.setHorizontalGroup( @@ -113,40 +181,98 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { .addGroup(modifyTagTypesListPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(tagTypesListLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(modifyTagTypesListPanelLayout.createSequentialGroup() - .addComponent(newTagNameButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(deleteTagNameButton) - .addGap(0, 113, Short.MAX_VALUE))) - .addContainerGap()) - ); - modifyTagTypesListPanelLayout.setVerticalGroup( - modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(modifyTagTypesListPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(tagTypesListLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 381, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(newTagNameButton) - .addComponent(deleteTagNameButton)) + .addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .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(panelDescriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 345, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); + modifyTagTypesListPanelLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteTagNameButton, editTagNameButton, newTagNameButton}); + + modifyTagTypesListPanelLayout.setVerticalGroup( + modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(modifyTagTypesListPanelLayout.createSequentialGroup() + .addGap(10, 10, 10) + .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(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) + .addComponent(editTagNameButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(deleteTagNameButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap()) + ); + + modifyTagTypesListPanelLayout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {deleteTagNameButton, editTagNameButton, newTagNameButton}); + jSplitPane1.setLeftComponent(modifyTagTypesListPanel); + org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.descriptionLabel.text")); // NOI18N + + descriptionTextArea.setEditable(false); + descriptionTextArea.setBackground(new java.awt.Color(240, 240, 240)); + descriptionTextArea.setColumns(20); + descriptionTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N + descriptionTextArea.setLineWrap(true); + descriptionTextArea.setRows(5); + descriptionTextArea.setWrapStyleWord(true); + descriptionTextArea.setFocusable(false); + descriptionScrollPane.setViewportView(descriptionTextArea); + + org.openide.awt.Mnemonics.setLocalizedText(isNotableLabel, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.isNotableLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(notableYesOrNoLabel, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.notableYesOrNoLabel.text")); // NOI18N + + ingestRunningWarningLabel.setFont(ingestRunningWarningLabel.getFont().deriveFont(ingestRunningWarningLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + ingestRunningWarningLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/modules/filetypeid/warning16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(ingestRunningWarningLabel, org.openide.util.NbBundle.getMessage(TagOptionsPanel.class, "TagOptionsPanel.ingestRunningWarningLabel.text")); // NOI18N + javax.swing.GroupLayout tagTypesAdditionalPanelLayout = new javax.swing.GroupLayout(tagTypesAdditionalPanel); tagTypesAdditionalPanel.setLayout(tagTypesAdditionalPanelLayout); tagTypesAdditionalPanelLayout.setHorizontalGroup( tagTypesAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 354, Short.MAX_VALUE) + .addGroup(tagTypesAdditionalPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(tagTypesAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(descriptionScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 361, Short.MAX_VALUE) + .addGroup(tagTypesAdditionalPanelLayout.createSequentialGroup() + .addGroup(tagTypesAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(descriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 92, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(tagTypesAdditionalPanelLayout.createSequentialGroup() + .addComponent(isNotableLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(notableYesOrNoLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(ingestRunningWarningLabel)) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) ); tagTypesAdditionalPanelLayout.setVerticalGroup( tagTypesAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 454, Short.MAX_VALUE) + .addGroup(tagTypesAdditionalPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(descriptionLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(descriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(tagTypesAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(isNotableLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(notableYesOrNoLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 304, Short.MAX_VALUE) + .addComponent(ingestRunningWarningLabel) + .addGap(31, 31, 31)) ); jSplitPane1.setRightComponent(tagTypesAdditionalPanel); @@ -158,27 +284,23 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(panelDescriptionLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jScrollPane2)) - .addContainerGap()) + .addGap(0, 0, 0) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(0, 0, 0)) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() - .addComponent(panelDescriptionLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 458, Short.MAX_VALUE) - .addContainerGap()) + .addGap(0, 0, 0) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(0, 0, 0)) ); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 778, Short.MAX_VALUE) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -186,12 +308,15 @@ 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 - NewTagNameDialog dialog = new NewTagNameDialog(); - NewTagNameDialog.BUTTON_PRESSED result = dialog.getResult(); - if (result == NewTagNameDialog.BUTTON_PRESSED.OK) { - String newTagDisplayName = dialog.getTagName(); - TagNameDefiniton newTagType = new TagNameDefiniton(newTagDisplayName, DEFAULT_DESCRIPTION, DEFAULT_COLOR, TskData.FileKnown.UNKNOWN); + TagNameDialog dialog = new TagNameDialog(); + TagNameDialog.BUTTON_PRESSED result = dialog.getResult(); + if (result == TagNameDialog.BUTTON_PRESSED.OK) { + TskData.FileKnown status = dialog.isTagNotable() ? TskData.FileKnown.BAD : TskData.FileKnown.UNKNOWN; + TagNameDefinition newTagType = new TagNameDefinition(dialog.getTagName(), dialog.getTagDesciption(), DEFAULT_COLOR, status); /* * If tag name already exists, don't add the tag name. */ @@ -199,35 +324,66 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { tagTypes.add(newTagType); updateTagNamesListModel(); tagNamesList.setSelectedValue(newTagType, true); - enableButtons(); + updatePanel(); 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); } } }//GEN-LAST:event_newTagNameButtonActionPerformed private void deleteTagNameButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTagNameButtonActionPerformed - TagNameDefiniton tagName = tagNamesList.getSelectedValue(); + TagNameDefinition tagName = tagNamesList.getSelectedValue(); tagTypes.remove(tagName); updateTagNamesListModel(); - enableButtons(); + updatePanel(); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_deleteTagNameButtonActionPerformed + private void editTagNameButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editTagNameButtonActionPerformed + TagNameDefinition originalTagName = tagNamesList.getSelectedValue(); + TagNameDialog dialog = new TagNameDialog(originalTagName); + TagNameDialog.BUTTON_PRESSED result = dialog.getResult(); + if (result == TagNameDialog.BUTTON_PRESSED.OK) { + TskData.FileKnown status = dialog.isTagNotable() ? TskData.FileKnown.BAD : TskData.FileKnown.UNKNOWN; + TagNameDefinition newTagType = new TagNameDefinition(dialog.getTagName(), dialog.getTagDesciption(), DEFAULT_COLOR, status); + /* + * 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; + private javax.swing.JTextArea descriptionTextArea; + private javax.swing.JButton editTagNameButton; + 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.JSplitPane jSplitPane1; private javax.swing.JPanel modifyTagTypesListPanel; private javax.swing.JButton newTagNameButton; - private javax.swing.JLabel panelDescriptionLabel; - private javax.swing.JList tagNamesList; + 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; // End of variables declaration//GEN-END:variables @@ -237,7 +393,7 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { */ private void updateTagNamesListModel() { tagTypesListModel.clear(); - for (TagNameDefiniton tagName : tagTypes) { + for (TagNameDefinition tagName : tagTypes) { tagTypesListModel.addElement(tagName); } } @@ -247,9 +403,9 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { */ @Override public void load() { - tagTypes = new TreeSet<>(TagNameDefiniton.getTagNameDefinitions()); + tagTypes = new TreeSet<>(TagNameDefinition.getTagNameDefinitions()); updateTagNamesListModel(); - enableButtons(); + updatePanel(); } /** @@ -257,19 +413,76 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { */ @Override public void store() { - TagNameDefiniton.setTagNameDefinitions(tagTypes); + 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(); } /** * Enables the button components based on the state of the tag types list * component. */ - private void enableButtons() { + private void updatePanel() { + boolean ingestIsRunning = IngestManager.getInstance().isIngestRunning(); /* * Only enable the delete button when there is a tag type selected in * the tag types JList. */ - deleteTagNameButton.setEnabled(tagNamesList.getSelectedIndex() != -1); + ingestRunningWarningLabel.setVisible(ingestIsRunning); + boolean isSelected = tagNamesList.getSelectedIndex() != -1; + boolean enableEdit = !ingestIsRunning && isSelected; + editTagNameButton.setEnabled(enableEdit); + boolean enableDelete = enableEdit && !TagNameDefinition.getStandardTagNames().contains(tagNamesList.getSelectedValue().getDisplayName()); + deleteTagNameButton.setEnabled(enableDelete); + if (isSelected) { + descriptionTextArea.setText(tagNamesList.getSelectedValue().getDescription()); + if (tagNamesList.getSelectedValue().getKnownStatus() == TskData.FileKnown.BAD) { + notableYesOrNoLabel.setText("Yes"); + } else { + notableYesOrNoLabel.setText("No"); + } + } else { + descriptionTextArea.setText(""); + notableYesOrNoLabel.setText(""); + } } + /** + * @inheritDoc + */ + @Override + @SuppressWarnings("FinalizeDeclaration") + protected void finalize() throws Throwable { + IngestManager.getInstance().removeIngestJobEventListener(ingestJobEventsListener); + super.finalize(); + } + + /** + * A property change listener that listens to ingest job events. + */ + private class IngestJobEventPropertyChangeListener implements PropertyChangeListener { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + updatePanel(); + } + }); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java index adfcbf0029..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 @@ -83,7 +93,7 @@ public class TagsManager implements Closeable { */ public static Set getTagDisplayNames() throws TskCoreException { Set tagDisplayNames = new HashSet<>(); - Set customNames = TagNameDefiniton.getTagNameDefinitions(); + Set customNames = TagNameDefinition.getTagNameDefinitions(); customNames.forEach((tagType) -> { tagDisplayNames.add(tagType.getDisplayName()); }); @@ -102,8 +112,8 @@ public class TagsManager implements Closeable { public static List getNotableTagDisplayNames() { List tagDisplayNames = new ArrayList<>(); - for (TagNameDefiniton tagDef : TagNameDefiniton.getTagNameDefinitions()) { - if (tagDef.isNotable()) { + for (TagNameDefinition tagDef : TagNameDefinition.getTagNameDefinitions()) { + if (tagDef.getKnownStatus() == TskData.FileKnown.BAD) { tagDisplayNames.add(tagDef.getDisplayName()); } } @@ -170,8 +180,8 @@ public class TagsManager implements Closeable { * map. */ Map tagNames = new HashMap<>(); - Set customTypes = TagNameDefiniton.getTagNameDefinitions(); - for (TagNameDefiniton tagType : customTypes) { + Set customTypes = TagNameDefinition.getTagNameDefinitions(); + for (TagNameDefinition tagType : customTypes) { tagNames.put(tagType.getDisplayName(), null); } for (TagName tagName : caseDb.getAllTagNames()) { @@ -256,9 +266,9 @@ public class TagsManager implements Closeable { public synchronized TagName addTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TagNameAlreadyExistsException, TskCoreException { try { TagName tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatus); - Set customTypes = TagNameDefiniton.getTagNameDefinitions(); - customTypes.add(new TagNameDefiniton(displayName, description, color, knownStatus)); - TagNameDefiniton.setTagNameDefinitions(customTypes); + Set customTypes = TagNameDefinition.getTagNameDefinitions(); + customTypes.add(new TagNameDefinition(displayName, description, color, knownStatus)); + TagNameDefinition.setTagNameDefinitions(customTypes); return tagName; } catch (TskCoreException ex) { List existingTagNames = caseDb.getAllTagNames(); 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/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 2a7fcf168b..fbc955ca72 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -177,7 +177,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D } caseDisplayName = eamCasePartial.getDisplayName(); // query case details - CorrelationCase eamCase = dbManager.getCaseByUUID(eamCasePartial.getCaseUUID()); + CorrelationCase eamCase = dbManager.getCase(Case.getCurrentCase()); if (eamCase == null) { JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails(), diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index f9c2435472..9560d635c4 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -170,9 +170,10 @@ public abstract class AbstractSqlEamDb implements EamDb { * Expects the Organization for this case to already exist in the database. * * @param eamCase The case to add + * @returns New Case class with populated database ID */ @Override - public void newCase(CorrelationCase eamCase) throws EamDbException { + public CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException { Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -225,6 +226,9 @@ public abstract class AbstractSqlEamDb implements EamDb { EamDbUtil.closePreparedStatement(preparedStatement); EamDbUtil.closeConnection(conn); } + + // get a new version with the updated ID + return getCaseByUUID(eamCase.getCaseUUID()); } /** @@ -249,9 +253,14 @@ public abstract class AbstractSqlEamDb implements EamDb { autopsyCase.getExaminerEmail(), autopsyCase.getExaminerPhone(), autopsyCase.getCaseNotes()); - newCase(curCeCase); - return curCeCase; + return newCase(curCeCase); } + + @Override + public CorrelationCase getCase(Case autopsyCase) throws EamDbException { + return getCaseByUUID(autopsyCase.getName()); + } + /** * Updates an existing Case in the database @@ -432,7 +441,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * @return The data source */ @Override - public CorrelationDataSource getDataSourceDetails(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException { + public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException { Connection conn = connect(); CorrelationDataSource eamDataSourceResult = null; @@ -450,7 +459,7 @@ public abstract class AbstractSqlEamDb implements EamDb { eamDataSourceResult = getEamDataSourceFromResultSet(resultSet); } } catch (SQLException ex) { - throw new EamDbException("Error getting case details.", ex); // NON-NLS + throw new EamDbException("Error getting data source.", ex); // NON-NLS } finally { EamDbUtil.closePreparedStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); @@ -1057,12 +1066,12 @@ public abstract class AbstractSqlEamDb implements EamDb { // We could improve effiency by keeping a list of all datasources and cases // in the database, but we don't expect the user to be tagging large numbers // of items (that didn't have the CE ingest module run on them) at once. - CorrelationCase correlationCase = getCaseByUUID(eamInstance.getCorrelationCase().getCaseUUID()); - if (null == correlationCase) { - newCase(eamInstance.getCorrelationCase()); - correlationCase = getCaseByUUID(eamInstance.getCorrelationCase().getCaseUUID()); + CorrelationCase correlationCaseWithId = getCaseByUUID(eamInstance.getCorrelationCase().getCaseUUID()); + if (null == correlationCaseWithId) { + correlationCaseWithId = newCase(eamInstance.getCorrelationCase()); } - if (null == getDataSourceDetails(correlationCase, eamInstance.getCorrelationDataSource().getDeviceID())) { + + if (null == getDataSource(correlationCaseWithId, eamInstance.getCorrelationDataSource().getDeviceID())) { newDataSource(eamInstance.getCorrelationDataSource()); } eamArtifact.getInstances().get(0).setKnownStatus(knownStatus); @@ -2146,7 +2155,7 @@ public abstract class AbstractSqlEamDb implements EamDb { return null; } CorrelationAttributeInstance eamArtifactInstance = new CorrelationAttributeInstance( - new CorrelationCase(resultSet.getString("case_uid"), resultSet.getString("case_name")), + new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), resultSet.getString("case_name")), new CorrelationDataSource(-1, resultSet.getInt("case_id"), resultSet.getString("device_id"), resultSet.getString("name")), resultSet.getString("file_path"), resultSet.getString("comment"), diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationCase.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationCase.java index 466c73a7ee..79d94837ee 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationCase.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationCase.java @@ -49,16 +49,12 @@ public class CorrelationCase implements Serializable { * * @param caseUUID Globally unique identifier * @param displayName - */ - public CorrelationCase(String caseUUID, String displayName) { - this(-1, caseUUID, null, displayName, DATE_FORMAT.format(new Date()), null, null, null, null, null); - } - + */ CorrelationCase(int ID, String caseUUID, String displayName) { this(ID, caseUUID, null, displayName, DATE_FORMAT.format(new Date()), null, null, null, null, null); } - public CorrelationCase(int ID, + CorrelationCase(int ID, String caseUUID, EamOrganization org, String displayName, diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java index b3194e4b7f..c2bb0e0016 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java @@ -96,10 +96,9 @@ public class EamArtifactUtil { } // make an instance for the BB source file - CorrelationCase correlationCase = EamDb.getInstance().getCaseByUUID(Case.getCurrentCase().getName()); + CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCase()); if (null == correlationCase) { - EamDb.getInstance().newCase(Case.getCurrentCase()); - correlationCase = EamDb.getInstance().getCaseByUUID(Case.getCurrentCase().getName()); + correlationCase = EamDb.getInstance().newCase(Case.getCurrentCase()); } CorrelationAttributeInstance eamInstance = new CorrelationAttributeInstance( correlationCase, @@ -250,10 +249,9 @@ public class EamArtifactUtil { try { CorrelationAttribute.Type filesType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); eamArtifact = new CorrelationAttribute(filesType, af.getMd5Hash()); - CorrelationCase correlationCase = EamDb.getInstance().getCaseByUUID(Case.getCurrentCase().getName()); + CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCase()); if (null == correlationCase) { - EamDb.getInstance().newCase(Case.getCurrentCase()); - correlationCase = EamDb.getInstance().getCaseByUUID(Case.getCurrentCase().getName()); + correlationCase = EamDb.getInstance().newCase(Case.getCurrentCase()); } CorrelationAttributeInstance cei = new CorrelationAttributeInstance( correlationCase, diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java index 5f2cdfa816..b9ce4320de 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java @@ -94,14 +94,6 @@ public interface EamDb { return EamDbUtil.useCentralRepo() && EamDbPlatformEnum.getSelectedPlatform() != EamDbPlatformEnum.DISABLED; } - - /** - * Placeholder version to use for non-read only databases - * @return The version that will be stored in the database - */ - static String getDefaultVersion() { - return ""; - } /** * Add a new name/value pair in the db_info table. @@ -141,7 +133,7 @@ public interface EamDb { * * @param eamCase The case to add */ - void newCase(CorrelationCase eamCase) throws EamDbException; + CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException; /** * Creates new Case in the database from the given case @@ -149,6 +141,8 @@ public interface EamDb { * @param autopsyCase The case to add */ CorrelationCase newCase(Case autopsyCase) throws EamDbException; + + /** * Updates an existing Case in the database @@ -157,6 +151,15 @@ public interface EamDb { */ void updateCase(CorrelationCase eamCase) throws EamDbException; + /** + * Retrieves Central Repo case based on an Autopsy Case + * + * @param autopsyCase Autopsy case to find corresponding CR case for + * @return CR Case + * @throws EamDbException + */ + CorrelationCase getCase(Case autopsyCase) throws EamDbException; + /** * Retrieves Case details based on Case UUID * @@ -189,7 +192,7 @@ public interface EamDb { * * @return The data source */ - CorrelationDataSource getDataSourceDetails(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException; + CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException; /** * Retrieves data sources that are in DB diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java index 5d70d6fdac..49f49f9d38 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java @@ -37,6 +37,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. @@ -175,11 +176,51 @@ public class EamDbUtil { return true; } + /** + * 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. * * @return true if the Central Repo may be configured, false if it should - * not be able to be + * not be able to be */ public static boolean useCentralRepo() { return Boolean.parseBoolean(ModuleSettings.getConfigSetting(CENTRAL_REPO_NAME, CENTRAL_REPO_USE_KEY)); @@ -190,7 +231,7 @@ public class EamDbUtil { * configured. * * @param centralRepoCheckBoxIsSelected - true if the central repo can be - * used + * used */ public static void setUseCentralRepo(boolean centralRepoCheckBoxIsSelected) { ModuleSettings.setConfigSetting(CENTRAL_REPO_NAME, CENTRAL_REPO_USE_KEY, Boolean.toString(centralRepoCheckBoxIsSelected)); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java index bfb3f04b32..324cc2ac7d 100755 --- 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.insertSchemaVersion(conn); + && EamDbUtil.insertSchemaVersion(conn) + && EamDbUtil.insertDefaultOrganization(conn); EamDbUtil.closeConnection(conn); return result; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java index 3e79f5abca..85fda8d4a1 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java @@ -149,16 +149,14 @@ public class SqliteEamDb extends AbstractSqlEamDb { * */ private void setupConnectionPool() throws EamDbException { + + if (dbSettings.dbFileExists() == false) { + throw new EamDbException("Central repository database missing"); + } + connectionPool = new BasicDataSource(); connectionPool.setDriverClassName(dbSettings.getDriver()); - - StringBuilder connectionURL = new StringBuilder(); - connectionURL.append(dbSettings.getJDBCBaseURI()); - connectionURL.append(dbSettings.getDbDirectory()); - connectionURL.append(File.separator); - connectionURL.append(dbSettings.getDbName()); - - connectionPool.setUrl(connectionURL.toString()); + connectionPool.setUrl(dbSettings.getConnectionURL()); // tweak pool configuration connectionPool.setInitialSize(50); @@ -279,10 +277,10 @@ public class SqliteEamDb extends AbstractSqlEamDb { * @param eamCase The case to add */ @Override - public void newCase(CorrelationCase eamCase) throws EamDbException { + public CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException { try{ acquireExclusiveLock(); - super.newCase(eamCase); + return super.newCase(eamCase); } finally { releaseExclusiveLock(); } @@ -359,10 +357,10 @@ public class SqliteEamDb extends AbstractSqlEamDb { * @return The data source */ @Override - public CorrelationDataSource getDataSourceDetails(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException { + public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException { try{ acquireSharedLock(); - return super.getDataSourceDetails(correlationCase, dataSourceDeviceId); + return super.getDataSource(correlationCase, dataSourceDeviceId); } finally { releaseSharedLock(); } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java index b4ea1aa8a2..c7a3730e46 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java @@ -434,7 +434,8 @@ public final class SqliteEamDbSettings { } boolean result = EamDbUtil.insertDefaultCorrelationTypes(conn) - && EamDbUtil.insertSchemaVersion(conn); + && EamDbUtil.insertSchemaVersion(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..88cf1ed59e 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; @@ -315,12 +430,11 @@ final class CaseEventListener implements PropertyChangeListener { try { String deviceId = Case.getCurrentCase().getSleuthkitCase().getDataSource(newDataSource.getId()).getDeviceId(); - CorrelationCase correlationCase = dbManager.getCaseByUUID(Case.getCurrentCase().getName()); + CorrelationCase correlationCase = dbManager.getCase(Case.getCurrentCase()); if (null == correlationCase) { - dbManager.newCase(Case.getCurrentCase()); - correlationCase = dbManager.getCaseByUUID(Case.getCurrentCase().getName()); + correlationCase = dbManager.newCase(Case.getCurrentCase()); } - if (null == dbManager.getDataSourceDetails(correlationCase, deviceId)) { + if (null == dbManager.getDataSource(correlationCase, deviceId)) { dbManager.newDataSource(CorrelationDataSource.fromTSKDataSource(correlationCase, newDataSource)); } } catch (EamDbException ex) { @@ -350,18 +464,6 @@ 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 - EamOrganization.getDefault(), - curCase.getDisplayName(), - curCase.getCreatedDate(), - curCase.getNumber(), - curCase.getExaminer(), - curCase.getExaminerEmail(), - curCase.getExaminerPhone(), - curCase.getCaseNotes()); if (!EamDb.isEnabled()) { return; @@ -370,10 +472,8 @@ final class CaseEventListener implements PropertyChangeListener { try { // NOTE: Cannot determine if the opened case is a new case or a reopened case, // so check for existing name in DB and insert if missing. - CorrelationCase existingCase = dbManager.getCaseByUUID(curCeCase.getCaseUUID()); - - if (null == existingCase) { - dbManager.newCase(curCeCase); + if (dbManager.getCase(curCase) == null) { + dbManager.newCase(curCase); } } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index 02d64904a4..0877bc1685 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -130,17 +130,18 @@ public class IngestEventsListener { try { AbstractFile af = bbArtifact.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID()); - + Collection attributes = new ArrayList<>(); String MODULE_NAME = Bundle.IngestEventsListener_ingestmodule_name(); BlackboardArtifact tifArtifact = af.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, Bundle.IngestEventsListener_prevTaggedSet_text()); BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, Bundle.IngestEventsListener_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", ""))); - tifArtifact.addAttribute(att); - tifArtifact.addAttribute(att2); - tifArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, bbArtifact.getArtifactID())); + attributes.add(att); + attributes.add(att2); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, bbArtifact.getArtifactID())); + tifArtifact.addAttributes(attributes); try { // index the artifact for keyword search Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java index 30d374f133..9e1e36cb8a 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java @@ -201,47 +201,36 @@ class IngestModule implements FileIngestModule { } jobId = context.getJobId(); - EamDb dbManager; + EamDb centralRepoDb; try { - dbManager = EamDb.getInstance(); + centralRepoDb = EamDb.getInstance(); } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error connecting to central repository database.", ex); // NON-NLS throw new IngestModuleException("Error connecting to central repository database.", ex); // NON-NLS } try { - filesType = dbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + filesType = centralRepoDb.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS throw new IngestModuleException("Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS } - Case curCase = Case.getCurrentCase(); + Case autopsyCase = Case.getCurrentCase(); try { - eamCase = dbManager.getCaseByUUID(curCase.getName()); + eamCase = centralRepoDb.getCase(autopsyCase); } catch (EamDbException ex) { throw new IngestModuleException("Unable to get case from central repository database ", ex); } if (eamCase == null) { // ensure we have this case defined in the EAM DB - CorrelationCase curCeCase = new CorrelationCase( - -1, - curCase.getName(), // unique case ID - EamOrganization.getDefault(), - curCase.getDisplayName(), - curCase.getCreatedDate(), - curCase.getNumber(), - curCase.getExaminer(), - curCase.getExaminerEmail(), - curCase.getExaminerPhone(), - curCase.getCaseNotes()); try { - dbManager.newCase(curCeCase); - eamCase = dbManager.getCaseByUUID(curCase.getName()); + eamCase = centralRepoDb.newCase(autopsyCase); } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error creating new case in ingest module start up.", ex); // NON-NLS throw new IngestModuleException("Error creating new case in ingest module start up.", ex); // NON-NLS } } + try { eamDataSource = CorrelationDataSource.fromTSKDataSource(eamCase, context.getDataSource()); } catch (EamDbException ex) { @@ -255,12 +244,12 @@ class IngestModule implements FileIngestModule { == 1) { // ensure we have this data source in the EAM DB try { - if (null == dbManager.getDataSourceDetails(eamCase, eamDataSource.getDeviceID())) { - dbManager.newDataSource(eamDataSource); + if (null == centralRepoDb.getDataSource(eamCase, eamDataSource.getDeviceID())) { + centralRepoDb.newDataSource(eamDataSource); } } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error creating new data source in ingest module start up.", ex); // NON-NLS - throw new IngestModuleException("Error creating new data source in ingest module start up.", ex); // NON-NLS + LOGGER.log(Level.SEVERE, "Error adding data source to Central Repository.", ex); // NON-NLS + throw new IngestModuleException("Error adding data source to Central Repository.", ex); // NON-NLS } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties old mode 100644 new mode 100755 index b9ab823b1d..72ecac2866 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties @@ -21,9 +21,6 @@ ImportHashDatabaseDialog.tfDatabaseName.tooltip=Name for this database ImportHashDatabaseDialog.tfDatabaseVersion.tooltip.text=Database Version Number ImportHashDatabaseDialog.tfDatabaseName.tooltip=Name for this database ImportHashDatabaseDialog.tfDatabaseVersion.tooltip.text=Database Version Number -GlobalSettingsPanel.bnManageTags.actionCommand= -GlobalSettingsPanel.bnManageTags.toolTipText= -GlobalSettingsPanel.bnManageTags.text=Manage Tags GlobalSettingsPanel.tbOops.text= GlobalSettingsPanel.lbDatabaseSettings.text=Database Settings GlobalSettingsPanel.bnImportDatabase.label=Import Hash Database diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form index 731c177818..53f65a4e6c 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form @@ -28,7 +28,6 @@ - @@ -47,14 +46,12 @@ - - - + @@ -174,109 +171,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java index 37d18036d8..8b6ce90e96 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java @@ -96,10 +96,6 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i lbDbNameValue = new javax.swing.JLabel(); lbDbLocationValue = new javax.swing.JLabel(); cbUseCentralRepo = new javax.swing.JCheckBox(); - pnTagManagement = new javax.swing.JPanel(); - bnManageTags = new javax.swing.JButton(); - manageTagsScrollPane = new javax.swing.JScrollPane(); - manageTagsTextArea = new javax.swing.JTextArea(); tbOops = new javax.swing.JTextField(); pnCorrelationProperties = new javax.swing.JPanel(); bnManageTypes = new javax.swing.JButton(); @@ -176,56 +172,6 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i } }); - pnTagManagement.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.pnTagManagement.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N - pnTagManagement.setPreferredSize(new java.awt.Dimension(674, 97)); - - org.openide.awt.Mnemonics.setLocalizedText(bnManageTags, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.bnManageTags.text")); // NOI18N - bnManageTags.setToolTipText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.bnManageTags.toolTipText")); // NOI18N - bnManageTags.setActionCommand(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.bnManageTags.actionCommand")); // NOI18N - bnManageTags.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - bnManageTagsActionPerformed(evt); - } - }); - - manageTagsScrollPane.setBorder(null); - - manageTagsTextArea.setEditable(false); - manageTagsTextArea.setBackground(new java.awt.Color(240, 240, 240)); - manageTagsTextArea.setColumns(20); - manageTagsTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N - manageTagsTextArea.setLineWrap(true); - manageTagsTextArea.setRows(2); - manageTagsTextArea.setText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.manageTagsTextArea.text")); // NOI18N - manageTagsTextArea.setToolTipText(""); - manageTagsTextArea.setWrapStyleWord(true); - manageTagsTextArea.setBorder(null); - manageTagsScrollPane.setViewportView(manageTagsTextArea); - - javax.swing.GroupLayout pnTagManagementLayout = new javax.swing.GroupLayout(pnTagManagement); - pnTagManagement.setLayout(pnTagManagementLayout); - pnTagManagementLayout.setHorizontalGroup( - pnTagManagementLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(pnTagManagementLayout.createSequentialGroup() - .addContainerGap() - .addGroup(pnTagManagementLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(pnTagManagementLayout.createSequentialGroup() - .addComponent(bnManageTags) - .addGap(0, 555, Short.MAX_VALUE)) - .addGroup(pnTagManagementLayout.createSequentialGroup() - .addComponent(manageTagsScrollPane) - .addContainerGap()))) - ); - pnTagManagementLayout.setVerticalGroup( - pnTagManagementLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(pnTagManagementLayout.createSequentialGroup() - .addGap(7, 7, 7) - .addComponent(manageTagsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(bnManageTags, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(8, 8, 8)) - ); - tbOops.setEditable(false); tbOops.setFont(tbOops.getFont().deriveFont(tbOops.getFont().getStyle() | java.awt.Font.BOLD, 12)); tbOops.setText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.tbOops.text")); // NOI18N @@ -338,7 +284,6 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addComponent(organizationPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lbCentralRepository, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(pnTagManagement, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(cbUseCentralRepo, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 186, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap()))) @@ -352,25 +297,15 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.PREFERRED_SIZE, 119, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) - .addComponent(pnTagManagement, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addComponent(organizationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(36, 36, 36)) + .addContainerGap()) ); - - pnTagManagement.getAccessibleContext().setAccessibleName(""); }// //GEN-END:initComponents - private void bnManageTagsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnManageTagsActionPerformed - store(); - ManageTagsDialog dialog = new ManageTagsDialog(); - firePropertyChange(OptionsPanelController.PROP_VALID, null, null); - }//GEN-LAST:event_bnManageTagsActionPerformed - private void bnManageTypesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnManageTypesActionPerformed store(); ManageCorrelationPropertiesDialog dialog = new ManageCorrelationPropertiesDialog(); @@ -543,10 +478,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i private boolean enableButtonSubComponents(Boolean enable) { boolean ingestRunning = IngestManager.getInstance().isIngestRunning(); pnCorrelationProperties.setEnabled(enable && !ingestRunning); - pnTagManagement.setEnabled(enable && !ingestRunning); bnManageTypes.setEnabled(enable && !ingestRunning); - bnManageTags.setEnabled(enable && !ingestRunning); - manageTagsTextArea.setEnabled(enable && !ingestRunning); correlationPropertiesTextArea.setEnabled(enable && !ingestRunning); organizationPanel.setEnabled(enable && !ingestRunning); organizationTextArea.setEnabled(enable && !ingestRunning); @@ -556,7 +488,6 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton bnDbConfigure; - private javax.swing.JButton bnManageTags; private javax.swing.JButton bnManageTypes; private javax.swing.JCheckBox cbUseCentralRepo; private javax.swing.JScrollPane correlationPropertiesScrollPane; @@ -569,14 +500,11 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i private javax.swing.JLabel lbDbPlatformTypeLabel; private javax.swing.JLabel lbDbPlatformValue; private javax.swing.JButton manageOrganizationButton; - private javax.swing.JScrollPane manageTagsScrollPane; - private javax.swing.JTextArea manageTagsTextArea; private javax.swing.JPanel organizationPanel; private javax.swing.JScrollPane organizationScrollPane; private javax.swing.JTextArea organizationTextArea; private javax.swing.JPanel pnCorrelationProperties; private javax.swing.JPanel pnDatabaseConfiguration; - private javax.swing.JPanel pnTagManagement; private javax.swing.JTextField tbOops; // End of variables declaration//GEN-END:variables } 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/centralrepository/optionspanel/ManageTagsDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.form deleted file mode 100755 index f1ad5a38e4..0000000000 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.form +++ /dev/null @@ -1,147 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.java deleted file mode 100755 index ff179c3032..0000000000 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageTagsDialog.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Central Repository - * - * Copyright 2015-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.centralrepository.optionspanel; - -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Toolkit; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import javax.swing.JFrame; -import javax.swing.table.DefaultTableModel; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; -import javax.swing.JOptionPane; -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.centralrepository.datamodel.EamDb; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.BlackboardArtifactTag; -import org.sleuthkit.datamodel.TagName; -import org.sleuthkit.datamodel.ContentTag; -import org.sleuthkit.datamodel.TskData; - -/** - * Instances of this class allow a user to select an existing hash database and - * add it to the set of hash databases used to classify files as unknown, known, - * or notable. - */ -final class ManageTagsDialog extends javax.swing.JDialog { - - private static final Logger LOGGER = Logger.getLogger(ManageTagsDialog.class.getName()); - - /** - * Displays a dialog that allows a user to select an existing hash database - * and add it to the set of hash databases used to classify files as - * unknown, known, or notable. - */ - @Messages({"ManageTagDialog.title=Manage Tags", - "ManageTagDialog.tagInfo.text=Select the tags that cause files and results to be recorded in the central repository. Additional tags can be created in the Tags options panel."}) - ManageTagsDialog() { - super((JFrame) WindowManager.getDefault().getMainWindow(), - Bundle.ManageTagDialog_title(), - true); // NON-NLS - initComponents(); - customizeComponents(); - setupHelpTextArea(); - display(); - } - - - @Messages({"ManageTagsDialog.init.failedConnection.msg=Cannot connect to central cepository.", - "ManageTagsDialog.init.failedGettingTags.msg=Unable to retrieve list of tags.", - "ManageTagsDialog.tagColumn.header.text=Tags", - "ManageTagsDialog.notableColumn.header.text=Notable"}) - private void setupHelpTextArea() { - helpTextArea.setText(Bundle.ManageTagDialog_tagInfo_text()); - } - - private void customizeComponents() { - lbWarnings.setText(""); - EamDb dbManager; - try { - dbManager = EamDb.getInstance(); - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Failed to connect to central repository database."); - lbWarnings.setText(Bundle.ManageTagsDialog_init_failedConnection_msg()); - return; - } - List badTags = TagsManager.getNotableTagDisplayNames(); - - List tagNames = new ArrayList<>(); - try { - tagNames.addAll(TagsManager.getTagDisplayNames()); - } catch (TskCoreException ex) { - LOGGER.log(Level.WARNING, "Could not get list of tags in case", ex); - lbWarnings.setText(Bundle.ManageTagsDialog_init_failedGettingTags_msg()); - } - - Collections.sort(tagNames); - - DefaultTableModel model = (DefaultTableModel) tblTagNames.getModel(); - model.setColumnIdentifiers(new String[] {Bundle.ManageTagsDialog_tagColumn_header_text(), Bundle.ManageTagsDialog_notableColumn_header_text()}); - for (String tagName : tagNames) { - boolean enabled = badTags.contains(tagName); - model.addRow(new Object[]{tagName, enabled}); - } - CheckBoxModelListener listener = new CheckBoxModelListener(this); - model.addTableModelListener(listener); - } - - private void display() { - Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); - setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); - 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 - * regenerated by the Form Editor. - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - // //GEN-BEGIN:initComponents - private void initComponents() { - - buttonGroup1 = new javax.swing.ButtonGroup(); - okButton = new javax.swing.JButton(); - cancelButton = new javax.swing.JButton(); - tagScrollArea = new javax.swing.JScrollPane(); - tblTagNames = new javax.swing.JTable(); - lbWarnings = new javax.swing.JLabel(); - helpScrollPane = new javax.swing.JScrollPane(); - helpTextArea = new javax.swing.JTextArea(); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - - org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(ManageTagsDialog.class, "ManageTagsDialog.okButton.text")); // NOI18N - okButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - okButtonActionPerformed(evt); - } - }); - - org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(ManageTagsDialog.class, "ManageTagsDialog.cancelButton.text")); // NOI18N - cancelButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cancelButtonActionPerformed(evt); - } - }); - - tblTagNames.setModel(new javax.swing.table.DefaultTableModel( - new Object [][] { - - }, - new String [] { - "", "" - } - ) { - Class[] types = new Class [] { - java.lang.Object.class, java.lang.Boolean.class - }; - boolean[] canEdit = new boolean [] { - false, true - }; - - public Class getColumnClass(int columnIndex) { - return types [columnIndex]; - } - - public boolean isCellEditable(int rowIndex, int columnIndex) { - return canEdit [columnIndex]; - } - }); - tagScrollArea.setViewportView(tblTagNames); - - helpScrollPane.setBorder(null); - - helpTextArea.setEditable(false); - helpTextArea.setBackground(new java.awt.Color(240, 240, 240)); - helpTextArea.setColumns(20); - helpTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N - helpTextArea.setLineWrap(true); - helpTextArea.setRows(3); - helpTextArea.setWrapStyleWord(true); - helpTextArea.setBorder(null); - helpTextArea.setFocusable(false); - helpScrollPane.setViewportView(helpTextArea); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addComponent(okButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cancelButton)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(helpScrollPane, javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(tagScrollArea, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 328, Short.MAX_VALUE) - .addComponent(lbWarnings, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGap(2, 2, 2))) - .addContainerGap()) - ); - - 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() - .addContainerGap() - .addComponent(helpScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 42, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) - .addComponent(tagScrollArea, javax.swing.GroupLayout.DEFAULT_SIZE, 341, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lbWarnings, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(okButton) - .addComponent(cancelButton)) - .addContainerGap()) - ); - - pack(); - }// //GEN-END:initComponents - - private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed - dispose(); - }//GEN-LAST:event_cancelButtonActionPerformed - - - private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed - if (setBadTags()) { - dispose(); - } - }//GEN-LAST:event_okButtonActionPerformed - - private boolean setBadTags() { - List badTags = new ArrayList<>(); - - DefaultTableModel model = (DefaultTableModel) tblTagNames.getModel(); - for (int i = 0; i < model.getRowCount(); ++i) { - String tagName = (String) model.getValueAt(i, 0); - boolean enabled = (boolean) model.getValueAt(i, 1); - - if (enabled) { - badTags.add(tagName); - } - } - try { - EamDb dbManager = EamDb.getInstance(); - dbManager.saveSettings(); - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Failed to connect to central repository database."); // NON-NLS - lbWarnings.setText(Bundle.ManageTagsDialog_init_failedConnection_msg()); - return false; - } - return true; - } - - /** - * If the user sets a tag to "Notable", give them the option to update - * any existing tagged items (in the current case only) in the central repo. - */ - public class CheckBoxModelListener implements TableModelListener { - @Messages({"ManageTagsDialog.updateCurrentCase.msg=Mark as notable any files/results in the current case that have this tag?", - "ManageTagsDialog.updateCurrentCase.title=Update current case?", - "ManageTagsDialog.updateCurrentCase.error=Error updating existing central repository entries"}) - - javax.swing.JDialog dialog; - public CheckBoxModelListener(javax.swing.JDialog dialog){ - this.dialog = dialog; - } - - @Override - public void tableChanged(TableModelEvent e) { - int row = e.getFirstRow(); - int column = e.getColumn(); - if (column == 1) { - DefaultTableModel model = (DefaultTableModel) e.getSource(); - String tagName = (String) model.getValueAt(row, 0); - Boolean checked = (Boolean) model.getValueAt(row, column); - if (checked) { - - // Don't do anything if there's no case open - if(Case.isCaseOpen()){ - int dialogButton = JOptionPane.YES_NO_OPTION; - int dialogResult = JOptionPane.showConfirmDialog ( - null, - Bundle.ManageTagsDialog_updateCurrentCase_msg(), - Bundle.ManageTagsDialog_updateCurrentCase_title(), - dialogButton); - if(dialogResult == JOptionPane.YES_OPTION){ - try{ - dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - setArtifactsKnownBadByTag(tagName, Case.getCurrentCase()); - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Failed to apply notable status to artifacts in current case", ex); - JOptionPane.showMessageDialog(null, Bundle.ManageTagsDialog_updateCurrentCase_error()); - } finally { - dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - } - } - } - } - } - } - - /** - * Set knownBad status for all files/artifacts in the given case that - * are tagged with the given tag name. - * Files/artifacts that are not already in the database will be added. - * @param tagName The name of the tag to search for - * @param curCase The case to search in - */ - public void setArtifactsKnownBadByTag(String tagNameString, Case curCase) throws EamDbException{ - try{ - TagName tagName = curCase.getServices().getTagsManager().getDisplayNamesToTagNamesMap().get(tagNameString); - - // First find any matching artifacts - List artifactTags = curCase.getSleuthkitCase().getBlackboardArtifactTagsByTagName(tagName); - - for(BlackboardArtifactTag bbTag:artifactTags){ - List convertedArtifacts = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbTag.getArtifact(), true, true); - for (CorrelationAttribute eamArtifact : convertedArtifacts) { - EamDb.getInstance().setArtifactInstanceKnownStatus(eamArtifact,TskData.FileKnown.BAD); - } - } - - // Now search for files - List fileTags = curCase.getSleuthkitCase().getContentTagsByTagName(tagName); - for(ContentTag contentTag:fileTags){ - final CorrelationAttribute eamArtifact = EamArtifactUtil.getEamArtifactFromContent(contentTag.getContent(), - TskData.FileKnown.BAD, ""); - if(eamArtifact != null){ - EamDb.getInstance().setArtifactInstanceKnownStatus(eamArtifact, TskData.FileKnown.BAD); - } - } - } catch (TskCoreException ex){ - throw new EamDbException("Error updating artifacts", ex); - } - - } - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.ButtonGroup buttonGroup1; - private javax.swing.JButton cancelButton; - private javax.swing.JScrollPane helpScrollPane; - private javax.swing.JTextArea helpTextArea; - private javax.swing.JLabel lbWarnings; - private javax.swing.JButton okButton; - private javax.swing.JScrollPane tagScrollArea; - private javax.swing.JTable tblTagNames; - // End of variables declaration//GEN-END:variables -} diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form index 06607d7d9b..00881c2ca7 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form @@ -22,12 +22,15 @@ - + + + + - + @@ -47,246 +50,475 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
- + - - - - - - - - + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java index 4f95087f66..a62ae4400c 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java @@ -18,28 +18,259 @@ */ package org.sleuthkit.autopsy.corecomponents; +import java.awt.image.BufferedImage; import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.StringJoiner; +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; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.coreutils.Version; 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", + "AutopsyOptionsPanel.restartNecessaryWarning.text=A restart is necessary for any changes to max memory to take effect.", + "AutopsyOptionsPanel.totalMemoryLabel.text=Total System Memory:", + "AutopsyOptionsPanel.maxMemoryLabel.text=Maximum JVM Memory:", + "AutopsyOptionsPanel.maxMemoryUnitsLabel.text=GB", + "AutopsyOptionsPanel.runtimePanel.border.title=Runtime", + "AutopsyOptionsPanel.invalidReasonLabel.not64BitInstall.text=JVM memory settings only enabled for 64 bit version", + "AutopsyOptionsPanel.invalidReasonLabel.noValueEntered.text=No value entered", + "AutopsyOptionsPanel.invalidReasonLabel.invalidCharacters.text=Invalid characters, value must be a positive integer", + "# {0} - minimumMemory", + "AutopsyOptionsPanel.invalidReasonLabel.underMinMemory.text=Value must be at least {0}GB", + "# {0} - systemMemory", + "AutopsyOptionsPanel.invalidReasonLabel.overMaxMemory.text=Value must be less than the total system memory of {0}GB", + "AutopsyOptionsPanel.invalidReasonLabel.developerMode.text=Memory settings are not available while running in developer mode"}) + final class AutopsyOptionsPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; private final JFileChooser fc; + private static final String ETC_FOLDER_NAME = "etc"; + private static final String CONFIG_FILE_EXTENSION = ".conf"; + private static final long ONE_BILLION = 1000000000L; //used to roughly convert system memory from bytes to gigabytes + private static final long MEGA_IN_GIGA = 1024; //used to convert memory settings saved as megabytes to gigabytes + private static final int MIN_MEMORY_IN_GB = 2; //the enforced minimum memory in gigabytes + private static final Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName()); + private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION); 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)); + if (!PlatformUtil.is64BitJVM() || Version.getBuildType() == Version.Type.DEVELOPMENT) { + //32 bit JVM has a max heap size of 1.4 gb to 4 gb depending on OS + //So disabling the setting of heap size when the JVM is not 64 bit + //Is the safest course of action + //And the file won't exist in the install folder when running through netbeans + memField.setEnabled(false); + } + systemMemoryTotal.setText(Long.toString(getSystemMemoryInGB())); + } + + /** + * Get the total system memory in gigabytes which exists on the machine + * which the application is running. + * + * @return the total system memory + */ + private long getSystemMemoryInGB() { + long memorySize = ((com.sun.management.OperatingSystemMXBean) ManagementFactory + .getOperatingSystemMXBean()).getTotalPhysicalMemorySize(); + return memorySize / ONE_BILLION; + } + + /** + * Gets the currently saved max java heap space in gigabytes. + * + * @return @throws IOException when unable to get a valid setting + */ + private long getCurrentJvmMaxMemoryInGB() throws IOException { + String currentXmx = getCurrentXmxValue(); + char units = '-'; + Long value = 0L; + if (currentXmx.length() > 1) { + units = currentXmx.charAt(currentXmx.length() - 1); + value = Long.parseLong(currentXmx.substring(0, currentXmx.length() - 1)); + } else { + throw new IOException("No memory setting found in String: " + currentXmx); + } + //some older .conf files might have the units as megabytes instead of gigabytes + switch (units) { + case 'g': + case 'G': + return value; + case 'm': + case 'M': + return value / MEGA_IN_GIGA; + default: + throw new IOException("Units were not recognized as parsed: " + units); + } + } + + /* + * The value currently saved in the conf file as the max java heap space + * available to this application. Form will be an integer followed by a + * character indicating units. Helper method for + * getCurrentJvmMaxMemoryInGB() + * + * @return the saved value for the max java heap space + * + * @throws IOException if the conf file does not exist in either the user + * directory or the install directory + */ + private String getCurrentXmxValue() throws IOException { + String[] settings; + String currentSetting = ""; + File userConfFile = getUserFolderConfFile(); + if (!userConfFile.exists()) { + settings = getDefaultsFromFileContents(readConfFile(getInstallFolderConfFile())); + } else { + settings = getDefaultsFromFileContents(readConfFile(userConfFile)); + } + for (String option : settings) { + if (option.startsWith("-J-Xmx")) { + currentSetting = option.replace("-J-Xmx", "").trim(); + } + } + return currentSetting; + } + + /** + * Get the conf file from the install directory which stores the default + * values for the settings. + * + * @return the file which has the applications default .conf file + * + * @throws IOException when the file does not exist. + */ + private static File getInstallFolderConfFile() throws IOException { + String confFileName = Version.getName() + CONFIG_FILE_EXTENSION; + String installFolder = PlatformUtil.getInstallPath(); + File installFolderEtc = new File(installFolder, ETC_FOLDER_NAME); + File installFolderConfigFile = new File(installFolderEtc, confFileName); + if (!installFolderConfigFile.exists()) { + throw new IOException("Conf file could not be found" + installFolderConfigFile.toString()); + } + return installFolderConfigFile; + } + + /** + * Get the conf file from the directory which stores the currently in use + * settings. Creates the directory for the file if the directory does not + * exist. + * + * @return the file which has the applications current .conf file + */ + private static File getUserFolderConfFile() { + String confFileName = Version.getName() + CONFIG_FILE_EXTENSION; + File userFolder = PlatformUtil.getUserDirectory(); + File userEtcFolder = new File(userFolder, ETC_FOLDER_NAME); + if (!userEtcFolder.exists()) { + userEtcFolder.mkdir(); + } + return new File(userEtcFolder, confFileName); + } + + /** + * Take the conf file in the install directory and save a copy of it to the + * user directory. The copy will be modified to include the current memory + * setting. + * + * @throws IOException when unable to write a conf file or access the + * install folders conf file + */ + private void writeEtcConfFile() throws IOException { + StringBuilder content = new StringBuilder(); + List confFile = readConfFile(getInstallFolderConfFile()); + for (String line : confFile) { + if (line.contains("-J-Xmx")) { + String[] splitLine = line.split(" "); + StringJoiner modifiedLine = new StringJoiner(" "); + for (String piece : splitLine) { + if (piece.contains("-J-Xmx")) { + piece = "-J-Xmx" + memField.getText() + "g"; + } + modifiedLine.add(piece); + } + content.append(modifiedLine.toString()); + } else { + content.append(line); + } + content.append("\n"); + } + Files.write(getUserFolderConfFile().toPath(), content.toString().getBytes()); + } + + /** + * Reads a conf file line by line putting each line into a list of strings + * which will be returned. + * + * @param configFile the .conf file which you wish to read. + * + * @return a list of strings with a string for each line in the conf file + * specified. + */ + private static List readConfFile(File configFile) { + List lines = new ArrayList<>(); + if (null != configFile) { + Path filePath = configFile.toPath(); + Charset charset = Charset.forName("UTF-8"); + try { + lines = Files.readAllLines(filePath, charset); + } catch (IOException e) { + logger.log(Level.SEVERE, "Error reading config file contents. {}", configFile.getAbsolutePath()); + } + } + return lines; + } + + /** + * Find the string in the list of strings which contains the default options + * settings and split it into an array of strings containing one element for + * each setting specified. + * + * @param list a list of string representing lines of a .conf file + * + * @return an array of strings for each argument on the line which has the + * default options, returns an empty array of Strings if default + * options is not present. + */ + private static String[] getDefaultsFromFileContents(List list) { + Optional defaultSettings = list.stream().filter(line -> line.startsWith("default_options=")).findFirst(); + + if (defaultSettings.isPresent()) { + return defaultSettings.get().replace("default_options=", "").replaceAll("\"", "").split(" "); + } + return new String[]{}; } void load() { @@ -53,7 +284,41 @@ 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); + } + if (memField.isEnabled()) { + try { + initialMemValue = Long.toString(getCurrentJvmMaxMemoryInGB()); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Can't read current Jvm max memory setting from file", ex); + memField.setEnabled(false); + } + memField.setText(initialMemValue); + } + isMemFieldValid(); //ensure the error message is up to date + } + + 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,15 +329,22 @@ 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()); } } + if (memField.isEnabled()) { //if the field could of been changed we need to try and save it + try { + writeEtcConfFile(); + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to save config file to " + PlatformUtil.getUserDirectory() + "\\" + ETC_FOLDER_NAME, ex); + } + } } boolean valid() { - return true; + return isMemFieldValid(); } /** @@ -87,24 +359,96 @@ 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(); + runtimePanel = new javax.swing.JPanel(); + maxMemoryLabel = new javax.swing.JLabel(); + maxMemoryUnitsLabel = new javax.swing.JLabel(); + totalMemoryLabel = new javax.swing.JLabel(); + systemMemoryTotal = new javax.swing.JLabel(); + restartNecessaryWarning = new javax.swing.JLabel(); + memField = new javax.swing.JTextField(); + invalidReasonLabel = new javax.swing.JLabel(); + maxMemoryUnitsLabel1 = new javax.swing.JLabel(); 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(javax.swing.GroupLayout.DEFAULT_SIZE, 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 +467,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 +517,41 @@ 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) + .addGroup(viewPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(viewPanelLayout.createSequentialGroup() + .addGap(10, 10, 10) + .addGroup(viewPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(viewPanelLayout.createSequentialGroup() + .addGroup(viewPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .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(viewsHideKnownCB)) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(viewPanelLayout.createSequentialGroup() + .addGroup(viewPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(dataSourcesHideSlackCB) - .addComponent(viewsHideSlackCB)))) - .addGap(0, 0, Short.MAX_VALUE)))) + .addComponent(viewsHideSlackCB) + .addComponent(useLocalTimeRB)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addGroup(viewPanelLayout.createSequentialGroup() + .addGroup(viewPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabelHideSlackFiles) + .addComponent(jLabelTimeDisplay) + .addComponent(jLabelHideKnownFiles) + .addComponent(jLabelSelectFile)) + .addGap(30, 30, 30)))) ); - 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 +575,97 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(useLocalTimeRB) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(useGMTTimeRB) + .addComponent(useGMTTimeRB)) + ); + + runtimePanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.runtimePanel.border.title"))); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(maxMemoryLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.maxMemoryLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(maxMemoryUnitsLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.maxMemoryUnitsLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(totalMemoryLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.totalMemoryLabel.text")); // NOI18N + + systemMemoryTotal.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING); + + restartNecessaryWarning.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/warning16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(restartNecessaryWarning, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.restartNecessaryWarning.text")); // NOI18N + + memField.setHorizontalAlignment(javax.swing.JTextField.TRAILING); + memField.addKeyListener(new java.awt.event.KeyAdapter() { + public void keyReleased(java.awt.event.KeyEvent evt) { + memFieldKeyReleased(evt); + } + }); + + invalidReasonLabel.setForeground(new java.awt.Color(255, 0, 0)); + + org.openide.awt.Mnemonics.setLocalizedText(maxMemoryUnitsLabel1, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.maxMemoryUnitsLabel.text")); // NOI18N + + javax.swing.GroupLayout runtimePanelLayout = new javax.swing.GroupLayout(runtimePanel); + runtimePanel.setLayout(runtimePanelLayout); + runtimePanelLayout.setHorizontalGroup( + runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(runtimePanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(maxMemoryLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 114, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(totalMemoryLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(systemMemoryTotal, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(memField, javax.swing.GroupLayout.DEFAULT_SIZE, 37, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(agencyLogoImageLabel) + .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(maxMemoryUnitsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(maxMemoryUnitsLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(restartNecessaryWarning, javax.swing.GroupLayout.DEFAULT_SIZE, 417, Short.MAX_VALUE) + .addComponent(invalidReasonLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + runtimePanelLayout.setVerticalGroup( + runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(runtimePanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(maxMemoryUnitsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(memField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(invalidReasonLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(maxMemoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(restartNecessaryWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(maxMemoryUnitsLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(totalMemoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(systemMemoryTotal, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + ); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(logoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(viewPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(runtimePanel, javax.swing.GroupLayout.Alignment.TRAILING, 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) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(agencyLogoPathField) - .addComponent(browseLogosButton)) - .addGap(35, 35, 35)) + .addComponent(runtimePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .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 +674,13 @@ 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) + .addGap(0, 0, 0)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) ); }// //GEN-END:initComponents @@ -310,22 +717,84 @@ 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 + private void memFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_memFieldKeyReleased + String memText = memField.getText(); + if (memText.equals(initialMemValue)) { + //if it is still the initial value don't fire change + return; + } + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_memFieldKeyReleased + + /** + * Checks that if the mem field is enabled it has a valid value. + * + * @return true if the memfield is valid false if it is not + */ + private boolean isMemFieldValid() { + String memText = memField.getText(); + invalidReasonLabel.setText(""); + if (!PlatformUtil.is64BitJVM()) { + invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_not64BitInstall_text()); + //the panel should be valid when it is a 32 bit jvm because the memfield will be disabled. + return true; + } + if (Version.getBuildType() == Version.Type.DEVELOPMENT) { + invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_developerMode_text()); + //the panel should be valid when you are running in developer mode because the memfield will be disabled + return true; + } + if (memText.length() == 0) { + invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_noValueEntered_text()); + return false; + } + if (memText.replaceAll("[^\\d]", "").length() != memText.length()) { + invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_invalidCharacters_text()); + return false; + } + int parsedInt = Integer.parseInt(memText); + if (parsedInt < MIN_MEMORY_IN_GB) { + invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_underMinMemory_text(MIN_MEMORY_IN_GB)); + return false; + } + if (parsedInt >= getSystemMemoryInGB()) { + invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_overMaxMemory_text(getSystemMemoryInGB())); + return false; + } + return true; + } // 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; private javax.swing.JCheckBox dataSourcesHideKnownCB; private javax.swing.JCheckBox dataSourcesHideSlackCB; + private javax.swing.JLabel invalidReasonLabel; private javax.swing.JLabel jLabelHideKnownFiles; private javax.swing.JLabel jLabelHideSlackFiles; private javax.swing.JLabel jLabelSelectFile; @@ -333,10 +802,21 @@ 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.JLabel maxMemoryLabel; + private javax.swing.JLabel maxMemoryUnitsLabel; + private javax.swing.JLabel maxMemoryUnitsLabel1; + private javax.swing.JTextField memField; + private javax.swing.JLabel restartNecessaryWarning; + private javax.swing.JPanel runtimePanel; + private javax.swing.JLabel systemMemoryTotal; + private javax.swing.JLabel totalMemoryLabel; 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/tags/Category.java b/Core/src/org/sleuthkit/autopsy/datamodel/DhsImageCategory.java similarity index 85% rename from Core/src/org/sleuthkit/autopsy/datamodel/tags/Category.java rename to Core/src/org/sleuthkit/autopsy/datamodel/DhsImageCategory.java index 39d624110f..2357c61599 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/tags/Category.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DhsImageCategory.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datamodel.tags; +package org.sleuthkit.autopsy.datamodel; import com.google.common.collect.ImmutableList; import java.util.Map; @@ -38,6 +38,7 @@ import javafx.scene.layout.CornerRadii; import javafx.scene.layout.Region; import javafx.scene.paint.Color; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.datamodel.Bundle; /** * Enum to represent the six categories in the DHS image categorization scheme. @@ -48,7 +49,7 @@ import org.openide.util.NbBundle; "Category.four=CAT-4: Exemplar/Comparison (Internal Use Only)", "Category.five=CAT-5: Non-pertinent", "Category.zero=CAT-0: Uncategorized"}) -public enum Category { +public enum DhsImageCategory { /* * This order of declaration is required so that Enum's compareTo method @@ -65,22 +66,21 @@ public enum Category { private static final BorderWidths BORDER_WIDTHS_2 = new BorderWidths(2); private static final CornerRadii CORNER_RADII_4 = new CornerRadii(4); - public static ImmutableList getNonZeroCategories() { + public static ImmutableList getNonZeroCategories() { return nonZeroCategories; } - private static final ImmutableList nonZeroCategories = - ImmutableList.of(Category.FIVE, Category.FOUR, Category.THREE, Category.TWO, Category.ONE); + private static final ImmutableList nonZeroCategories = + ImmutableList.of(DhsImageCategory.FIVE, DhsImageCategory.FOUR, DhsImageCategory.THREE, DhsImageCategory.TWO, DhsImageCategory.ONE); /** * map from displayName to enum value */ - private static final Map nameMap = - Stream.of(values()).collect(Collectors.toMap( - Category::getDisplayName, + private static final Map nameMap = + Stream.of(values()).collect(Collectors.toMap(DhsImageCategory::getDisplayName, Function.identity())); - public static Category fromDisplayName(String displayName) { + public static DhsImageCategory fromDisplayName(String displayName) { return nameMap.get(displayName); } @@ -99,7 +99,7 @@ public enum Category { private final int id; private Image snapshot; - private Category(Color color, int id, String name) { + private DhsImageCategory(Color color, int id, String name) { this.color = color; this.displayName = name; this.id = id; 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/ingest/Bundle.properties b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties index f23dd57f84..d240310b1c 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties @@ -20,8 +20,7 @@ IngestMessagePanel.totalUniqueMessagesNameVal.text=- IngestJob.progress.dataSourceIngest.initialDisplayName=Analyzing {0} IngestJob.progress.dataSourceIngest.displayName={0} for {1} IngestJob.progress.fileIngest.displayName=Analyzing files from {0} -IngestJob.progress.fileIngest.cancelMessage=Waiting for {0} on {1} -IngestJob.progress.cancelling={0} (Cancelling...) +IngestJob.progress.cancelling=Cancelling... IngestJob.cancellationDialog.title=Cancel Ingest IngestDialog.startButton.title=Start IngestDialog.closeButton.title=Close diff --git a/Core/src/org/sleuthkit/autopsy/ingest/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/ingest/Bundle_ja.properties index f7af8964cc..2fc82d0dd9 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/ingest/Bundle_ja.properties @@ -2,7 +2,7 @@ CTL_IngestMessageTopComponent=\u30e1\u30c3\u30bb\u30fc\u30b8 HINT_IngestMessageTopComponent=\u30e1\u30c3\u30bb\u30fc\u30b8\u30a6\u30a3\u30f3\u30c9\u30a6 IngestDialog.closeButton.title=\u9589\u3058\u308b IngestDialog.startButton.title=\u958b\u59cb -IngestJob.progress.cancelling={0}\uff08\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026\uff09 +IngestJob.progress.cancelling=\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026 IngestJob.progress.dataSourceIngest.displayName={1}\u306e{0} IngestJob.progress.fileIngest.displayName={0}\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u4e2d IngestManager.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc @@ -90,7 +90,6 @@ IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.jobID=\u30b8\u30e7\u30d ModuleTableModel.colName.module=\u30e2\u30b8\u30e5\u30fc\u30eb Menu/Tools/RunIngestModules=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c -IngestJob.progress.fileIngest.cancelMessage={1}\u306e{0}\u3092\u5f85\u3063\u3066\u3044\u307e\u3059 IngestManager.OpenEventChannel.Fail.ErrMsg=\u3053\u306e\u30b1\u30fc\u30b9\u3067\u4f7f\u308f\u308c\u3066\u3044\u308b\u304b\u3082\u3057\u308c\u306a\u3044\u4ed6\u306e\u30ce\u30fc\u30c9\u306b\u89e3\u6790\u30d7\u30ed\u30bb\u30b9\u304c\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 IngestManager.OpenEventChannel.Fail.Title=\u63a5\u7d9a\u5931\u6557 IngestJobSettings.moduleSettingsSave.warning={1}\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e{0}\u30e2\u30b8\u30e5\u30fc\u30eb\u7528\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java index f9caad7683..b8d5b7e359 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2014-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -125,10 +125,9 @@ final class DataSourceIngestJob { * class. */ private volatile boolean currentDataSourceIngestModuleCancelled; + private final List cancelledDataSourceIngestModules = new CopyOnWriteArrayList<>(); private volatile boolean cancelled; private volatile IngestJob.CancellationReason cancellationReason = IngestJob.CancellationReason.NOT_CANCELLED; - private final Object cancellationStateMonitor = new Object(); - private final List cancelledDataSourceIngestModules = new CopyOnWriteArrayList<>(); /** * A data source ingest job uses the task scheduler singleton to create and @@ -989,66 +988,25 @@ final class DataSourceIngestJob { * @param reason The cancellation reason. */ void cancel(IngestJob.CancellationReason reason) { - if (this.doUI) { - /** - * Put a cancellation message on data source level ingest progress - * bar, if it is still running. - */ - synchronized (this.dataSourceIngestProgressLock) { - if (dataSourceIngestProgress != null) { - final String displayName = NbBundle.getMessage(this.getClass(), - "IngestJob.progress.dataSourceIngest.initialDisplayName", - dataSource.getName()); - dataSourceIngestProgress.setDisplayName( - NbBundle.getMessage(this.getClass(), - "IngestJob.progress.cancelling", - displayName)); - } - } - - /** - * Put a cancellation message on the file level ingest progress bar, - * if it is still running. - */ - synchronized (this.fileIngestProgressLock) { - if (this.fileIngestProgress != null) { - final String displayName = NbBundle.getMessage(this.getClass(), - "IngestJob.progress.fileIngest.displayName", - this.dataSource.getName()); - this.fileIngestProgress.setDisplayName( - NbBundle.getMessage(this.getClass(), "IngestJob.progress.cancelling", - displayName)); - if (!this.currentFileIngestModule.isEmpty() && !this.currentFileIngestTask.isEmpty()) { - this.fileIngestProgress.progress(NbBundle.getMessage(this.getClass(), - "IngestJob.progress.fileIngest.cancelMessage", - this.currentFileIngestModule, this.currentFileIngestTask)); - } - - } - } - } - - /* - * If the work is not already done, show this job as cancelled for the - * given reason. - */ - if (Stages.FINALIZATION != stage) { - synchronized (cancellationStateMonitor) { - /* - * These fields are volatile for reading, synchronized on the - * monitor here for writing. - */ - this.cancelled = true; - this.cancellationReason = reason; - } - } - - /** - * Tell the task scheduler to cancel all pending tasks, i.e., tasks not - * not being performed by an ingest thread. - */ + this.cancelled = true; + this.cancellationReason = reason; DataSourceIngestJob.taskScheduler.cancelPendingTasksForIngestJob(this); - this.checkForStageCompleted(); + + if (this.doUI) { + synchronized (this.dataSourceIngestProgressLock) { + if (null != dataSourceIngestProgress) { + dataSourceIngestProgress.setDisplayName(NbBundle.getMessage(this.getClass(), "IngestJob.progress.dataSourceIngest.initialDisplayName", dataSource.getName())); + dataSourceIngestProgress.progress(NbBundle.getMessage(this.getClass(), "IngestJob.progress.cancelling")); + } + } + + synchronized (this.fileIngestProgressLock) { + if (null != this.fileIngestProgress) { + this.fileIngestProgress.setDisplayName(NbBundle.getMessage(this.getClass(), "IngestJob.progress.fileIngest.displayName", this.dataSource.getName())); + this.fileIngestProgress.progress(NbBundle.getMessage(this.getClass(), "IngestJob.progress.cancelling")); + } + } + } } /** 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 new file mode 100755 index 0000000000..15fccb6a74 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java @@ -0,0 +1,280 @@ +/* + * 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 java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.Blackboard; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestMessage; +import org.sleuthkit.autopsy.ingest.IngestModule; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.ReadContentInputStream; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; + +/** + * File ingest module to detect encryption. + */ +final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter { + + 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; + + private final IngestServices SERVICES = IngestServices.getInstance(); + private final Logger LOGGER = SERVICES.getLogger(EncryptionDetectionModuleFactory.getModuleName()); + private FileTypeDetector fileTypeDetector; + private Blackboard blackboard; + 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. + * The supplied EncryptionDetectionIngestJobSettings object is used to + * configure the module. + */ + EncryptionDetectionFileIngestModule(EncryptionDetectionIngestJobSettings settings) { + minimumEntropy = settings.getMinimumEntropy(); + minimumFileSize = settings.getMinimumFileSize(); + fileSizeMultipleEnforced = settings.isFileSizeMultipleEnforced(); + slackFilesAllowed = settings.isSlackFilesAllowed(); + } + + @Override + public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException { + blackboard = Case.getCurrentCase().getServices().getBlackboard(); + try { + fileTypeDetector = new FileTypeDetector(); + } catch (FileTypeDetector.FileTypeDetectorInitException ex) { + throw new IngestModule.IngestModuleException("Failed to create file type detector", ex); + } + } + + @Override + public IngestModule.ProcessResult process(AbstractFile file) { + + try { + if (isFileEncrypted(file)) { + return flagFile(file); + } + } catch (IOException | TskCoreException ex) { + LOGGER.log(Level.SEVERE, String.format("Unable to process file '%s'", Paths.get(file.getParentPath(), file.getName())), ex); + return IngestModule.ProcessResult.ERROR; + } + + return IngestModule.ProcessResult.OK; + } + + /** + * Create a blackboard artifact. + * + * @param The file to be processed. + * + * @return 'OK' if the file was processed successfully, or 'ERROR' if there + * was a problem. + */ + private IngestModule.ProcessResult flagFile(AbstractFile file) { + try { + BlackboardArtifact artifact = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED); + + try { + /* + * Index the artifact for keyword search. + */ + blackboard.indexArtifact(artifact); + } catch (Blackboard.BlackboardException ex) { + LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS + } + + /* + * Send an event to update the view with the new result. + */ + SERVICES.fireModuleDataEvent(new ModuleDataEvent(EncryptionDetectionModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED, Collections.singletonList(artifact))); + + /* + * Make an ingest inbox message. + */ + StringBuilder detailsSb = new StringBuilder(); + detailsSb.append("File: ").append(file.getParentPath()).append(file.getName()).append("
\n"); + detailsSb.append("Entropy: ").append(calculatedEntropy); + + SERVICES.postMessage(IngestMessage.createDataMessage(EncryptionDetectionModuleFactory.getModuleName(), + "Encryption Detected Match: " + file.getName(), + detailsSb.toString(), + file.getName(), + artifact)); + + return IngestModule.ProcessResult.OK; + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, String.format("Failed to create blackboard artifact for '%s'.", Paths.get(file.getParentPath(), file.getName())), ex); //NON-NLS + return IngestModule.ProcessResult.ERROR; + } + } + + /** + * This method checks if the AbstractFile input is encrypted. Initial + * qualifications require that it be an actual file that is not known, meets + * file size requirements, and has a MIME type of + * 'application/octet-stream'. + * + * @param file AbstractFile to be checked. + * + * @return True if the AbstractFile is encrypted. + */ + private boolean isFileEncrypted(AbstractFile file) throws IOException, TskCoreException { + /* + * Criteria for the checks in this method are partially based on + * http://www.forensicswiki.org/wiki/TrueCrypt#Detection + */ + + boolean possiblyEncrypted = false; + + /* + * Qualify the file type. + */ + 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.SLACK) || slackFilesAllowed)) { + /* + * Qualify the file against hash databases. + */ + if (!file.getKnown().equals(TskData.FileKnown.KNOWN)) { + /* + * Qualify the size. + */ + long contentSize = file.getSize(); + 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); + } + } + } + } + } + + if (possiblyEncrypted) { + try { + calculatedEntropy = calculateEntropy(file); + if (calculatedEntropy >= minimumEntropy) { + return true; + } + } catch (IOException ex) { + throw new IOException("Unable to calculate the entropy.", ex); + } + } + + return false; + } + + /** + * Calculate the entropy of the file. The result is used to qualify the file + * as an encrypted file. + * + * @param file The file to be calculated against. + * + * @return The entropy of the file. + * + * @throws IOException If there is a failure closing or reading from the + * InputStream. + */ + private double calculateEntropy(AbstractFile file) throws IOException { + /* + * Logic in this method is based on + * https://github.com/willjasen/entropy/blob/master/entropy.java + */ + + InputStream in = null; + BufferedInputStream bin = null; + + try { + in = new ReadContentInputStream(file); + bin = new BufferedInputStream(in); + + /* + * Determine the number of times each byte value appears. + */ + int[] byteOccurences = new int[BYTE_OCCURENCES_BUFFER_SIZE]; + int readByte; + while ((readByte = bin.read()) != -1) { + byteOccurences[readByte]++; + } + + /* + * Calculate the entropy based on the byte occurence counts. + */ + long dataLength = file.getSize() - 1; + double entropyAccumulator = 0; + for (int i = 0; i < BYTE_OCCURENCES_BUFFER_SIZE; i++) { + if (byteOccurences[i] > 0) { + double byteProbability = (double) byteOccurences[i] / (double) dataLength; + entropyAccumulator += (byteProbability * Math.log(byteProbability) * ONE_OVER_LOG2); + } + } + + return -entropyAccumulator; + + } catch (IOException ex) { + throw new IOException("IOException occurred while trying to read data from InputStream.", ex); + } finally { + try { + if (in != null) { + in.close(); + } + if (bin != null) { + bin.close(); + } + } catch (IOException ex) { + throw new IOException("Failed to close InputStream.", 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 new file mode 100755 index 0000000000..27549f648f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java @@ -0,0 +1,116 @@ +/* + * 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.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.IngestModuleGlobalSettingsPanel; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; + +/** + * A factory that creates file ingest modules that detect encryption. + */ +@ServiceProvider(service = IngestModuleFactory.class) +@Messages({ + "EncryptionDetectionFileIngestModule.moduleName.text=Encryption Detection", + "EncryptionDetectionFileIngestModule.getDesc.text=Looks for files with the specified minimum entropy." +}) +public class EncryptionDetectionModuleFactory implements IngestModuleFactory { + + @Override + public String getModuleDisplayName() { + return getModuleName(); + } + + /** + * Get the name of the module. + * + * @return The module name. + */ + static String getModuleName() { + return NbBundle.getMessage(EncryptionDetectionFileIngestModule.class, "EncryptionDetectionFileIngestModule.moduleName.text"); + } + + @Override + public String getModuleDescription() { + return NbBundle.getMessage(EncryptionDetectionFileIngestModule.class, "EncryptionDetectionFileIngestModule.getDesc.text"); + } + + @Override + public String getModuleVersionNumber() { + return Version.getVersion(); + } + + @Override + public boolean isFileIngestModuleFactory() { + return true; + } + + @Override + 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); + } + + @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 eb1ab33591..b1aff14a8f 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java @@ -18,7 +18,9 @@ */ package org.sleuthkit.autopsy.modules.filetypeid; +import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.SortedSet; @@ -26,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; @@ -35,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; @@ -49,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; @@ -269,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. */ @@ -360,8 +357,9 @@ public class FileTypeDetector { if (fileType.createInterestingFileHit()) { BlackboardArtifact artifact; artifact = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); + Collection attributes = new ArrayList<>(); BlackboardAttribute setNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, FileTypeIdModuleFactory.getModuleName(), fileType.getInterestingFilesSetName()); - artifact.addAttribute(setNameAttribute); + attributes.add(setNameAttribute); /* * Use the MIME type as the category attribute, i.e., the @@ -369,8 +367,9 @@ public class FileTypeDetector { * files set. */ BlackboardAttribute ruleNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, FileTypeIdModuleFactory.getModuleName(), fileType.getMimeType()); - artifact.addAttribute(ruleNameAttribute); + attributes.add(ruleNameAttribute); + artifact.addAttributes(attributes); /* * Index the artifact for keyword search. */ 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/EncaseHashSetParser.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/EncaseHashSetParser.java new file mode 100644 index 0000000000..4c6a58d9bf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/EncaseHashSetParser.java @@ -0,0 +1,168 @@ +/* + * 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.InputStream; +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Parser for Encase format hash sets (*.hash) + */ +class EncaseHashSetParser implements HashSetParser { + + private final byte[] encaseHeader = {(byte) 0x48, (byte) 0x41, (byte) 0x53, (byte) 0x48, (byte) 0x0d, (byte) 0x0a, (byte) 0xff, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00}; + private final String filename; // Name of the input file (saved for logging) + private InputStream inputStream; // File stream for file being imported + private final long expectedHashCount; // Number of hashes we expect to read from the file + private int totalHashesRead = 0; // Number of hashes that have been read + + /** + * Opens the import file and parses the header. If this is successful, the + * file will be set up to call getNextHash() to read the hash values. + * + * @param filename The Encase hash set + * @throws TskCoreException There was an error opening/reading the file or + * it is not the correct format + */ + EncaseHashSetParser(String filename) throws TskCoreException { + try { + this.filename = filename; + inputStream = new BufferedInputStream(new FileInputStream(filename)); + + // Read in and test the 16 byte header + byte[] header = new byte[16]; + readBuffer(header, 16); + if (!Arrays.equals(header, encaseHeader)) { + close(); + throw new TskCoreException("File " + filename + " does not have an Encase header"); + } + + // Read in the expected number of hashes (little endian) + byte[] sizeBuffer = new byte[4]; + readBuffer(sizeBuffer, 4); + expectedHashCount = ((sizeBuffer[3] & 0xff) << 24) | ((sizeBuffer[2] & 0xff) << 16) + | ((sizeBuffer[1] & 0xff) << 8) | (sizeBuffer[0] & 0xff); + + // Read in a bunch of nulls + byte[] filler = new byte[0x3f4]; + readBuffer(filler, 0x3f4); + + // Read in the hash set name + byte[] nameBuffer = new byte[0x50]; + readBuffer(nameBuffer, 0x50); + + // Read in the hash set type + byte[] typeBuffer = new byte[0x28]; + readBuffer(typeBuffer, 0x28); + + // At this point we're past the header and ready to read in the hashes + } catch (IOException ex) { + close(); + throw new TskCoreException("Error reading " + filename, ex); + } catch (TskCoreException ex) { + close(); + throw ex; + } + } + + /** + * 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; + } + + /** + * 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 (totalHashesRead >= expectedHashCount); + } + + /** + * 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 (inputStream == null) { + throw new TskCoreException("Attempting to read from null inputStream"); + } + + byte[] hashBytes = new byte[16]; + byte[] divider = new byte[2]; + try { + + readBuffer(hashBytes, 16); + readBuffer(divider, 2); + + StringBuilder sb = new StringBuilder(); + for (byte b : hashBytes) { + sb.append(String.format("%02x", b)); + } + + totalHashesRead++; + return sb.toString(); + } catch (IOException ex) { + throw new TskCoreException("Ran out of data while reading Encase hash set " + filename, ex); + } + } + + /** + * Closes the import file + */ + @Override + public final void close() { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ex) { + Logger.getLogger(EncaseHashSetParser.class.getName()).log(Level.SEVERE, "Error closing Encase hash set " + filename, ex); + } finally { + inputStream = null; + } + } + } + + private void readBuffer(byte[] buffer, int length) throws TskCoreException, IOException { + if (inputStream == null) { + throw new TskCoreException("readBuffer called on null inputStream"); + } + if (length != inputStream.read(buffer)) { + throw new TskCoreException("Ran out of data unexpectedly while parsing Encase file " + filename); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java index 0ecb3a5ad4..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) { @@ -423,7 +428,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { }//GEN-LAST:event_saveAsButtonActionPerformed @NbBundle.Messages({"HashDbCreateDatabaseDialog.missingOrg=An organization must be selected", - "HashDbCreateDatabaseDialog.duplicateName=A hashset with this name and version already exists", + "HashDbCreateDatabaseDialog.duplicateName=A hashset with this name already exists", "HashDbCreateDatabaseDialog.databaseLookupError=Error accessing central repository", "HashDbCreateDatabaseDialog.databaseCreationError=Error creating new hash set" }) @@ -500,7 +505,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { } else { // Check if a hash set with the same name/version already exists try{ - if(EamDb.getInstance().referenceSetExists(hashSetNameTextField.getText(), EamDb.getDefaultVersion())){ + if(EamDb.getInstance().referenceSetExists(hashSetNameTextField.getText(), "")){ JOptionPane.showMessageDialog(this, NbBundle.getMessage(this.getClass(), "HashDbCreateDatabaseDialog.duplicateName"), @@ -522,9 +527,9 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { try{ int referenceSetID = EamDb.getInstance().newReferenceSet(new EamGlobalSet(selectedOrg.getOrgID(), hashSetNameTextField.getText(), - EamDb.getDefaultVersion(), fileKnown, false)); + "", fileKnown, false)); newHashDb = HashDbManager.getInstance().addExistingCentralRepoHashSet(hashSetNameTextField.getText(), - EamDb.getDefaultVersion(), referenceSetID, + "", referenceSetID, true, sendIngestMessagesCheckbox.isSelected(), type, false); } catch (EamDbException | TskCoreException ex){ Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Error creating new reference set", 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 d32fc3cf9c..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 (.idx only)"}) - private void updateFileChooserFilter() { - fileChooser.resetChoosableFileFilters(); - if(centralRepoRadioButton.isSelected()){ - String[] EXTENSION = new String[]{"idx"}; //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 { @@ -447,7 +445,8 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { @NbBundle.Messages({"HashDbImportDatabaseDialog.missingVersion=A version must be entered", "HashDbImportDatabaseDialog.missingOrg=An organization must be selected", "HashDbImportDatabaseDialog.duplicateName=A hashset with this name and version already exists", - "HashDbImportDatabaseDialog.databaseLookupError=Error accessing central repository" + "HashDbImportDatabaseDialog.databaseLookupError=Error accessing central repository", + "HashDbImportDatabaseDialog.mustEnterHashSetNameMsg=A hash set name must be entered." }) private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed // Note that the error handlers in this method call return without disposing of the @@ -456,7 +455,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { if (hashSetNameTextField.getText().isEmpty()) { JOptionPane.showMessageDialog(this, NbBundle.getMessage(this.getClass(), - "HashDbCreateDatabaseDialog.mustEnterHashSetNameMsg"), + "HashDbImportDatabaseDialog.mustEnterHashSetNameMsg"), NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.importHashDbErr"), JOptionPane.ERROR_MESSAGE); @@ -464,7 +463,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { } if(centralRepoRadioButton.isSelected()){ - if(versionTextField.getText().isEmpty()){ + if(readOnlyCheckbox.isSelected() && versionTextField.getText().isEmpty()){ JOptionPane.showMessageDialog(this, NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.missingVersion"), @@ -557,7 +556,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { version = versionTextField.getText(); } else { // Editable databases don't have a version - version = EamDb.getDefaultVersion(); + version = ""; } ImportCentralRepoDbProgressDialog progressDialog = new ImportCentralRepoDbProgressDialog(); progressDialog.importFile(hashSetNameTextField.getText(), version, diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java index 16bcb1d2e9..f022f6b657 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.modules.hashdatabase; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -296,14 +297,14 @@ public class HashDbIngestModule implements FileIngestModule { String MODULE_NAME = NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.moduleName"); BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT); + Collection attributes = new ArrayList<>(); //TODO Revisit usage of deprecated constructor as per TSK-583 //BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName); - BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, hashSetName); - badFile.addAttribute(att2); - BlackboardAttribute att3 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, MODULE_NAME, md5Hash); - badFile.addAttribute(att3); - BlackboardAttribute att4 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, comment); - badFile.addAttribute(att4); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, hashSetName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, MODULE_NAME, md5Hash)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, comment)); + + badFile.addAttributes(attributes); try { // index the artifact for keyword search diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java index 8e0f5422a1..34770aa6fa 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java @@ -728,8 +728,6 @@ public class HashDbManager implements PropertyChangeListener { public abstract HashDb.KnownFilesType getKnownFilesType(); public abstract boolean getSearchDuringIngest(); - - abstract boolean getDefaultSearchDuringIngest(); abstract void setSearchDuringIngest(boolean useForIngest); @@ -869,12 +867,6 @@ public class HashDbManager implements PropertyChangeListener { public boolean getSearchDuringIngest() { return searchDuringIngest; } - - @Override - boolean getDefaultSearchDuringIngest(){ - // File type hash sets are on by default - return true; - } @Override void setSearchDuringIngest(boolean useForIngest) { @@ -1176,12 +1168,6 @@ public class HashDbManager implements PropertyChangeListener { public boolean getSearchDuringIngest() { return searchDuringIngest; } - - @Override - boolean getDefaultSearchDuringIngest(){ - // Central repo hash sets are off by default - return false; - } @Override void setSearchDuringIngest(boolean useForIngest) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleSettings.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleSettings.java index 3e331cb1da..99dd50d291 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleSettings.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleSettings.java @@ -135,8 +135,8 @@ final class HashLookupModuleSettings implements IngestModuleIngestJobSettings { } } - // We didn't find it, so use the default value - return db.getDefaultSearchDuringIngest(); + // We didn't find it, so use the value in the HashDb object + return db.getSearchDuringIngest(); } /** 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 b5c6ca805a..1adb75594d 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java @@ -127,7 +127,6 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan hashDbOrgLabel.setText(NO_SELECTION_TEXT); hashDbReadOnlyLabel.setText(NO_SELECTION_TEXT); indexPathLabel.setText(NO_SELECTION_TEXT); - // Update indexing components. hashDbIndexStatusLabel.setText(NO_SELECTION_TEXT); @@ -162,14 +161,14 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan // Update descriptive labels. hashDbNameLabel.setText(db.getHashSetName()); - hashDbTypeLabel.setText(db.getKnownFilesType().getDisplayName()); - try{ - if(db.isUpdateable()){ + hashDbTypeLabel.setText(db.getKnownFilesType().getDisplayName()); + try { + if (db.isUpdateable()) { hashDbReadOnlyLabel.setText(Bundle.HashLookupSettingsPanel_editable()); } else { hashDbReadOnlyLabel.setText(Bundle.HashLookupSettingsPanel_readOnly()); } - } catch (TskCoreException ex){ + } catch (TskCoreException ex) { hashDbReadOnlyLabel.setText(Bundle.HashLookupSettingsPanel_updateStatusError()); } @@ -180,30 +179,30 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan addHashesToDatabaseButton.setEnabled(false); } - if(db instanceof SleuthkitHashSet){ - SleuthkitHashSet hashDb = (SleuthkitHashSet)db; - + if (db instanceof SleuthkitHashSet) { + SleuthkitHashSet hashDb = (SleuthkitHashSet) db; + // Disable the central repo fields hashDbVersionLabel.setText(Bundle.HashLookupSettingsPanel_notApplicable()); hashDbOrgLabel.setText(Bundle.HashLookupSettingsPanel_notApplicable()); - + // Enable the delete button if ingest is not running deleteDatabaseButton.setEnabled(!ingestIsRunning); - + try { hashDbLocationLabel.setText(shortenPath(db.getDatabasePath())); } catch (TskCoreException ex) { Logger.getLogger(HashLookupSettingsPanel.class.getName()).log(Level.SEVERE, "Error getting database path of " + db.getHashSetName() + " hash database", ex); //NON-NLS hashDbLocationLabel.setText(ERROR_GETTING_PATH_TEXT); } - + try { indexPathLabel.setText(shortenPath(hashDb.getIndexPath())); } catch (TskCoreException ex) { Logger.getLogger(HashLookupSettingsPanel.class.getName()).log(Level.SEVERE, "Error getting index path of " + db.getHashSetName() + " hash database", ex); //NON-NLS indexPathLabel.setText(ERROR_GETTING_PATH_TEXT); } - + // Update indexing components. try { if (hashDb.isIndexing()) { @@ -245,15 +244,15 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan indexButton.setEnabled(false); } } else { - + // Disable the file type fields/buttons indexPathLabel.setText(Bundle.HashLookupSettingsPanel_notApplicable()); hashDbIndexStatusLabel.setText(Bundle.HashLookupSettingsPanel_notApplicable()); hashDbLocationLabel.setText(Bundle.HashLookupSettingsPanel_centralRepo()); indexButton.setEnabled(false); deleteDatabaseButton.setEnabled(false); - - CentralRepoHashSet crDb = (CentralRepoHashSet)db; + + CentralRepoHashSet crDb = (CentralRepoHashSet) db; hashDbVersionLabel.setText(crDb.getVersion()); hashDbOrgLabel.setText(crDb.getOrgName()); @@ -302,13 +301,17 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan @Override @Messages({"HashLookupSettingsPanel.saveFail.message=Couldn't save hash db settings.", "HashLookupSettingsPanel.saveFail.title=Save Fail"}) - public void saveSettings() { + public void saveSettings() { + // Clear out the list of new central repo hash sets. They don't need to be + // indexed so will all be saved on both code paths. + newReferenceSetIDs.clear(); + //Checking for for any unindexed databases List unindexed = new ArrayList<>(); for (HashDb db : hashSetManager.getAllHashSets()) { - if(db instanceof SleuthkitHashSet){ + if (db instanceof SleuthkitHashSet) { try { - SleuthkitHashSet hashDatabase = (SleuthkitHashSet)db; + SleuthkitHashSet hashDatabase = (SleuthkitHashSet) db; if (!hashDatabase.hasIndex()) { unindexed.add(hashDatabase); } @@ -320,10 +323,10 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan // If there are unindexed databases, give the user the option to index them now. This // needs to be on the EDT, and will save the hash settings after completing - if(! unindexed.isEmpty()){ - SwingUtilities.invokeLater(new Runnable(){ + if (!unindexed.isEmpty()) { + SwingUtilities.invokeLater(new Runnable() { @Override - public void run(){ + public void run() { //If unindexed ones are found, show a popup box that will either index them, or remove them. if (unindexed.size() == 1) { showInvalidIndex(false, unindexed); @@ -362,20 +365,19 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan */ if (IngestManager.getInstance().isIngestRunning() == false) { // Remove any new central repo hash sets from the database - for(int refID:newReferenceSetIDs){ - try{ - if(EamDb.isEnabled()){ + for (int refID : newReferenceSetIDs) { + try { + if (EamDb.isEnabled()) { EamDb.getInstance().deleteReferenceSet(refID); } else { // This is the case where the user imported a database, then switched over to the central // repo panel and disabled it before cancelling. We can't delete the database at this point. Logger.getLogger(HashLookupSettingsPanel.class.getName()).log(Level.WARNING, "Error reverting central repository hash sets"); //NON-NLS } - } catch (EamDbException ex){ + } catch (EamDbException ex) { Logger.getLogger(HashLookupSettingsPanel.class.getName()).log(Level.SEVERE, "Error reverting central repository hash sets", ex); //NON-NLS } } - HashDbManager.getInstance().loadLastSavedConfiguration(); } } @@ -397,7 +399,7 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan * unindexed, along with solutions. This method is related to * ModalNoButtons, to be removed at a later date. * - * @param plural Whether or not there are multiple unindexed databases + * @param plural Whether or not there are multiple unindexed databases * @param unindexed The list of unindexed databases. Can be of size 1. */ private void showInvalidIndex(boolean plural, List unindexed) { @@ -470,8 +472,8 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan getSelectionModel().setSelectionInterval(index, index); } } - - public void selectRowByDatabase(HashDb db){ + + public void selectRowByDatabase(HashDb db) { setSelection(hashSetTableModel.getIndexByDatabase(db)); } @@ -509,7 +511,7 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan return hashSets.get(rowIndex).getDisplayName(); } - private boolean isValid(int rowIndex) { + private boolean isValid(int rowIndex) { try { return hashSets.get(rowIndex).isValid(); } catch (TskCoreException ex) { @@ -542,15 +544,15 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan } } - int getIndexByDatabase(HashDb db){ + int getIndexByDatabase(HashDb db) { for (int i = 0; i < hashSets.size(); ++i) { if (hashSets.get(i).equals(db)) { return i; } } - return -1; + return -1; } - + @Deprecated int getIndexByName(String name) { for (int i = 0; i < hashSets.size(); ++i) { @@ -785,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) @@ -814,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)) @@ -933,11 +933,11 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan private void createDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createDatabaseButtonActionPerformed HashDb hashDb = new HashDbCreateDatabaseDialog().getHashDatabase(); if (null != hashDb) { - if(hashDb instanceof CentralRepoHashSet){ - int newDbIndex = ((CentralRepoHashSet)hashDb).getReferenceSetID(); + if (hashDb instanceof CentralRepoHashSet) { + int newDbIndex = ((CentralRepoHashSet) hashDb).getReferenceSetID(); newReferenceSetIDs.add(newDbIndex); } - + hashSetTableModel.refreshModel(); ((HashSetTable) hashSetTable).selectRowByDatabase(hashDb); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); @@ -959,7 +959,7 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan // Add a listener for the INDEXING_DONE event. This listener will update // the UI. - SleuthkitHashSet hashDb = (SleuthkitHashSet)hashDatabase; + SleuthkitHashSet hashDb = (SleuthkitHashSet) hashDatabase; hashDb.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { @@ -987,11 +987,11 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan private void importDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importDatabaseButtonActionPerformed HashDb hashDb = new HashDbImportDatabaseDialog().getHashDatabase(); if (null != hashDb) { - if(hashDb instanceof CentralRepoHashSet){ - int newReferenceSetID = ((CentralRepoHashSet)hashDb).getReferenceSetID(); + if (hashDb instanceof CentralRepoHashSet) { + int newReferenceSetID = ((CentralRepoHashSet) hashDb).getReferenceSetID(); newReferenceSetIDs.add(newReferenceSetID); } - + hashSetTableModel.refreshModel(); ((HashSetTable) hashSetTable).selectRowByDatabase(hashDb); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); @@ -1001,21 +1001,21 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan @Messages({}) private void deleteDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteDatabaseButtonActionPerformed if (JOptionPane.showConfirmDialog(null, - NbBundle.getMessage(this.getClass(), - "HashDbConfigPanel.deleteDbActionConfirmMsg"), - NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.deleteDbActionMsg"), - JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { - HashDb hashDb = ((HashSetTable) hashSetTable).getSelection(); - if (hashDb != null) { - try { - hashSetManager.removeHashDatabaseNoSave(hashDb); - } catch (HashDbManager.HashDbManagerException ex) { - JOptionPane.showMessageDialog(null, Bundle.HashLookupSettingsPanel_removeDatabaseFailure_message(hashDb.getHashSetName())); + NbBundle.getMessage(this.getClass(), + "HashDbConfigPanel.deleteDbActionConfirmMsg"), + NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.deleteDbActionMsg"), + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { + HashDb hashDb = ((HashSetTable) hashSetTable).getSelection(); + if (hashDb != null) { + try { + hashSetManager.removeHashDatabaseNoSave(hashDb); + } catch (HashDbManager.HashDbManagerException ex) { + JOptionPane.showMessageDialog(null, Bundle.HashLookupSettingsPanel_removeDatabaseFailure_message(hashDb.getHashSetName())); + } + hashSetTableModel.refreshModel(); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); } - hashSetTableModel.refreshModel(); - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - } } }//GEN-LAST:event_deleteDatabaseButtonActionPerformed diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashSetParser.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashSetParser.java new file mode 100644 index 0000000000..8a7a3ae034 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashSetParser.java @@ -0,0 +1,53 @@ +/* + * 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 org.sleuthkit.datamodel.TskCoreException; + +interface HashSetParser { + + /** + * 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 + */ + String getNextHash() throws TskCoreException; + + /** + * Check if there are more hashes to read + * + * @return true if we've read all expected hash values, false otherwise + */ + boolean doneReading(); + + /** + * Get the expected number of hashes in the file. This number can be an + * estimate. + * + * @return The expected hash count + */ + long getExpectedHashCount(); + + /** + * Closes the import file + */ + void close(); +} 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 new file mode 100644 index 0000000000..0db5442c0c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/IdxHashSetParser.java @@ -0,0 +1,121 @@ +/* + * 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.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * 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 { + + private final String filename; // Name of the input file (saved for logging) + private BufferedReader reader; // Input file + private final long totalHashes; // Estimated number of hashes + private boolean doneReading = false; // Flag for if we've hit the end of the file + + IdxHashSetParser(String filename) throws TskCoreException { + this.filename = filename; + try { + reader = new BufferedReader(new FileReader(filename)); + } catch (FileNotFoundException ex) { + throw new TskCoreException("Error opening file " + filename, ex); + } + + // Estimate the total number of hashes in the file since counting them all can be slow + 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) + } + + /** + * 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 { + String line; + + try { + while ((line = reader.readLine()) != null) { + + // idx files have a pipe after the hash, md5sum files should have a space + String[] parts = line.split("\\|| "); + + String hashStr = parts[0].toLowerCase(); + if (!hashStr.matches("^[0-9a-f]{32}$")) { + continue; + } + + return hashStr; + } + } catch (IOException ex) { + throw new TskCoreException("Error reading file " + filename, ex); + } + + // We've run out of data + doneReading = true; + 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 doneReading; + } + + /** + * 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 totalHashes; + } + + /** + * Closes the import file + */ + @Override + public void close() { + try { + reader.close(); + } catch (IOException ex) { + Logger.getLogger(IdxHashSetParser.class.getName()).log(Level.SEVERE, "Error closing 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 880feabcb7..37d3a20009 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ImportCentralRepoDbProgressDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ImportCentralRepoDbProgressDialog.java @@ -18,12 +18,9 @@ */ package org.sleuthkit.autopsy.modules.hashdatabase; -import java.awt.Cursor; +import java.awt.Color; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; import java.util.HashSet; import java.util.Set; import java.util.logging.Level; @@ -31,8 +28,9 @@ import javax.swing.JFrame; import javax.swing.SwingWorker; import javax.swing.WindowConstants; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.Executors; -import javax.swing.JOptionPane; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; @@ -45,95 +43,111 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; /** - * + * Imports a hash set into the central repository and updates a progress dialog */ -class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements PropertyChangeListener{ +class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements PropertyChangeListener { + + private CentralRepoImportWorker worker; // Swing worker that will import the file and send updates to the dialog + + @NbBundle.Messages({"ImportCentralRepoDbProgressDialog.title.text=Central Repository Import Progress",}) + ImportCentralRepoDbProgressDialog() { + super((JFrame) WindowManager.getDefault().getMainWindow(), + Bundle.ImportCentralRepoDbProgressDialog_title_text(), + true); + + initComponents(); + customizeComponents(); + } + + private void customizeComponents() { + // This is preventing the user from closing the dialog using the X + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + + bnOk.setEnabled(false); + } - private CentralRepoImportWorker worker; - /** - * + * Import the selected hash set into the central repository. Will bring up a + * progress dialog while the import is in progress. + * * @param hashSetName * @param version * @param orgId * @param searchDuringIngest * @param sendIngestMessages * @param knownFilesType - * @param importFile + * @param readOnly + * @param importFileName */ - @NbBundle.Messages({"ImportCentralRepoDbProgressDialog.title.text=Central Repository Import Progress", - }) - ImportCentralRepoDbProgressDialog() { - super((JFrame) WindowManager.getDefault().getMainWindow(), - Bundle.ImportCentralRepoDbProgressDialog_title_text(), - true); - - initComponents(); - customizeComponents(); - } - - private void customizeComponents(){ - setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - bnOk.setEnabled(false); - } - void importFile(String hashSetName, String version, int orgId, boolean searchDuringIngest, boolean sendIngestMessages, HashDbManager.HashDb.KnownFilesType knownFilesType, - boolean readOnly, String importFileName){ - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - File importFile = new File(importFileName); - worker = new ImportIDXWorker(hashSetName, version, orgId, searchDuringIngest, sendIngestMessages, - knownFilesType, readOnly, importFile); + boolean readOnly, String importFileName) { + + worker = new CentralRepoImportWorker(hashSetName, version, orgId, searchDuringIngest, sendIngestMessages, + knownFilesType, readOnly, importFileName); worker.addPropertyChangeListener(this); worker.execute(); - - setLocationRelativeTo((JFrame) WindowManager.getDefault().getMainWindow()); - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + + setLocationRelativeTo((JFrame) WindowManager.getDefault().getMainWindow()); this.setVisible(true); } - - HashDbManager.HashDb getDatabase(){ - if(worker != null){ + + /** + * Get the HashDb object for the newly imported data. Should be called after + * importFile completes. + * + * @return The new HashDb object or null if the import failed/was canceled + */ + HashDbManager.HashDb getDatabase() { + if (worker != null) { return worker.getDatabase(); } return null; } - - @NbBundle.Messages({"ImportCentralRepoDbProgressDialog.linesProcessed= lines processed"}) + + /** + * Updates the dialog from events from the worker. The two events we handle + * are progress updates and the done event. + * + * @param evt + */ + @NbBundle.Messages({"ImportCentralRepoDbProgressDialog.errorParsingFile.message=Error parsing hash set file"}) @Override public void propertyChange(PropertyChangeEvent evt) { - - if("progress".equals(evt.getPropertyName())){ - progressBar.setValue(worker.getProgressPercentage()); + + if ("progress".equals(evt.getPropertyName())) { + // The progress has been updated. Update the progress bar and text + progressBar.setValue(worker.getProgress()); lbProgress.setText(getProgressString()); } else if ("state".equals(evt.getPropertyName()) && (SwingWorker.StateValue.DONE.equals(evt.getNewValue()))) { - // Disable cancel and enable ok + + // The worker is done processing + // Disable cancel button and enable ok bnCancel.setEnabled(false); bnOk.setEnabled(true); - - progressBar.setValue(progressBar.getMaximum()); - lbProgress.setText(getProgressString()); + + if (worker.getImportSuccess()) { + // If the import succeeded, finish the progress bar and display the + // total number of imported hashes + progressBar.setValue(progressBar.getMaximum()); + lbProgress.setText(getProgressString()); + } else { + // If there was an error, reset the progress bar and display an error message + progressBar.setValue(0); + lbProgress.setForeground(Color.red); + lbProgress.setText(Bundle.ImportCentralRepoDbProgressDialog_errorParsingFile_message()); + } } } - - private String getProgressString(){ - return worker.getLinesProcessed() + Bundle.ImportCentralRepoDbProgressDialog_linesProcessed(); + + @NbBundle.Messages({"ImportCentralRepoDbProgressDialog.linesProcessed.message= hashes processed"}) + private String getProgressString() { + return worker.getNumHashesProcessed() + Bundle.ImportCentralRepoDbProgressDialog_linesProcessed_message(); } - - private interface CentralRepoImportWorker{ - - void execute(); - boolean cancel(boolean mayInterruptIfRunning); - void addPropertyChangeListener(PropertyChangeListener dialog); - int getProgressPercentage(); - long getLinesProcessed(); - HashDbManager.HashDb getDatabase(); - } - - class ImportIDXWorker extends SwingWorker implements CentralRepoImportWorker{ - + + private class CentralRepoImportWorker extends SwingWorker { + private final int HASH_IMPORT_THRESHOLD = 10000; private final String hashSetName; private final String version; @@ -142,16 +156,16 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P private final boolean sendIngestMessages; private final HashDbManager.HashDb.KnownFilesType knownFilesType; private final boolean readOnly; - private final File importFile; - private final long totalLines; - private int referenceSetID = -1; + private final String importFileName; private HashDbManager.CentralRepoHashSet newHashDb = null; - private final AtomicLong numLines = new AtomicLong(); - - ImportIDXWorker(String hashSetName, String version, int orgId, - boolean searchDuringIngest, boolean sendIngestMessages, HashDbManager.HashDb.KnownFilesType knownFilesType, - boolean readOnly, File importFile){ - + private final AtomicInteger referenceSetID = new AtomicInteger(); + private final AtomicLong hashCount = new AtomicLong(); + private final AtomicBoolean importSuccess = new AtomicBoolean(); + + CentralRepoImportWorker(String hashSetName, String version, int orgId, + boolean searchDuringIngest, boolean sendIngestMessages, HashDbManager.HashDb.KnownFilesType knownFilesType, + boolean readOnly, String importFileName) { + this.hashSetName = hashSetName; this.version = version; this.orgId = orgId; @@ -159,146 +173,165 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P this.sendIngestMessages = sendIngestMessages; this.knownFilesType = knownFilesType; this.readOnly = readOnly; - this.importFile = importFile; - this.numLines.set(0); - - this.totalLines = getEstimatedTotalHashes(); + this.importFileName = importFileName; + this.hashCount.set(0); + this.importSuccess.set(false); + this.referenceSetID.set(-1); } - + /** - * Doing an actual count of the number of lines in a large idx file (such - * as the nsrl) is slow, so just get something in the general area for the - * progress bar. - * @return Approximate number of hashes in the file + * Get the newly created database + * + * @return the imported database. May be null if an error occurred or + * the user canceled */ - final long getEstimatedTotalHashes(){ - long fileSize = importFile.length(); - return (fileSize / 0x33 + 1); // IDX file lines are generally 0x33 bytes long, and we don't want this to be zero - } - - @Override - public HashDbManager.HashDb getDatabase(){ + synchronized HashDbManager.CentralRepoHashSet getDatabase() { return newHashDb; } - - @Override - public long getLinesProcessed(){ - return numLines.get(); + + /** + * Get the number of hashes that have been read in so far + * + * @return current hash count + */ + long getNumHashesProcessed() { + return hashCount.get(); } - - @Override - public int getProgressPercentage(){ - return this.getProgress(); + + /** + * Check if the import was successful or if there was an error. + * + * @return true if the import process completed without error, false + * otherwise + */ + boolean getImportSuccess() { + return importSuccess.get(); } - + @Override protected Void doInBackground() throws Exception { - TskData.FileKnown knownStatus; - if (knownFilesType.equals(HashDbManager.HashDb.KnownFilesType.KNOWN)) { - knownStatus = TskData.FileKnown.KNOWN; + // Create the hash set parser + HashSetParser hashSetParser; + if (importFileName.toLowerCase().endsWith(".idx") || importFileName.toLowerCase().endsWith(".txt")) { + hashSetParser = new IdxHashSetParser(importFileName); + } else if (importFileName.toLowerCase().endsWith(".hash")) { + hashSetParser = new EncaseHashSetParser(importFileName); + } else if (importFileName.toLowerCase().endsWith(".kdb")) { + hashSetParser = new KdbHashSetParser(importFileName); + } else if (importFileName.toLowerCase().endsWith(".hsh")) { + hashSetParser = new HashkeeperHashSetParser(importFileName); } else { - knownStatus = TskData.FileKnown.BAD; + // We've gotten here with a format that can't be processed + throw new TskCoreException("Hash set to import is an unknown format : " + importFileName); } - - // Create an empty hashset in the central repository - referenceSetID = EamDb.getInstance().newReferenceSet(new EamGlobalSet(orgId, hashSetName, version, knownStatus, readOnly)); - EamDb dbManager = EamDb.getInstance(); - CorrelationAttribute.Type contentType = dbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); // get "FILES" type - BufferedReader reader = new BufferedReader(new FileReader(importFile)); - String line; - Set globalInstances = new HashSet<>(); - - while ((line = reader.readLine()) != null) { - if(isCancelled()){ - return null; + try { + // Conver to the FileKnown enum used by EamGlobalSet + TskData.FileKnown knownStatus; + if (knownFilesType.equals(HashDbManager.HashDb.KnownFilesType.KNOWN)) { + knownStatus = TskData.FileKnown.KNOWN; + } else { + knownStatus = TskData.FileKnown.BAD; } - String[] parts = line.split("\\|"); + // Create an empty hashset in the central repository + EamDb dbManager = EamDb.getInstance(); + referenceSetID.set(dbManager.newReferenceSet(new EamGlobalSet(orgId, hashSetName, version, knownStatus, readOnly))); - // 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) { - continue; - } + // Get the "FILES" content type. This is a database lookup so we + // only want to do it once. + CorrelationAttribute.Type contentType = dbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); - EamGlobalFileInstance eamGlobalFileInstance = new EamGlobalFileInstance( - referenceSetID, - parts[0].toLowerCase(), - knownStatus, - ""); + // Holds the current batch of hashes that need to be written to the central repo + Set globalInstances = new HashSet<>(); - globalInstances.add(eamGlobalFileInstance); - numLines.incrementAndGet(); + while (!hashSetParser.doneReading()) { + if (isCancelled()) { + return null; + } - if(numLines.get() % HASH_IMPORT_THRESHOLD == 0){ - dbManager.bulkInsertReferenceTypeEntries(globalInstances, contentType); - globalInstances.clear(); + String newHash = hashSetParser.getNextHash(); - int progress = (int)(numLines.get() * 100 / totalLines); - if(progress < 100){ - this.setProgress(progress); - } else { - this.setProgress(99); + if (newHash != null) { + EamGlobalFileInstance eamGlobalFileInstance = new EamGlobalFileInstance( + referenceSetID.get(), + newHash, + knownStatus, + ""); + + globalInstances.add(eamGlobalFileInstance); + + // If we've hit the threshold for writing the hashes, write them + // all to the central repo + if (hashCount.incrementAndGet() % HASH_IMPORT_THRESHOLD == 0) { + dbManager.bulkInsertReferenceTypeEntries(globalInstances, contentType); + globalInstances.clear(); + + int progress = (int) (hashCount.get() * 100 / hashSetParser.getExpectedHashCount()); + if (progress < 100) { + this.setProgress(progress); + } else { + this.setProgress(99); + } + } } } + + // Add any remaining hashes to the central repo + dbManager.bulkInsertReferenceTypeEntries(globalInstances, contentType); + this.setProgress(100); + return null; + } finally { + hashSetParser.close(); } - - dbManager.bulkInsertReferenceTypeEntries(globalInstances, contentType); - this.setProgress(100); - - return null; } - - private void deleteIncompleteSet(int idToDelete){ - if(idToDelete >= 0){ - + + private void deleteIncompleteSet() { + if (referenceSetID.get() >= 0) { + // This can be slow on large reference sets Executors.newSingleThreadExecutor().execute(new Runnable() { - @Override + @Override public void run() { - try{ - EamDb.getInstance().deleteReferenceSet(idToDelete); - } catch (EamDbException ex2){ + try { + EamDb.getInstance().deleteReferenceSet(referenceSetID.get()); + } catch (EamDbException ex2) { Logger.getLogger(ImportCentralRepoDbProgressDialog.class.getName()).log(Level.SEVERE, "Error deleting incomplete hash set from central repository", ex2); } } }); } } - - @NbBundle.Messages({"ImportCentralRepoDbProgressDialog.addDbError.message=Error adding new hash set"}) + @Override - protected void done() { - if(isCancelled()){ + synchronized protected void done() { + + if (isCancelled()) { // If the user hit cancel, delete this incomplete hash set from the central repo - deleteIncompleteSet(referenceSetID); + deleteIncompleteSet(); return; } - + try { get(); - try{ - newHashDb = HashDbManager.getInstance().addExistingCentralRepoHashSet(hashSetName, version, - referenceSetID, + try { + newHashDb = HashDbManager.getInstance().addExistingCentralRepoHashSet(hashSetName, version, + referenceSetID.get(), searchDuringIngest, sendIngestMessages, knownFilesType, readOnly); - } catch (TskCoreException ex){ - JOptionPane.showMessageDialog(null, Bundle.ImportCentralRepoDbProgressDialog_addDbError_message()); + importSuccess.set(true); + } catch (TskCoreException ex) { Logger.getLogger(ImportCentralRepoDbProgressDialog.class.getName()).log(Level.SEVERE, "Error adding imported hash set", ex); } } catch (Exception ex) { // Delete this incomplete hash set from the central repo - if(referenceSetID >= 0){ - try{ - EamDb.getInstance().deleteReferenceSet(referenceSetID); - } catch (EamDbException ex2){ - Logger.getLogger(ImportCentralRepoDbProgressDialog.class.getName()).log(Level.SEVERE, "Error deleting incomplete hash set from central repository", ex); - } - } + deleteIncompleteSet(); + Logger.getLogger(ImportCentralRepoDbProgressDialog.class.getName()).log(Level.SEVERE, "Error importing hash set", ex); } - } + } + } - + /** * 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 diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/KdbHashSetParser.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/KdbHashSetParser.java new file mode 100644 index 0000000000..5935784087 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/KdbHashSetParser.java @@ -0,0 +1,160 @@ +/* + * 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.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Parser for Autopsy/TSK-created databases (*.kdb) + */ +public class KdbHashSetParser implements HashSetParser { + + private final String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS + private final String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS + + private final String filename; // Name of the input file (saved for logging) + private final long totalHashes; // Estimated number of hashes + private int totalHashesRead = 0; // Number of hashes that have been read + private Connection conn; + private Statement statement; + private ResultSet resultSet; + + KdbHashSetParser(String filename) throws TskCoreException { + this.filename = filename; + + conn = null; + statement = null; + resultSet = null; + + try { + // Open the database + StringBuilder connectionURL = new StringBuilder(); + connectionURL.append(JDBC_BASE_URI); + connectionURL.append(filename); + Class.forName(JDBC_DRIVER); + conn = DriverManager.getConnection(connectionURL.toString()); + + // Get the number of hashes in the table + statement = conn.createStatement(); + resultSet = statement.executeQuery("SELECT count(*) AS count FROM hashes"); + if (resultSet.next()) { + totalHashes = resultSet.getLong("count"); + } else { + close(); + throw new TskCoreException("Error getting hash count from database " + filename); + } + + // Get the hashes + resultSet = statement.executeQuery("SELECT md5 FROM hashes"); + + // At this point, getNextHash can read each hash from the result set + } catch (ClassNotFoundException | SQLException ex) { + throw new TskCoreException("Error opening/reading database " + filename, ex); + } + + } + + /** + * Get the next hash to import + * + * @return The hash as a string + * @throws TskCoreException + */ + @Override + public String getNextHash() throws TskCoreException { + + try { + if (resultSet.next()) { + byte[] hashBytes = resultSet.getBytes("md5"); + StringBuilder sb = new StringBuilder(); + for (byte b : hashBytes) { + sb.append(String.format("%02x", b)); + } + + if (sb.toString().length() != 32) { + throw new TskCoreException("Hash has incorrect length: " + sb.toString()); + } + + totalHashesRead++; + return sb.toString(); + } else { + throw new TskCoreException("Could not read expected number of hashes from database " + filename); + } + } catch (SQLException ex) { + throw new TskCoreException("Error reading hash from result set for database " + filename, ex); + } + } + + /** + * 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 (totalHashesRead >= totalHashes); + } + + /** + * Get the expected number of hashes in the file. + * + * @return The expected hash count + */ + @Override + public long getExpectedHashCount() { + return totalHashes; + } + + /** + * Closes the import file + */ + @Override + public final void close() { + if (statement != null) { + try { + statement.close(); + } catch (SQLException ex) { + Logger.getLogger(KdbHashSetParser.class.getName()).log(Level.SEVERE, "Error closing prepared statement.", ex); + } + } + + if (resultSet != null) { + try { + resultSet.close(); + } catch (SQLException ex) { + Logger.getLogger(KdbHashSetParser.class.getName()).log(Level.SEVERE, "Error closing result set.", ex); + } + } + + if (conn != null) { + try { + conn.close(); + } catch (SQLException ex) { + Logger.getLogger(KdbHashSetParser.class.getName()).log(Level.SEVERE, "Error closing connection.", ex); + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java index bb2657c8ef..cfc7272315 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java @@ -23,6 +23,8 @@ import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle.Messages; @@ -116,16 +118,18 @@ class CallLogAnalyzer { type = resultSet.getString("type"); //NON-NLS bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG); //create a call log and then add attributes from result set. + Collection attributes = new ArrayList<>(); if (type.equalsIgnoreCase("outgoing")) { //NON-NLS - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, moduleName, number)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, moduleName, number)); } else { /// Covers INCOMING and MISSED - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, moduleName, number)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, moduleName, number)); } - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START, moduleName, date)); // RC: Should be long! - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END, moduleName, duration + date)); // RC: Should be long! - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, type)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, name)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START, moduleName, date)); // RC: Should be long! + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END, moduleName, duration + date)); // RC: Should be long! + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, type)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, name)); + bba.addAttributes(attributes); try { // index the artifact for keyword search blackboard.indexArtifact(bba); diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java index ecfce79ae4..efa4494f26 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java @@ -28,6 +28,8 @@ import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle.Messages; @@ -128,6 +130,7 @@ class ContactAnalyzer { BlackboardArtifact bba; bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); + Collection attributes = new ArrayList<>(); String name; String oldName = ""; String mimetype; // either phone or email @@ -137,16 +140,16 @@ class ContactAnalyzer { data1 = resultSet.getString("data1"); //NON-NLS mimetype = resultSet.getString("mimetype"); //NON-NLS if (name.equals(oldName) == false) { - bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, name)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, name)); } if (mimetype.equals("vnd.android.cursor.item/phone_v2")) { //NON-NLS - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, moduleName, data1)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, moduleName, data1)); } else { - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, moduleName, data1)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, moduleName, data1)); } oldName = name; + bba.addAttributes(attributes); try { // index the artifact for keyword search blackboard.indexArtifact(bba); diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java index 7a7e2f0cfa..d1a81663fe 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java @@ -23,6 +23,8 @@ import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle; @@ -116,21 +118,22 @@ class TextMessageAnalyzer { body = resultSet.getString("body"); //NON-NLS bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); //create Message artifact and then add attributes from result set. - + Collection attributes = new ArrayList<>(); // @@@ NEed to put into more specific TO or FROM if (type.equals("1")) { - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, NbBundle.getMessage(this.getClass(), "TextMessageAnalyzer.bbAttribute.incoming"))); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, moduleName, address)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, NbBundle.getMessage(this.getClass(), "TextMessageAnalyzer.bbAttribute.incoming"))); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, moduleName, address)); } else { - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, NbBundle.getMessage(this.getClass(), "TextMessageAnalyzer.bbAttribute.outgoing"))); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, moduleName, address)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, NbBundle.getMessage(this.getClass(), "TextMessageAnalyzer.bbAttribute.outgoing"))); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, moduleName, address)); } - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, date)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, type)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT, moduleName, subject)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, moduleName, body)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName, NbBundle.getMessage(this.getClass(), "TextMessageAnalyzer.bbAttribute.smsMessage"))); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, date)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, type)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT, moduleName, subject)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, moduleName, body)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName, NbBundle.getMessage(this.getClass(), "TextMessageAnalyzer.bbAttribute.smsMessage"))); + bba.addAttributes(attributes); try { // index the artifact for keyword search blackboard.indexArtifact(bba); diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java index 78a8481f17..88ee3ae1b7 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.modules.interestingitems; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -121,6 +122,7 @@ final class FilesIdentifierIngestModule implements FileIngestModule { // blackboard. String moduleName = InterestingItemsIngestModuleFactory.getModuleName(); BlackboardArtifact artifact = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); + Collection attributes = new ArrayList<>(); // Add a set name attribute to the artifact. This adds a // fair amount of redundant data to the attributes table @@ -128,13 +130,14 @@ final class FilesIdentifierIngestModule implements FileIngestModule { // otherwise would requires reworking the interesting files // set hit artifact. BlackboardAttribute setNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, filesSet.getName()); - artifact.addAttribute(setNameAttribute); + attributes.add(setNameAttribute); // Add a category attribute to the artifact to record the // interesting files set membership rule that was satisfied. BlackboardAttribute ruleNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, moduleName, ruleSatisfied); - artifact.addAttribute(ruleNameAttribute); + attributes.add(ruleNameAttribute); + artifact.addAttributes(attributes); try { // index the artifact for keyword search blackboard.indexArtifact(artifact); diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java b/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java index 8a8466d89f..afc84d87aa 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.modules.stix; +import java.util.ArrayList; +import java.util.Collection; import java.util.logging.Level; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; @@ -70,10 +72,12 @@ class StixArtifactData { } BlackboardArtifact bba = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, "Stix", setName)); //NON-NLS - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE, "Stix", observableId)); //NON-NLS - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, "Stix", objType)); //NON-NLS + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, "Stix", setName)); //NON-NLS + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE, "Stix", observableId)); //NON-NLS + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, "Stix", objType)); //NON-NLS + bba.addAttributes(attributes); try { // index the artifact for keyword search blackboard.indexArtifact(bba); 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 6de7e62f86..976804f1e7 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; @@ -305,7 +306,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; } @@ -316,7 +318,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; @@ -382,12 +384,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 @@ -969,7 +972,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")); @@ -1006,7 +1010,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/Core/src/org/sleuthkit/autopsy/test/InterestingArtifactCreatorIngestModule.java b/Core/src/org/sleuthkit/autopsy/test/InterestingArtifactCreatorIngestModule.java index 5e9a876ab6..a57623f02e 100755 --- a/Core/src/org/sleuthkit/autopsy/test/InterestingArtifactCreatorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/test/InterestingArtifactCreatorIngestModule.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.test; +import java.util.ArrayList; +import java.util.Collection; import java.util.logging.Level; import org.openide.util.Exceptions; @@ -77,6 +79,7 @@ final class InterestingArtifactCreatorIngestModule extends FileIngestModuleAdapt Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); BlackboardArtifact.Type artifactTypeBase = blackboard.getOrAddArtifactType(ARTIFACT_TYPE_NAMES[randomArtIndex], ARTIFACT_DISPLAY_NAMES[randomArtIndex]); BlackboardArtifact artifactBase = file.newArtifact(artifactTypeBase.getTypeID()); + Collection baseAttributes = new ArrayList<>(); String commentTxt; BlackboardAttribute baseAttr; switch (artifactBase.getArtifactTypeID()) { @@ -84,7 +87,7 @@ final class InterestingArtifactCreatorIngestModule extends FileIngestModuleAdapt commentTxt = "www.placeholderWebsiteDOTCOM"; baseAttr = new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, "Fake Web BookMark", "www.thisWebsiteIsStillFake.com"); - artifactBase.addAttribute(baseAttr); + baseAttributes.add(baseAttr); break; case 9: commentTxt = "fakeKeyword"; @@ -94,29 +97,32 @@ final class InterestingArtifactCreatorIngestModule extends FileIngestModuleAdapt BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, "Fake Keyword Search", "Fake"); BlackboardAttribute keyword = new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, "Fake Keyword Search", "FakeKeyword"); - artifactBase.addAttribute(baseAttr); - artifactBase.addAttribute(set); - artifactBase.addAttribute(keyword); + baseAttributes.add(baseAttr); + baseAttributes.add(set); + baseAttributes.add(keyword); break; case 25: commentTxt = "fake phone number from"; baseAttr = new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, "Fake Call Log Whatever", "555-555-5555"); - artifactBase.addAttribute(baseAttr); + baseAttributes.add(baseAttr); break; default: commentTxt = "DEPENDENT ON ARTIFACT TYPE"; break; } + artifactBase.addAttributes(baseAttributes); BlackboardArtifact artifact = file.newArtifact(artifactType.getTypeID()); + Collection attributes = new ArrayList<>(); BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, "ArtifactsAndTxt"); BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, commentTxt); BlackboardAttribute att3 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, MODULE_NAME, ""); - artifact.addAttribute(att); - artifact.addAttribute(att2); - artifact.addAttribute(att3); - artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactBase.getArtifactID())); + attributes.add(att); + attributes.add(att2); + attributes.add(att3); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactBase.getArtifactID())); + artifact.addAttributes(attributes); } catch (TskCoreException ex) { logger.log(Level.SEVERE, String.format("Failed to process file (obj_id = %d)", file.getId()), ex); return ProcessResult.ERROR; 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/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsCollector.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsCollector.java index 9567b70470..7b07a15aec 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsCollector.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsCollector.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,14 +29,15 @@ import org.sleuthkit.autopsy.coreutils.Logger; * Collects metrics for an auto ingest cluster. */ final class AutoIngestMetricsCollector { - + private static final Logger LOGGER = Logger.getLogger(AutoIngestMetricsCollector.class.getName()); + private static final int MINIMUM_SUPPORTED_JOB_NODE_VERSION = 1; private CoordinationService coordinationService; - + /** * Creates an instance of the AutoIngestMetricsCollector. - * - * @throws AutoIngestMetricsCollector.AutoIngestMetricsCollectorException + * + * @throws AutoIngestMetricsCollector.AutoIngestMetricsCollectorException */ AutoIngestMetricsCollector() throws AutoIngestMetricsCollectorException { try { @@ -45,7 +46,7 @@ final class AutoIngestMetricsCollector { throw new AutoIngestMetricsCollectorException("Failed to get coordination service", ex); //NON-NLS } } - + /** * Gets a new metrics snapshot from the coordination service for an auto * ingest cluster. @@ -59,7 +60,7 @@ final class AutoIngestMetricsCollector { for (String node : nodeList) { try { AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, node)); - if (nodeData.getVersion() < 1) { + if (nodeData.getVersion() < MINIMUM_SUPPORTED_JOB_NODE_VERSION) { /* * Ignore version '0' nodes that have not been * "upgraded" since they don't carry enough data. @@ -78,7 +79,7 @@ final class AutoIngestMetricsCollector { */ break; case COMPLETED: - newMetricsSnapshot.addCompletedJobDate(job.getCompletedDate()); + newMetricsSnapshot.addCompletedJobMetric(job.getCompletedDate(), job.getDataSourceSize()); break; default: LOGGER.log(Level.SEVERE, "Unknown AutoIngestJobData.ProcessingStatus"); @@ -92,41 +93,80 @@ final class AutoIngestMetricsCollector { LOGGER.log(Level.SEVERE, String.format("Failed to create a job for '%s'", node), ex); } } - + return newMetricsSnapshot; - + } catch (CoordinationService.CoordinationServiceException ex) { LOGGER.log(Level.SEVERE, "Failed to get node list from coordination service", ex); return new MetricsSnapshot(); } } - + /** * A snapshot of metrics for an auto ingest cluster. */ static final class MetricsSnapshot { - - private final List completedJobDates = new ArrayList<>(); - + + private final List completedJobMetrics = new ArrayList<>(); + /** - * Gets a list of completed job dates, formatted in milliseconds. - * - * @return The completed job dates, formatted in milliseconds. + * Gets a list of completed job metrics. + * + * @return The completed job metrics. */ - List getCompletedJobDates() { - return new ArrayList<>(completedJobDates); + List getCompletedJobMetrics() { + return new ArrayList<>(completedJobMetrics); } - + /** - * Adds a new date to the list of completed job dates. - * - * @param date The date to be added. + * Adds a new metric to the list of completed job metrics. + * + * @param completedDate The completed job date. + * @param dataSourceSize The data source size. */ - void addCompletedJobDate(java.util.Date date) { - completedJobDates.add(date.getTime()); + void addCompletedJobMetric(java.util.Date completedDate, long dataSourceSize) { + completedJobMetrics.add(new JobMetric(completedDate, dataSourceSize)); } } - + + /** + * A single job metric for an auto ingest cluster. + */ + static final class JobMetric { + + private final long completedDate; + private final long dataSourceSize; + + /** + * Instantiates a job metric. + * + * @param completedDate The job completion date. + * @param dataSourceSize The data source size. + */ + JobMetric(java.util.Date completedDate, long dataSourceSize) { + this.completedDate = completedDate.getTime(); + this.dataSourceSize = dataSourceSize; + } + + /** + * Gets the job completion date, formatted in milliseconds. + * + * @return The job completion date. + */ + long getCompletedDate() { + return completedDate; + } + + /** + * Gets the data source size. + * + * @return The data source size. + */ + long getDataSourceSize() { + return dataSourceSize; + } + } + /** * Exception type thrown when there is an error completing an auto ingest * metrics collector operation. @@ -157,4 +197,4 @@ final class AutoIngestMetricsCollector { } } -} \ No newline at end of file +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.form b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.form index 49e700ca21..cbe7b53c2f 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.form +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.form @@ -30,10 +30,14 @@ - - + + - + + + + + @@ -43,17 +47,17 @@ - + + + + + + + - - - - - - - + @@ -109,5 +113,12 @@ + + + + + + + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java index 80bcb6958b..af0679be5f 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,24 +25,28 @@ import java.awt.Window; import java.sql.Date; import java.text.SimpleDateFormat; import java.time.ZoneOffset; +import java.util.List; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMetricsCollector.JobMetric; /** * Displays auto ingest metrics for a cluster. */ final class AutoIngestMetricsDialog extends javax.swing.JDialog { - + + private static final int GIGABYTE_SIZE = 1073741824; + private final AutoIngestMetricsCollector autoIngestMetricsCollector; /** * Creates an instance of AutoIngestMetricsDialog - * + * * @param parent The parent container. */ @Messages({ "AutoIngestMetricsDialog.title.text=Auto Ingest Cluster Metrics", - "AutoIngestMetricsDialog.initReportText=Select a date below and click the 'Get Metrics Since...' button to generate\na metrics report." + "AutoIngestMetricsDialog.initReportText=Select a date above and click the 'Generate Metrics Report' button to generate\na metrics report." }) AutoIngestMetricsDialog(Container parent) throws AutoIngestMetricsDialogException { super((Window) parent, NbBundle.getMessage(AutoIngestMetricsDialog.class, "AutoIngestMetricsDialog.title.text"), ModalityType.MODELESS); @@ -58,34 +62,39 @@ final class AutoIngestMetricsDialog extends javax.swing.JDialog { setLocationRelativeTo(parent); setVisible(true); } - + /** * Update the metrics shown in the report text area. */ private void updateMetrics() { - if(datePicker.getDate() == null) { + if (datePicker.getDate() == null) { return; } - + AutoIngestMetricsCollector.MetricsSnapshot metricsSnapshot = autoIngestMetricsCollector.queryCoordinationServiceForMetrics(); - Object[] completedJobDates = metricsSnapshot.getCompletedJobDates().toArray(); - int count = 0; + List completedJobMetrics = metricsSnapshot.getCompletedJobMetrics(); + int jobsCompleted = 0; + long dataSourceSizeTotal = 0; long pickedDate = datePicker.getDate().atStartOfDay().toEpochSecond(ZoneOffset.UTC) * 1000; - for(int i = completedJobDates.length - 1; i >= 0; i--) { - if((Long)completedJobDates[i] >= pickedDate) { - count++; + + for (JobMetric jobMetric : completedJobMetrics) { + if (jobMetric.getCompletedDate() >= pickedDate) { + jobsCompleted++; + dataSourceSizeTotal += jobMetric.getDataSourceSize(); } } - + SimpleDateFormat dateFormatter = new SimpleDateFormat("MMM d, yyyy"); reportTextArea.setText(String.format( - "Since %s:\n" + - "\tNumber of Jobs Completed: %d\n", + "Since %s:\n" + + "Number of Jobs Completed: %d\n" + + "Total Size of Data Sources: %.1f GB\n", dateFormatter.format(Date.valueOf(datePicker.getDate())), - count + jobsCompleted, + (double) dataSourceSizeTotal / GIGABYTE_SIZE )); } - + /** * Exception type thrown when there is an error completing an auto ingest * metrics dialog operation. @@ -131,6 +140,7 @@ final class AutoIngestMetricsDialog extends javax.swing.JDialog { reportTextArea = new javax.swing.JTextArea(); metricsButton = new javax.swing.JButton(); datePicker = new DatePicker(); + startingDataLabel = new javax.swing.JLabel(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setAlwaysOnTop(true); @@ -158,6 +168,8 @@ final class AutoIngestMetricsDialog extends javax.swing.JDialog { datePicker.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestMetricsDialog.class, "AutoIngestMetricsDialog.datePicker.toolTipText")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(startingDataLabel, org.openide.util.NbBundle.getMessage(AutoIngestMetricsDialog.class, "AutoIngestMetricsDialog.startingDataLabel.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( @@ -167,24 +179,28 @@ final class AutoIngestMetricsDialog extends javax.swing.JDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane1) .addGroup(layout.createSequentialGroup() - .addComponent(metricsButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(startingDataLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(datePicker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 33, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 7, Short.MAX_VALUE) + .addComponent(metricsButton)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) .addComponent(closeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE))) .addContainerGap()) ); 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.LEADING) + .addComponent(metricsButton) + .addComponent(datePicker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(startingDataLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 128, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(closeButton) - .addComponent(metricsButton)) - .addComponent(datePicker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(closeButton) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); @@ -208,5 +224,6 @@ final class AutoIngestMetricsDialog extends javax.swing.JDialog { private javax.swing.JScrollPane jScrollPane1; private javax.swing.JButton metricsButton; private javax.swing.JTextArea reportTextArea; + private javax.swing.JLabel startingDataLabel; // End of variables declaration//GEN-END:variables -} \ No newline at end of file +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties index 5f335b1ba4..d953b0ceb3 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties @@ -228,10 +228,11 @@ AutoIngestDashboard.prioritizeCaseButton.toolTipText=Move all images associated AutoIngestDashboard.prioritizeCaseButton.text=Prioritize &Case AutoIngestMetricsDialog.reportTextArea.text= AutoIngestDashboard.clusterMetricsButton.text=Cluster Metrics -AutoIngestMetricsDialog.metricsButton.text=Get Metrics Since... +AutoIngestMetricsDialog.metricsButton.text=Generate Metrics Report AutoIngestMetricsDialog.closeButton.text=Close AutoIngestMetricsDialog.datePicker.toolTipText=Choose a date ArchiveFilePanel.pathLabel.text=Browse for an archive file: ArchiveFilePanel.browseButton.text=Browse ArchiveFilePanel.pathTextField.text= -ArchiveFilePanel.errorLabel.text=Error Label \ No newline at end of file +ArchiveFilePanel.errorLabel.text=Error Label +AutoIngestMetricsDialog.startingDataLabel.text=Starting Date: 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/CategorizeAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java index e55078018e..3f8b3aecd0 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java @@ -40,7 +40,7 @@ import org.controlsfx.control.action.ActionUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; @@ -60,15 +60,15 @@ public class CategorizeAction extends Action { private final ImageGalleryController controller; private final UndoRedoManager undoManager; - private final Category cat; + private final DhsImageCategory cat; private final Set selectedFileIDs; private final Boolean createUndo; - public CategorizeAction(ImageGalleryController controller, Category cat, Set selectedFileIDs) { + public CategorizeAction(ImageGalleryController controller, DhsImageCategory cat, Set selectedFileIDs) { this(controller, cat, selectedFileIDs, true); } - private CategorizeAction(ImageGalleryController controller, Category cat, Set selectedFileIDs, Boolean createUndo) { + private CategorizeAction(ImageGalleryController controller, DhsImageCategory cat, Set selectedFileIDs, Boolean createUndo) { super(cat.getDisplayName()); this.controller = controller; this.undoManager = controller.getUndoManager(); @@ -103,7 +103,7 @@ public class CategorizeAction extends Action { // Each category get an item in the sub-menu. Selecting one of these menu items adds // a tag with the associated category. - for (final Category cat : Category.values()) { + for (final DhsImageCategory cat : DhsImageCategory.values()) { MenuItem categoryItem = ActionUtils.createMenuItem(new CategorizeAction(controller, cat, selected)); getItems().add(categoryItem); } @@ -118,9 +118,9 @@ public class CategorizeAction extends Action { private final Set fileIDs; private final boolean createUndo; - private final Category cat; + private final DhsImageCategory cat; - CategorizeTask(Set fileIDs, @Nonnull Category cat, boolean createUndo) { + CategorizeTask(Set fileIDs, @Nonnull DhsImageCategory cat, boolean createUndo) { super(); this.fileIDs = fileIDs; java.util.Objects.requireNonNull(cat); @@ -132,14 +132,14 @@ public class CategorizeAction extends Action { public void run() { final DrawableTagsManager tagsManager = controller.getTagsManager(); final CategoryManager categoryManager = controller.getCategoryManager(); - Map oldCats = new HashMap<>(); + Map oldCats = new HashMap<>(); TagName tagName = categoryManager.getTagName(cat); - TagName catZeroTagName = categoryManager.getTagName(Category.ZERO); + TagName catZeroTagName = categoryManager.getTagName(DhsImageCategory.ZERO); for (long fileID : fileIDs) { try { DrawableFile file = controller.getFileFromId(fileID); //drawable db access if (createUndo) { - Category oldCat = file.getCategory(); //drawable db access + DhsImageCategory oldCat = file.getCategory(); //drawable db access TagName oldCatTagName = categoryManager.getTagName(oldCat); if (false == tagName.equals(oldCatTagName)) { oldCats.put(fileID, oldCat); @@ -147,7 +147,7 @@ public class CategorizeAction extends Action { } final List fileTags = tagsManager.getContentTags(file); - if (tagName == categoryManager.getTagName(Category.ZERO)) { + if (tagName == categoryManager.getTagName(DhsImageCategory.ZERO)) { // delete all cat tags for cat-0 fileTags.stream() .filter(tag -> CategoryManager.isCategoryTagName(tag.getName())) @@ -189,11 +189,11 @@ public class CategorizeAction extends Action { @Immutable private final class CategorizationChange implements UndoRedoManager.UndoableCommand { - private final Category newCategory; - private final ImmutableMap oldCategories; + private final DhsImageCategory newCategory; + private final ImmutableMap oldCategories; private final ImageGalleryController controller; - CategorizationChange(ImageGalleryController controller, Category newCategory, Map oldCategories) { + CategorizationChange(ImageGalleryController controller, DhsImageCategory newCategory, Map oldCategories) { this.controller = controller; this.newCategory = newCategory; this.oldCategories = ImmutableMap.copyOf(oldCategories); @@ -216,7 +216,7 @@ public class CategorizeAction extends Action { @Override public void undo() { - for (Map.Entry entry : oldCategories.entrySet()) { + for (Map.Entry entry : oldCategories.entrySet()) { new CategorizeAction(controller, entry.getValue(), Collections.singleton(entry.getKey()), false) .handle(null); } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeGroupAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeGroupAction.java index 439bb59512..f568fbd105 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeGroupAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeGroupAction.java @@ -38,7 +38,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.ImageGalleryPreferences; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.datamodel.TskCoreException; /** @@ -49,7 +49,7 @@ public class CategorizeGroupAction extends CategorizeAction { private final static Logger LOGGER = Logger.getLogger(CategorizeGroupAction.class.getName()); - public CategorizeGroupAction(Category newCat, ImageGalleryController controller) { + public CategorizeGroupAction(DhsImageCategory newCat, ImageGalleryController controller) { super(controller, newCat, null); setEventHandler(actionEvent -> { ObservableList fileIDs = controller.viewState().get().getGroup().getFileIDs(); @@ -58,12 +58,12 @@ public class CategorizeGroupAction extends CategorizeAction { //if they have preveiously disabled the warning, just go ahead and apply categories. addCatToFiles(ImmutableSet.copyOf(fileIDs)); } else { - final Map catCountMap = new HashMap<>(); + final Map catCountMap = new HashMap<>(); for (Long fileID : fileIDs) { try { - Category category = controller.getFileFromId(fileID).getCategory(); - if (false == Category.ZERO.equals(category) && newCat.equals(category) == false) { + DhsImageCategory category = controller.getFileFromId(fileID).getCategory(); + if (false == DhsImageCategory.ZERO.equals(category) && newCat.equals(category) == false) { catCountMap.merge(category, 1L, Long::sum); } } catch (TskCoreException ex) { @@ -86,14 +86,14 @@ public class CategorizeGroupAction extends CategorizeAction { "CategorizeGroupAction.fileCountMessage={0} with {1}", "CategorizeGroupAction.dontShowAgain=Don't show this message again", "CategorizeGroupAction.fileCountHeader=Files in the following categories will have their categories overwritten: "}) - private void showConfirmationDialog(final Map catCountMap, Category newCat, ObservableList fileIDs) { + private void showConfirmationDialog(final Map catCountMap, DhsImageCategory newCat, ObservableList fileIDs) { ButtonType categorizeButtonType = new ButtonType(Bundle.CategorizeGroupAction_OverwriteButton_text(), ButtonBar.ButtonData.APPLY); VBox textFlow = new VBox(); - for (Map.Entry entry : catCountMap.entrySet()) { + for (Map.Entry entry : catCountMap.entrySet()) { if (entry.getKey().equals(newCat) == false) { if (entry.getValue() > 0) { Label label = new Label(Bundle.CategorizeGroupAction_fileCountMessage(entry.getValue(), entry.getKey().getDisplayName()), diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeSelectedFilesAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeSelectedFilesAction.java index be8c3644bb..bb8cd9de96 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeSelectedFilesAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeSelectedFilesAction.java @@ -19,14 +19,14 @@ package org.sleuthkit.autopsy.imagegallery.actions; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; /** * */ public class CategorizeSelectedFilesAction extends CategorizeAction { - public CategorizeSelectedFilesAction(Category cat, ImageGalleryController controller) { + public CategorizeSelectedFilesAction(DhsImageCategory cat, ImageGalleryController controller) { super(controller, cat, null); setEventHandler(actionEvent -> addCatToFiles(controller.getSelectionModel().getSelected()) 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); } } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/CategoryManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/CategoryManager.java index 33bc9a58d4..66aa6c578d 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/CategoryManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/CategoryManager.java @@ -35,7 +35,7 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; @@ -80,13 +80,13 @@ public class CategoryManager { * the count related methods go through this cache, which loads initial * values from the database if needed. */ - private final LoadingCache categoryCounts = + private final LoadingCache categoryCounts = CacheBuilder.newBuilder().build(CacheLoader.from(this::getCategoryCountHelper)); /** * cached TagNames corresponding to Categories, looked up from * autopsyTagManager at initial request or if invalidated by case change. */ - private final LoadingCache catTagNameMap = + private final LoadingCache catTagNameMap = CacheBuilder.newBuilder().build(CacheLoader.from( cat -> getController().getTagsManager().getTagName(cat) )); @@ -119,18 +119,18 @@ public class CategoryManager { } /** - * get the number of file with the given {@link Category} + * get the number of file with the given {@link DhsImageCategory} * * @param cat get the number of files with Category = cat * * @return the number of files with the given Category */ - synchronized public long getCategoryCount(Category cat) { - if (cat == Category.ZERO) { + synchronized public long getCategoryCount(DhsImageCategory cat) { + if (cat == DhsImageCategory.ZERO) { // Keeping track of the uncategorized files is a bit tricky while ingest // is going on, so always use the list of file IDs we already have along with the // other category counts instead of trying to track it separately. - long allOtherCatCount = getCategoryCount(Category.ONE) + getCategoryCount(Category.TWO) + getCategoryCount(Category.THREE) + getCategoryCount(Category.FOUR) + getCategoryCount(Category.FIVE); + long allOtherCatCount = getCategoryCount(DhsImageCategory.ONE) + getCategoryCount(DhsImageCategory.TWO) + getCategoryCount(DhsImageCategory.THREE) + getCategoryCount(DhsImageCategory.FOUR) + getCategoryCount(DhsImageCategory.FIVE); return db.getNumberOfImageFilesInList() - allOtherCatCount; } else { return categoryCounts.getUnchecked(cat).sum(); @@ -139,24 +139,24 @@ public class CategoryManager { /** * increment the cached value for the number of files with the given - * {@link Category} + * {@link DhsImageCategory} * * @param cat the Category to increment */ - synchronized public void incrementCategoryCount(Category cat) { - if (cat != Category.ZERO) { + synchronized public void incrementCategoryCount(DhsImageCategory cat) { + if (cat != DhsImageCategory.ZERO) { categoryCounts.getUnchecked(cat).increment(); } } /** * decrement the cached value for the number of files with the given - * {@link Category} + * {@link DhsImageCategory} * * @param cat the Category to decrement */ - synchronized public void decrementCategoryCount(Category cat) { - if (cat != Category.ZERO) { + synchronized public void decrementCategoryCount(DhsImageCategory cat) { + if (cat != DhsImageCategory.ZERO) { categoryCounts.getUnchecked(cat).decrement(); } } @@ -171,7 +171,7 @@ public class CategoryManager { * @return a LongAdder whose value is set to the number of file with the * given Category */ - synchronized private LongAdder getCategoryCountHelper(Category cat) { + synchronized private LongAdder getCategoryCountHelper(DhsImageCategory cat) { LongAdder longAdder = new LongAdder(); longAdder.decrement(); try { @@ -188,7 +188,7 @@ public class CategoryManager { * * @param fileIDs */ - public void fireChange(Collection fileIDs, Category newCategory) { + public void fireChange(Collection fileIDs, DhsImageCategory newCategory) { categoryEventBus.post(new CategoryChangeEvent(fileIDs, newCategory)); } @@ -231,21 +231,21 @@ public class CategoryManager { * * @return the TagName used for this Category */ - synchronized public TagName getTagName(Category cat) { + synchronized public TagName getTagName(DhsImageCategory cat) { return catTagNameMap.getUnchecked(cat); } - public static Category categoryFromTagName(TagName tagName) { - return Category.fromDisplayName(tagName.getDisplayName()); + public static DhsImageCategory categoryFromTagName(TagName tagName) { + return DhsImageCategory.fromDisplayName(tagName.getDisplayName()); } public static boolean isCategoryTagName(TagName tName) { - return Category.isCategoryName(tName.getDisplayName()); + return DhsImageCategory.isCategoryName(tName.getDisplayName()); } public static boolean isNotCategoryTagName(TagName tName) { - return Category.isNotCategoryName(tName.getDisplayName()); + return DhsImageCategory.isNotCategoryName(tName.getDisplayName()); } @@ -270,8 +270,8 @@ public class CategoryManager { } catch (TskCoreException tskException) { LOGGER.log(Level.SEVERE, "Failed to get content tags for content. Unable to maintain category in a consistent state.", tskException); //NON-NLS } - Category newCat = CategoryManager.categoryFromTagName(addedTag.getName()); - if (newCat != Category.ZERO) { + DhsImageCategory newCat = CategoryManager.categoryFromTagName(addedTag.getName()); + if (newCat != DhsImageCategory.ZERO) { incrementCategoryCount(newCat); } @@ -285,8 +285,8 @@ public class CategoryManager { TagName tagName = deletedTagInfo.getName(); if (isCategoryTagName(tagName)) { - Category deletedCat = CategoryManager.categoryFromTagName(tagName); - if (deletedCat != Category.ZERO) { + DhsImageCategory deletedCat = CategoryManager.categoryFromTagName(tagName); + if (deletedCat != DhsImageCategory.ZERO) { decrementCategoryCount(deletedCat); } fireChange(Collections.singleton(deletedTagInfo.getContentID()), null); @@ -301,15 +301,15 @@ public class CategoryManager { public static class CategoryChangeEvent { private final ImmutableSet fileIDs; - private final Category newCategory; + private final DhsImageCategory newCategory; - public CategoryChangeEvent(Collection fileIDs, Category newCategory) { + public CategoryChangeEvent(Collection fileIDs, DhsImageCategory newCategory) { super(); this.fileIDs = ImmutableSet.copyOf(fileIDs); this.newCategory = newCategory; } - public Category getNewCategory() { + public DhsImageCategory getNewCategory() { return newCategory; } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableAttribute.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableAttribute.java index 0ac5142f3c..c3b8771b6d 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableAttribute.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableAttribute.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.imagegallery.datamodel; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -84,14 +84,14 @@ public class DrawableAttribute> { * //TODO: this has lead to awkward hard to maintain code, and little * advantage. move categories into DrawableDB? */ - public final static DrawableAttribute CATEGORY = - new DrawableAttribute(AttributeName.CATEGORY, Bundle.DrawableAttribute_category(), + public final static DrawableAttribute CATEGORY = + new DrawableAttribute(AttributeName.CATEGORY, Bundle.DrawableAttribute_category(), false, "category-icon.png", //NON-NLS f -> Collections.singleton(f.getCategory())) { @Override - public Node getGraphicForValue(Category val) { + public Node getGraphicForValue(DhsImageCategory val) { return val.getGraphic(); } }; diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index 5426ac205a..fa008fe3e9 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.imagegallery.datamodel; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -231,7 +231,7 @@ public final class DrawableDB { insertHashHitStmt = prepareStatement("INSERT OR IGNORE INTO hash_set_hits (hash_set_id, obj_id) VALUES (?,?)"); //NON-NLS - for (Category cat : Category.values()) { + for (DhsImageCategory cat : DhsImageCategory.values()) { insertGroup(cat.getDisplayName(), DrawableAttribute.CATEGORY); } initializeImageList(); @@ -1016,7 +1016,7 @@ public final class DrawableDB { case MIME_TYPE: return groupManager.getFileIDsWithMimeType((String) groupKey.getValue()); case CATEGORY: - return groupManager.getFileIDsWithCategory((Category) groupKey.getValue()); + return groupManager.getFileIDsWithCategory((DhsImageCategory) groupKey.getValue()); case TAGS: return groupManager.getFileIDsWithTag((TagName) groupKey.getValue()); } @@ -1195,7 +1195,7 @@ public final class DrawableDB { * * @return the number of the with the given category */ - public long getCategoryCount(Category cat) { + public long getCategoryCount(DhsImageCategory cat) { try { TagName tagName = controller.getTagsManager().getTagName(cat); if (nonNull(tagName)) { @@ -1233,7 +1233,7 @@ public final class DrawableDB { DrawableTagsManager tagsManager = controller.getTagsManager(); // get a comma seperated list of TagName ids for non zero categories - String catTagNameIDs = Category.getNonZeroCategories().stream() + String catTagNameIDs = DhsImageCategory.getNonZeroCategories().stream() .map(tagsManager::getTagName) .map(TagName::getId) .map(Object::toString) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableFile.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableFile.java index 5b40c9240c..af26dd2e97 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableFile.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableFile.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.imagegallery.datamodel; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import java.lang.ref.SoftReference; import java.text.MessageFormat; import java.util.ArrayList; @@ -85,7 +85,7 @@ public abstract class DrawableFile { private final SimpleBooleanProperty analyzed; - private final SimpleObjectProperty category = new SimpleObjectProperty<>(null); + private final SimpleObjectProperty category = new SimpleObjectProperty<>(null); private String make; @@ -216,17 +216,17 @@ public abstract class DrawableFile { return ""; } - public void setCategory(Category category) { + public void setCategory(DhsImageCategory category) { categoryProperty().set(category); } - public Category getCategory() { + public DhsImageCategory getCategory() { updateCategory(); return category.get(); } - public SimpleObjectProperty categoryProperty() { + public SimpleObjectProperty categoryProperty() { return category; } @@ -238,9 +238,9 @@ public abstract class DrawableFile { category.set(getContentTags().stream() .map(Tag::getName).filter(CategoryManager::isCategoryTagName) .map(TagName::getDisplayName) - .map(Category::fromDisplayName) + .map(DhsImageCategory::fromDisplayName) .sorted().findFirst() //sort by severity and take the first - .orElse(Category.ZERO) + .orElse(DhsImageCategory.ZERO) ); } catch (TskCoreException ex) { LOGGER.log(Level.WARNING, "problem looking up category for " + this.getContentPathSafe(), ex); //NON-NLS diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableTagsManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableTagsManager.java index 0497068924..cb30c37bab 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableTagsManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableTagsManager.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.imagegallery.datamodel; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import com.google.common.eventbus.AsyncEventBus; import com.google.common.eventbus.EventBus; import java.util.Collections; @@ -229,7 +229,7 @@ public class DrawableTagsManager { } } - public TagName getTagName(Category cat) { + public TagName getTagName(DhsImageCategory cat) { try { return getTagName(cat.getDisplayName()); } catch (TskCoreException ex) { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java index e62c2b6541..7dddfa5ca8 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java @@ -71,7 +71,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB; @@ -332,7 +332,7 @@ public class GroupManager { switch (groupBy.attrName) { //these cases get special treatment case CATEGORY: - values = (List) Arrays.asList(Category.values()); + values = (List) Arrays.asList(DhsImageCategory.values()); break; case TAGS: values = (List) controller.getTagsManager().getTagNamesInUse().stream() @@ -388,7 +388,7 @@ public class GroupManager { switch (groupKey.getAttribute().attrName) { //these cases get special treatment case CATEGORY: - fileIDsToReturn = getFileIDsWithCategory((Category) groupKey.getValue()); + fileIDsToReturn = getFileIDsWithCategory((DhsImageCategory) groupKey.getValue()); break; case TAGS: fileIDsToReturn = getFileIDsWithTag((TagName) groupKey.getValue()); @@ -409,13 +409,13 @@ public class GroupManager { // @@@ This was kind of slow in the profiler. Maybe we should cache it. // Unless the list of file IDs is necessary, use countFilesWithCategory() to get the counts. - public Set getFileIDsWithCategory(Category category) throws TskCoreException { + public Set getFileIDsWithCategory(DhsImageCategory category) throws TskCoreException { Set fileIDsToReturn = Collections.emptySet(); if (nonNull(db)) { try { final DrawableTagsManager tagsManager = controller.getTagsManager(); - if (category == Category.ZERO) { - List< TagName> tns = Stream.of(Category.ONE, Category.TWO, Category.THREE, Category.FOUR, Category.FIVE) + if (category == DhsImageCategory.ZERO) { + List< TagName> tns = Stream.of(DhsImageCategory.ONE, DhsImageCategory.TWO, DhsImageCategory.THREE, DhsImageCategory.FOUR, DhsImageCategory.FIVE) .map(tagsManager::getTagName) .collect(Collectors.toList()); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SummaryTablePane.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SummaryTablePane.java index 2841eb5325..44814e2c9a 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SummaryTablePane.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SummaryTablePane.java @@ -36,7 +36,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.imagegallery.FXMLConstructor; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; /** * Displays summary statistics (counts) for each group @@ -44,13 +44,13 @@ import org.sleuthkit.autopsy.datamodel.tags.Category; public class SummaryTablePane extends AnchorPane { @FXML - private TableColumn, String> catColumn; + private TableColumn, String> catColumn; @FXML - private TableColumn, Long> countColumn; + private TableColumn, Long> countColumn; @FXML - private TableView> tableView; + private TableView> tableView; private final ImageGalleryController controller; @FXML @@ -67,11 +67,11 @@ public class SummaryTablePane extends AnchorPane { tableView.prefHeightProperty().set(7 * 25); //set up columns - catColumn.setCellValueFactory((TableColumn.CellDataFeatures, String> p) -> new SimpleObjectProperty<>(p.getValue().getKey().getDisplayName())); + catColumn.setCellValueFactory((TableColumn.CellDataFeatures, String> p) -> new SimpleObjectProperty<>(p.getValue().getKey().getDisplayName())); catColumn.setPrefWidth(USE_COMPUTED_SIZE); catColumn.setText(Bundle.SummaryTablePane_catColumn()); - countColumn.setCellValueFactory((TableColumn.CellDataFeatures, Long> p) -> new SimpleObjectProperty<>(p.getValue().getValue())); + countColumn.setCellValueFactory((TableColumn.CellDataFeatures, Long> p) -> new SimpleObjectProperty<>(p.getValue().getValue())); countColumn.setPrefWidth(USE_COMPUTED_SIZE); countColumn.setText(Bundle.SummaryTablePane_countColumn()); @@ -93,9 +93,9 @@ public class SummaryTablePane extends AnchorPane { */ @Subscribe public void handleCategoryChanged(org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager.CategoryChangeEvent evt) { - final ObservableList> data = FXCollections.observableArrayList(); + final ObservableList> data = FXCollections.observableArrayList(); if (Case.isCaseOpen()) { - for (Category cat : Category.values()) { + for (DhsImageCategory cat : DhsImageCategory.values()) { data.add(new Pair<>(cat, controller.getCategoryManager().getCategoryCount(cat))); } } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java index 6cb29d46d1..40dad21cf2 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java @@ -49,7 +49,7 @@ import org.sleuthkit.autopsy.imagegallery.FXMLConstructor; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.actions.CategorizeGroupAction; import org.sleuthkit.autopsy.imagegallery.actions.TagGroupAction; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupSortBy; @@ -154,13 +154,13 @@ public class Toolbar extends ToolBar { } }); - CategorizeGroupAction cat5GroupAction = new CategorizeGroupAction(Category.FIVE, controller); + CategorizeGroupAction cat5GroupAction = new CategorizeGroupAction(DhsImageCategory.FIVE, controller); catGroupMenuButton.setOnAction(cat5GroupAction); catGroupMenuButton.setText(cat5GroupAction.getText()); catGroupMenuButton.setGraphic(cat5GroupAction.getGraphic()); catGroupMenuButton.showingProperty().addListener(showing -> { if (catGroupMenuButton.isShowing()) { - List categoryMenues = Lists.transform(Arrays.asList(Category.values()), + List categoryMenues = Lists.transform(Arrays.asList(DhsImageCategory.values()), cat -> GuiUtils.createAutoAssigningMenuItem(catGroupMenuButton, new CategorizeGroupAction(cat, controller))); catGroupMenuButton.getItems().setAll(categoryMenues); } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableView.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableView.java index e6d7bf06f2..afed2fd832 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableView.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableView.java @@ -17,7 +17,7 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; @@ -38,17 +38,17 @@ public interface DrawableView { static final Border HASH_BORDER = new Border(new BorderStroke(Color.PURPLE, BorderStrokeStyle.DASHED, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); - static final Border CAT1_BORDER = new Border(new BorderStroke(Category.ONE.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); + static final Border CAT1_BORDER = new Border(new BorderStroke(DhsImageCategory.ONE.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); - static final Border CAT2_BORDER = new Border(new BorderStroke(Category.TWO.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); + static final Border CAT2_BORDER = new Border(new BorderStroke(DhsImageCategory.TWO.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); - static final Border CAT3_BORDER = new Border(new BorderStroke(Category.THREE.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); + static final Border CAT3_BORDER = new Border(new BorderStroke(DhsImageCategory.THREE.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); - static final Border CAT4_BORDER = new Border(new BorderStroke(Category.FOUR.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); + static final Border CAT4_BORDER = new Border(new BorderStroke(DhsImageCategory.FOUR.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); - static final Border CAT5_BORDER = new Border(new BorderStroke(Category.FIVE.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); + static final Border CAT5_BORDER = new Border(new BorderStroke(DhsImageCategory.FIVE.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); - static final Border CAT0_BORDER = new Border(new BorderStroke(Category.ZERO.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); + static final Border CAT0_BORDER = new Border(new BorderStroke(DhsImageCategory.ZERO.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS)); Region getCategoryBorderRegion(); @@ -97,7 +97,7 @@ public interface DrawableView { } } - static Border getCategoryBorder(Category category) { + static Border getCategoryBorder(DhsImageCategory category) { if (category != null) { switch (category) { case ONE: @@ -121,14 +121,14 @@ public interface DrawableView { } @ThreadConfined(type = ThreadConfined.ThreadType.ANY) - default Category updateCategory() { + default DhsImageCategory updateCategory() { if (getFile().isPresent()) { - final Category category = getFile().map(DrawableFile::getCategory).orElse(Category.ZERO); - final Border border = hasHashHit() && (category == Category.ZERO) ? HASH_BORDER : getCategoryBorder(category); + final DhsImageCategory category = getFile().map(DrawableFile::getCategory).orElse(DhsImageCategory.ZERO); + final Border border = hasHashHit() && (category == DhsImageCategory.ZERO) ? HASH_BORDER : getCategoryBorder(category); Platform.runLater(() -> getCategoryBorderRegion().setBorder(border)); return category; } else { - return Category.ZERO; + return DhsImageCategory.ZERO; } } } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java index bd84611b93..169856ab88 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java @@ -122,7 +122,7 @@ import org.sleuthkit.autopsy.imagegallery.actions.RedoAction; import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter; import org.sleuthkit.autopsy.imagegallery.actions.TagSelectedFilesAction; import org.sleuthkit.autopsy.imagegallery.actions.UndoAction; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewMode; @@ -352,7 +352,7 @@ public class GroupPane extends BorderPane { return grouping.getReadOnlyProperty(); } - private ToggleButton getToggleForCategory(Category category) { + private ToggleButton getToggleForCategory(DhsImageCategory category) { switch (category) { case ZERO: return cat0Toggle; @@ -397,7 +397,7 @@ public class GroupPane extends BorderPane { assert slideShowToggle != null : "fx:id=\"segButton\" was not injected: check your FXML file 'GroupHeader.fxml'."; assert tileToggle != null : "fx:id=\"tileToggle\" was not injected: check your FXML file 'GroupHeader.fxml'."; - for (Category cat : Category.values()) { + for (DhsImageCategory cat : DhsImageCategory.values()) { ToggleButton toggleForCategory = getToggleForCategory(cat); toggleForCategory.setBorder(new Border(new BorderStroke(cat.getColor(), BorderStrokeStyle.SOLID, CORNER_RADII_2, BORDER_WIDTHS_2))); toggleForCategory.getStyleClass().remove("radio-button"); @@ -445,13 +445,13 @@ public class GroupPane extends BorderPane { }); - CategorizeSelectedFilesAction cat5SelectedAction = new CategorizeSelectedFilesAction(Category.FIVE, controller); + CategorizeSelectedFilesAction cat5SelectedAction = new CategorizeSelectedFilesAction(DhsImageCategory.FIVE, controller); catSelectedSplitMenu.setOnAction(cat5SelectedAction); catSelectedSplitMenu.setText(cat5SelectedAction.getText()); catSelectedSplitMenu.setGraphic(cat5SelectedAction.getGraphic()); catSelectedSplitMenu.showingProperty().addListener(showing -> { if (catSelectedSplitMenu.isShowing()) { - List categoryMenues = Lists.transform(Arrays.asList(Category.values()), + List categoryMenues = Lists.transform(Arrays.asList(DhsImageCategory.values()), cat -> GuiUtils.createAutoAssigningMenuItem(catSelectedSplitMenu, new CategorizeSelectedFilesAction(cat, controller))); catSelectedSplitMenu.getItems().setAll(categoryMenues); } @@ -765,7 +765,7 @@ public class GroupPane extends BorderPane { } ObservableSet selected = selectionModel.getSelected(); if (selected.isEmpty() == false) { - Category cat = keyCodeToCat(t.getCode()); + DhsImageCategory cat = keyCodeToCat(t.getCode()); if (cat != null) { new CategorizeAction(controller, cat, selected).handle(null); } @@ -773,27 +773,27 @@ public class GroupPane extends BorderPane { } } - private Category keyCodeToCat(KeyCode t) { + private DhsImageCategory keyCodeToCat(KeyCode t) { if (t != null) { switch (t) { case NUMPAD0: case DIGIT0: - return Category.ZERO; + return DhsImageCategory.ZERO; case NUMPAD1: case DIGIT1: - return Category.ONE; + return DhsImageCategory.ONE; case NUMPAD2: case DIGIT2: - return Category.TWO; + return DhsImageCategory.TWO; case NUMPAD3: case DIGIT3: - return Category.THREE; + return DhsImageCategory.THREE; case NUMPAD4: case DIGIT4: - return Category.FOUR; + return DhsImageCategory.FOUR; case NUMPAD5: case DIGIT5: - return Category.FIVE; + return DhsImageCategory.FIVE; } } return null; diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/MetaDataPane.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/MetaDataPane.java index 881925a098..60f30564b1 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/MetaDataPane.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/MetaDataPane.java @@ -57,7 +57,7 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.FXMLConstructor; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; @@ -171,7 +171,7 @@ public class MetaDataPane extends DrawableUIBase { if (p.getKey() == DrawableAttribute.TAGS) { return ((Collection) p.getValue()).stream() .map(TagName::getDisplayName) - .filter(Category::isNotCategoryName) + .filter(DhsImageCategory::isNotCategoryName) .collect(Collectors.joining(" ; ")); } else { return p.getValue().stream() diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/SlideShowView.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/SlideShowView.java index b40e121102..11d109bbb5 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/SlideShowView.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/SlideShowView.java @@ -50,7 +50,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType; import org.sleuthkit.autopsy.imagegallery.FXMLConstructor; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; -import org.sleuthkit.autopsy.datamodel.tags.Category; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile; import org.sleuthkit.autopsy.imagegallery.gui.VideoPlayer; @@ -306,14 +306,14 @@ public class SlideShowView extends DrawableTileBase { */ @Override @ThreadConfined(type = ThreadType.ANY) - public Category updateCategory() { + public DhsImageCategory updateCategory() { Optional file = getFile(); if (file.isPresent()) { - Category updateCategory = super.updateCategory(); + DhsImageCategory updateCategory = super.updateCategory(); Platform.runLater(() -> getGroupPane().syncCatToggle(file.get())); return updateCategory; } else { - return Category.ZERO; + return DhsImageCategory.ZERO; } } diff --git a/InternalPythonModules/android/browserlocation.py b/InternalPythonModules/android/browserlocation.py index ef79a623c2..6db230c2e6 100755 --- a/InternalPythonModules/android/browserlocation.py +++ b/InternalPythonModules/android/browserlocation.py @@ -28,6 +28,7 @@ from java.sql import ResultSet from java.sql import SQLException from java.sql import Statement from java.util.logging import Level +from java.util import ArrayList from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Blackboard from org.sleuthkit.autopsy.casemodule.services import FileManager @@ -89,14 +90,16 @@ class BrowserLocationAnalyzer(general.AndroidComponentAnalyzer): latitude = Double.valueOf(resultSet.getString("latitude")) longitude = Double.valueOf(resultSet.getString("longitude")) + attributes = ArrayList() artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, general.MODULE_NAME, latitude)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, general.MODULE_NAME, longitude)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, timestamp)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, general.MODULE_NAME, "Browser Location History")) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, general.MODULE_NAME, latitude)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, general.MODULE_NAME, longitude)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, timestamp)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, general.MODULE_NAME, "Browser Location History")) # artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(),moduleName, accuracy)) # NOTE: originally commented out + artifact.addAttributes(attributes); try: # index the artifact for keyword search blackboard = Case.getCurrentCase().getServices().getBlackboard() diff --git a/InternalPythonModules/android/cachelocation.py b/InternalPythonModules/android/cachelocation.py index c28aa48fc3..019c03c654 100755 --- a/InternalPythonModules/android/cachelocation.py +++ b/InternalPythonModules/android/cachelocation.py @@ -25,6 +25,7 @@ from java.lang import ClassNotFoundException from java.math import BigInteger from java.nio import ByteBuffer from java.util.logging import Level +from java.util import ArrayList from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Blackboard from org.sleuthkit.autopsy.casemodule.services import FileManager @@ -120,13 +121,15 @@ class CacheLocationAnalyzer(general.AndroidComponentAnalyzer): inputStream.read(tempBytes) timestamp = BigInteger(tempBytes).longValue() / 1000 + attributes = ArrayList() artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, AndroidAnalyzer.MODULE_NAME, latitude)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, AndroidAnalyzer.MODULE_NAME, longitude)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, AndroidModuleFactorymodule.Name, timestamp)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, AndroidAnalyzer.MODULE_NAME, + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, AndroidAnalyzer.MODULE_NAME, latitude)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, AndroidAnalyzer.MODULE_NAME, longitude)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, AndroidModuleFactorymodule.Name, timestamp)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, AndroidAnalyzer.MODULE_NAME, file.getName() + "Location History")) + artifact.addAttributes(attributes) #Not storing these for now. # artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(), AndroidModuleFactorymodule.moduleName, accuracy)) # artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID(), AndroidModuleFactorymodule.moduleName, confidence)) diff --git a/InternalPythonModules/android/calllog.py b/InternalPythonModules/android/calllog.py index 504cd4d4a7..7ea3fd85df 100755 --- a/InternalPythonModules/android/calllog.py +++ b/InternalPythonModules/android/calllog.py @@ -28,6 +28,7 @@ from java.sql import ResultSet from java.sql import SQLException from java.sql import Statement from java.util.logging import Level +from java.util import ArrayList from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Blackboard from org.sleuthkit.autopsy.casemodule.services import FileManager @@ -132,16 +133,19 @@ class CallLogAnalyzer(general.AndroidComponentAnalyzer): name = resultSet.getString("name") # name of person dialed or called. None if unregistered try: + attributes = ArrayList() artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG) # create a call log and then add attributes from result set. if direction == CallLogAnalyzer.OUTGOING: - artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, general.MODULE_NAME, number)) + attributes.add(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, general.MODULE_NAME, number)) else: # Covers INCOMING and MISSED - artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, general.MODULE_NAME, number)) + attributes.add(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, general.MODULE_NAME, number)) - artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_START, general.MODULE_NAME, date)) - artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_END, general.MODULE_NAME, duration + date)) - artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, directionString)) - artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, name)) + attributes.add(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_START, general.MODULE_NAME, date)) + attributes.add(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_END, general.MODULE_NAME, duration + date)) + attributes.add(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, directionString)) + attributes.add(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, name)) + + artifact.addAttributes(attributes) # Create an account calllogAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, number, general.MODULE_NAME, abstractFile); diff --git a/InternalPythonModules/android/contact.py b/InternalPythonModules/android/contact.py index eaff7871f5..2a4af06859 100755 --- a/InternalPythonModules/android/contact.py +++ b/InternalPythonModules/android/contact.py @@ -27,6 +27,7 @@ from java.sql import ResultSet from java.sql import SQLException from java.sql import Statement from java.util.logging import Level +from java.util import ArrayList from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Blackboard from org.sleuthkit.autopsy.casemodule.services import FileManager @@ -131,6 +132,7 @@ class ContactAnalyzer(general.AndroidComponentAnalyzer): + "WHERE mimetype = 'vnd.android.cursor.item/phone_v2' OR mimetype = 'vnd.android.cursor.item/email_v2'\n" + "ORDER BY raw_contacts.display_name ASC;") + attributes = ArrayList() artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) oldName = "" while resultSet.next(): @@ -139,14 +141,16 @@ class ContactAnalyzer(general.AndroidComponentAnalyzer): mimetype = resultSet.getString("mimetype") # either phone or email if name != oldName: artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, name)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, name)) if mimetype == "vnd.android.cursor.item/phone_v2": - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, general.MODULE_NAME, data1)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, general.MODULE_NAME, data1)) acctType = Account.Type.PHONE else: - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, general.MODULE_NAME, data1)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, general.MODULE_NAME, data1)) acctType = Account.Type.EMAIL + artifact.addAttributes(attributes) + # Create an account instance contactAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance (acctType, data1, general.MODULE_NAME, abstractFile); diff --git a/InternalPythonModules/android/googlemaplocation.py b/InternalPythonModules/android/googlemaplocation.py index 066ed2f792..444c0477ee 100755 --- a/InternalPythonModules/android/googlemaplocation.py +++ b/InternalPythonModules/android/googlemaplocation.py @@ -28,6 +28,7 @@ from java.sql import ResultSet from java.sql import SQLException from java.sql import Statement from java.util.logging import Level +from java.util import ArrayList from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Blackboard from org.sleuthkit.autopsy.casemodule.services import FileManager @@ -96,17 +97,19 @@ class GoogleMapLocationAnalyzer(general.AndroidComponentAnalyzer): source_lat = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lat")) source_lng = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lng")) + attributes = ArrayList() artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, general.MODULE_NAME, "Destination")) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, time)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END, general.MODULE_NAME, dest_lat)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END, general.MODULE_NAME, dest_lng)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START, general.MODULE_NAME, source_lat)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START, general.MODULE_NAME, source_lng)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, dest_title)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION, general.MODULE_NAME, dest_address)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, general.MODULE_NAME, "Google Maps History")) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, general.MODULE_NAME, "Destination")) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, time)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END, general.MODULE_NAME, dest_lat)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END, general.MODULE_NAME, dest_lng)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START, general.MODULE_NAME, source_lat)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START, general.MODULE_NAME, source_lng)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, dest_title)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION, general.MODULE_NAME, dest_address)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, general.MODULE_NAME, "Google Maps History")) + artifact.addAttributes(attributes) try: # index the artifact for keyword search blackboard = Case.getCurrentCase().getServices().getBlackboard() diff --git a/InternalPythonModules/android/tangomessage.py b/InternalPythonModules/android/tangomessage.py index 0d23e0ea3a..c63f87a6af 100755 --- a/InternalPythonModules/android/tangomessage.py +++ b/InternalPythonModules/android/tangomessage.py @@ -28,6 +28,7 @@ from java.sql import ResultSet from java.sql import SQLException from java.sql import Statement from java.util.logging import Level +from java.util import ArrayList from org.apache.commons.codec.binary import Base64 from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Blackboard @@ -105,12 +106,14 @@ class TangoMessageAnalyzer(general.AndroidComponentAnalyzer): direction = "Outgoing" payload = resultSet.getString("payload") + attributes = ArrayList() artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) #create a call log and then add attributes from result set. - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, create_time)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, direction)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, TangoMessageAnalyzer.decodeMessage(conv_id, payload))) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Tango Message")) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, create_time)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, direction)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, TangoMessageAnalyzer.decodeMessage(conv_id, payload))) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Tango Message")) + artifact.addAttributes(attributes) try: # index the artifact for keyword search blackboard = Case.getCurrentCase().getServices().getBlackboard() diff --git a/InternalPythonModules/android/textmessage.py b/InternalPythonModules/android/textmessage.py index 61e1ec627f..ccf7456728 100755 --- a/InternalPythonModules/android/textmessage.py +++ b/InternalPythonModules/android/textmessage.py @@ -28,6 +28,7 @@ from java.sql import ResultSet from java.sql import SQLException from java.sql import Statement from java.util.logging import Level +from java.util import ArrayList from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Blackboard from org.sleuthkit.autopsy.casemodule.services import FileManager @@ -105,18 +106,21 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer): read = resultSet.getInt("read") # may be unread = 0, read = 1 subject = resultSet.getString("subject") # message subject body = resultSet.getString("body") # message body + attributes = ArrayList() artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); #create Message artifact and then add attributes from result set. if resultSet.getString("type") == "1": - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, "Incoming")) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, general.MODULE_NAME, address)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, "Incoming")) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, general.MODULE_NAME, address)) else: - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, "Outgoing")) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, general.MODULE_NAME, address)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, date)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS, general.MODULE_NAME, Integer(read))) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT, general.MODULE_NAME, subject)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, body)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "SMS Message")) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, "Outgoing")) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, general.MODULE_NAME, address)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, date)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS, general.MODULE_NAME, Integer(read))) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT, general.MODULE_NAME, subject)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, body)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "SMS Message")) + + artifact.addAttributes(attributes) # Create an account msgAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, address, general.MODULE_NAME, abstractFile); diff --git a/InternalPythonModules/android/wwfmessage.py b/InternalPythonModules/android/wwfmessage.py index 3754daa5c7..f2e1a8b067 100755 --- a/InternalPythonModules/android/wwfmessage.py +++ b/InternalPythonModules/android/wwfmessage.py @@ -26,6 +26,7 @@ from java.sql import ResultSet from java.sql import SQLException from java.sql import Statement from java.util.logging import Level +from java.util import ArrayList from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Blackboard from org.sleuthkit.autopsy.casemodule.services import FileManager @@ -105,12 +106,15 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer): user_id = resultSet.getString("user_id") # the ID of the user who sent the message. game_id = resultSet.getString("game_id") # ID of the game which the the message was sent. + attributes = ArrayList() artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) # create a call log and then add attributes from result set. - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, created_at)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, user_id)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, general.MODULE_NAME, game_id)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, message)) - artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Words With Friends Message")) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, created_at)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, user_id)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, general.MODULE_NAME, game_id)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, message)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Words With Friends Message")) + + artifact.addAttributes(attributes) # Create an account wwfAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(wwfAccountType, user_id, general.MODULE_NAME, abstractFile); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index 830c93325a..dbbca6394b 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -280,7 +280,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule { return ProcessResult.OK; } List keywordListNames = settings.getNamesOfEnabledKeyWordLists(); - SearchRunner.getInstance().startJob(jobId, dataSourceId, keywordListNames); + SearchRunner.getInstance().startJob(context, keywordListNames); startedSearching = true; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java index b998294fe4..da1d5092a7 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 - 2017 Basis Technology Corp. + * Copyright 2014 - 2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -43,6 +43,7 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.StopWatch; +import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestMessage; import org.sleuthkit.autopsy.ingest.IngestServices; @@ -79,19 +80,15 @@ public final class SearchRunner { } /** - * Add a new job. Searches will be periodically performed after this is - * called. * - * @param jobId Job ID that this is associated with - * @param dataSourceId Data source that is being indexed and that - * searches should be restricted to. - * @param keywordListNames List of keyword lists that will be searched. List - * contents will be refreshed each search. + * @param jobContext + * @param keywordListNames */ - public synchronized void startJob(long jobId, long dataSourceId, List keywordListNames) { + public synchronized void startJob(IngestJobContext jobContext, List keywordListNames) { + long jobId = jobContext.getJobId(); if (jobs.containsKey(jobId) == false) { logger.log(Level.INFO, "Adding job {0}", jobId); //NON-NLS - SearchJobInfo jobData = new SearchJobInfo(jobId, dataSourceId, keywordListNames); + SearchJobInfo jobData = new SearchJobInfo(jobContext, keywordListNames); jobs.put(jobId, jobData); } @@ -266,6 +263,7 @@ public final class SearchRunner { */ private class SearchJobInfo { + private final IngestJobContext jobContext; private final long jobId; private final long dataSourceId; // mutable state: @@ -278,15 +276,20 @@ public final class SearchRunner { private AtomicLong moduleReferenceCount = new AtomicLong(0); private final Object finalSearchLock = new Object(); //used for a condition wait - private SearchJobInfo(long jobId, long dataSourceId, List keywordListNames) { - this.jobId = jobId; - this.dataSourceId = dataSourceId; + private SearchJobInfo(IngestJobContext jobContext, List keywordListNames) { + this.jobContext = jobContext; + this.jobId = jobContext.getJobId(); + this.dataSourceId = jobContext.getDataSource().getId(); this.keywordListNames = new ArrayList<>(keywordListNames); currentResults = new HashMap<>(); workerRunning = false; currentSearcher = null; } + private IngestJobContext getJobContext() { + return jobContext; + } + private long getJobId() { return jobId; } @@ -435,7 +438,7 @@ public final class SearchRunner { int keywordsSearched = 0; for (Keyword keyword : keywords) { - if (this.isCancelled()) { + if (this.isCancelled() || this.job.getJobContext().fileIngestIsCancelled()) { logger.log(Level.INFO, "Cancel detected, bailing before new keyword processed: {0}", keyword.getSearchTerm()); //NON-NLS return null; } @@ -480,7 +483,6 @@ public final class SearchRunner { if (!newResults.getKeywords().isEmpty()) { // Write results to BB - //scale progress bar more more granular, per result sub-progress, within per keyword int totalUnits = newResults.getKeywords().size(); subProgresses[keywordsSearched].start(totalUnits); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java index 4ccc8d76c5..7183cf7346 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java @@ -152,7 +152,7 @@ class StringsTextExtractor extends FileTextExtractor { private static final Logger logger = Logger.getLogger(EnglishOnlyStream.class.getName()); private static final String NLS = Character.toString((char) 10); //new line - private static final int READ_BUF_SIZE = 256; + private static final int READ_BUF_SIZE = 65536; private static final int MIN_PRINTABLE_CHARS = 4; //num. of chars needed to qualify as a char string //args diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index d22ce2ca76..b0af801089 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -649,14 +649,15 @@ class ExtractRegistry extends Extract { String homeDir = value; String sid = artnode.getAttribute("sid"); //NON-NLS String username = artnode.getAttribute("username"); //NON-NLS - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); - bbart.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, parentModuleName, username)); - bbart.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, parentModuleName, sid)); - bbart.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, parentModuleName, homeDir)); + + bbart.addAttributes(bbattributes); // index the artifact for keyword search this.indexArtifact(bbart); } catch (TskCoreException ex) { @@ -669,10 +670,11 @@ class ExtractRegistry extends Extract { String localPath = artnode.getAttribute("localPath"); //NON-NLS String remoteName = value; BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_REMOTE_DRIVE); - bbart.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCAL_PATH, + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCAL_PATH, parentModuleName, localPath)); - bbart.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REMOTE_PATH, + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REMOTE_PATH, parentModuleName, remoteName)); + bbart.addAttributes(bbattributes); // index the artifact for keyword search this.indexArtifact(bbart); } catch (TskCoreException ex) { diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 0de39782ca..8bacf7c22d 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Wed, 08 Nov 2017 17:45:11 -0500 +#Wed, 06 Dec 2017 09:53:33 -0500 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index fa55dddb62..daed31a757 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Wed, 08 Nov 2017 17:45:11 -0500 +#Wed, 06 Dec 2017 09:53:33 -0500 CTL_MainWindow_Title=Autopsy 4.5.0 CTL_MainWindow_Title_No_Project=Autopsy 4.5.0 diff --git a/build-windows-installer.xml b/build-windows-installer.xml index e3f0401632..b82e28ca24 100755 --- a/build-windows-installer.xml +++ b/build-windows-installer.xml @@ -109,7 +109,7 @@ - + @@ -144,7 +144,7 @@ - + @@ -170,7 +170,7 @@ - + diff --git a/build.xml b/build.xml index bba4207f1b..4eaa7df1c8 100755 --- a/build.xml +++ b/build.xml @@ -92,7 +92,7 @@ - + diff --git a/docs/doxygen-user/images/runtime_settings.PNG b/docs/doxygen-user/images/runtime_settings.PNG new file mode 100644 index 0000000000..1d02f96a42 Binary files /dev/null and b/docs/doxygen-user/images/runtime_settings.PNG differ diff --git a/docs/doxygen-user/installation.dox b/docs/doxygen-user/installation.dox index c924708201..aa67e55d7a 100755 --- a/docs/doxygen-user/installation.dox +++ b/docs/doxygen-user/installation.dox @@ -6,12 +6,16 @@ It is _highly_ recommended to remove or disable any antivirus software from computers that will be processing or reviewing cases. Antivirus software will often conflict with forensic software, and may quarantine or even delete some of your results before you get a chance to look at them.

- \section install Deployment Types Starting with Autopsy 4.0, there are two ways to deploy Autopsy: - **Single-User**: Cases can be open by only a single instance of Autopsy at a time. Autopsy installations do not communicate with each other. This is the easiest to install and deploy. This page outlines that installation process. - **Multi-User**: Cases can be open by multiple users at the same time and users can see what each other is doing. This collaborative deployment requires installation and configuration of other network-based services. The installation of this deployment is covered in \ref install_multiuser_page. +\section sysreqs System Memory Requirements +The 64 bit version of Autopsy requires a minimum of 8GB RAM (16 GB recommended). +When the 64 bit version of Autopsy is installed on Windows it will be limited to a maximum heap size of 4GB leaving the remaining memory for the operating system, the internal Solr text indexing service and other applications. If you wish to change the maximum heap size you can do so after installation by changing the Maximum JVM Memory value in the Runtime section under Tools -> Options -> Application. + +\image html runtime_settings.PNG \section download Download Download Autopsy from the website: diff --git a/nbproject/project.properties b/nbproject/project.properties index ac6c347444..04a6d5800b 100755 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -16,7 +16,7 @@ update_versions=false #custom JVM options #Note: can be higher on 64 bit systems, should be in sync with build.xml # for Japanese version add: -J-Duser.language=ja -run.args.extra=-J-Xms24m -J-XX:MaxPermSize=128M -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication +run.args.extra=-J-Xms24m -J-Xmx4g -J-XX:MaxPermSize=128M -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication auxiliary.org-netbeans-modules-apisupport-installer.license-type=apache.v2 auxiliary.org-netbeans-modules-apisupport-installer.os-linux=false auxiliary.org-netbeans-modules-apisupport-installer.os-macosx=false diff --git a/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py b/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py index 7a11ed913d..2aa5d9d9a4 100755 --- a/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py +++ b/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py @@ -40,6 +40,7 @@ from java.lang import Class from java.lang import System from java.sql import DriverManager, SQLException from java.util.logging import Level +from java.util import ArrayList from java.io import File from org.sleuthkit.datamodel import SleuthkitCase from org.sleuthkit.datamodel import AbstractFile @@ -162,17 +163,18 @@ class ContactsDbIngestModule(DataSourceIngestModule): # Make an artifact on the blackboard, TSK_CONTACT and give it attributes for each of the fields art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) - - art.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON.getTypeID(), + attributes = ArrayList() + + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON.getTypeID(), ContactsDbIngestModuleFactory.moduleName, name)) - art.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getTypeID(), + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getTypeID(), ContactsDbIngestModuleFactory.moduleName, email)) - art.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), ContactsDbIngestModuleFactory.moduleName, phone)) - - + + art.addAttributes(attributes) try: # index the artifact for keyword search blackboard.indexArtifact(art) @@ -195,4 +197,4 @@ class ContactsDbIngestModule(DataSourceIngestModule): "ContactsDb Analyzer", "Found %d files" % fileCount) IngestServices.getInstance().postMessage(message) - return IngestModule.ProcessResult.OK \ No newline at end of file + return IngestModule.ProcessResult.OK