diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java index 4ac2e31ab1..3791df932f 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java @@ -34,6 +34,13 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Instances of this Action allow users to apply tags to blackboard artifacts. */ +@NbBundle.Messages({ + "AddBlackboardArtifactTagAction.singularTagResult=Tag Result", + "AddBlackboardArtifactTagAction.pluralTagResult=Tag Results", + "# {0} - artifactName", + "AddBlackboardArtifactTagAction.unableToTag.msg=Unable to tag {0}.", + "AddBlackboardArtifactTagAction.taggingErr=Tagging Error" +}) public class AddBlackboardArtifactTagAction extends AddTagAction { // This class is a singleton to support multi-selection of nodes, since diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java index c386858aa4..227755e1f6 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.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"); @@ -29,15 +29,24 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.FsContent; -import org.sleuthkit.datamodel.LayoutFile; -import org.sleuthkit.datamodel.LocalFile; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; /** * Instances of this Action allow users to apply tags to content. */ +@NbBundle.Messages({ + "AddContentTagAction.singularTagFile=Tag File", + "AddContentTagAction.pluralTagFile=Tag Files", + "# {0} - fileName", + "AddContentTagAction.unableToTag.msg=Unable to tag {0}, not a regular file.", + "AddContentTagAction.cannotApplyTagErr=Cannot Apply Tag", + "# {0} - fileName", + "AddContentTagAction.unableToTag.msg2=Unable to tag {0}.", + "AddContentTagAction.taggingErr=Tagging Error", + "# {0} - fileName", "# {1} - tagName", + "AddContentTagAction.tagExists={0} has been tagged as {1}. Cannot reapply the same tag." +}) public class AddContentTagAction extends AddTagAction { // This class is a singleton to support multi-selection of nodes, since diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties index 6314dc6f40..28d4e4006a 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties @@ -14,29 +14,12 @@ GetTagNameAndCommentDialog.commentLabel.text=Comment: GetTagNameAndCommentDialog.cancelButton.text=Cancel GetTagNameAndCommentDialog.tagCombo.toolTipText=Select tag to use GetTagNameAndCommentDialog.tagLabel.text=Tag: -AddBlackboardArtifactTagAction.singularTagResult=Tag Result -AddBlackboardArtifactTagAction.pluralTagResult=Tag Results -AddBlackboardArtifactTagAction.unableToTag.msg=Unable to tag {0}. -AddBlackboardArtifactTagAction.taggingErr=Tagging Error -AddContentTagAction.singularTagFile=Tag File -AddContentTagAction.pluralTagFile=Tag Files -AddContentTagAction.unableToTag.msg=Unable to tag {0}, not a regular file. -AddContentTagAction.cannotApplyTagErr=Cannot Apply Tag -AddContentTagAction.unableToTag.msg2=Unable to tag {0}. -AddContentTagAction.taggingErr=Tagging Error -AddContentTagAction.tagExists={0} has been tagged as {1}. Cannot reapply the same tag. AddTagAction.bookmarkFile=Bookmark file AddTagAction.quickTag=Quick Tag AddTagAction.noTags=No tags AddTagAction.newTag=New Tag... AddTagAction.tagAndComment=Tag and Comment... AddBookmarkTagAction.bookmark.text=Bookmark -DeleteBlackboardArtifactTagAction.deleteTags=Delete Tag(s) -DeleteBlackboardArtifactTagAction.unableToDelTag.msg=Unable to delete tag {0}. -DeleteBlackboardArtifactTagAction.tagDelErr=Tag Deletion Error -DeleteContentTagAction.deleteTags=Delete Tag(s) -DeleteContentTagAction.unableToDelTag.msg=Unable to delete tag {0}. -DeleteContentTagAction.tagDelErr=Tag Deletion Error GetTagNameAndCommentDialog.noTags=No Tags GetTagNameAndCommentDialog.createTag=Create Tag GetTagNameAndCommentDialog.cancelName=cancel diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties index 28b9af065b..195d12c695 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties @@ -1,57 +1,51 @@ -GetTagNameDialog.cancelButton.text=\u30AD\u30E3\u30F3\u30BB\u30EB +GetTagNameDialog.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb GetTagNameDialog.okButton.text=OK -GetTagNameDialog.preexistingLabel.text=\u65E2\u5B58\u30BF\u30B0\uFF1A -GetTagNameDialog.newTagPanel.border.title=\u65B0\u898F\u30BF\u30B0 -GetTagNameDialog.tagNameLabel.text=\u30BF\u30B0\u540D\uFF1A -GetTagNameAndCommentDialog.newTagButton.text=\u65B0\u898F\u30BF\u30B0 +GetTagNameDialog.preexistingLabel.text=\u65e2\u5b58\u30bf\u30b0\uff1a +GetTagNameDialog.newTagPanel.border.title=\u65b0\u898f\u30bf\u30b0 +GetTagNameDialog.tagNameLabel.text=\u30bf\u30b0\u540d\uff1a +GetTagNameAndCommentDialog.newTagButton.text=\u65b0\u898f\u30bf\u30b0 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 -AddBlackboardArtifactTagAction.unableToTag.msg={0}\u306B\u30BF\u30B0\u3092\u8FFD\u52A0\u3067\u304D\u307E\u305B\u3093\u3002 -AddBlackboardArtifactTagAction.taggingErr=\u30BF\u30B0\u4ED8\u3051\u30A8\u30E9\u30FC -AddContentTagAction.singularTagFile=\u30D5\u30A1\u30A4\u30EB\u306B\u30BF\u30B0\u3092\u8FFD\u52A0 -AddContentTagAction.pluralTagFile=\u30D5\u30A1\u30A4\u30EB\u306B\u30BF\u30B0\u3092\u8FFD\u52A0 -AddContentTagAction.unableToTag.msg={0}\u306B\u30BF\u30B0\u3092\u8FFD\u52A0\u3067\u304D\u307E\u305B\u3093\u3002\u901A\u5E38\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002 -AddContentTagAction.unableToTag.msg2={0}\u306B\u30BF\u30B0\u3092\u8FFD\u52A0\u3067\u304D\u307E\u305B\u3093\u3002 -AddContentTagAction.taggingErr=\u30BF\u30B0\u4ED8\u3051\u30A8\u30E9\u30FC -AddTagAction.quickTag=\u30AF\u30A4\u30C3\u30AF\u30BF\u30B0 -AddTagAction.noTags=\u30BF\u30B0\u7121\u3057 -AddTagAction.newTag=\u65B0\u898F\u30BF\u30B0\u2026 -AddTagAction.tagAndComment=\u30BF\u30B0\u3068\u30B3\u30E1\u30F3\u30C8\u3092\u8FFD\u52A0\u2026 -DeleteBlackboardArtifactTagAction.deleteTags=\u30BF\u30B0\u3092\u524A\u9664 -DeleteBlackboardArtifactTagAction.unableToDelTag.msg=\u30BF\u30B0{0}\u3092\u524A\u9664\u3067\u304D\u307E\u305B\u3093\u3002 -DeleteBlackboardArtifactTagAction.tagDelErr=\u30BF\u30B0\u524A\u9664\u30A8\u30E9\u30FC -DeleteContentTagAction.deleteTags=\u30BF\u30B0\u3092\u524A\u9664 -DeleteContentTagAction.unableToDelTag.msg=\u30BF\u30B0{0}\u3092\u524A\u9664\u3067\u304D\u307E\u305B\u3093\u3002 -DeleteContentTagAction.tagDelErr=\u30BF\u30B0\u306E\u524A\u9664\u30A8\u30E9\u30FC -GetTagNameAndCommentDialog.noTags=\u30BF\u30B0\u7121\u3057 -GetTagNameAndCommentDialog.createTag=\u30BF\u30B0\u3092\u4F5C\u6210 -GetTagNameAndCommentDialog.cancelName=\u30AD\u30E3\u30F3\u30BB\u30EB -GetTagNameDialog.createTag=\u30BF\u30B0\u3092\u4F5C\u6210 -GetTagNameDialog.cancelName=\u30AD\u30E3\u30F3\u30BB\u30EB -GetTagNameDialog.mustSupplyTtagName.msg=\u30BF\u30B0\u540D\u3092\u6307\u5B9A\u3057\u306A\u3051\u308C\u3070\u5148\u306B\u9032\u3081\u307E\u305B\u3093\u3002 -GetTagNameDialog.tagNameErr=\u30BF\u30B0\u540D -GetTagNameDialog.illegalCharsErr=\u4F7F\u7528\u3067\u304D\u306A\u3044\u6587\u5B57 -GetTagNameDialog.unableToAddTagNameToCase.msg=\u30BF\u30B0\u540D{0}\u3092\u30B1\u30FC\u30B9\u306B\u8FFD\u52A0\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -GetTagNameDialog.taggingErr=\u30BF\u30B0\u4ED8\u3051\u30A8\u30E9\u30FC -GetTagNameDialog.tagNameAlreadyDef.msg=\u30BF\u30B0\u540D{0}\u306F\u65E2\u306B\u5B9A\u7FA9\u3055\u308C\u3066\u3044\u307E\u3059\u3002 -GetTagNameDialog.dupTagErr=\u30BF\u30B0\u306E\u91CD\u8907\u30A8\u30E9\u30FC -AddContentTagAction.cannotApplyTagErr=\u30BF\u30B0\u3092\u9069\u7528\u3067\u304D\u307E\u305B\u3093 -OpenLogFolder.error1=\u30ED\u30B0\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\uFF1A{0} -CTL_OpenLogFolder=\u30ED\u30B0\u30D5\u30A9\u30EB\u30C0\u3092\u958B\u304F -ShowIngestProgressSnapshotAction.actionName.text=\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30D7\u30ED\u30B0\u30EC\u30B9\u306E\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u3092\u53D6\u5F97 -CTL_OpenPythonModulesFolderAction=Python\u30D7\u30E9\u30B0\u30A4\u30F3 -OpenPythonModulesFolderAction.actionName.text=Python\u30D7\u30E9\u30B0\u30A4\u30F3 -OpenPythonModulesFolderAction.errorMsg.folderNotFound=Python\u30D7\u30E9\u30B0\u30A4\u30F3\u30D5\u30A9\u30EB\u30C0\u30FC\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\uFF1A{0} -AddContentTagAction.tagExists={0}\u306F\u65E2\u306B{1}\u3068\u30BF\u30B0\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u540C\u3058\u30BF\u30B0\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002 -OpenLogFolder.CouldNotOpenLogFolder=\u30ED\u30B0\u30D5\u30A9\u30EB\u30C0\u30FC\u3092\u958B\u3051\u307E\u305B\u3093\u3067\u3057\u305F -CTL_OpenOutputFolder=\u30A2\u30A6\u30C8\u30D7\u30C3\u30C8\u30D5\u30A9\u30EB\u30C0\u3092\u3092\u958B\u304F -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 +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 +AddBlackboardArtifactTagAction.unableToTag.msg={0}\u306b\u30bf\u30b0\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3002 +AddBlackboardArtifactTagAction.taggingErr=\u30bf\u30b0\u4ed8\u3051\u30a8\u30e9\u30fc +AddContentTagAction.singularTagFile=\u30d5\u30a1\u30a4\u30eb\u306b\u30bf\u30b0\u3092\u8ffd\u52a0 +AddContentTagAction.pluralTagFile=\u30d5\u30a1\u30a4\u30eb\u306b\u30bf\u30b0\u3092\u8ffd\u52a0 +AddContentTagAction.unableToTag.msg={0}\u306b\u30bf\u30b0\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3002\u901a\u5e38\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AddContentTagAction.unableToTag.msg2={0}\u306b\u30bf\u30b0\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3002 +AddContentTagAction.taggingErr=\u30bf\u30b0\u4ed8\u3051\u30a8\u30e9\u30fc +AddTagAction.quickTag=\u30af\u30a4\u30c3\u30af\u30bf\u30b0 +AddTagAction.noTags=\u30bf\u30b0\u7121\u3057 +AddTagAction.newTag=\u65b0\u898f\u30bf\u30b0\u2026 +AddTagAction.tagAndComment=\u30bf\u30b0\u3068\u30b3\u30e1\u30f3\u30c8\u3092\u8ffd\u52a0\u2026 +GetTagNameAndCommentDialog.noTags=\u30bf\u30b0\u7121\u3057 +GetTagNameAndCommentDialog.createTag=\u30bf\u30b0\u3092\u4f5c\u6210 +GetTagNameAndCommentDialog.cancelName=\u30ad\u30e3\u30f3\u30bb\u30eb +GetTagNameDialog.createTag=\u30bf\u30b0\u3092\u4f5c\u6210 +GetTagNameDialog.cancelName=\u30ad\u30e3\u30f3\u30bb\u30eb +GetTagNameDialog.mustSupplyTtagName.msg=\u30bf\u30b0\u540d\u3092\u6307\u5b9a\u3057\u306a\u3051\u308c\u3070\u5148\u306b\u9032\u3081\u307e\u305b\u3093\u3002 +GetTagNameDialog.tagNameErr=\u30bf\u30b0\u540d +GetTagNameDialog.illegalCharsErr=\u4f7f\u7528\u3067\u304d\u306a\u3044\u6587\u5b57 +GetTagNameDialog.unableToAddTagNameToCase.msg=\u30bf\u30b0\u540d{0}\u3092\u30b1\u30fc\u30b9\u306b\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +GetTagNameDialog.taggingErr=\u30bf\u30b0\u4ed8\u3051\u30a8\u30e9\u30fc +GetTagNameDialog.tagNameAlreadyDef.msg=\u30bf\u30b0\u540d{0}\u306f\u65e2\u306b\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u307e\u3059\u3002 +GetTagNameDialog.dupTagErr=\u30bf\u30b0\u306e\u91cd\u8907\u30a8\u30e9\u30fc +AddContentTagAction.cannotApplyTagErr=\u30bf\u30b0\u3092\u9069\u7528\u3067\u304d\u307e\u305b\u3093 +OpenLogFolder.error1=\u30ed\u30b0\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} +CTL_OpenLogFolder=\u30ed\u30b0\u30d5\u30a9\u30eb\u30c0\u3092\u958b\u304f +ShowIngestProgressSnapshotAction.actionName.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d7\u30ed\u30b0\u30ec\u30b9\u306e\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u3092\u53d6\u5f97 +CTL_OpenPythonModulesFolderAction=Python\u30d7\u30e9\u30b0\u30a4\u30f3 +OpenPythonModulesFolderAction.actionName.text=Python\u30d7\u30e9\u30b0\u30a4\u30f3 +OpenPythonModulesFolderAction.errorMsg.folderNotFound=Python\u30d7\u30e9\u30b0\u30a4\u30f3\u30d5\u30a9\u30eb\u30c0\u30fc\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} +AddContentTagAction.tagExists={0}\u306f\u65e2\u306b{1}\u3068\u30bf\u30b0\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u540c\u3058\u30bf\u30b0\u306f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +OpenLogFolder.CouldNotOpenLogFolder=\u30ed\u30b0\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f +CTL_OpenOutputFolder=\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30d5\u30a9\u30eb\u30c0\u3092\u3092\u958b\u304f +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 diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java index 2ce4ebd666..fadf1f5adb 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2016 Basis Technology Corp. + * Copyright 2013-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,26 +20,42 @@ package org.sleuthkit.autopsy.actions; import java.awt.event.ActionEvent; import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; +import javafx.application.Platform; +import javafx.scene.control.Alert; import javax.swing.AbstractAction; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; /** * Instances of this Action allow users to delete tags applied to blackboard * artifacts. */ +@NbBundle.Messages({ + "DeleteBlackboardArtifactTagAction.deleteTag=Delete Tag", + "# {0} - tagName", + "DeleteBlackboardArtifactTagAction.unableToDelTag.msg=Unable to delete tag {0}.", + "DeleteBlackboardArtifactTagAction.tagDelErr=Tag Deletion Error" +}) public class DeleteBlackboardArtifactTagAction extends AbstractAction { + + private static final Logger LOGGER = Logger.getLogger(DeleteBlackboardArtifactTagAction.class.getName()); private static final long serialVersionUID = 1L; private static final String MENU_TEXT = NbBundle.getMessage(DeleteBlackboardArtifactTagAction.class, - "DeleteBlackboardArtifactTagAction.deleteTags"); + "DeleteBlackboardArtifactTagAction.deleteTag"); // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every @@ -65,7 +81,7 @@ public class DeleteBlackboardArtifactTagAction extends AbstractAction { try { Case.getCurrentCase().getServices().getTagsManager().deleteBlackboardArtifactTag(tag); } catch (TskCoreException ex) { - Logger.getLogger(AddContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS + Logger.getLogger(DeleteBlackboardArtifactTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS SwingUtilities.invokeLater(() -> { JOptionPane.showMessageDialog(null, NbBundle.getMessage(this.getClass(), @@ -81,28 +97,4 @@ public class DeleteBlackboardArtifactTagAction extends AbstractAction { }).start(); } - /** - * Deprecated, use actionPerformed() instead. - * - * @param event The event associated with the action. - * - * @deprecated - */ - @Deprecated - protected void doAction(ActionEvent event) { - actionPerformed(event); - } - - /** - * Deprecated, does nothing. The TagManager methods to create, update or - * delete tags now notify the case that there is a tag change. The case then - * publishes an event that triggers a refresh of the tags sub-tree in the - * tree view. - * - * @deprecated - */ - @Deprecated - protected void refreshDirectoryTree() { - } - } diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java index aabc483247..44eb19a790 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2015 Basis Technology Corp. + * Copyright 2013-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,25 +20,41 @@ package org.sleuthkit.autopsy.actions; import java.awt.event.ActionEvent; import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; +import javafx.application.Platform; +import javafx.scene.control.Alert; import javax.swing.AbstractAction; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; /** * Instances of this Action allow users to delete tags applied to content. */ +@NbBundle.Messages({ + "DeleteContentTagAction.deleteTag=Delete Tag", + "# {0} - tagName", + "DeleteContentTagAction.unableToDelTag.msg=Unable to delete tag {0}.", + "DeleteContentTagAction.tagDelErr=Tag Deletion Error" +}) public class DeleteContentTagAction extends AbstractAction { + + private static final Logger LOGGER = Logger.getLogger(DeleteContentTagAction.class.getName()); private static final long serialVersionUID = 1L; private static final String MENU_TEXT = NbBundle.getMessage(DeleteContentTagAction.class, - "DeleteContentTagAction.deleteTags"); + "DeleteContentTagAction.deleteTag"); // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every @@ -64,7 +80,7 @@ public class DeleteContentTagAction extends AbstractAction { try { Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(tag); } catch (TskCoreException ex) { - Logger.getLogger(AddContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS + Logger.getLogger(DeleteContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS SwingUtilities.invokeLater(() -> { JOptionPane.showMessageDialog(null, NbBundle.getMessage(this.getClass(), diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.java new file mode 100755 index 0000000000..afb17804b7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.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.actions; + +import java.awt.event.ActionEvent; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javax.swing.AbstractAction; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.SwingWorker; +import org.openide.util.NbBundle; +import org.openide.util.Utilities; +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.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Instances of this Action allow users to delete tags applied to blackboard + * artifacts. + */ +@NbBundle.Messages({ + "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; + private static final String MENU_TEXT = NbBundle.getMessage(DeleteFileBlackboardArtifactTagAction.class, + "DeleteFileBlackboardArtifactTagAction.deleteTag"); + + // This class is a singleton to support multi-selection of nodes, since + // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every + // node in the array returns a reference to the same action object from Node.getActions(boolean). + private static DeleteFileBlackboardArtifactTagAction instance; + + public static synchronized DeleteFileBlackboardArtifactTagAction getInstance() { + if (null == instance) { + instance = new DeleteFileBlackboardArtifactTagAction(); + } + return instance; + } + + private DeleteFileBlackboardArtifactTagAction() { + super(MENU_TEXT); + } + + @Override + public JMenuItem getPopupPresenter() { + return new TagMenu(); + } + + @Override + public void actionPerformed(ActionEvent event) { + } + + protected String getActionDisplayName() { + return MENU_TEXT; + } + + @NbBundle.Messages({"# {0} - artifactID", + "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)); + 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() + ); + } + return null; + } + + @Override + protected void done() { + super.done(); + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while untagging artifact", ex); //NON-NLS + } + } + }.execute(); + } + + /** + * Instances of this class implement a context menu user interface for + * creating or selecting a tag name for a tag and specifying an optional tag + * comment. + */ + @NbBundle.Messages({"# {0} - artifactID", + "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(); + + // Get the current set of tag names. + TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + + Map tagNamesMap = null; + try { + tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap()); + } catch (TskCoreException ex) { + Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS + } + + // Each tag name in the current set of tags gets its own menu item in + // the "Quick Tags" sub-menu. Selecting one of these menu items adds + // a tag with the associated tag name. + if (null != tagNamesMap && !tagNamesMap.isEmpty()) { + try { + 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 the bookmark tag name only, added shortcut label + if (tagDisplayName.equals(NbBundle.getMessage(AddTagAction.class, "AddBookmarkTagAction.bookmark.text"))) { + tagNameItem.setAccelerator(AddBookmarkTagAction.BOOKMARK_SHORTCUT); + } + tagNameItem.addActionListener((ActionEvent e) -> { + deleteTag(tagName, artifactTag, artifact.getArtifactID()); + }); + add(tagNameItem); + } + } + } + } catch (TskCoreException ex) { + Logger.getLogger(TagMenu.class.getName()) + .log(Level.SEVERE, "Error retrieving tags for TagMenu", ex); //NON-NLS + } + } + + 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 new file mode 100755 index 0000000000..5cc37e3900 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileContentTagAction.java @@ -0,0 +1,199 @@ +/* + * 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.actions; + +import java.awt.event.ActionEvent; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javax.swing.AbstractAction; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.SwingWorker; +import org.openide.util.NbBundle; +import org.openide.util.Utilities; +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.AbstractFile; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Instances of this Action allow users to delete tags applied to content. + */ +@NbBundle.Messages({ + "DeleteFileContentTagAction.deleteTag=Remove File Tag" +}) +public class DeleteFileContentTagAction extends AbstractAction implements Presenter.Popup { + + private static final Logger LOGGER = Logger.getLogger(DeleteFileContentTagAction.class.getName()); + + private static final long serialVersionUID = 1L; + private static final String MENU_TEXT = NbBundle.getMessage(DeleteFileContentTagAction.class, + "DeleteFileContentTagAction.deleteTag"); + + // This class is a singleton to support multi-selection of nodes, since + // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every + // node in the array returns a reference to the same action object from Node.getActions(boolean). + private static DeleteFileContentTagAction instance; + + public static synchronized DeleteFileContentTagAction getInstance() { + if (null == instance) { + instance = new DeleteFileContentTagAction(); + } + return instance; + } + + private DeleteFileContentTagAction() { + super(MENU_TEXT); + } + + @Override + public JMenuItem getPopupPresenter() { + return new TagMenu(); + } + + @Override + public void actionPerformed(ActionEvent e) { + } + + protected String getActionDisplayName() { + return MENU_TEXT; + } + + @NbBundle.Messages({ + "# {0} - fileID", + "DeleteFileContentTagAction.deleteTag.alert=Unable to untag file {0}."}) + protected void deleteTag(TagName tagName, ContentTag contentTag, long fileId) { + 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)); + 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.DeleteFileContentTagAction_deleteTag_alert(fileId)).show() + ); + } + return null; + } + + @Override + protected void done() { + super.done(); + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while untagging file", ex); //NON-NLS + } + } + }.execute(); + } + + /** + * Instances of this class implement a context menu user interface for + * creating or selecting a tag name for a tag and specifying an optional tag + * comment. + */ + private class TagMenu extends JMenu { + + private static final long serialVersionUID = 1L; + + TagMenu() { + super(getActionDisplayName()); + + final Collection selectedAbstractFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + + if(!selectedAbstractFilesList.isEmpty()) { + AbstractFile file = selectedAbstractFilesList.iterator().next(); + + // Get the current set of tag names. + TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + + Map tagNamesMap = null; + try { + tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap()); + } catch (TskCoreException ex) { + Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS + } + + // Each tag name in the current set of tags gets its own menu item in + // the "Quick Tags" sub-menu. Selecting one of these menu items adds + // 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() + .getContentTagsByContent(file); + + for (Map.Entry entry : tagNamesMap.entrySet()) { + String tagDisplayName = entry.getKey(); + + TagName tagName = entry.getValue(); + for(ContentTag contentTag : existingTagsList) { + if(tagDisplayName.equals(contentTag.getName().getDisplayName())) { + JMenuItem tagNameItem = new JMenuItem(tagDisplayName); + // for the bookmark tag name only, added shortcut label + if (tagDisplayName.equals(NbBundle.getMessage(AddTagAction.class, "AddBookmarkTagAction.bookmark.text"))) { + tagNameItem.setAccelerator(AddBookmarkTagAction.BOOKMARK_SHORTCUT); + } + tagNameItem.addActionListener((ActionEvent e) -> { + deleteTag(tagName, contentTag, file.getId()); + }); + add(tagNameItem); + } + } + } + } catch (TskCoreException ex) { + Logger.getLogger(TagMenu.class.getName()) + .log(Level.SEVERE, "Error retrieving tags for TagMenu", ex); //NON-NLS + } + } + + if(getItemCount() == 0) { + setEnabled(false); + } + } + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java index 05668a5087..f667a75825 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.casemodule; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; +import javax.annotation.concurrent.GuardedBy; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult; @@ -61,6 +62,8 @@ class AddImageTask implements Runnable { * TODO (AUT-2021): Merge SleuthkitJNI.AddImageProcess and AddImageTask */ private final Object tskAddImageProcessLock; + + @GuardedBy("tskAddImageProcessLock") private boolean tskAddImageProcessStopped; private SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess; @@ -77,8 +80,9 @@ class AddImageTask implements Runnable { * java.util.TimeZone.getID. * @param ignoreFatOrphanFiles Whether to parse orphans if the image has a * FAT filesystem. - * @param imageWriterPath Path that a copy of the image should be written to. - * Use empty string to disable image writing + * @param imageWriterPath Path that a copy of the image should be + * written to. Use empty string to disable image + * writing * @param progressMonitor Progress monitor to report progress during * processing. * @param callback Callback to call when processing is done. @@ -104,7 +108,7 @@ class AddImageTask implements Runnable { progressMonitor.setProgress(0); Case currentCase = Case.getCurrentCase(); String imageWriterPath = ""; - if(imageWriterSettings != null){ + if (imageWriterSettings != null) { imageWriterPath = imageWriterSettings.getPath(); } List errorMessages = new ArrayList<>(); @@ -112,8 +116,12 @@ class AddImageTask implements Runnable { try { currentCase.getSleuthkitCase().acquireExclusiveLock(); synchronized (tskAddImageProcessLock) { - tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, - ignoreFatOrphanFiles, imageWriterPath); + if (!tskAddImageProcessStopped) { //if we have already cancelled don't bother making an addImageProcess + tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, + ignoreFatOrphanFiles, imageWriterPath); + } else { + return; //we have already cancelled so we do not want to add the image, returning will execute the finally block + } } Thread progressUpdateThread = new Thread(new ProgressUpdater(progressMonitor, tskAddImageProcess)); progressUpdateThread.start(); @@ -142,6 +150,7 @@ class AddImageTask implements Runnable { */ public void cancelTask() { synchronized (tskAddImageProcessLock) { + tskAddImageProcessStopped = true; if (null != tskAddImageProcess) { try { /* @@ -153,7 +162,7 @@ class AddImageTask implements Runnable { * called. */ tskAddImageProcess.stop(); - tskAddImageProcessStopped = true; + } catch (TskCoreException ex) { logger.log(Level.SEVERE, String.format("Error cancelling adding image %s to the case database", imagePath), ex); //NON-NLS } @@ -213,13 +222,13 @@ class AddImageTask implements Runnable { if (!verificationError.isEmpty()) { errorMessages.add(verificationError); } - if(imageWriterSettings != null){ - ImageWriterService.createImageWriter(imageId, imageWriterSettings); + if (imageWriterSettings != null) { + ImageWriterService.createImageWriter(imageId, imageWriterSettings); } newDataSources.add(newImage); } else { String errorMessage = String.format("Error commiting adding image %s to the case database, no object id returned", imagePath); //NON-NLS - logger.log(Level.SEVERE, errorMessage); + logger.log(Level.SEVERE, errorMessage); errorMessages.add(errorMessage); criticalErrorOccurred = true; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java index 22a6ee9762..cf206a39d8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.casemodule; import java.awt.Color; +import java.awt.Cursor; import java.awt.EventQueue; import java.awt.Window; import java.util.ArrayList; @@ -36,6 +37,7 @@ import org.openide.WizardDescriptor; import org.openide.util.HelpCtx; import org.openide.util.Lookup; import org.openide.util.NbBundle; +import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; @@ -319,8 +321,10 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { cleanupTask = addImageAction.new CleanupTask() { @Override void cleanup() throws Exception { + WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); cancelDataSourceProcessing(dataSourceId); cancelled = true; + WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } }; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index be0ddd285b..3570da2497 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -148,6 +148,7 @@ GeneralFilter.encaseImageDesc.text=Encase Images (*.e01) GeneralFilter.virtualMachineImageDesc.text=Virtual Machines (*.vmdk, *.vhd) GeneralFilter.executableDesc.text=Executables (*.exe) ImageDSProcessor.dsType.text=Disk Image or VM File +GeneralFilter.graphicImageDesc.text=Images (*.png, *.jpg, *.jpeg, *.gif, *.bmp) ImageDSProcessor.allDesc.text=All Supported Types ImageFilePanel.moduleErr=Module Error ImageFilePanel.moduleErr.msg=A module caused an error listening to ImageFilePanel updates. See log to determine which module. Some data could be incomplete. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 474ffa18ff..08880d9b9b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -113,10 +113,8 @@ import org.sleuthkit.datamodel.TskCoreException; */ public class Case { - private static final int NAME_LOCK_TIMOUT_HOURS = 12; - private static final int SHARED_DIR_LOCK_TIMOUT_HOURS = 12; - private static final int RESOURCE_LOCK_TIMOUT_HOURS = 12; - private static final int MAX_CASEDB_NAME_LEN_MINUS_TIMESTAMP = 47; //Truncate to 63-16=47 chars to accomodate the timestamp + private static final int DIR_LOCK_TIMOUT_HOURS = 12; + private static final int RESOURCES_LOCK_TIMOUT_HOURS = 12; private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db"; private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS private static final String CACHE_FOLDER = "Cache"; //NON-NLS @@ -475,18 +473,21 @@ public class Case { } if (null != currentCase) { + String previousCaseDisplayName = currentCase.getDisplayName(); + String previousCaseName = currentCase.getName(); + String previousCaseDir = currentCase.getCaseDirectory(); try { closeCurrentCase(); } catch (CaseActionException ex) { - logger.log(Level.SEVERE, "Error closing the previous current case", ex); //NON-NLS + logger.log(Level.SEVERE, String.format("Error closing the previous current case %s (%s) in %s", previousCaseDisplayName, previousCaseName, previousCaseDir), ex); //NON-NLS } } - logger.log(Level.INFO, "Creating current case with display name {0} in {1}", new Object[]{caseDisplayName, caseDir}); //NON-NLS + logger.log(Level.INFO, "Creating current case {0} in {1}", new Object[]{caseDisplayName, caseDir}); //NON-NLS Case newCurrentCase = new Case(); - newCurrentCase.open(caseDir, caseDisplayName, caseNumber, examiner, caseType); + newCurrentCase.create(caseType, caseDir, caseDisplayName, caseNumber, examiner); currentCase = newCurrentCase; - logger.log(Level.INFO, "Created currrent case {0} (display name {1}) in {2}", new Object[]{newCurrentCase.getName(), caseDisplayName, caseDir}); //NON-NLS + logger.log(Level.INFO, "Created currrent case {0} ({1}) in {2}", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS if (RuntimeProperties.runningWithGUI()) { updateGUIForCaseOpened(newCurrentCase); } @@ -723,83 +724,42 @@ public class Case { } /** - * Transforms the display name for a case to make a suitable case name for - * use in case directory paths, coordination service locks, Active MQ - * message channels, etc. - * - * ActiveMQ: - * http://activemq.2283324.n4.nabble.com/What-are-limitations-restrictions-on-destination-name-td4664141.html - * may not be ? + * Transforms a case display name into a unique case name that can be used + * to identify the case even if the display name is changed. * * @param caseDisplayName A case display name. * - * @return The case display name transformed into a corresponding case name. - * - * @throws org.sleuthkit.autopsy.casemodule.Case.IllegalCaseNameException + * @return The unique case name. */ - public static String displayNameToCaseName(String caseDisplayName) throws IllegalCaseNameException { + private static String displayNameToUniqueName(String caseDisplayName) { + /* + * Replace all non-ASCII characters. + */ + String uniqueCaseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS /* - * Remove all non-ASCII characters. + * Replace all control characters. */ - String caseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS + uniqueCaseName = uniqueCaseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS /* - * Remove all control characters. + * Replace /, \, :, ?, space, ' ". */ - caseName = caseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS - - /* - * Remove /, \, :, ?, space, ' ". - */ - caseName = caseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS + uniqueCaseName = uniqueCaseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS /* * Make it all lowercase. */ - caseName = caseName.toLowerCase(); - - if (caseName.isEmpty()) { - throw new IllegalCaseNameException(String.format("Failed to convert case name '%s'", caseDisplayName)); - } - - return caseName; - } - - /** - * Transforms a case name into a name for a PostgreSQL database that can be - * safely used in SQL commands as described at - * http://www.postgresql.org/docs/9.4/static/sql-syntax-lexical.html: 63 - * chars max, must start with a letter or underscore, following chars can be - * letters, underscores, or digits. A timestamp suffix is added to ensure - * uniqueness. - * - * @param caseName The case name. - * - * @return The case name transformed into a corresponding PostgreSQL case - * database name. - */ - private static String caseNameToCaseDbName(String caseName) throws IllegalCaseNameException { - /* - * Must start with letter or underscore. If not, prepend an underscore. - */ - String dbName = caseName; - if (dbName.length() > 0 && !(Character.isLetter(dbName.codePointAt(0))) && !(dbName.codePointAt(0) == '_')) { - dbName = "_" + dbName; - } + uniqueCaseName = uniqueCaseName.toLowerCase(); /* - * Truncate to 63-16=47 chars to accomodate the timestamp, then add the - * timestamp. + * Add a time stamp for uniqueness. */ - if (dbName.length() > MAX_CASEDB_NAME_LEN_MINUS_TIMESTAMP) { - dbName = dbName.substring(0, MAX_CASEDB_NAME_LEN_MINUS_TIMESTAMP); - } SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss"); Date date = new Date(); - dbName = dbName + "_" + dateFormat.format(date); + uniqueCaseName = uniqueCaseName + "_" + dateFormat.format(date); - return dbName; + return uniqueCaseName; } /** @@ -973,7 +933,7 @@ public class Case { private static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir) throws CaseActionException { try { String resourcesNodeName = caseDir + "_resources"; - Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, RESOURCE_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); + Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, RESOURCES_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); if (null == lock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock()); } @@ -1585,6 +1545,7 @@ public class Case { } /** + * @param caseType The type of case (single-user or multi-user). * @param caseDir The full path of the case directory. The directory * will be created if it doesn't already exist; if it * exists, it is ASSUMED it was created by calling @@ -1594,7 +1555,6 @@ public class Case { * @param caseNumber The case number, can be the empty string. * @param examiner The examiner to associate with the case, can be * the empty string. - * @param caseType The type of case (single-user or multi-user). * * @throws CaseActionException if there is a problem creating the case. The * exception will have a user-friendly message @@ -1602,24 +1562,12 @@ public class Case { * exception. */ @Messages({ - "Case.exceptionMessage.illegalCaseName=Case name contains illegal characters.", "Case.progressIndicatorTitle.creatingCase=Creating Case", "Case.progressIndicatorCancelButton.label=Cancel", "Case.progressMessage.preparing=Preparing...", "Case.progressMessage.openingCaseResources=Preparing to open case resources.
This may take time if another user is upgrading the case." }) - private void open(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException { - /* - * Clean up the display name for the case to make a suitable immutable - * case name. - */ - String caseName; - try { - caseName = displayNameToCaseName(caseDisplayName); - } catch (IllegalCaseNameException ex) { - throw new CaseActionException(Bundle.Case_exceptionMessage_wrapperMessage(Bundle.Case_exceptionMessage_illegalCaseName()), ex); - } - + private void create(CaseType caseType, String caseDir, String caseDisplayName, String caseNumber, String examiner) throws CaseActionException { /* * Set up either a GUI progress indicator or a logging progress * indicator. @@ -1648,7 +1596,7 @@ public class Case { caseLockingExecutor = Executors.newSingleThreadExecutor(); Future future = caseLockingExecutor.submit(() -> { if (CaseType.SINGLE_USER_CASE == caseType) { - open(caseDir, caseName, caseDisplayName, caseNumber, examiner, caseType, progressIndicator); + create(caseType, caseDir, caseDisplayName, caseNumber, examiner, progressIndicator); } else { /* * Acquire a shared case directory lock that will be held as @@ -1657,6 +1605,7 @@ public class Case { */ progressIndicator.start(Bundle.Case_progressMessage_openingCaseResources()); acquireSharedCaseDirLock(caseDir); + /* * Acquire an exclusive case resources lock to ensure only one * node at a time can create/open/upgrade/close the case @@ -1665,14 +1614,14 @@ public class Case { try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(caseDir)) { assert (null != resourcesLock); try { - open(caseDir, caseName, caseDisplayName, caseNumber, examiner, caseType, progressIndicator); + create(caseType, caseDir, caseDisplayName, caseNumber, examiner, progressIndicator); } catch (CaseActionException ex) { /* * Release the case directory lock immediately if there * was a problem opening the case. */ if (CaseType.MULTI_USER_CASE == caseType) { - releaseSharedCaseDirLock(caseName); + releaseSharedCaseDirLock(caseDir); } throw ex; } @@ -1696,7 +1645,6 @@ public class Case { */ try { future.get(); - } catch (InterruptedException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_wrapperMessage(ex.getMessage()), ex); } catch (ExecutionException ex) { @@ -1711,18 +1659,20 @@ public class Case { } /** - * Creates and opens a new case. + * Creates a new case. * - * @param caseDir The full path of the case directory. The directory - * will be created if it doesn't already exist; if it - * exists, it is ASSUMED it was created by calling - * createCaseDirectory. - * @param caseDisplayName The display name of case, which may be changed - * later by the user. - * @param caseNumber The case number, can be the empty string. - * @param examiner The examiner to associate with the case, can be - * the empty string. - * @param caseType The type of case (single-user or multi-user). + * @param caseType The type of case (single-user or multi-user). + * @param caseDir The full path of the case directory. The + * directory will be created if it doesn't already + * exist; if it exists, it is ASSUMED it was + * created by calling createCaseDirectory. + * @param caseName The case name. + * @param caseDisplayName The display name of case, which may be changed + * later by the user. + * @param caseNumber The case number, can be the empty string. + * @param examiner The examiner to associate with the case, can be + * the empty string. + * @param progressIndicator A progress indicator. * * @throws CaseActionException if there is a problem creating the case. The * exception will have a user-friendly message @@ -1730,6 +1680,7 @@ public class Case { * exception. */ @Messages({ + "Case.exceptionMessage.emptyCaseName=Case name is empty.", "Case.progressMessage.creatingCaseDirectory=Creating case directory...", "Case.progressMessage.creatingCaseDatabase=Creating case database...", "Case.exceptionMessage.couldNotCreateCaseDatabaseName=Failed to create case database name from case name.", @@ -1737,7 +1688,15 @@ public class Case { "Case.exceptionMessage.couldNotCreateMetadataFile=Failed to create case metadata file.", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database." }) - private void open(String caseDir, String caseName, String caseDisplayName, String caseNumber, String examiner, CaseType caseType, ProgressIndicator progressIndicator) throws CaseActionException { + private void create(CaseType caseType, String caseDir, String caseDisplayName, String caseNumber, String examiner, ProgressIndicator progressIndicator) throws CaseActionException { + /* + * Create a unique and immutable case name from the case display name. + */ + if (caseDisplayName.isEmpty()) { + throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName()); + } + String caseName = displayNameToUniqueName(caseDisplayName); + /* * Create the case directory, if it does not already exist. * @@ -1768,32 +1727,26 @@ public class Case { */ progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase()); String dbName = null; - try { - if (CaseType.SINGLE_USER_CASE == caseType) { - dbName = SINGLE_USER_CASE_DB_NAME; - } else if (CaseType.MULTI_USER_CASE == caseType) { - dbName = caseNameToCaseDbName(caseName); - } - } catch (IllegalCaseNameException ex) { - throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabaseName(), ex); - } try { if (CaseType.SINGLE_USER_CASE == caseType) { /* * For single-user cases, the case database is a SQLite database - * physically located in the root of the case directory. + * with a standard name, physically located in the root of the + * case directory. */ + dbName = SINGLE_USER_CASE_DB_NAME; this.caseDb = SleuthkitCase.newCase(Paths.get(caseDir, SINGLE_USER_CASE_DB_NAME).toString()); } else if (CaseType.MULTI_USER_CASE == caseType) { /* * For multi-user cases, the case database is a PostgreSQL - * database physically located on the database server. + * database with a name derived from the case display name, + * physically located on the database server. */ - this.caseDb = SleuthkitCase.newCase(dbName, UserPreferences.getDatabaseConnectionInfo(), caseDir); + this.caseDb = SleuthkitCase.newCase(caseDisplayName, UserPreferences.getDatabaseConnectionInfo(), caseDir); + dbName = this.caseDb.getDatabaseName(); } } catch (TskCoreException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(), ex); - } catch (UserPreferencesException ex) { throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.databaseConnectionInfo.error.msg"), ex); } @@ -1814,7 +1767,7 @@ public class Case { /** * Opens an existing case. * - * @param caseMetadataFilePath The apth to the case metadata file. + * @param caseMetadataFilePath The path to the case metadata file. * * @throws CaseActionException if there is a problem creating the case. The * exception will have a user-friendly message @@ -1980,7 +1933,8 @@ public class Case { "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",}) private void openServices(ProgressIndicator progressIndicator) throws CaseActionException { /* - * Switch to writing to the application logs in the logs subdirectory. + * Switch to writing to the application logs in the logs subdirectory of + * the case directory. */ progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory()); Logger.setLogDirectory(getLogDirectoryPath()); @@ -1989,12 +1943,11 @@ public class Case { * Hook up a SleuthKit layer error reporter. */ progressIndicator.progress(Bundle.Case_progressMessage_settingUpTskErrorReporting()); - this.sleuthkitErrorReporter = new SleuthkitErrorReporter(MIN_SECS_BETWEEN_TSK_ERROR_REPORTS, NbBundle.getMessage(Case.class, "IntervalErrorReport.ErrorText")); this.caseDb.addErrorObserver(this.sleuthkitErrorReporter); /* - * Clear the temp subdirectory. + * Clear the temp subdirectory of the case directory. */ progressIndicator.progress(Bundle.Case_progressMessage_clearingTempDirectory()); Case.clearTempSubDir(this.getTempDirectory()); @@ -2335,7 +2288,7 @@ public class Case { @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory."}) private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException { try { - caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, SHARED_DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); + caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); if (null == caseDirLock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock()); } @@ -2400,36 +2353,6 @@ public class Case { } - /** - * An exception to throw when a case name with invalid characters is - * encountered. - */ - public final static class IllegalCaseNameException extends Exception { - - private static final long serialVersionUID = 1L; - - /** - * Constructs an exception to throw when a case name with invalid - * characters is encountered. - * - * @param message The exception message. - */ - IllegalCaseNameException(String message) { - super(message); - } - - /** - * Constructs an exception to throw when a case name with invalid - * characters is encountered. - * - * @param message The exception message. - * @param cause The exceptin cause. - */ - IllegalCaseNameException(String message, Throwable cause) { - super(message, cause); - } - } - /** * Creates a new, single-user Autopsy case. * diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java index d53b8b3c66..9a870e25a2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java @@ -125,7 +125,7 @@ public final class CaseMetadata { * created. */ CaseMetadata(String caseDirectory, Case.CaseType caseType, String caseName, String caseDisplayName, String caseNumber, String examiner, String caseDatabase) throws CaseMetadataException { - metadataFilePath = Paths.get(caseDirectory, caseName + FILE_EXTENSION); + metadataFilePath = Paths.get(caseDirectory, caseDisplayName + FILE_EXTENSION); this.caseType = caseType; this.caseName = caseName; this.caseDisplayName = caseDisplayName; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralFilter.java b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralFilter.java index 0f90bc9fae..f848722953 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralFilter.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralFilter.java @@ -37,14 +37,17 @@ public class GeneralFilter extends FileFilter { public static final List ENCASE_IMAGE_EXTS = Arrays.asList(new String[]{".e01"}); //NON-NLS public static final String ENCASE_IMAGE_DESC = NbBundle.getMessage(GeneralFilter.class, "GeneralFilter.encaseImageDesc.text"); - + public static final List VIRTUAL_MACHINE_EXTS = Arrays.asList(new String[]{".vmdk", ".vhd"}); //NON-NLS public static final String VIRTUAL_MACHINE_DESC = NbBundle.getMessage(GeneralFilter.class, - "GeneralFilter.virtualMachineImageDesc.text"); + "GeneralFilter.virtualMachineImageDesc.text"); public static final List EXECUTABLE_EXTS = Arrays.asList(new String[]{".exe"}); //NON-NLS public static final String EXECUTABLE_DESC = NbBundle.getMessage(GeneralFilter.class, "GeneralFilter.executableDesc.text"); - + + public static final List GRAPHIC_IMAGE_EXTS = Arrays.asList(new String[]{".png", ".jpeg", ".jpg", ".gif", ".bmp"}); //NON-NLS + public static final String GRAPHIC_IMG_DECR = NbBundle.getMessage(GeneralFilter.class, "GeneralFilter.graphicImageDesc.text"); + private List extensions; private String desc; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel1.java index 8ceda8fc2a..dcfaab449c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel1.java @@ -204,25 +204,19 @@ class NewCaseWizardPanel1 implements WizardDescriptor.ValidatingPanel | - if (!Case.isValidName(caseDisplayName)) { + /* + * Check whether or not the case name is valid. To be valid, the case + * name must not contain any characters that are not allowed in file + * names, since it will be used as the name of the case directory. + */ + String caseName = getComponent().getCaseName(); + if (!Case.isValidName(caseName)) { String errorMsg = NbBundle .getMessage(this.getClass(), "NewCaseWizardPanel1.validate.errMsg.invalidSymbols"); validationError(errorMsg); } else { - String caseName = ""; - try { - caseName = Case.displayNameToCaseName(caseDisplayName); - } catch (Case.IllegalCaseNameException ex) { - String errorMsg = NbBundle - .getMessage(this.getClass(), "NewCaseWizardPanel1.validate.errMsg.invalidSymbols"); - validationError(errorMsg); - } + String caseParentDir = getComponent().getCaseParentDir(); String caseDirPath = caseParentDir + caseName; // check if the directory exist diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java b/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java index ddc0ccd1d5..50494f47a0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java @@ -43,7 +43,7 @@ import org.sleuthkit.datamodel.TskData; /** * Import a case from single-user to multi-user. - * + * * DO NOT USE, NEEDS TO BE UPDATED */ public class SingleUserCaseConverter { @@ -175,9 +175,15 @@ public class SingleUserCaseConverter { } // Create sanitized names for PostgreSQL and Solr + /* + * RJC: Removed package access sanitizeCaseName method, so this is no + * longer correct, but this whole class is currently out-of-date (out of + * synch with case database schema) and probably belongs in the TSK + * layer anyway, see JIRA-1984. + */ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss"); //NON-NLS Date date = new Date(); - String dbName = Case.displayNameToCaseName(icd.getNewCaseName()) + "_" + dateFormat.format(date); //NON-NLS + String dbName = icd.getNewCaseName() + "_" + dateFormat.format(date); //NON-NLS icd.setPostgreSQLDbName(dbName); // Copy items to new hostname folder structure @@ -493,12 +499,12 @@ public class SingleUserCaseConverter { if (value > biggestPK) { biggestPK = value; } - + // If the entry contains an encoding type, copy it. Otherwise use NONE. // The test on column count can be removed if we upgrade the database before conversion. int encoding = TskData.EncodingType.NONE.getType(); ResultSetMetaData rsMetaData = inputResultSet.getMetaData(); - if(rsMetaData.getColumnCount() == 3){ + if (rsMetaData.getColumnCount() == 3) { encoding = inputResultSet.getInt(3); } outputStatement.executeUpdate("INSERT INTO tsk_files_path (obj_id, path, encoding_type) VALUES (" //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java index 519a1efda4..543318e710 100755 --- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java @@ -55,15 +55,16 @@ public final class UserPreferences { public static final String EXTERNAL_DATABASE_NAME = "ExternalDatabaseName"; //NON-NLS public static final String EXTERNAL_DATABASE_USER = "ExternalDatabaseUsername"; //NON-NLS public static final String EXTERNAL_DATABASE_PASSWORD = "ExternalDatabasePassword"; //NON-NLS - public static final String EXTERNAL_DATABASE_TYPE = "ExternalDatabaseType"; //NON-NLS + public static final String EXTERNAL_DATABASE_TYPE = "ExternalDatabaseType"; //NON-NLS public static final String INDEXING_SERVER_HOST = "IndexingServerHost"; //NON-NLS public static final String INDEXING_SERVER_PORT = "IndexingServerPort"; //NON-NLS private static final String MESSAGE_SERVICE_PASSWORD = "MessageServicePassword"; //NON-NLS private static final String MESSAGE_SERVICE_USER = "MessageServiceUser"; //NON-NLS private static final String MESSAGE_SERVICE_HOST = "MessageServiceHost"; //NON-NLS private static final String MESSAGE_SERVICE_PORT = "MessageServicePort"; //NON-NLS - public static final String PROCESS_TIME_OUT_ENABLED = "ProcessTimeOutEnabled"; //NON-NLS - public static final String PROCESS_TIME_OUT_HOURS = "ProcessTimeOutHours"; //NON-NLS + public static final String PROCESS_TIME_OUT_ENABLED = "ProcessTimeOutEnabled"; //NON-NLS + public static final String PROCESS_TIME_OUT_HOURS = "ProcessTimeOutHours"; //NON-NLS + public static final String AGENCY_LOGO_IMAGE_PATH = "AgencyLogoImagePath"; //NON-NLS private static final int DEFAULT_PROCESS_TIMEOUT_HR = 60; private static final String DEFAULT_PORT_STRING = "61616"; private static final int DEFAULT_PORT_INT = 61616; @@ -302,7 +303,7 @@ public final class UserPreferences { public static void setIsTimeOutEnabled(boolean enabled) { preferences.putBoolean(PROCESS_TIME_OUT_ENABLED, enabled); } - + /** * Get the display name for this program * @return Name of this program @@ -310,17 +311,17 @@ public final class UserPreferences { public static String getAppName(){ return preferences.get(APP_NAME, "Autopsy"); } - + /** * Set the display name for this program - * + * * @param name Display name */ public static void setAppName(String name){ preferences.put(APP_NAME, name); } - - + + /** * Provides ability to convert text to hex text. */ diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form index 822d8bf95c..06607d7d9b 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form @@ -46,37 +46,19 @@ - + - - - - - - - - - - - - - - + + + - - - - - - - @@ -84,7 +66,26 @@ - + + + + + + + + + + + + + + + + + + + + @@ -92,7 +93,7 @@ - + @@ -117,7 +118,14 @@ - + + + + + + + + @@ -249,6 +257,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java index dbc6358fcc..4f95087f66 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java @@ -18,8 +18,13 @@ */ package org.sleuthkit.autopsy.corecomponents; +import java.io.File; +import javax.swing.JFileChooser; import org.netbeans.spi.options.OptionsPanelController; +import org.sleuthkit.autopsy.casemodule.GeneralFilter; import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.report.ReportBranding; /** * Options panel that allow users to set application preferences. @@ -27,9 +32,14 @@ import org.sleuthkit.autopsy.core.UserPreferences; final class AutopsyOptionsPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; + private final JFileChooser fc; AutopsyOptionsPanel() { initComponents(); + fc = new JFileChooser(); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setMultiSelectionEnabled(false); + fc.setFileFilter(new GeneralFilter(GeneralFilter.GRAPHIC_IMAGE_EXTS, GeneralFilter.GRAPHIC_IMG_DECR)); } void load() { @@ -43,6 +53,7 @@ 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)); } void store() { @@ -52,6 +63,12 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { UserPreferences.setHideSlackFilesInDataSourcesTree(dataSourcesHideSlackCB.isSelected()); UserPreferences.setHideSlackFilesInViewsTree(viewsHideSlackCB.isSelected()); UserPreferences.setDisplayTimesInLocalTime(useLocalTimeRB.isSelected()); + if (!agencyLogoPathField.getText().isEmpty()) { + File image = new File(agencyLogoPathField.getText()); + if (image.exists()) { + ModuleSettings.setConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP, agencyLogoPathField.getText()); + } + } } boolean valid() { @@ -82,6 +99,9 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { dataSourcesHideSlackCB = new javax.swing.JCheckBox(); viewsHideSlackCB = new javax.swing.JCheckBox(); jLabelHideSlackFiles = new javax.swing.JLabel(); + agencyLogoImageLabel = new javax.swing.JLabel(); + agencyLogoPathField = new javax.swing.JTextField(); + browseLogosButton = new javax.swing.JButton(); jScrollPane1.setBorder(null); @@ -155,6 +175,21 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { 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( @@ -162,33 +197,32 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabelTimeDisplay) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(10, 10, 10) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(useLocalTimeRB) - .addComponent(useGMTTimeRB)))) - .addContainerGap(512, Short.MAX_VALUE)) .addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabelTimeDisplay) .addComponent(jLabelHideKnownFiles) .addComponent(jLabelSelectFile) .addGroup(jPanel1Layout.createSequentialGroup() .addGap(10, 10, 10) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(useLocalTimeRB) + .addComponent(useGMTTimeRB) .addComponent(keepCurrentViewerRB) .addComponent(useBestViewerRB) .addComponent(dataSourcesHideKnownCB) .addComponent(viewsHideKnownCB)))) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(140, Short.MAX_VALUE)) .addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabelHideSlackFiles) + .addComponent(agencyLogoImageLabel) .addGroup(jPanel1Layout.createSequentialGroup() .addGap(10, 10, 10) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(agencyLogoPathField, javax.swing.GroupLayout.PREFERRED_SIZE, 259, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(browseLogosButton)) .addComponent(dataSourcesHideSlackCB) .addComponent(viewsHideSlackCB)))) .addGap(0, 0, Short.MAX_VALUE)))) @@ -220,7 +254,13 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addComponent(useLocalTimeRB) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(useGMTTimeRB) - .addContainerGap(148, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(agencyLogoImageLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(agencyLogoPathField) + .addComponent(browseLogosButton)) + .addGap(35, 35, 35)) ); jScrollPane1.setViewportView(jPanel1); @@ -269,7 +309,19 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); }//GEN-LAST:event_viewsHideSlackCBActionPerformed + private void browseLogosButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseLogosButtonActionPerformed + int returnState = fc.showOpenDialog(this); + if (returnState == JFileChooser.APPROVE_OPTION) { + String path = fc.getSelectedFile().getPath(); + agencyLogoPathField.setText(path); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + }//GEN-LAST:event_browseLogosButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel agencyLogoImageLabel; + private javax.swing.JTextField agencyLogoPathField; + private javax.swing.JButton browseLogosButton; private javax.swing.ButtonGroup buttonGroup1; private javax.swing.ButtonGroup buttonGroup3; private javax.swing.JCheckBox dataSourcesHideKnownCB; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 250f855374..183a66a263 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -194,3 +194,7 @@ MultiUserSettingsPanel.InvalidPortNumber=Invalid port number AutopsyOptionsPanel.jLabelHideSlackFiles.text=Hide slack files in the: AutopsyOptionsPanel.dataSourcesHideSlackCB.text=Data Sources area (the directory hierarchy) AutopsyOptionsPanel.viewsHideSlackCB.text=Views area +AutopsyOptionsPanel.agencyLogoImageLabel.toolTipText= +AutopsyOptionsPanel.agencyLogoImageLabel.text=Image to use as the agency logo for HTML reports: +AutopsyOptionsPanel.browseLogosButton.text=Browse +AutopsyOptionsPanel.agencyLogoPathField.text= diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java index 7ab744d712..dc1aa9a8bf 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,18 +19,25 @@ package org.sleuthkit.autopsy.datamodel; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import javax.swing.Action; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.HashSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.directorytree.ViewContextAction; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.Directory; @@ -64,129 +71,227 @@ public class DataModelActionsFactory { .getMessage(DataModelActionsFactory.class, "DataModelActionsFactory.srfFileSameMD5.text"); public static List getActions(File file, boolean isArtifactSource) { - List actions = new ArrayList<>(); - actions.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), file)); + List actionsList = new ArrayList<>(); + actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), file)); final FileNode fileNode = new FileNode(file); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, fileNode)); - actions.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, fileNode)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - actions.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, fileNode)); - actions.add(null); // creates a menu separator - actions.add(AddContentTagAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, fileNode)); + actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, fileNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); + actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, fileNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { - actions.add(AddBlackboardArtifactTagAction.getInstance()); + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); } - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + if(isArtifactSource) { + final Collection selectedArtifactsList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + if(selectedArtifactsList.size() == 1) { + actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance()); + } + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } public static List getActions(SlackFile slackFile, boolean isArtifactSource) { - List actions = new ArrayList<>(); - actions.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), slackFile)); + List actionsList = new ArrayList<>(); + actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), slackFile)); final SlackFileNode slackFileNode = new SlackFileNode(slackFile); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, slackFileNode)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - actions.add(null); // creates a menu separator - actions.add(AddContentTagAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, slackFileNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { - actions.add(AddBlackboardArtifactTagAction.getInstance()); + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); } - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + if(isArtifactSource) { + final Collection selectedArtifactsList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + if(selectedArtifactsList.size() == 1) { + actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance()); + } + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } public static List getActions(LayoutFile file, boolean isArtifactSource) { - List actions = new ArrayList<>(); - actions.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), file)); + List actionsList = new ArrayList<>(); + actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), file)); LayoutFileNode layoutFileNode = new LayoutFileNode(file); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, layoutFileNode)); - actions.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, layoutFileNode)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance());// - actions.add(null); // creates a menu separator - actions.add(AddContentTagAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, layoutFileNode)); + actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, layoutFileNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance());// + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { - actions.add(AddBlackboardArtifactTagAction.getInstance()); + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); } - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + if(isArtifactSource) { + final Collection selectedArtifactsList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + if(selectedArtifactsList.size() == 1) { + actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance()); + } + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } public static List getActions(Directory directory, boolean isArtifactSource) { - List actions = new ArrayList<>(); - actions.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), directory)); + List actionsList = new ArrayList<>(); + actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), directory)); DirectoryNode directoryNode = new DirectoryNode(directory); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, directoryNode)); - actions.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, directoryNode)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - actions.add(null); // creates a menu separator - actions.add(AddContentTagAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, directoryNode)); + actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, directoryNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { - actions.add(AddBlackboardArtifactTagAction.getInstance()); + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); } - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + if(isArtifactSource) { + final Collection selectedArtifactsList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + if(selectedArtifactsList.size() == 1) { + actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance()); + } + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } public static List getActions(VirtualDirectory directory, boolean isArtifactSource) { - List actions = new ArrayList<>(); - actions.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), directory)); + List actionsList = new ArrayList<>(); + actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), directory)); VirtualDirectoryNode directoryNode = new VirtualDirectoryNode(directory); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, directoryNode)); - actions.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, directoryNode)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - actions.add(null); // creates a menu separator - actions.add(AddContentTagAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, directoryNode)); + actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, directoryNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { - actions.add(AddBlackboardArtifactTagAction.getInstance()); + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); } - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + if(isArtifactSource) { + final Collection selectedArtifactsList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + if(selectedArtifactsList.size() == 1) { + actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance()); + } + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } public static List getActions(LocalFile file, boolean isArtifactSource) { - List actions = new ArrayList<>(); - actions.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), file)); + List actionsList = new ArrayList<>(); + actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), file)); final LocalFileNode localFileNode = new LocalFileNode(file); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, localFileNode)); - actions.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, localFileNode)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - actions.add(null); // creates a menu separator - actions.add(AddContentTagAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, localFileNode)); + actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, localFileNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { - actions.add(AddBlackboardArtifactTagAction.getInstance()); + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); } - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + if(isArtifactSource) { + final Collection selectedArtifactsList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + if(selectedArtifactsList.size() == 1) { + actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance()); + } + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } public static List getActions(DerivedFile file, boolean isArtifactSource) { - List actions = new ArrayList<>(); - actions.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), file)); + List actionsList = new ArrayList<>(); + actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), file)); final LocalFileNode localFileNode = new LocalFileNode(file); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, localFileNode)); - actions.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, localFileNode)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - actions.add(null); // creates a menu separator - actions.add(AddContentTagAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, localFileNode)); + actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, localFileNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { - actions.add(AddBlackboardArtifactTagAction.getInstance()); + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); } - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + if(isArtifactSource) { + final Collection selectedArtifactsList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + if(selectedArtifactsList.size() == 1) { + actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance()); + } + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } public static List getActions(Content content, boolean isArtifactSource) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java index 59240d204a..68aeb01e8c 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 - 2013 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,11 +19,16 @@ package org.sleuthkit.autopsy.datamodel; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import javax.swing.Action; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.directorytree.ViewContextAction; @@ -37,6 +42,8 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; * are more directories. */ public class DirectoryNode extends AbstractFsContentNode { + + private static final Logger LOGGER = Logger.getLogger(DirectoryNode.class.getName()); public static final String DOTDOTDIR = NbBundle.getMessage(DirectoryNode.class, "DirectoryNode.parFolder.text"); public static final String DOTDIR = NbBundle.getMessage(DirectoryNode.class, "DirectoryNode.curFolder.text"); @@ -71,23 +78,29 @@ public class DirectoryNode extends AbstractFsContentNode { */ @Override public Action[] getActions(boolean popup) { - List actions = new ArrayList<>(); + List actionsList = new ArrayList<>(); for (Action a : super.getActions(true)) { - actions.add(a); + actionsList.add(a); } if (!getDirectoryBrowseMode()) { - actions.add(new ViewContextAction( + actionsList.add(new ViewContextAction( NbBundle.getMessage(this.getClass(), "DirectoryNode.getActions.viewFileInDir.text"), this)); - actions.add(null); // creates a menu separator + actionsList.add(null); // creates a menu separator } - actions.add(new NewWindowViewAction(NbBundle.getMessage(this.getClass(), "DirectoryNode.viewInNewWin.text"), this)); - actions.add(ViewFileInTimelineAction.createViewFileAction(getContent())); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); - actions.add(null); // creates a menu separator - actions.add(AddContentTagAction.getInstance()); - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions.toArray(new Action[actions.size()]); + actionsList.add(new NewWindowViewAction(NbBundle.getMessage(this.getClass(), "DirectoryNode.viewInNewWin.text"), this)); + actionsList.add(ViewFileInTimelineAction.createViewFileAction(getContent())); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); + + final Collection selectedFilesList = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList.toArray(new Action[actionsList.size()]); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java index 7ee5acf08f..10ba139efd 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.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"); @@ -19,13 +19,18 @@ package org.sleuthkit.autopsy.datamodel; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import javax.swing.Action; import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.ImageUtils; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.HashSearchAction; @@ -41,6 +46,8 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; * children. */ public class FileNode extends AbstractFsContentNode { + + private static final Logger LOGGER = Logger.getLogger(FileNode.class.getName()); /** * Constructor @@ -96,6 +103,12 @@ public class FileNode extends AbstractFsContentNode { actionsList.add(new HashSearchAction(Bundle.FileNode_getActions_searchFilesSameMD5_text(), this)); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); + + final Collection selectedFilesList = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + actionsList.addAll(ContextMenuExtensionPoint.getActions()); return actionsList.toArray(new Action[actionsList.size()]); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java index 495737488b..c759b425d7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,17 +19,22 @@ package org.sleuthkit.autopsy.datamodel; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.swing.Action; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; +import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.TskData; @@ -121,6 +126,13 @@ public class LayoutFileNode extends AbstractAbstractFileNode { actionsList.add(ExtractAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + actionsList.addAll(ContextMenuExtensionPoint.getActions()); return actionsList.toArray(new Action[0]); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java index ecccb26272..5738356c46 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2016 Basis Technology Corp. + * Copyright 2013-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,14 +20,19 @@ package org.sleuthkit.autopsy.datamodel; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.swing.Action; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.HashSearchAction; @@ -39,6 +44,8 @@ import org.sleuthkit.datamodel.AbstractFile; * A Node for a LocalFile or DerivedFile content object. */ public class LocalFileNode extends AbstractAbstractFileNode { + + private static final Logger LOGGER = Logger.getLogger(LocalFileNode.class.getName()); public LocalFileNode(AbstractFile af) { super(af); @@ -98,6 +105,13 @@ public class LocalFileNode extends AbstractAbstractFileNode { NbBundle.getMessage(this.getClass(), "LocalFileNode.getActions.searchFilesSameMd5.text"), this)); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + actionsList.addAll(ContextMenuExtensionPoint.getActions()); return actionsList.toArray(new Action[0]); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileNode.java index 863de3d9d8..742b75ddcb 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileNode.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"); @@ -19,10 +19,14 @@ package org.sleuthkit.autopsy.datamodel; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import javax.swing.Action; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; @@ -84,6 +88,13 @@ public class SlackFileNode extends AbstractFsContentNode { actionsList.add(ExtractAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + actionsList.addAll(ContextMenuExtensionPoint.getActions()); return actionsList.toArray(new Action[actionsList.size()]); } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 81820218ec..58cba4102f 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.directorytree; import java.awt.event.ActionEvent; import java.beans.PropertyVetoException; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.logging.Level; import java.util.prefs.PreferenceChangeEvent; @@ -33,8 +35,11 @@ import org.openide.nodes.FilterNode; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; @@ -70,6 +75,8 @@ import org.sleuthkit.datamodel.VirtualDirectory; * defines the actions that the node should have. */ public class DataResultFilterNode extends FilterNode { + + private static final Logger LOGGER = Logger.getLogger(DataResultFilterNode.class.getName()); private static boolean filterKnownFromDataSources = UserPreferences.hideKnownFilesInDataSourcesTree(); private static boolean filterKnownFromViews = UserPreferences.hideKnownFilesInViewsTree(); @@ -260,29 +267,29 @@ public class DataResultFilterNode extends FilterNode { //they should be set in individual Node subclass and using a utility to get Actions per Content sub-type // TODO UPDATE: There is now a DataModelActionsFactory utility; - List actions = new ArrayList<>(); + List actionsList = new ArrayList<>(); //merge predefined specific node actions if bban subclasses have their own for (Action a : ban.getActions(true)) { - actions.add(a); + actionsList.add(a); } BlackboardArtifact ba = ban.getLookup().lookup(BlackboardArtifact.class); final int artifactTypeID = ba.getArtifactTypeID(); if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() || artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { - actions.add(new ViewContextAction( + actionsList.add(new ViewContextAction( NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewFileInDir.text"), ban)); } else { // if the artifact links to another file, add an action to go to // that file Content c = findLinked(ban); if (c != null) { - actions.add(new ViewContextAction( + actionsList.add(new ViewContextAction( NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewFileInDir.text"), c)); } // action to go to the source file of the artifact - actions.add(new ViewContextAction( + actionsList.add(new ViewContextAction( NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewSrcFileInDir.text"), ban)); } Content c = ban.getLookup().lookup(File.class); @@ -304,28 +311,44 @@ public class DataResultFilterNode extends FilterNode { n = new SlackFileNode((SlackFile) c); } if (n != null) { - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction( + actionsList.add(null); // creates a menu separator + actionsList.add(new NewWindowViewAction( NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewInNewWin.text"), n)); - actions.add(new ExternalViewerAction( + actionsList.add(new ExternalViewerAction( NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.openInExtViewer.text"), n)); - actions.add(null); // creates a menu separator - actions.add(ExtractAction.getInstance()); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); if (md5Action) { - actions.add(new HashSearchAction( + actionsList.add(new HashSearchAction( NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.searchFilesSameMd5.text"), n)); } - actions.add(null); // creates a menu separator - actions.add(AddContentTagAction.getInstance()); - actions.add(AddBlackboardArtifactTagAction.getInstance()); - actions.addAll(ContextMenuExtensionPoint.getActions()); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } } else { // There's no specific file associated with the artifact, but // we can still tag the artifact itself - actions.add(null); - actions.add(AddBlackboardArtifactTagAction.getInstance()); + actionsList.add(null); + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); } - return actions; + + final Collection selectedArtifactsList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + if(selectedArtifactsList.size() == 1) { + actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance()); + } + + if(n != null) { + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + } + + return actionsList; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExplorerNodeActionVisitor.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExplorerNodeActionVisitor.java index 27cc6dbcca..86c3df1fe3 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExplorerNodeActionVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExplorerNodeActionVisitor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,13 +19,18 @@ package org.sleuthkit.autopsy.directorytree; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; +import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentVisitor; import org.sleuthkit.datamodel.DerivedFile; @@ -85,48 +90,82 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default visit(final Directory d) { - List actions = new ArrayList<>(); - actions.add(AddContentTagAction.getInstance()); - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + List actionsList = new ArrayList<>(); + actionsList.add(AddContentTagAction.getInstance()); + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } @Override public List visit(final VirtualDirectory d) { - List actions = new ArrayList<>(); + List actionsList = new ArrayList<>(); if (!d.isDataSource()) { - actions.add(AddContentTagAction.getInstance()); + actionsList.add(AddContentTagAction.getInstance()); + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } } - actions.add(ExtractAction.getInstance()); - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + actionsList.add(ExtractAction.getInstance()); + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } @Override public List visit(final DerivedFile d) { - List actions = new ArrayList<>(); - actions.add(ExtractAction.getInstance()); - actions.add(AddContentTagAction.getInstance()); - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + List actionsList = new ArrayList<>(); + actionsList.add(ExtractAction.getInstance()); + actionsList.add(AddContentTagAction.getInstance()); + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } @Override public List visit(final LocalFile d) { - List actions = new ArrayList<>(); - actions.add(ExtractAction.getInstance()); - actions.add(AddContentTagAction.getInstance()); - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + List actionsList = new ArrayList<>(); + actionsList.add(ExtractAction.getInstance()); + actionsList.add(AddContentTagAction.getInstance()); + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } @Override public List visit(final org.sleuthkit.datamodel.File d) { - List actions = new ArrayList<>(); - actions.add(ExtractAction.getInstance()); - actions.add(AddContentTagAction.getInstance()); - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + List actionsList = new ArrayList<>(); + actionsList.add(ExtractAction.getInstance()); + actionsList.add(AddContentTagAction.getInstance()); + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java index 59b978bf24..e338b79805 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java @@ -31,7 +31,6 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.examples.SampleExecutableIngestModuleFactory; import org.sleuthkit.autopsy.examples.SampleIngestModuleFactory; -import org.sleuthkit.autopsy.modules.android.AndroidModuleFactory; import org.sleuthkit.autopsy.modules.e01verify.E01VerifierModuleFactory; import org.sleuthkit.autopsy.modules.exif.ExifParserModuleFactory; import org.sleuthkit.autopsy.modules.fileextmismatch.FileExtMismatchDetectorModuleFactory; @@ -63,7 +62,6 @@ final class IngestModuleFactoryLoader { add("org.sleuthkit.autopsy.thunderbirdparser.EmailParserModuleFactory"); //NON-NLS add(FileExtMismatchDetectorModuleFactory.class.getCanonicalName()); add(E01VerifierModuleFactory.class.getCanonicalName()); - add(AndroidModuleFactory.class.getCanonicalName()); add(InterestingItemsIngestModuleFactory.class.getCanonicalName()); add(PhotoRecCarverIngestModuleFactory.class.getCanonicalName()); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/AndroidIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/android/AndroidIngestModule.java deleted file mode 100755 index 8dea2a95a2..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/AndroidIngestModule.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.modules.android; - -import java.util.ArrayList; - -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; -import org.sleuthkit.autopsy.ingest.IngestModule; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.autopsy.ingest.IngestMessage; - -class AndroidIngestModule implements DataSourceIngestModule { - - private IngestJobContext context = null; - - @Override - public void startUp(IngestJobContext context) throws IngestModuleException { - this.context = context; - } - - @Override - public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) { - ArrayList errors = new ArrayList<>(); - progressBar.switchToDeterminate(9); - FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); - - try { - ContactAnalyzer.findContacts(dataSource, fileManager, context); - progressBar.progress(1); - if (context.dataSourceIngestIsCancelled()) { - return IngestModule.ProcessResult.OK; - } - } catch (Exception e) { - errors.add("Error getting Contacts"); //NON-NLS - } - - try { - CallLogAnalyzer.findCallLogs(dataSource, fileManager, context); - progressBar.progress(2); - if (context.dataSourceIngestIsCancelled()) { - return IngestModule.ProcessResult.OK; - } - } catch (Exception e) { - errors.add("Error getting Call Logs"); //NON-NLS - } - - try { - TextMessageAnalyzer.findTexts(dataSource, fileManager, context); - progressBar.progress(3); - if (context.dataSourceIngestIsCancelled()) { - return IngestModule.ProcessResult.OK; - } - } catch (Exception e) { - errors.add("Error getting Text Messages"); //NON-NLS - } - - try { - TangoMessageAnalyzer.findTangoMessages(dataSource, fileManager, context); - progressBar.progress(4); - if (context.dataSourceIngestIsCancelled()) { - return IngestModule.ProcessResult.OK; - } - } catch (Exception e) { - errors.add("Error getting Tango Messages"); //NON-NLS - } - - try { - WWFMessageAnalyzer.findWWFMessages(dataSource, fileManager, context); - progressBar.progress(5); - if (context.dataSourceIngestIsCancelled()) { - return IngestModule.ProcessResult.OK; - } - } catch (Exception e) { - errors.add("Error getting Words with Friends Messages"); //NON-NLS - } - - try { - GoogleMapLocationAnalyzer.findGeoLocations(dataSource, fileManager, context); - progressBar.progress(6); - if (context.dataSourceIngestIsCancelled()) { - return IngestModule.ProcessResult.OK; - } - } catch (Exception e) { - errors.add("Error getting Google Map Locations"); //NON-NLS - } - - try { - BrowserLocationAnalyzer.findGeoLocations(dataSource, fileManager, context); - progressBar.progress(7); - if (context.dataSourceIngestIsCancelled()) { - return IngestModule.ProcessResult.OK; - } - } catch (Exception e) { - errors.add("Error getting Browser Locations"); //NON-NLS - } - - try { - CacheLocationAnalyzer.findGeoLocations(dataSource, fileManager, context); - progressBar.progress(8); - } catch (Exception e) { - errors.add("Error getting Cache Locations"); //NON-NLS - } - - // create the final message for inbox - StringBuilder errorMessage = new StringBuilder(); - String errorMsgSubject; - IngestMessage.MessageType msgLevel = IngestMessage.MessageType.INFO; - if (errors.isEmpty() == false) { - msgLevel = IngestMessage.MessageType.ERROR; - errorMessage.append("Errors were encountered"); //NON-NLS - for (String msg : errors) { - errorMessage.append("
  • ").append(msg).append("
  • \n"); //NON-NLS - } - errorMessage.append("\n"); //NON-NLS - - if (errors.size() == 1) { - errorMsgSubject = "One error was found"; //NON-NLS - } else { - errorMsgSubject = "errors found: " + errors.size(); //NON-NLS - } - } else { - errorMessage.append("No errors"); //NON-NLS - errorMsgSubject = "No errors"; //NON-NLS - } - - return IngestModule.ProcessResult.OK; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/AndroidModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/android/AndroidModuleFactory.java deleted file mode 100755 index c30e7c3c64..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/AndroidModuleFactory.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 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.android; - -import org.openide.util.lookup.ServiceProvider; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.Version; -import org.sleuthkit.autopsy.ingest.IngestModuleFactory; -import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; -import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; -import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; - -@ServiceProvider(service = IngestModuleFactory.class) // -public class AndroidModuleFactory extends IngestModuleFactoryAdapter { - - static String getModuleName() { - return NbBundle.getMessage(AndroidModuleFactory.class, "AndroidModuleFactory.moduleName"); - } - - @Override - public String getModuleDisplayName() { - return getModuleName(); - } - - @Override - public String getModuleDescription() { - return NbBundle.getMessage(AndroidModuleFactory.class, "AndroidModuleFactory.moduleDescription"); - } - - @Override - public String getModuleVersionNumber() { - return Version.getVersion(); - } - - @Override - public boolean isDataSourceIngestModuleFactory() { - return true; - } - - @Override - public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings) { - return new AndroidIngestModule(); - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/BrowserLocationAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/BrowserLocationAnalyzer.java deleted file mode 100755 index 38592e432d..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/BrowserLocationAnalyzer.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 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.android; - -import java.io.File; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; -import java.util.logging.Level; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.services.Blackboard; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Analyzes database created by browser that stores GEO location info. - */ -class BrowserLocationAnalyzer { - - private static final String moduleName = AndroidModuleFactory.getModuleName(); - private static final Logger logger = Logger.getLogger(BrowserLocationAnalyzer.class.getName()); - private static Blackboard blackboard; - - public static void findGeoLocations(Content dataSource, FileManager fileManager, - IngestJobContext context) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - try { - List abstractFiles = fileManager.findFiles(dataSource, "CachedGeoposition%.db"); //NON-NLS - - for (AbstractFile abstractFile : abstractFiles) { - try { - if (abstractFile.getSize() == 0) { - continue; - } - File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName()); - ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled); - findGeoLocationsInDB(jFile.toString(), abstractFile); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing Browser Location files", e); //NON-NLS - } - } - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error finding Browser Location files", e); //NON-NLS - - } - } - - @NbBundle.Messages({"BrowserLocationAnalyzer.indexError.message=Failed to index GPS trackpoint artifact for keyword search."}) - private static void findGeoLocationsInDB(String DatabasePath, AbstractFile f) { - Connection connection = null; - ResultSet resultSet = null; - Statement statement = null; - if (DatabasePath == null || DatabasePath.isEmpty()) { - return; - } - try { - Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver - connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS - statement = connection.createStatement(); - } catch (ClassNotFoundException | SQLException e) { - logger.log(Level.SEVERE, "Error connecting to sql database", e); //NON-NLS - return; - } - - try { - resultSet = statement.executeQuery( - "SELECT timestamp, latitude, longitude, accuracy FROM CachedPosition;"); //NON-NLS - - while (resultSet.next()) { - Long timestamp = Long.valueOf(resultSet.getString("timestamp")) / 1000; //NON-NLS - double latitude = Double.valueOf(resultSet.getString("latitude")); //NON-NLS - double longitude = Double.valueOf(resultSet.getString("longitude")); //NON-NLS - - BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, moduleName, latitude)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, moduleName, longitude)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, timestamp)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, moduleName, - NbBundle.getMessage(BrowserLocationAnalyzer.class, - "BrowserLocationAnalyzer.bbAttribute.browserLocationHistory"))); - // bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(),moduleName, accuracy)); - - try { - // index the artifact for keyword search - blackboard.indexArtifact(bba); - } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactTypeName(), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - Bundle.BrowserLocationAnalyzer_indexError_message(), bba.getDisplayName()); - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Error Putting artifacts to Blackboard", e); //NON-NLS - } finally { - try { - if (resultSet != null) { - resultSet.close(); - } - statement.close(); - connection.close(); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error closing database", e); //NON-NLS - } - } - - } -} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/android/Bundle.properties deleted file mode 100755 index 9ef8ab1338..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/Bundle.properties +++ /dev/null @@ -1,11 +0,0 @@ -AndroidModuleFactory.moduleName=Android Analyzer -AndroidModuleFactory.moduleDescription=Extracts Android system and third-party app data. -BrowserLocationAnalyzer.bbAttribute.browserLocationHistory=Browser Location History -CacheLocationAnalyzer.bbAttribute.fileLocationHistory={0} Location History -GoogleMapLocationAnalyzer.bbAttribute.destination=Destination -GoogleMapLocationAnalyzer.bbAttribute.googleMapsHistory=Google Maps History -TangoMessageAnalyzer.bbAttribute.tangoMessage=Tango Message -TextMessageAnalyzer.bbAttribute.incoming=Incoming -TextMessageAnalyzer.bbAttribute.outgoing=Outgoing -TextMessageAnalyzer.bbAttribute.smsMessage=SMS Message -WWFMessageAnalyzer.bbAttribute.wordsWithFriendsMsg=Words With Friends Message diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/android/Bundle_ja.properties deleted file mode 100644 index e555ddc54c..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/Bundle_ja.properties +++ /dev/null @@ -1,10 +0,0 @@ -AndroidModuleFactory.moduleDescription=Android\u30b7\u30b9\u30c6\u30e0\u304a\u3088\u3073\u7b2c\u4e09\u8005\u30a2\u30d7\u30ea\u30c7\u30fc\u30bf\u3092\u62bd\u51fa -BrowserLocationAnalyzer.bbAttribute.browserLocationHistory=\u30d6\u30e9\u30a6\u30b6\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u5c65\u6b74 -CacheLocationAnalyzer.bbAttribute.fileLocationHistory={0} \u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u5c65\u6b74 -GoogleMapLocationAnalyzer.bbAttribute.destination=\u76ee\u7684\u5730 -GoogleMapLocationAnalyzer.bbAttribute.googleMapsHistory=Google\u30de\u30c3\u30d7\u5c65\u6b74 -TangoMessageAnalyzer.bbAttribute.tangoMessage=Tango\u30e1\u30c3\u30bb\u30fc\u30b8 -TextMessageAnalyzer.bbAttribute.incoming=\u53d7\u4fe1 -TextMessageAnalyzer.bbAttribute.outgoing=\u9001\u4fe1 -TextMessageAnalyzer.bbAttribute.smsMessage=SMS\u30e1\u30c3\u30bb\u30fc\u30b8 -WWFMessageAnalyzer.bbAttribute.wordsWithFriendsMsg=Words With Friends\u30e1\u30c3\u30bb\u30fc\u30b8 \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/CacheLocationAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/CacheLocationAnalyzer.java deleted file mode 100755 index debc4a0066..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/CacheLocationAnalyzer.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 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.android; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.List; -import java.util.logging.Level; -import org.openide.util.NbBundle; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.services.Blackboard; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Parses cache files that Android maintains for Wifi and cell towers. Adds GPS - * points to blackboard. - */ -class CacheLocationAnalyzer { - - private static final String moduleName = AndroidModuleFactory.getModuleName(); - private static final Logger logger = Logger.getLogger(CacheLocationAnalyzer.class.getName()); - private static Blackboard blackboard; - - /** - * cache.cell stores mobile tower GPS locations and cache.wifi stores GPS - * and MAC info from Wifi points. - */ - public static void findGeoLocations(Content dataSource, FileManager fileManager, - IngestJobContext context) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - try { - List abstractFiles = fileManager.findFiles(dataSource, "cache.cell"); //NON-NLS - abstractFiles.addAll(fileManager.findFiles(dataSource, "cache.wifi")); //NON-NLS - - for (AbstractFile abstractFile : abstractFiles) { - try { - if (abstractFile.getSize() == 0) { - continue; - } - File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName()); - ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled); - - findGeoLocationsInFile(jFile, abstractFile); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing cached Location files", e); //NON-NLS - } - } - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error finding cached Location files", e); //NON-NLS - } - } - - @Messages({"CacheLocationAnalyzer.indexError.message=Failed to index GPS trackpoint artifact for keyword search."}) - private static void findGeoLocationsInFile(File file, AbstractFile f) { - byte[] bytes; // will temporarily hold bytes to be converted into the correct data types - - try { - InputStream inputStream = new FileInputStream(file); - - bytes = new byte[2]; // version - inputStream.read(bytes); - - bytes = new byte[2]; - inputStream.read(bytes); //number of location entries - - int iterations = new BigInteger(bytes).intValue(); - - for (int i = 0; i < iterations; i++) { //loop through every entry - bytes = new byte[2]; - inputStream.read(bytes); - - bytes = new byte[1]; - inputStream.read(bytes); - while (new BigInteger(bytes).intValue() != 0) { //pass through non important values until the start of accuracy(around 7-10 bytes) - if (0 > inputStream.read(bytes)) { - break; /// we've passed the end of the file, so stop - } - } - bytes = new byte[3]; - inputStream.read(bytes); - if (new BigInteger(bytes).intValue() <= 0) {//This refers to a location that could not be calculated. - bytes = new byte[28]; //read rest of the row's bytes - inputStream.read(bytes); - continue; - } - String accuracy = "" + new BigInteger(bytes).intValue(); - - bytes = new byte[4]; - inputStream.read(bytes); - String confidence = "" + new BigInteger(bytes).intValue(); - - bytes = new byte[8]; - inputStream.read(bytes); - double latitude = toDouble(bytes); - - bytes = new byte[8]; - inputStream.read(bytes); - double longitude = toDouble(bytes); - - bytes = new byte[8]; - inputStream.read(bytes); - Long timestamp = new BigInteger(bytes).longValue() / 1000; - - BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, moduleName, latitude)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, moduleName, longitude)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, timestamp)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, moduleName, - NbBundle.getMessage(CacheLocationAnalyzer.class, - "CacheLocationAnalyzer.bbAttribute.fileLocationHistory", - file.getName()))); - - //Not storing these for now. - // bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(),moduleName, accuracy)); - // bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID(),moduleName, confidence)); - try { - // index the artifact for keyword search - blackboard.indexArtifact(bba); - } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - Bundle.CacheLocationAnalyzer_indexError_message(), bba.getDisplayName()); - } - } - - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing Cached GPS locations to Blackboard", e); //NON-NLS - } - } - - private static double toDouble(byte[] bytes) { - return ByteBuffer.wrap(bytes).getDouble(); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java deleted file mode 100755 index 27129456e2..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 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.android; - -import java.io.File; -import java.io.IOException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.logging.Level; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.services.Blackboard; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.autopsy.ingest.IngestServices; -import org.sleuthkit.autopsy.ingest.ModuleDataEvent; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Locates a variety of different call log databases, parses them, and populates - * the blackboard. - */ -class CallLogAnalyzer { - - private static final String moduleName = AndroidModuleFactory.getModuleName(); - private static final Logger logger = Logger.getLogger(CallLogAnalyzer.class.getName()); - private static Blackboard blackboard; - - private static final IngestServices services = IngestServices.getInstance(); - - - /** - * the names of tables that potentially hold call logs in the dbs - */ - private static final Iterable tableNames = Arrays.asList("calls", "logs"); //NON-NLS - - public static void findCallLogs(Content dataSource, FileManager fileManager, - IngestJobContext context) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - try { - List absFiles = fileManager.findFiles(dataSource, "logs.db"); //NON-NLS - absFiles.addAll(fileManager.findFiles(dataSource, "contacts.db")); //NON-NLS - absFiles.addAll(fileManager.findFiles(dataSource, "contacts2.db")); //NON-NLS - for (AbstractFile abstractFile : absFiles) { - try { - File file = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName()); - ContentUtils.writeToFile(abstractFile, file, context::dataSourceIngestIsCancelled); - findCallLogsInDB(file.toString(), abstractFile); - } catch (IOException e) { - logger.log(Level.SEVERE, "Error writing temporary call log db to disk", e); //NON-NLS - } - } - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error finding call logs", e); //NON-NLS - } - } - - @Messages({"CallLogAnalyzer.indexError.message=Failed to index call log artifact for keyword search."}) - private static void findCallLogsInDB(String DatabasePath, AbstractFile f) { - - if (DatabasePath == null || DatabasePath.isEmpty()) { - return; - } - - Collection bbartifacts = new ArrayList<>(); - try (Connection connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS - Statement statement = connection.createStatement();) { - - for (String tableName : tableNames) { - try (ResultSet resultSet = statement.executeQuery( - "SELECT number,date,duration,type, name FROM " + tableName + " ORDER BY date DESC;");) { //NON-NLS - logger.log(Level.INFO, "Reading call log from table {0} in db {1}", new Object[]{tableName, DatabasePath}); //NON-NLS - while (resultSet.next()) { - Long date = resultSet.getLong("date") / 1000; - final CallDirection direction = CallDirection.fromType(resultSet.getInt("type")); //NON-NLS - String directionString = direction != null ? direction.getDisplayName() : ""; - final String number = resultSet.getString("number"); //NON-NLS - final long duration = resultSet.getLong("duration"); //NON-NLS //duration of call is in seconds - final String name = resultSet.getString("name"); //NON-NLS // name of person dialed or called. null if unregistered - - try { - BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG); //create a call log and then add attributes from result set. - if (direction == CallDirection.OUTGOING) { - bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, moduleName, number)); - } else { /// Covers INCOMING and MISSED - bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, moduleName, number)); - } - bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_START, moduleName, date)); - bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_END, moduleName, duration + date)); - bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, directionString)); - bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, moduleName, name)); - - bbartifacts.add(bba); - - try { - // index the artifact for keyword search - blackboard.indexArtifact(bba); - } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - Bundle.CallLogAnalyzer_indexError_message(), bba.getDisplayName()); - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error posting call log record to the Blackboard", ex); //NON-NLS - } - } - } catch (SQLException e) { - logger.log(Level.WARNING, String.format("Could not read table %s in db %s", tableName, DatabasePath), e); //NON-NLS - } - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "Could not parse call log; error connecting to db " + DatabasePath, e); //NON-NLS - } - finally { - if (!bbartifacts.isEmpty()) { - services.fireModuleDataEvent(new ModuleDataEvent( - moduleName, - BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG, bbartifacts)); - } - } - } - - private static enum CallDirection { - - INCOMING(1, "Incoming"), OUTGOING(2, "Outgoing"), MISSED(3, "Missed"); //NON-NLS - - private final int type; - - private final String displayName; - - public String getDisplayName() { - return displayName; - } - - private CallDirection(int type, String displayName) { - this.type = type; - this.displayName = displayName; - } - - static CallDirection fromType(int t) { - switch (t) { - case 1: - return INCOMING; - case 2: - return OUTGOING; - case 3: - return MISSED; - default: - return null; - } - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/ContactAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/ContactAnalyzer.java deleted file mode 100755 index 17cc8ad2cc..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/ContactAnalyzer.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 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.android; - -import java.io.File; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -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; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.services.Blackboard; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.autopsy.ingest.IngestServices; -import org.sleuthkit.autopsy.ingest.ModuleDataEvent; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Locates a variety of different contacts databases, parses them, and populates - * the blackboard. - */ -class ContactAnalyzer { - - private static final String moduleName = AndroidModuleFactory.getModuleName(); - private static final Logger logger = Logger.getLogger(ContactAnalyzer.class.getName()); - private static final IngestServices services = IngestServices.getInstance(); - - public static void findContacts(Content dataSource, FileManager fileManager, - IngestJobContext context) { - List absFiles; - try { - absFiles = fileManager.findFiles(dataSource, "contacts.db"); //NON-NLS - absFiles.addAll(fileManager.findFiles(dataSource, "contacts2.db")); //NON-NLS - if (absFiles.isEmpty()) { - return; - } - for (AbstractFile AF : absFiles) { - try { - File jFile = new File(Case.getCurrentCase().getTempDirectory(), AF.getName()); - ContentUtils.writeToFile(AF, jFile, context::dataSourceIngestIsCancelled); - findContactsInDB(jFile.toString(), AF); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing Contacts", e); //NON-NLS - } - } - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error finding Contacts", e); //NON-NLS - } - } - - /** - * - * @param databasePath - * @param fId Will create artifact from a database given by the - * path The fileId will be the Abstract file associated - * with the artifacts - */ - @Messages({"ContactAnalyzer.indexError.message=Failed to index contact artifact for keyword search."}) - private static void findContactsInDB(String databasePath, AbstractFile f) { - Connection connection = null; - ResultSet resultSet = null; - Statement statement = null; - Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); - - if (databasePath == null || databasePath.isEmpty()) { - return; - } - try { - Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver - connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath); //NON-NLS - statement = connection.createStatement(); - } catch (ClassNotFoundException | SQLException e) { - logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS - return; - } - - Collection bbartifacts = new ArrayList<>(); - try { - // get display_name, mimetype(email or phone number) and data1 (phonenumber or email address depending on mimetype) - //sorted by name, so phonenumber/email would be consecutive for a person if they exist. - // check if contacts.name_raw_contact_id exists. Modify the query accordingly. - Boolean column_found = false; - DatabaseMetaData metadata = connection.getMetaData(); - ResultSet columnListResultSet = metadata.getColumns(null, null, "contacts", null); //NON-NLS - while (columnListResultSet.next()) { - if (columnListResultSet.getString("COLUMN_NAME").equals("name_raw_contact_id")) { //NON-NLS - column_found = true; - break; - } - } - if (column_found) { - resultSet = statement.executeQuery( - "SELECT mimetype,data1, name_raw_contact.display_name AS display_name \n" //NON-NLS - + "FROM raw_contacts JOIN contacts ON (raw_contacts.contact_id=contacts._id) \n" //NON-NLS - + "JOIN raw_contacts AS name_raw_contact ON(name_raw_contact_id=name_raw_contact._id) " //NON-NLS - + "LEFT OUTER JOIN data ON (data.raw_contact_id=raw_contacts._id) \n" //NON-NLS - + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id=mimetypes._id) \n" //NON-NLS - + "WHERE mimetype = 'vnd.android.cursor.item/phone_v2' OR mimetype = 'vnd.android.cursor.item/email_v2'\n" //NON-NLS - + "ORDER BY name_raw_contact.display_name ASC;"); //NON-NLS - } else { - resultSet = statement.executeQuery( - "SELECT mimetype,data1, raw_contacts.display_name AS display_name \n" //NON-NLS - + "FROM raw_contacts JOIN contacts ON (raw_contacts.contact_id=contacts._id) \n" //NON-NLS - + "LEFT OUTER JOIN data ON (data.raw_contact_id=raw_contacts._id) \n" //NON-NLS - + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id=mimetypes._id) \n" //NON-NLS - + "WHERE mimetype = 'vnd.android.cursor.item/phone_v2' OR mimetype = 'vnd.android.cursor.item/email_v2'\n" //NON-NLS - + "ORDER BY raw_contacts.display_name ASC;"); //NON-NLS - } - - BlackboardArtifact bba; - bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); - String name; - String oldName = ""; - String mimetype; // either phone or email - String data1; // the phone number or email - while (resultSet.next()) { - name = resultSet.getString("display_name"); //NON-NLS - 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)); - } - if (mimetype.equals("vnd.android.cursor.item/phone_v2")) { //NON-NLS - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, moduleName, data1)); - } else { - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, moduleName, data1)); - } - oldName = name; - - bbartifacts.add(bba); - - try { - // index the artifact for keyword search - blackboard.indexArtifact(bba); - } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - Bundle.ContactAnalyzer_indexError_message(), bba.getDisplayName()); - } - } - - } catch (SQLException e) { - logger.log(Level.WARNING, "Unable to execute contacts SQL query against {0} : {1}", new Object[]{databasePath, e}); //NON-NLS - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error posting to blackboard", e); //NON-NLS - } finally { - if (!bbartifacts.isEmpty()) { - services.fireModuleDataEvent(new ModuleDataEvent( - moduleName, - BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT, bbartifacts)); - } - - try { - if (resultSet != null) { - resultSet.close(); - } - statement.close(); - connection.close(); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error closing database", e); //NON-NLS - } - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/GoogleMapLocationAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/GoogleMapLocationAnalyzer.java deleted file mode 100755 index f39e8620d9..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/GoogleMapLocationAnalyzer.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 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.android; - -import java.io.File; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; -import java.util.logging.Level; -import org.openide.util.NbBundle; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.services.Blackboard; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Finds and parses the Google Maps database. - */ -class GoogleMapLocationAnalyzer { - - private static final String moduleName = AndroidModuleFactory.getModuleName(); - private static final Logger logger = Logger.getLogger(GoogleMapLocationAnalyzer.class.getName()); - private static Blackboard blackboard; - - public static void findGeoLocations(Content dataSource, FileManager fileManager, - IngestJobContext context) { - List absFiles; - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - try { - absFiles = fileManager.findFiles(dataSource, "da_destination_history"); //NON-NLS - if (absFiles.isEmpty()) { - return; - } - for (AbstractFile abstractFile : absFiles) { - try { - File jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName()); - ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled); - findGeoLocationsInDB(jFile.toString(), abstractFile); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing Google map locations", e); //NON-NLS - } - } - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error finding Google map locations", e); //NON-NLS - } - } - - @Messages({"GoogleMapLocationAnalyzer.indexError.message=Failed to index GPS route artifact for keyword search."}) - private static void findGeoLocationsInDB(String DatabasePath, AbstractFile f) { - Connection connection = null; - ResultSet resultSet = null; - Statement statement = null; - - if (DatabasePath == null || DatabasePath.isEmpty()) { - return; - } - try { - Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver - connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS - statement = connection.createStatement(); - } catch (ClassNotFoundException | SQLException e) { - logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS - return; - } - - try { - resultSet = statement.executeQuery( - "SELECT time,dest_lat,dest_lng,dest_title,dest_address,source_lat,source_lng FROM destination_history;"); //NON-NLS - - while (resultSet.next()) { - Long time = Long.valueOf(resultSet.getString("time")) / 1000; //NON-NLS - String dest_title = resultSet.getString("dest_title"); //NON-NLS - String dest_address = resultSet.getString("dest_address"); //NON-NLS - - double dest_lat = convertGeo(resultSet.getString("dest_lat")); //NON-NLS - double dest_lng = convertGeo(resultSet.getString("dest_lng")); //NON-NLS - double source_lat = convertGeo(resultSet.getString("source_lat")); //NON-NLS - double source_lng = convertGeo(resultSet.getString("source_lng")); //NON-NLS - -// bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT);//src -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY.getTypeID(), moduleName, "Source")); -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, source_lat)); -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, source_lng)); -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, time)); -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID(), moduleName, "Google Maps History")); -// -// bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT);//dest -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY.getTypeID(), moduleName, "Destination")); -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, time)); -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, dest_lat)); -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, dest_lng)); -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, dest_title)); -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION.getTypeID(), moduleName, dest_address)); -// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "Google Maps History")); - BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, moduleName, - NbBundle.getMessage(GoogleMapLocationAnalyzer.class, - "GoogleMapLocationAnalyzer.bbAttribute.destination"))); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, time)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END, moduleName, dest_lat)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END, moduleName, dest_lng)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START, moduleName, source_lat)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START, moduleName, source_lng)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, dest_title)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION, moduleName, dest_address)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, moduleName, - NbBundle.getMessage(GoogleMapLocationAnalyzer.class, - "GoogleMapLocationAnalyzer.bbAttribute.googleMapsHistory"))); - - try { - // index the artifact for keyword search - blackboard.indexArtifact(bba); - } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - Bundle.GoogleMapLocationAnalyzer_indexError_message(), bba.getDisplayName()); - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing Google map locations to the Blackboard", e); //NON-NLS - } finally { - try { - if (resultSet != null) { - resultSet.close(); - } - statement.close(); - connection.close(); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error closing the database", e); //NON-NLS - } - } - } - - //add periods 6 decimal places before the end. - private static double convertGeo(String s) { - if (s.length() > 6) { - return Double.valueOf(s.substring(0, s.length() - 6) + "." + s.substring(s.length() - 6, s.length())); - } else { - return Double.valueOf(s); - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/TangoMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/TangoMessageAnalyzer.java deleted file mode 100755 index 8da1c167ef..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/TangoMessageAnalyzer.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 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.android; - -import java.io.File; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; -import java.util.logging.Level; -import org.apache.commons.codec.binary.Base64; -import org.openide.util.NbBundle; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.services.Blackboard; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Locates database for the Tango app and adds info to blackboard. - */ -class TangoMessageAnalyzer { - - private static final String moduleName = AndroidModuleFactory.getModuleName(); - private static final Logger logger = Logger.getLogger(TangoMessageAnalyzer.class.getName()); - private static Blackboard blackboard; - - public static void findTangoMessages(Content dataSource, FileManager fileManager, - IngestJobContext context) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - List absFiles; - try { - absFiles = fileManager.findFiles(dataSource, "tc.db"); //NON-NLS - for (AbstractFile abstractFile : absFiles) { - try { - File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName()); - ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled); - findTangoMessagesInDB(jFile.toString(), abstractFile); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing Tango messages", e); //NON-NLS - } - } - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error finding Tango messages", e); //NON-NLS - } - } - - @Messages({"TangoMessageAnalyzer.indexError.message=Failed to index Tango message artifact for keyword search."}) - private static void findTangoMessagesInDB(String DatabasePath, AbstractFile f) { - Connection connection = null; - ResultSet resultSet = null; - Statement statement = null; - - if (DatabasePath == null || DatabasePath.isEmpty()) { - return; - } - try { - Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver - connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS - statement = connection.createStatement(); - } catch (ClassNotFoundException | SQLException e) { - logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS - return; - } - - try { - resultSet = statement.executeQuery( - "SELECT conv_id, create_time,direction,payload FROM messages ORDER BY create_time DESC;"); //NON-NLS - - String conv_id; // seems to wrap around the message found in payload after decoding from base-64 - String direction; // 1 incoming, 2 outgoing - String payload; // seems to be a base64 message wrapped by the conv_id - - while (resultSet.next()) { - conv_id = resultSet.getString("conv_id"); //NON-NLS - Long create_time = Long.valueOf(resultSet.getString("create_time")) / 1000; //NON-NLS - if (resultSet.getString("direction").equals("1")) { //NON-NLS - direction = "Incoming"; //NON-NLS - } else { - direction = "Outgoing"; //NON-NLS - } - payload = resultSet.getString("payload"); //NON-NLS - - BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); //create a call log and then add attributes from result set. - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, create_time)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, direction)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, moduleName, decodeMessage(conv_id, payload))); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName, - NbBundle.getMessage(TangoMessageAnalyzer.class, - "TangoMessageAnalyzer.bbAttribute.tangoMessage"))); - - try { - // index the artifact for keyword search - blackboard.indexArtifact(bba); - } catch (Blackboard.BlackboardException ex) { - MessageNotifyUtil.Notify.error( - Bundle.TangoMessageAnalyzer_indexError_message(), bba.getDisplayName()); - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing Tango messages to the Blackboard", e); //NON-NLS - } finally { - try { - if (resultSet != null) { - resultSet.close(); - } - statement.close(); - connection.close(); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error closing database", e); //NON-NLS - } - } - } - - //take the message string which is wrapped by a certain string, and return the text enclosed. - private static String decodeMessage(String wrapper, String message) { - String result = ""; - byte[] decoded = Base64.decodeBase64(message); - try { - String Z = new String(decoded, "UTF-8"); - result = Z.split(wrapper)[1]; - } catch (Exception e) { - logger.log(Level.SEVERE, "Error decoding a Tango message", e); //NON-NLS - } - return result; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/TextMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/TextMessageAnalyzer.java deleted file mode 100755 index 979291d5d3..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/TextMessageAnalyzer.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 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.android; - -import java.io.File; -import java.sql.Connection; -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; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.services.Blackboard; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.autopsy.ingest.IngestServices; -import org.sleuthkit.autopsy.ingest.ModuleDataEvent; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Finds database with SMS/MMS messages and adds them to blackboard. - */ -class TextMessageAnalyzer { - - private static final String moduleName = AndroidModuleFactory.getModuleName(); - private static final Logger logger = Logger.getLogger(TextMessageAnalyzer.class.getName()); - private static final IngestServices services = IngestServices.getInstance(); - private static Blackboard blackboard; - - public static void findTexts(Content dataSource, FileManager fileManager, - IngestJobContext context) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - try { - - List absFiles = fileManager.findFiles(dataSource, "mmssms.db"); //NON-NLS - for (AbstractFile abstractFile : absFiles) { - try { - File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName()); - ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled); - findTextsInDB(jFile.toString(), abstractFile); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing text messages", e); //NON-NLS - } - } - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error finding text messages", e); //NON-NLS - } - } - - @Messages({"TextMessageAnalyzer.indexError.message=Failed to index text message artifact for keyword search."}) - private static void findTextsInDB(String DatabasePath, AbstractFile f) { - Connection connection = null; - ResultSet resultSet = null; - Statement statement = null; - - if (DatabasePath == null || DatabasePath.isEmpty()) { - return; - } - try { - Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver - connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS - statement = connection.createStatement(); - } catch (ClassNotFoundException | SQLException e) { - logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS - return; - } - - Collection bbartifacts = new ArrayList<>(); - try { - resultSet = statement.executeQuery( - "SELECT address,date,read,type,subject,body FROM sms;"); //NON-NLS - - String address; // may be phone number, or other addresses - - String direction; // message received in inbox = 1, message sent = 2 - String subject;//message subject - Integer read; // may be unread = 0, read = 1 - String body; //message body - while (resultSet.next()) { - address = resultSet.getString("address"); //NON-NLS - Long date = Long.valueOf(resultSet.getString("date")) / 1000; //NON-NLS - - read = resultSet.getInt("read"); //NON-NLS - subject = resultSet.getString("subject"); //NON-NLS - body = resultSet.getString("body"); //NON-NLS - - BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); //create Message artifact and then add attributes from result set. - if (resultSet.getString("type").equals("1")) { //NON-NLS - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, - NbBundle.getMessage(TextMessageAnalyzer.class, - "TextMessageAnalyzer.bbAttribute.incoming"))); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, moduleName, address)); - } else { - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, - NbBundle.getMessage(TextMessageAnalyzer.class, - "TextMessageAnalyzer.bbAttribute.outgoing"))); - bba.addAttribute(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_READ_STATUS, moduleName, read)); - 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(TextMessageAnalyzer.class, - "TextMessageAnalyzer.bbAttribute.smsMessage"))); - - bbartifacts.add(bba); - - try { - // index the artifact for keyword search - blackboard.indexArtifact(bba); - } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - Bundle.TextMessageAnalyzer_indexError_message(), bba.getDisplayName()); - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing text messages to Blackboard", e); //NON-NLS - } finally { - if (!bbartifacts.isEmpty()) { - services.fireModuleDataEvent(new ModuleDataEvent( - moduleName, - BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE, bbartifacts)); - } - - try { - if (resultSet != null) { - resultSet.close(); - } - statement.close(); - connection.close(); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error closing database", e); //NON-NLS - } - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/WWFMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/WWFMessageAnalyzer.java deleted file mode 100755 index 5685880afc..0000000000 --- a/Core/src/org/sleuthkit/autopsy/modules/android/WWFMessageAnalyzer.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 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.android; - -import java.io.File; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; -import java.util.logging.Level; -import org.openide.util.NbBundle; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.services.Blackboard; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Finds database for words with friends, parses it, and adds info to - * blackboard. - */ -class WWFMessageAnalyzer { - - private static final String moduleName = AndroidModuleFactory.getModuleName(); - private static final Logger logger = Logger.getLogger(WWFMessageAnalyzer.class.getName()); - private static Blackboard blackboard; - - public static void findWWFMessages(Content dataSource, FileManager fileManager, - IngestJobContext context) { - List absFiles; - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - try { - absFiles = fileManager.findFiles(dataSource, "WordsFramework"); //NON-NLS - - for (AbstractFile abstractFile : absFiles) { - try { - File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName()); - ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled); - - findWWFMessagesInDB(jFile.toString(), abstractFile); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing WWF messages", e); //NON-NLS - } - } - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error finding WWF messages", e); //NON-NLS - } - } - - @Messages({"WWFMessageAnalyzer.indexError.message=Failed to index WWF message artifact for keyword search."}) - private static void findWWFMessagesInDB(String DatabasePath, AbstractFile f) { - Connection connection = null; - ResultSet resultSet = null; - Statement statement = null; - - if (DatabasePath == null || DatabasePath.isEmpty()) { - return; - } - try { - Class.forName("org.sqlite.JDBC"); //load JDBC driver - connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS - statement = connection.createStatement(); - } catch (ClassNotFoundException | SQLException e) { - logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS - return; - } - - try { - resultSet = statement.executeQuery( - "SELECT message,strftime('%s' ,created_at) as datetime,user_id,game_id FROM chat_messages ORDER BY game_id DESC, created_at DESC;"); //NON-NLS - - String message; // WWF Message - String user_id; // the ID of the user who sent the message. - String game_id; // ID of the game which the the message was sent. - - while (resultSet.next()) { - message = resultSet.getString("message"); //NON-NLS - Long created_at = resultSet.getLong("datetime"); //NON-NLS - user_id = resultSet.getString("user_id"); //NON-NLS - game_id = resultSet.getString("game_id"); //NON-NLS - - BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); //create a call log and then add attributes from result set. - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, created_at)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, user_id)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, moduleName, game_id)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, moduleName, message)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName, - NbBundle.getMessage(WWFMessageAnalyzer.class, - "WWFMessageAnalyzer.bbAttribute.wordsWithFriendsMsg"))); - - try { - // index the artifact for keyword search - blackboard.indexArtifact(bba); - } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - Bundle.WWFMessageAnalyzer_indexError_message(), bba.getDisplayName()); - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing WWF messages to the Blackboard", e); //NON-NLS - } finally { - try { - if (resultSet != null) { - resultSet.close(); - } - statement.close(); - connection.close(); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error closing database", e); //NON-NLS - } - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties index 015f818b56..c80ccaa863 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties @@ -31,7 +31,6 @@ EmbeddedFileExtractorIngestModule.ImageExtractor.pptContainer.init.err=Ppt conta EmbeddedFileExtractorIngestModule.ImageExtractor.pptxContainer.init.err=Pptx container could not be initialized while reading: {0} EmbeddedFileExtractorIngestModule.ImageExtractor.xlsContainer.init.err=Xls container could not be initialized while reading: {0} EmbeddedFileExtractorIngestModule.ImageExtractor.xlsxContainer.init.err=Xlsx container could not be initialized while reading: {0} -EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err=Embedded File Extractor is unable to read content of {0} EmbeddedFileExtractorIngestModule.ImageExtractor.extractImage.addToDB.exception.msg=Unable to add the derived files to the database. EmbeddedFileExtractorIngestModule.ImageExtractor.getOutputFolderPath.exception.msg=Could not get path for image extraction from Abstract File\: {0} EmbeddedFileExtractorIngestModule.ImageExtractor.getOutputFolderPath.exception.msg=Could not get path for image extraction from Abstract File: {0} diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java index efc5ec6504..9df9927ad8 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,8 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; +import org.apache.poi.OldFileFormatException; +import org.apache.poi.POIXMLException; import org.apache.poi.hslf.model.Picture; import org.apache.poi.hslf.usermodel.PictureData; import org.apache.poi.hslf.usermodel.SlideShow; @@ -120,7 +122,7 @@ class ImageExtractor { } return false; } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error executing FileTypeDetector.getFileType()", ex); // NON-NLS + logger.log(Level.SEVERE, "Error executing FileTypeDetector.getFileType()", ex); // NON-NLS return false; } } @@ -152,7 +154,7 @@ class ImageExtractor { } } } catch (TskCoreException e) { - logger.log(Level.WARNING, 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; } switch (abstractFileExtractionFormat) { @@ -189,7 +191,7 @@ class ImageExtractor { extractedImage.getCtime(), extractedImage.getCrtime(), extractedImage.getAtime(), extractedImage.getAtime(), true, abstractFile, null, EmbeddedFileExtractorModuleFactory.getModuleName(), null, null, TskData.EncodingType.XOR1)); } catch (TskCoreException ex) { - logger.log(Level.WARNING, 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()) { @@ -207,24 +209,24 @@ class ImageExtractor { * extracted. */ private List extractImagesFromDoc(AbstractFile af) { - List listOfExtractedImages; - HWPFDocument doc = null; + List listOfAllPictures; + try { - doc = new HWPFDocument(new ReadContentInputStream(af)); + HWPFDocument doc = new HWPFDocument(new ReadContentInputStream(af)); + PicturesTable pictureTable = doc.getPicturesTable(); + listOfAllPictures = pictureTable.getAllPictures(); + } catch (OldFileFormatException | IOException ex) { + // OldFileFormatException: + // Thrown when the document version is unsupported (Word 95 and + // older) + + // IOException: + // Thrown when the document has issues being read. + + return null; } catch (Throwable ex) { // instantiating POI containers throw RuntimeExceptions - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.docContainer.init.err", af.getName()), ex); //NON-NLS - return null; - } - - PicturesTable pictureTable = null; - List listOfAllPictures = null; - try { - pictureTable = doc.getPicturesTable(); - listOfAllPictures = pictureTable.getAllPictures(); - } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.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; } @@ -237,15 +239,13 @@ class ImageExtractor { if (outputFolderPath == null) { return null; } - listOfExtractedImages = new ArrayList<>(); + List listOfExtractedImages = new ArrayList<>(); byte[] data = null; for (org.apache.poi.hwpf.usermodel.Picture picture : listOfAllPictures) { String fileName = picture.suggestFullFileName(); try { data = picture.getContent(); } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err", af.getName()), ex); //NON-NLS return null; } writeExtractedImage(Paths.get(outputFolderPath, fileName).toString(), data); @@ -265,21 +265,22 @@ class ImageExtractor { * extracted. */ private List extractImagesFromDocx(AbstractFile af) { - List listOfExtractedImages; - XWPFDocument docx = null; + List listOfAllPictures = null; + try { - docx = new XWPFDocument(new ReadContentInputStream(af)); + 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.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.docxContainer.init.err", af.getName()), ex); //NON-NLS - return null; - } - List listOfAllPictures = null; - try { - listOfAllPictures = docx.getAllPictures(); - } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err", af.getName()), ex); //NON-NLS + logger.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.docxContainer.init.err", af.getName()), ex); //NON-NLS return null; } @@ -292,18 +293,15 @@ class ImageExtractor { outputFolderPath = getOutputFolderPath(this.parentFileName); } if (outputFolderPath == null) { - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.extractImageFrom.outputPath.exception.msg", af.getName())); //NON-NLS return null; } - listOfExtractedImages = new ArrayList<>(); + List listOfExtractedImages = new ArrayList<>(); byte[] data = null; for (XWPFPictureData xwpfPicture : listOfAllPictures) { String fileName = xwpfPicture.getFileName(); try { data = xwpfPicture.getData(); } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err", af.getName()), ex); //NON-NLS return null; } writeExtractedImage(Paths.get(outputFolderPath, fileName).toString(), data); @@ -321,23 +319,22 @@ class ImageExtractor { * extracted. */ private List extractImagesFromPpt(AbstractFile af) { - List listOfExtractedImages; - SlideShow ppt = null; + PictureData[] listOfAllPictures = null; + try { - ppt = new SlideShow(new ReadContentInputStream(af)); + SlideShow ppt = new SlideShow(new ReadContentInputStream(af)); + listOfAllPictures = ppt.getPictureData(); + } catch (OldFileFormatException | IOException ex) { + // OldFileFormatException: + // Thrown when the document version is unsupported + + // IOException: + // Thrown when the document has issues being read + + return null; } catch (Throwable ex) { // instantiating POI containers throw RuntimeExceptions - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.pptContainer.init.err", af.getName()), ex); //NON-NLS - return null; - } - - //extract all pictures contained in the presentation - PictureData[] listOfAllPictures = null; - try { - listOfAllPictures = ppt.getPictureData(); - } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.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; } @@ -350,14 +347,13 @@ class ImageExtractor { outputFolderPath = getOutputFolderPath(this.parentFileName); } if (outputFolderPath == null) { - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.extractImageFrom.outputPath.exception.msg", af.getName())); //NON-NLS return null; } // extract the images to the above initialized outputFolder. // extraction path - outputFolder/image_number.ext int i = 0; - listOfExtractedImages = new ArrayList<>(); + List listOfExtractedImages = new ArrayList<>(); byte[] data = null; for (PictureData pictureData : listOfAllPictures) { @@ -388,8 +384,6 @@ class ImageExtractor { try { data = pictureData.getData(); } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err", af.getName()), ex); //NON-NLS return null; } writeExtractedImage(Paths.get(outputFolderPath, imageName).toString(), data); @@ -408,21 +402,22 @@ class ImageExtractor { * extracted. */ private List extractImagesFromPptx(AbstractFile af) { - List listOfExtractedImages; - XMLSlideShow pptx; + List listOfAllPictures = null; + try { - pptx = new XMLSlideShow(new ReadContentInputStream(af)); + XMLSlideShow pptx = new XMLSlideShow(new ReadContentInputStream(af)); + listOfAllPictures = pptx.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.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.pptxContainer.init.err", af.getName()), ex); //NON-NLS - return null; - } - List listOfAllPictures = null; - try { - listOfAllPictures = pptx.getAllPictures(); - } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err", af.getName()), ex); //NON-NLS + logger.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.pptxContainer.init.err", af.getName()), ex); //NON-NLS return null; } @@ -435,11 +430,10 @@ class ImageExtractor { outputFolderPath = getOutputFolderPath(this.parentFileName); } if (outputFolderPath == null) { - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.extractImageFrom.outputPath.exception.msg", af.getName())); //NON-NLS return null; } - listOfExtractedImages = new ArrayList<>(); + List listOfExtractedImages = new ArrayList<>(); byte[] data = null; for (XSLFPictureData xslsPicture : listOfAllPictures) { @@ -449,8 +443,6 @@ class ImageExtractor { try { data = xslsPicture.getData(); } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err", af.getName()), ex); //NON-NLS return null; } writeExtractedImage(Paths.get(outputFolderPath, fileName).toString(), data); @@ -471,23 +463,22 @@ class ImageExtractor { * extracted. */ private List extractImagesFromXls(AbstractFile af) { - List listOfExtractedImages; - - Workbook xls; + List listOfAllPictures = null; + try { - xls = new HSSFWorkbook(new ReadContentInputStream(af)); + Workbook xls = new HSSFWorkbook(new ReadContentInputStream(af)); + listOfAllPictures = xls.getAllPictures(); + } catch (OldFileFormatException | IOException ex) { + // OldFileFormatException: + // Thrown when the document version is unsupported + + // IOException: + // Thrown when the document has issues being read + + return null; } catch (Throwable ex) { // instantiating POI containers throw RuntimeExceptions - logger.log(Level.WARNING, String.format("%s%s", NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.xlsContainer.init.err", af.getName()), af.getName()), ex); //NON-NLS - return null; - } - - List listOfAllPictures = null; - try { - listOfAllPictures = xls.getAllPictures(); - } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err", 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; } @@ -500,20 +491,17 @@ class ImageExtractor { outputFolderPath = getOutputFolderPath(this.parentFileName); } if (outputFolderPath == null) { - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.extractImageFrom.outputPath.exception.msg", af.getName())); //NON-NLS return null; } int i = 0; - 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 try { data = pictureData.getData(); } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err", af.getName()), ex); //NON-NLS return null; } writeExtractedImage(Paths.get(outputFolderPath, imageName).toString(), data); @@ -533,22 +521,22 @@ class ImageExtractor { * extracted. */ private List extractImagesFromXlsx(AbstractFile af) { - List listOfExtractedImages; - Workbook xlsx; + List listOfAllPictures = null; + try { - xlsx = new XSSFWorkbook(new ReadContentInputStream(af)); + 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.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.xlsxContainer.init.err", af.getName()), ex); //NON-NLS - return null; - } - - List listOfAllPictures = null; - try { - listOfAllPictures = xlsx.getAllPictures(); - } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err", af.getName()), ex); //NON-NLS + logger.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.xlsxContainer.init.err", af.getName()), ex); //NON-NLS return null; } @@ -561,20 +549,17 @@ class ImageExtractor { outputFolderPath = getOutputFolderPath(this.parentFileName); } if (outputFolderPath == null) { - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.extractImageFrom.outputPath.exception.msg", af.getName())); //NON-NLS return null; } int i = 0; - 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(); try { data = pictureData.getData(); } catch (Exception ex) { - // log internal Java and Apache errors as WARNING - logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.processing.err", af.getName()), ex); //NON-NLS return null; } writeExtractedImage(Paths.get(outputFolderPath, imageName).toString(), data); diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportBranding.java b/Core/src/org/sleuthkit/autopsy/report/ReportBranding.java index e79eba45d5..66970eae9b 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportBranding.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportBranding.java @@ -1,15 +1,15 @@ /* * * Autopsy Forensic Browser - * + * * Copyright 2013-2014 Basis Technology Corp. - * + * * 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,7 +36,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil; public final class ReportBranding implements ReportBrandingProviderI { //property names - private static final String AGENCY_LOGO_PATH_PROP = "AgencyLogoPath"; //NON-NLS + public static final String AGENCY_LOGO_PATH_PROP = "AgencyLogoPath"; //NON-NLS private static final String REPORT_TITLE_PROP = "ReportTitle"; //NON-NLS private static final String REPORT_FOOTER_PROP = "ReportFooter"; //NON-NLS //default settings @@ -46,7 +46,7 @@ public final class ReportBranding implements ReportBrandingProviderI { private static final String DEFAULT_REPORT_FOOTER = NbBundle .getMessage(ReportBranding.class, "ReportBranding.defaultReportFooter.text"); private String reportsBrandingDir; //dir with extracted reports branding resources - private static final String MODULE_NAME = ReportBranding.class.getSimpleName(); + public static final String MODULE_NAME = ReportBranding.class.getSimpleName(); private static final Logger logger = Logger.getLogger(ReportBranding.class.getName()); // this is static so that it can be set by another object @@ -130,7 +130,7 @@ public final class ReportBranding implements ReportBrandingProviderI { @Override public void setAgencyLogoPath(String path) { - // Use properties to persist the logo to use. + // Use properties to persist the logo to use. // Should use static variable instead ModuleSettings.setConfigSetting(MODULE_NAME, AGENCY_LOGO_PATH_PROP, path); } diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportBrandingProviderI.java b/Core/src/org/sleuthkit/autopsy/report/ReportBrandingProviderI.java index e59d6dd866..7c495823e5 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportBrandingProviderI.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportBrandingProviderI.java @@ -1,15 +1,15 @@ /* * * Autopsy Forensic Browser - * + * * Copyright 2013 Basis Technology Corp. - * + * * 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. diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index 059ac8530f..dd20031e0b 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -1,19 +1,19 @@ /* * * Autopsy Forensic Browser - * + * * Copyright 2012-2014 Basis Technology Corp. - * + * * Copyright 2012 42six Solutions. * Contact: aebadirad 42six com * Project Contact/Architect: 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. @@ -32,6 +32,9 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -628,7 +631,7 @@ class ReportHTML implements TableReportModule { } if (totalCount == MAX_THUMBS_PER_PAGE) { - // manually set the row count so the count of items shown in the + // manually set the row count so the count of items shown in the // navigation page reflects the number of thumbnails instead of // the number of rows. rowCount = totalCount; @@ -759,7 +762,7 @@ class ReportHTML implements TableReportModule { localFilePath.append(File.separator); localFilePath.append(fileName); - // If the local file doesn't already exist, create it now. + // If the local file doesn't already exist, create it now. // The existence check is necessary because it is possible to apply multiple tags with the same tagName to a file. File localFile = new File(localFilePath.toString()); if (!localFile.exists()) { @@ -856,7 +859,7 @@ class ReportHTML implements TableReportModule { // use default Autopsy icon if custom icon is not set iconPath = "favicon.ico"; } else { - iconPath = "agency_logo"; //ref to writeNav() for agency_logo + iconPath = Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString(); //ref to writeNav() for agency_logo } index.append("\n").append(reportTitle).append(" ").append( NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.title", currentCase.getDisplayName())).append( @@ -949,9 +952,8 @@ class ReportHTML implements TableReportModule { String agencyLogoPath = reportBranding.getAgencyLogoPath(); if (agencyLogoPath != null && !agencyLogoPath.isEmpty()) { - File from = new File(agencyLogoPath); - File to = new File(path); - FileUtil.copyFile(FileUtil.toFileObject(from), FileUtil.toFileObject(to), "agency_logo"); //NON-NLS + Path destinationPath = Paths.get(path); + Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), destinationPath.resolve(Paths.get(agencyLogoPath).getFileName())); //NON-NLS } in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico"); //NON-NLS @@ -1049,7 +1051,9 @@ class ReportHTML implements TableReportModule { summary.append("<div class=\"title\">\n"); //NON-NLS if (agencyLogoSet) { summary.append("<div class=\"left\">\n"); //NON-NLS - summary.append("<img src=\"agency_logo.png\" />\n"); //NON-NLS + summary.append("<img src=\""); + summary.append(Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString()); + summary.append("\" />\n"); //NON-NLS summary.append("</div>\n"); //NON-NLS } final String align = agencyLogoSet ? "right" : "left"; //NON-NLS NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java index 4f4580bcd6..a9bce0acb8 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java @@ -156,7 +156,11 @@ public class SnapShotReportWriter { summaryContext.put("generationDateTime", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(generationDate)); //NON-NLS summaryContext.put("ingestRunning", IngestManager.getInstance().isIngestRunning()); //NON-NLS summaryContext.put("currentCase", currentCase); //NON-NLS - + String agencyLogo = "agency_logo.png"; //default name for agency logo. + if (StringUtils.isNotBlank(reportBranding.getAgencyLogoPath())){ + agencyLogo = Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString(); + } + summaryContext.put("agencyLogoFileName", agencyLogo); fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html", "Summary", summaryContext, reportFolderPath.resolve("summary.html")); //NON-NLS } @@ -199,7 +203,7 @@ public class SnapShotReportWriter { } String agencyLogoPath = reportBranding.getAgencyLogoPath(); if (StringUtils.isNotBlank(agencyLogoPath)) { - Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve("agency_logo.png")); //NON-NLS + Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve(Paths.get(reportBranding.getAgencyLogoPath()).getFileName())); //NON-NLS } //copy navigation html diff --git a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html index dda4a3bc1f..2d1e37a2a8 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html +++ b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html @@ -13,7 +13,7 @@ <div class="title"> {{#reportBranding.getAgencyLogoPath}} <div class="left"> - <img src="agency_logo.png" /> + <img src="{{agencyLogoFileName}}" /> </div> <div class="right"> {{/reportBranding.getAgencyLogoPath}} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java index 8b82cdcba8..20d2a989cb 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java @@ -234,7 +234,7 @@ public final class AutoIngestCasePanel extends JPanel { try { int selectedRow = casesTable.getSelectedRow(); if (selectedRow >= 0 && selectedRow < casesTable.getRowCount()) { - return Paths.get(caseTableModel.getValueAt(selectedRow, COLUMN_HEADERS.CASE.ordinal()).toString()); + return Paths.get(caseTableModel.getValueAt(casesTable.convertRowIndexToModel(selectedRow), COLUMN_HEADERS.CASE.ordinal()).toString()); } } catch (Exception ignored) { return null; @@ -251,7 +251,7 @@ public final class AutoIngestCasePanel extends JPanel { if (path != null) { try { for (int row = 0; row < casesTable.getRowCount(); ++row) { - Path temp = Paths.get(caseTableModel.getValueAt(row, COLUMN_HEADERS.CASE.ordinal()).toString()); + Path temp = Paths.get(caseTableModel.getValueAt(casesTable.convertRowIndexToModel(row), COLUMN_HEADERS.CASE.ordinal()).toString()); if (temp.compareTo(path) == 0) { // found it casesTable.setRowSelectionInterval(row, row); return; @@ -546,9 +546,10 @@ public final class AutoIngestCasePanel extends JPanel { * @param evt -- The event that caused this to be called */ private void bnOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenActionPerformed - Path caseMetadataFilePath = Paths.get((String) caseTableModel.getValueAt(casesTable.getSelectedRow(), + int modelRow = casesTable.convertRowIndexToModel(casesTable.getSelectedRow()); + Path caseMetadataFilePath = Paths.get((String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.OUTPUTFOLDER.ordinal()), - caseTableModel.getValueAt(casesTable.getSelectedRow(), COLUMN_HEADERS.CASE.ordinal()) + CaseMetadata.getFileExtension()); + caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.CASE.ordinal()) + CaseMetadata.getFileExtension()); openCase(caseMetadataFilePath); }//GEN-LAST:event_bnOpenActionPerformed @@ -587,7 +588,7 @@ public final class AutoIngestCasePanel extends JPanel { }//GEN-LAST:event_rbWeeksItemStateChanged private void bnShowLogActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnShowLogActionPerformed - int selectedRow = casesTable.getSelectedRow(); + int selectedRow = casesTable.convertRowIndexToModel(casesTable.getSelectedRow()); int rowCount = casesTable.getRowCount(); if (selectedRow >= 0 && selectedRow < rowCount) { String thePath = (String) caseTableModel.getValueAt(selectedRow, COLUMN_HEADERS.OUTPUTFOLDER.ordinal()); @@ -611,9 +612,10 @@ public final class AutoIngestCasePanel extends JPanel { private void casesTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_casesTableMouseClicked if (evt.getClickCount() == 2) { - Path caseMetadataFilePath = Paths.get((String) caseTableModel.getValueAt(casesTable.getSelectedRow(), + int modelRow = casesTable.convertRowIndexToModel(casesTable.getSelectedRow()); + Path caseMetadataFilePath = Paths.get((String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.OUTPUTFOLDER.ordinal()), - caseTableModel.getValueAt(casesTable.getSelectedRow(), COLUMN_HEADERS.CASE.ordinal()) + CaseMetadata.getFileExtension()); + caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.CASE.ordinal()) + CaseMetadata.getFileExtension()); openCase(caseMetadataFilePath); } }//GEN-LAST:event_casesTableMouseClicked diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index 9ffec1bedc..900a5a603b 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -64,7 +64,6 @@ import javax.annotation.concurrent.ThreadSafe; import org.openide.util.Lookup; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case.CaseType; -import org.sleuthkit.autopsy.casemodule.Case.IllegalCaseNameException; import org.sleuthkit.autopsy.casemodule.CaseActionException; import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; @@ -1880,14 +1879,8 @@ public final class AutoIngestManager extends Observable implements PropertyChang */ private Case openCase() throws CoordinationServiceException, CaseManagementException, InterruptedException { Manifest manifest = currentJob.getManifest(); - String caseDisplayName = manifest.getCaseName(); - String caseName; - try { - caseName = Case.displayNameToCaseName(caseDisplayName); - } catch (IllegalCaseNameException ex) { - throw new CaseManagementException(String.format("Error creating or opening case %s for %s", manifest.getCaseName(), manifest.getFilePath()), ex); - } - SYS_LOGGER.log(Level.INFO, "Opening case {0} ({1}) for {2}", new Object[]{caseDisplayName, caseName, manifest.getFilePath()}); + String caseName = manifest.getCaseName(); + SYS_LOGGER.log(Level.INFO, "Opening case {0} for {2}", new Object[]{caseName, manifest.getFilePath()}); currentJob.setStage(AutoIngestJob.Stage.OPENING_CASE); /* * Acquire and hold a case name lock so that only one node at as @@ -1917,16 +1910,16 @@ public final class AutoIngestManager extends Observable implements PropertyChang return caseForJob; } catch (CaseActionException ex) { - throw new CaseManagementException(String.format("Error creating or opening case %s (%s) for %s", manifest.getCaseName(), caseName, manifest.getFilePath()), ex); + throw new CaseManagementException(String.format("Error creating or opening case %s for %s", caseName, manifest.getFilePath()), ex); } catch (IllegalStateException ex) { /* * Deal with the unfortunate fact that * Case.getCurrentCase throws IllegalStateException. */ - throw new CaseManagementException(String.format("Error getting current case %s (%s) for %s", caseName, manifest.getCaseName(), manifest.getFilePath()), ex); + throw new CaseManagementException(String.format("Error getting current case %s for %s", caseName, manifest.getFilePath()), ex); } } else { - throw new CaseManagementException(String.format("Timed out acquiring case name lock for %s for %s", manifest.getCaseName(), manifest.getFilePath())); + throw new CaseManagementException(String.format("Timed out acquiring case name lock for %s for %s", caseName, manifest.getFilePath())); } } } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/PathUtils.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/PathUtils.java index 9b99e8c13f..772dcf667e 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/PathUtils.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/PathUtils.java @@ -46,18 +46,12 @@ final class PathUtils { * @return The path of the case folder, or null if it is not found. */ static Path findCaseDirectory(Path folderToSearch, String caseName) { - String sanitizedCaseName; - try { - sanitizedCaseName = Case.displayNameToCaseName(caseName); - } catch (Case.IllegalCaseNameException unused) { - return null; - } File searchFolder = new File(folderToSearch.toString()); if (!searchFolder.isDirectory()) { return null; } Path caseFolderPath = null; - String[] candidateFolders = searchFolder.list(new CaseFolderFilter(sanitizedCaseName)); + String[] candidateFolders = searchFolder.list(new CaseFolderFilter(caseName)); long mostRecentModified = 0; for (String candidateFolder : candidateFolders) { File file = new File(candidateFolder); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java new file mode 100755 index 0000000000..53a033a2c9 --- /dev/null +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java @@ -0,0 +1,159 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.imagegallery.actions; + +import java.awt.Window; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javafx.application.Platform; +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.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; + +/** + * Instances of this Action allow users to remove tags from content. + */ +public class DeleteTagAction extends Action { + + private static final Logger LOGGER = Logger.getLogger(DeleteTagAction.class.getName()); + + private final ImageGalleryController controller; + private final long fileId; + private final TagName tagName; + private final ContentTag contentTag; + + public DeleteTagAction(ImageGalleryController controller, TagName tagName, ContentTag contentTag, long fileId) { + super(tagName.getDisplayName()); + this.controller = controller; + this.fileId = fileId; + this.tagName = tagName; + this.contentTag = contentTag; + setGraphic(controller.getTagsManager().getGraphic(tagName)); + setText(tagName.getDisplayName()); + setEventHandler(actionEvent -> deleteTag()); + } + + static public Menu getTagMenu(ImageGalleryController controller) { + return new TagMenu(controller); + } + + @NbBundle.Messages({"# {0} - fileID", + "DeleteDrawableTagAction.deleteTag.alert=Unable to untag file {0}."}) + private void deleteTag() { + new SwingWorker<Void, Void>() { + + @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<AbstractFile> 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() + ); + } + return null; + } + + @Override + protected void done() { + super.done(); + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while untagging file", ex); //NON-NLS + } + } + }.execute(); + } + + @NbBundle.Messages({"DeleteDrawableTagAction.displayName=Remove File Tag"}) + private static class TagMenu extends Menu { + + 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<AbstractFile> selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + AbstractFile file = selectedFilesList.iterator().next(); + + try { + List<ContentTag> existingTagsList = + Case.getCurrentCase().getServices().getTagsManager() + .getContentTagsByContent(file); + + Collection<TagName> tagNamesList = + controller.getTagsManager().getNonCategoryTagNames(); + Iterator<TagName> tagNameIterator = tagNamesList.iterator(); + for(int i=0; tagNameIterator.hasNext(); i++) { + TagName tagName = tagNameIterator.next(); + 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); + } + } + } + } catch (TskCoreException ex) { + Logger.getLogger(TagMenu.class.getName()) + .log(Level.SEVERE, "Error retrieving tags for TagMenu", ex); //NON-NLS + } + + if(getItems().isEmpty()) { + setDisable(true); + } + } + } +} diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java index d9f93fe03c..243045d789 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-16 Basis Technology Corp. + * Copyright 2013-2017 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.imagegallery.gui.drawableviews; import com.google.common.eventbus.Subscribe; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.Optional; import java.util.logging.Level; import javafx.application.Platform; @@ -49,6 +50,7 @@ import javax.swing.SwingUtilities; import org.controlsfx.control.action.ActionUtils; import org.openide.util.Lookup; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.openide.util.actions.Presenter; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; @@ -66,10 +68,12 @@ import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent; import org.sleuthkit.autopsy.imagegallery.actions.AddTagAction; import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction; import org.sleuthkit.autopsy.imagegallery.actions.DeleteFollowUpTagAction; +import org.sleuthkit.autopsy.imagegallery.actions.DeleteTagAction; import org.sleuthkit.autopsy.imagegallery.actions.OpenExternalViewerAction; import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; +import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; @@ -184,6 +188,11 @@ public abstract class DrawableTileBase extends DrawableUIBase { menuItems.add(CategorizeAction.getCategoriesMenu(getController())); menuItems.add(AddTagAction.getTagMenu(getController())); + + final Collection<AbstractFile> selectedFilesList = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + menuItems.add(DeleteTagAction.getTagMenu(getController())); + } final MenuItem extractMenuItem = new MenuItem(Bundle.DrawableTileBase_menuItem_extractFiles()); extractMenuItem.setOnAction(actionEvent -> { diff --git a/InternalPythonModules/android/browserlocation.py b/InternalPythonModules/android/browserlocation.py new file mode 100644 index 0000000000..ef79a623c2 --- /dev/null +++ b/InternalPythonModules/android/browserlocation.py @@ -0,0 +1,120 @@ +""" +Autopsy Forensic Browser + +Copyright 2016 Basis Technology Corp. +Contact: carrier <at> sleuthkit <dot> org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from java.io import File +from java.lang import Class +from java.lang import ClassNotFoundException +from java.lang import Double +from java.lang import Long +from java.sql import Connection +from java.sql import DriverManager +from java.sql import ResultSet +from java.sql import SQLException +from java.sql import Statement +from java.util.logging import Level +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import FileManager +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil +from org.sleuthkit.autopsy.datamodel import ContentUtils +from org.sleuthkit.autopsy.ingest import IngestJobContext +from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import BlackboardArtifact +from org.sleuthkit.datamodel import BlackboardAttribute +from org.sleuthkit.datamodel import Content +from org.sleuthkit.datamodel import TskCoreException + +import traceback +import general + +""" +Analyzes database created by browser that stores GEO location info. +""" +class BrowserLocationAnalyzer(general.AndroidComponentAnalyzer): + + def __init__(self): + self._logger = Logger.getLogger(self.__class__.__name__) + + def analyze(self, dataSource, fileManager, context): + try: + abstractFiles = fileManager.findFiles(dataSource, "CachedGeoposition%.db") + for abstractFile in abstractFiles: + if abstractFile.getSize() == 0: + continue + try: + jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName()) + ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled) + self.__findGeoLocationsInDB(jFile.toString(), abstractFile) + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing browser location files", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Error finding browser location files", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + def __findGeoLocationsInDB(self, databasePath, abstractFile): + if not databasePath: + return + + try: + Class.forName("org.sqlite.JDBC") #load JDBC driver + connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) + statement = connection.createStatement() + except (ClassNotFoundException, SQLException) as ex: + self._logger.log(Level.SEVERE, "Error connecting to SQL database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + return + + try: + resultSet = statement.executeQuery("SELECT timestamp, latitude, longitude, accuracy FROM CachedPosition;") + while resultSet.next(): + timestamp = Long.valueOf(resultSet.getString("timestamp")) / 1000 + latitude = Double.valueOf(resultSet.getString("latitude")) + longitude = Double.valueOf(resultSet.getString("longitude")) + + 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")) + # artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(),moduleName, accuracy)) + # NOTE: originally commented out + + try: + # index the artifact for keyword search + blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard.indexArtifact(artifact) + except Blackboard.BlackboardException as ex: + self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactTypeName(), ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + MessageNotifyUtil.Notify.error("Failed to index GPS trackpoint artifact for keyword search.", artifact.getDisplayName()) + + except Exception as ex: + self._logger.log(Level.SEVERE, "Error putting artifacts to blackboard", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + finally: + try: + if resultSet is not None: + resultSet.close() + statement.close() + connection.close() + except Exception as ex: + self._logger.log(Level.SEVERE, "Error closing database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) diff --git a/InternalPythonModules/android/cachelocation.py b/InternalPythonModules/android/cachelocation.py new file mode 100644 index 0000000000..c28aa48fc3 --- /dev/null +++ b/InternalPythonModules/android/cachelocation.py @@ -0,0 +1,147 @@ +""" +Autopsy Forensic Browser + +Copyright 2016 Basis Technology Corp. +Contact: carrier <at> sleuthkit <dot> org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from java.io import File +from java.io import FileInputStream +from java.io import InputStream +from java.lang import Class +from java.lang import ClassNotFoundException +from java.math import BigInteger +from java.nio import ByteBuffer +from java.util.logging import Level +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import FileManager +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil +from org.sleuthkit.autopsy.datamodel import ContentUtils +from org.sleuthkit.autopsy.ingest import IngestJobContext +from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import BlackboardArtifact +from org.sleuthkit.datamodel import BlackboardAttribute +from org.sleuthkit.datamodel import Content +from org.sleuthkit.datamodel import TskCoreException + +import traceback +import general + +""" +Parses cache files that Android maintains for Wifi and cell towers. Adds GPS points to blackboard. +""" +class CacheLocationAnalyzer(general.AndroidComponentAnalyzer): + + def __init__(self): + self._logger = Logger.getLogger(self.__class__.__name__) + + """ + cache.cell stores mobile tower GPS locations and cache.wifi stores GPS + and MAC info from Wifi points. + """ + def analyze(self, dataSource, fileManager, context): + try: + abstractFiles = fileManager.findFiles(dataSource, "cache.cell") + abstractFiles.addAll(fileManager.findFiles(dataSource, "cache.wifi")) + for abstractFile in abstractFiles: + if abstractFile.getSize() == 0: + continue + try: + jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName()) + ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled) + self.__findGeoLocationsInFile(jFile, abstractFile) + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing cached location files", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Error finding cached location files", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + def __findGeoLocationsInFile(self, file, abstractFile): + + tempBytes = bytearray([0] * 2) # will temporarily hold bytes to be converted into the correct data types + + try: + inputStream = FileInputStream(file) + + inputStream.read(tempBytes) # version + + tempBytes = bytearray([0] * 2) + inputStream.read(tempBytes) # number of location entries + + iterations = BigInteger(tempBytes).intValue() + + for i in range(iterations): # loop through every entry + tempBytes = bytearray([0] * 2) + inputStream.read(tempBytes) + + tempBytes = bytearray([0]) + inputStream.read(tempBytes) + + while BigInteger(tempBytes).intValue() != 0: # pass through non important values until the start of accuracy(around 7-10 bytes) + if 0 > inputStream.read(tempBytes): + break # we've passed the end of the file, so stop + + tempBytes = bytearray([0] * 3) + inputStream.read(tempBytes) + if BigInteger(tempBytes).intValue() <= 0: # This refers to a location that could not be calculated + tempBytes = bytearray([0] * 28) # read rest of the row's bytes + inputStream.read(tempBytes) + continue + accuracy = "" + BigInteger(tempBytes).intValue() + + tempBytes = bytearray([0] * 4) + inputStream.read(tempBytes) + confidence = "" + BigInteger(tempBytes).intValue() + + tempBytes = bytearray([0] * 8) + inputStream.read(tempBytes) + latitude = CacheLocationAnalyzer.toDouble(bytes) + + tempBytes = bytearray([0] * 8) + inputStream.read(tempBytes) + longitude = CacheLocationAnalyzer.toDouble(bytes) + + tempBytes = bytearray([0] * 8) + inputStream.read(tempBytes) + timestamp = BigInteger(tempBytes).longValue() / 1000 + + 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, + file.getName() + "Location History")) + + #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)) + try: + # index the artifact for keyword search + blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard.indexArtifact(artifact) + except Blackboard.BlackboardException as ex: + self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + MessageNotifyUtil.Notify.error("Failed to index GPS trackpoint artifact for keyword search.", artifact.getDisplayName()) + + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing Cached GPS locations to blackboard", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + def toDouble(byteArray): + return ByteBuffer.wrap(byteArray).getDouble() diff --git a/InternalPythonModules/android/calllog.py b/InternalPythonModules/android/calllog.py new file mode 100644 index 0000000000..42e3a293a1 --- /dev/null +++ b/InternalPythonModules/android/calllog.py @@ -0,0 +1,155 @@ +""" +Autopsy Forensic Browser + +Copyright 2016 Basis Technology Corp. +Contact: carrier <at> sleuthkit <dot> org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from java.io import File +from java.io import IOException +from java.lang import Class +from java.lang import ClassNotFoundException +from java.lang import String +from java.sql import Connection +from java.sql import DriverManager +from java.sql import ResultSet +from java.sql import SQLException +from java.sql import Statement +from java.util.logging import Level +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import FileManager +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil +from org.sleuthkit.autopsy.datamodel import ContentUtils +from org.sleuthkit.autopsy.ingest import IngestJobContext +from org.sleuthkit.autopsy.ingest import IngestServices +from org.sleuthkit.autopsy.ingest import ModuleDataEvent +from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import BlackboardArtifact +from org.sleuthkit.datamodel import BlackboardAttribute +from org.sleuthkit.datamodel.BlackboardAttribute import ATTRIBUTE_TYPE +from org.sleuthkit.datamodel import Content +from org.sleuthkit.datamodel import TskCoreException + +import traceback +import general + +""" +Locates a variety of different call log databases, parses them, and populates the blackboard. +""" +class CallLogAnalyzer(general.AndroidComponentAnalyzer): + + def __init__(self): + self._logger = Logger.getLogger(self.__class__.__name__) + + # the names of tables that potentially hold call logs in the dbs + _tableNames = ["calls", "logs"] + + class CallDirection: + + def __init__(self, type, displayName): + self.type = type + self.displayName = displayName + + def getDisplayName(self): + return self.displayName + + INCOMING = CallDirection(1, "Incoming") + OUTGOING = CallDirection(2, "Outgoing") + MISSED = CallDirection(3, "Missed") + + @staticmethod + def fromType(t): + return { + 1: CallLogAnalyzer.INCOMING, + 2: CallLogAnalyzer.OUTGOING, + 3: CallLogAnalyzer.MISSED + }.get(t, None) + + def analyze(self, dataSource, fileManager, context): + try: + absFiles = fileManager.findFiles(dataSource, "logs.db") + absFiles.addAll(fileManager.findFiles(dataSource, "contacts.db")) + absFiles.addAll(fileManager.findFiles(dataSource, "contacts2.db")) + for abstractFile in absFiles: + try: + file = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName()) + ContentUtils.writeToFile(abstractFile, file, context.dataSourceIngestIsCancelled) + self.__findCallLogsInDB(file.toString(), abstractFile) + except IOException as ex: + self._logger.log(Level.SEVERE, "Error writing temporary call log db to disk", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Error finding call logs", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + def __findCallLogsInDB(self, databasePath, abstractFile): + if not databasePath: + return + + bbartifacts = list() + try: + connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) + statement = connection.createStatement() + + + for tableName in CallLogAnalyzer._tableNames: + try: + resultSet = statement.executeQuery("SELECT number, date, duration, type, name FROM " + tableName + " ORDER BY date DESC;") + self._logger.log(Level.INFO, "Reading call log from table {0} in db {1}", [tableName, databasePath]) + while resultSet.next(): + date = resultSet.getLong("date") / 1000 + direction = CallLogAnalyzer.fromType(resultSet.getInt("type")) + directionString = direction.getDisplayName() if direction is not None else "" + number = resultSet.getString("number") + duration = resultSet.getLong("duration") # duration of call is in seconds + name = resultSet.getString("name") # name of person dialed or called. None if unregistered + + try: + 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)) + else: # Covers INCOMING and MISSED + artifact.addAttribute(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)) + + bbartifacts.append(artifact) + + try: + # index the artifact for keyword search + blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard.indexArtifact(artifact) + except Blackboard.BlackboardException as ex: + self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + MessageNotifyUtil.Notify.error("Failed to index call log artifact for keyword search.", artifact.getDisplayName()) + + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Error posting call log record to the blackboard", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except SQLException as ex: + self._logger.log(Level.WARNING, String.format("Could not read table %s in db %s", tableName, databasePath), ex) + except SQLException as ex: + self._logger.log(Level.SEVERE, "Could not parse call log; error connecting to db " + databasePath, ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + finally: + if bbartifacts: + IngestServices.getInstance().fireModuleDataEvent(ModuleDataEvent(general.MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG, bbartifacts)) + diff --git a/InternalPythonModules/android/contact.py b/InternalPythonModules/android/contact.py new file mode 100644 index 0000000000..fe56cc1cbb --- /dev/null +++ b/InternalPythonModules/android/contact.py @@ -0,0 +1,164 @@ +""" +Autopsy Forensic Browser + +Copyright 2016 Basis Technology Corp. +Contact: carrier <at> sleuthkit <dot> org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from java.io import File +from java.lang import Class +from java.lang import ClassNotFoundException +from java.sql import Connection +from java.sql import DatabaseMetaData +from java.sql import DriverManager +from java.sql import ResultSet +from java.sql import SQLException +from java.sql import Statement +from java.util.logging import Level +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import FileManager +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil +from org.sleuthkit.autopsy.datamodel import ContentUtils +from org.sleuthkit.autopsy.ingest import IngestJobContext +from org.sleuthkit.autopsy.ingest import IngestServices +from org.sleuthkit.autopsy.ingest import ModuleDataEvent +from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import BlackboardArtifact +from org.sleuthkit.datamodel import BlackboardAttribute +from org.sleuthkit.datamodel import Content +from org.sleuthkit.datamodel import TskCoreException + +import traceback +import general + +""" +Locates a variety of different contacts databases, parses them, and populates the blackboard. +""" +class ContactAnalyzer(general.AndroidComponentAnalyzer): + + def __init__(self): + self._logger = Logger.getLogger(self.__class__.__name__) + + def analyze(self, dataSource, fileManager, context): + try: + absFiles = fileManager.findFiles(dataSource, "contacts.db") + absFiles.addAll(fileManager.findFiles(dataSource, "contacts2.db")) + if absFiles.isEmpty(): + return + for abstractFile in absFiles: + try: + jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName()) + ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled) + self.__findContactsInDB(str(jFile.toString()), abstractFile) + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing Contacts", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Error finding Contacts", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + """ + Will create artifact from a database given by the path + The fileId will be the abstract file associated with the artifacts + """ + def __findContactsInDB(self, databasePath, abstractFile): + if not databasePath: + return + + bbartifacts = list() + try: + Class.forName("org.sqlite.JDBC") # load JDBC driver + connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) + statement = connection.createStatement() + except (ClassNotFoundException, SQLException) as ex: + self._logger.log(Level.SEVERE, "Error opening database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + return + + try: + # get display_name, mimetype(email or phone number) and data1 (phonenumber or email address depending on mimetype) + # sorted by name, so phonenumber/email would be consecutive for a person if they exist. + # check if contacts.name_raw_contact_id exists. Modify the query accordingly. + columnFound = False + metadata = connection.getMetaData() + columnListResultSet = metadata.getColumns(None, None, "contacts", None) + while columnListResultSet.next(): + if columnListResultSet.getString("COLUMN_NAME") == "name_raw_contact_id": + columnFound = True + break + if columnFound: + resultSet = statement.executeQuery( + "SELECT mimetype, data1, name_raw_contact.display_name AS display_name \n" + + "FROM raw_contacts JOIN contacts ON (raw_contacts.contact_id=contacts._id) \n" + + "JOIN raw_contacts AS name_raw_contact ON(name_raw_contact_id=name_raw_contact._id) " + + "LEFT OUTER JOIN data ON (data.raw_contact_id=raw_contacts._id) \n" + + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id=mimetypes._id) \n" + + "WHERE mimetype = 'vnd.android.cursor.item/phone_v2' OR mimetype = 'vnd.android.cursor.item/email_v2'\n" + + "ORDER BY name_raw_contact.display_name ASC;") + else: + resultSet = statement.executeQuery( + "SELECT mimetype, data1, raw_contacts.display_name AS display_name \n" + + "FROM raw_contacts JOIN contacts ON (raw_contacts.contact_id=contacts._id) \n" + + "LEFT OUTER JOIN data ON (data.raw_contact_id=raw_contacts._id) \n" + + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id=mimetypes._id) \n" + + "WHERE mimetype = 'vnd.android.cursor.item/phone_v2' OR mimetype = 'vnd.android.cursor.item/email_v2'\n" + + "ORDER BY raw_contacts.display_name ASC;") + + artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) + oldName = "" + while resultSet.next(): + name = resultSet.getString("display_name") + data1 = resultSet.getString("data1") # the phone number or email + 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)) + if mimetype == "vnd.android.cursor.item/phone_v2": + artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, general.MODULE_NAME, data1)) + else: + artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, general.MODULE_NAME, data1)) + + oldName = name + + bbartifacts.append(artifact) + + try: + # index the artifact for keyword search + blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard.indexArtifact(artifact) + except Blackboard.BlackboardException as ex: + self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + MessageNotifyUtil.Notify.error("Failed to index contact artifact for keyword search.", artifact.getDisplayName()) + + except SQLException as ex: + self._logger.log(Level.WARNING, "Unable to execute contacts SQL query against {0} : {1}", [databasePath, ex]) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Error posting to blackboard", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + finally: + if bbartifacts: + IngestServices.getInstance().fireModuleDataEvent(ModuleDataEvent(general.MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT, bbartifacts)) + + try: + if resultSet is not None: + resultSet.close() + statement.close() + connection.close() + except Exception as ex: + self._logger.log(Level.SEVERE, "Error closing database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) diff --git a/InternalPythonModules/android/general.py b/InternalPythonModules/android/general.py new file mode 100644 index 0000000000..28c96be9b9 --- /dev/null +++ b/InternalPythonModules/android/general.py @@ -0,0 +1,28 @@ +""" +Autopsy Forensic Browser + +Copyright 2016 Basis Technology Corp. +Contact: carrier <at> sleuthkit <dot> org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +MODULE_NAME = "Android Analyzer" + +""" +A parent class of the analyzers +""" +class AndroidComponentAnalyzer: + # The Analyzer should implement this method + def analyze(self, dataSource, fileManager, context): + raise NotImplementedError diff --git a/InternalPythonModules/android/googlemaplocation.py b/InternalPythonModules/android/googlemaplocation.py new file mode 100644 index 0000000000..066ed2f792 --- /dev/null +++ b/InternalPythonModules/android/googlemaplocation.py @@ -0,0 +1,139 @@ +""" +Autopsy Forensic Browser + +Copyright 2016 Basis Technology Corp. +Contact: carrier <at> sleuthkit <dot> org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from java.io import File +from java.lang import Class +from java.lang import ClassNotFoundException +from java.lang import Double +from java.lang import Long +from java.sql import Connection +from java.sql import DriverManager +from java.sql import ResultSet +from java.sql import SQLException +from java.sql import Statement +from java.util.logging import Level +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import FileManager +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil +from org.sleuthkit.autopsy.datamodel import ContentUtils +from org.sleuthkit.autopsy.ingest import IngestJobContext +from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import BlackboardArtifact +from org.sleuthkit.datamodel import BlackboardAttribute +from org.sleuthkit.datamodel import Content +from org.sleuthkit.datamodel import TskCoreException + +import traceback +import general + +""" +Finds and parses the Google Maps database. +""" +class GoogleMapLocationAnalyzer(general.AndroidComponentAnalyzer): + + def __init__(self): + self._logger = Logger.getLogger(self.__class__.__name__) + + def analyze(self, dataSource, fileManager, context): + try: + absFiles = fileManager.findFiles(dataSource, "da_destination_history") + if absFiles.isEmpty(): + return + for abstractFile in absFiles: + try: + jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName()) + ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled) + self.__findGeoLocationsInDB(jFile.toString(), abstractFile) + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing Google map locations", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Error finding Google map locations", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + def __findGeoLocationsInDB(self, databasePath, abstractFile): + if not databasePath: + return + + try: + Class.forName("org.sqlite.JDBC") # load JDBC driver + connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) + statement = connection.createStatement() + except (ClassNotFoundException, SQLException) as ex: + self._logger.log(Level.SEVERE, "Error opening database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + return + + try: + resultSet = statement.executeQuery( + "SELECT time, dest_lat, dest_lng, dest_title, dest_address, source_lat, source_lng FROM destination_history;") + + while resultSet.next(): + time = Long.valueOf(resultSet.getString("time")) / 1000 + dest_title = resultSet.getString("dest_title") + dest_address = resultSet.getString("dest_address") + + dest_lat = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("dest_lat")) + dest_lng = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("dest_lng")) + source_lat = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lat")) + source_lng = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lng")) + + 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")) + + try: + # index the artifact for keyword search + blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard.indexArtifact(artifact) + except Blackboard.BlackboardException as ex: + self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + MessageNotifyUtil.Notify.error("Failed to index GPS route artifact for keyword search.", artifact.getDisplayName()) + + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing Google map locations to the blackboard", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + finally: + try: + if resultSet is not None: + resultSet.close() + statement.close() + connection.close() + except Exception as ex: + self._logger.log(Level.SEVERE, "Error closing the database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + # add periods 6 decimal places before the end. + @staticmethod + def convertGeo(s): + length = len(s) + if length > 6: + return Double.valueOf(s[0 : length-6] + "." + s[length-6 : length]) + else: + return Double.valueOf(s) diff --git a/InternalPythonModules/android/module.py b/InternalPythonModules/android/module.py new file mode 100644 index 0000000000..07ef78ed8d --- /dev/null +++ b/InternalPythonModules/android/module.py @@ -0,0 +1,126 @@ +""" +Autopsy Forensic Browser + +Copyright 2016 Basis Technology Corp. +Contact: carrier <at> sleuthkit <dot> org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import jarray +import inspect +import traceback + +from java.util.logging import Level +from org.sleuthkit.autopsy.coreutils import Version +from org.sleuthkit.autopsy.ingest import IngestModuleFactory +from org.sleuthkit.autopsy.ingest import DataSourceIngestModule +from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter +from org.sleuthkit.autopsy.ingest import IngestModuleIngestJobSettings +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import FileManager +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.ingest import DataSourceIngestModuleProgress +from org.sleuthkit.autopsy.ingest import IngestModule +from org.sleuthkit.datamodel import Content +from org.sleuthkit.autopsy.ingest import DataSourceIngestModule +from org.sleuthkit.autopsy.ingest import IngestJobContext +from org.sleuthkit.autopsy.ingest import IngestMessage + +import general +import browserlocation +import cachelocation +import calllog +import contact +import googlemaplocation +import tangomessage +import textmessage +import wwfmessage + +class AndroidModuleFactory(IngestModuleFactoryAdapter): + + moduleName = general.MODULE_NAME + + def getModuleDisplayName(self): + return self.moduleName + + def getModuleDescription(self): + return "Extracts Android system and third-party app data." + + def getModuleVersionNumber(self): + return Version.getVersion() + + def isDataSourceIngestModuleFactory(self): + return True + + def createDataSourceIngestModule(self, ingestOptions): + return AndroidIngestModule() + + +class AndroidIngestModule(DataSourceIngestModule): + + _logger = Logger.getLogger(AndroidModuleFactory.moduleName) + + def log(self, level, msg): + self._logger.logp(level, self.__class__.__name__, inspect.stack()[1][3], msg) + + def __init__(self): + self.context = None + + def startUp(self, context): + self.context = context + + # Throw an IngestModule.IngestModuleException exception if there was a problem setting up + + # Where the analysis is done. + def process(self, dataSource, progressBar): + + errors = [] + fileManager = Case.getCurrentCase().getServices().getFileManager() + analyzers = [contact.ContactAnalyzer(), calllog.CallLogAnalyzer(), textmessage.TextMessageAnalyzer(), tangomessage.TangoMessageAnalyzer(), wwfmessage.WWFMessageAnalyzer(), googlemaplocation.GoogleMapLocationAnalyzer(), browserlocation.BrowserLocationAnalyzer(), cachelocation.CacheLocationAnalyzer()] + self.log(Level.INFO, "running " + str(len(analyzers)) + " analyzers") + progressBar.switchToDeterminate(len(analyzers)) + + n = 0 + for analyzer in analyzers: + if self.context.dataSourceIngestIsCancelled(): + return IngestModule.ProcessResult.OK + try: + analyzer.analyze(dataSource, fileManager, self.context) + n += 1 + progressBar.progress(n) + except Exception as ex: + errors.append("Error running " + analyzer.__class__.__name__) + self.log(Level.SEVERE, traceback.format_exc()) + errorMessage = [] # NOTE: this isn't used? + errorMessageSubject = "" # NOTE: this isn't used? + msgLevel = IngestMessage.MessageType.INFO + + if errors: + msgLevel = IngestMessage.MessageType.ERROR + errorMessage.append("Errors were encountered") + + errorMessage.append("<ul>") # NOTE: this was missing in the original java code + for msg in errors: + errorMessage.extend(["<li>", msg, "</li>\n"]) + errorMessage.append("</ul>\n") + + if len(errors) == 1: + errorMsgSubject = "One error was found" + else: + errorMsgSubject = "errors found: " + str(len(errors)) + else: + errorMessage.append("No errors") + errorMsgSubject = "No errors" + + return IngestModule.ProcessResult.OK diff --git a/InternalPythonModules/android/tangomessage.py b/InternalPythonModules/android/tangomessage.py new file mode 100644 index 0000000000..04223f7fe1 --- /dev/null +++ b/InternalPythonModules/android/tangomessage.py @@ -0,0 +1,136 @@ +""" +Autopsy Forensic Browser + +Copyright 2016 Basis Technology Corp. +Contact: carrier <at> sleuthkit <dot> org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from java.io import File +from java.lang import Class +from java.lang import ClassNotFoundException +from java.lang import Long +from java.lang import String +from java.sql import Connection +from java.sql import DriverManager +from java.sql import ResultSet +from java.sql import SQLException +from java.sql import Statement +from java.util.logging import Level +from org.apache.commons.codec.binary import Base64 +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import FileManager +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil +from org.sleuthkit.autopsy.datamodel import ContentUtils +from org.sleuthkit.autopsy.ingest import IngestJobContext +from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import BlackboardArtifact +from org.sleuthkit.datamodel import BlackboardAttribute +from org.sleuthkit.datamodel import Content +from org.sleuthkit.datamodel import TskCoreException + +import traceback +import general + +""" +Locates database for the Tango app and adds info to blackboard. +""" +class TangoMessageAnalyzer(general.AndroidComponentAnalyzer): + + def __init__(self): + self._logger = Logger.getLogger(self.__class__.__name__) + + def analyze(self, dataSource, fileManager, context): + try: + absFiles = fileManager.findFiles(dataSource, "tc.db") + for abstractFile in absFiles: + try: + jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName()) + ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled) + self.__findTangoMessagesInDB(jFile.toString(), abstractFile) + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing Tango messages", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Error finding Tango messages", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + def __findTangoMessagesInDB(self, databasePath, abstractFile): + if not databasePath: + return + + try: + Class.forName("org.sqlite.JDBC") # load JDBC driver + connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) + statement = connection.createStatement() + except (ClassNotFoundException, SQLException) as ex: + self._logger.log(Level.SEVERE, "Error opening database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + return + + try: + resultSet = statement.executeQuery( + "SELECT conv_id, create_time, direction, payload FROM messages ORDER BY create_time DESC;") + + while resultSet.next(): + conv_id = resultSet.getString("conv_id") # seems to wrap around the message found in payload after decoding from base-64 + create_time = Long.valueOf(resultSet.getString("create_time")) / 1000 + if resultSet.getString("direction") == "1": # 1 incoming, 2 outgoing + direction = "Incoming" + else: + direction = "Outgoing" + payload = resultSet.getString("payload") + + 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")) + + try: + # index the artifact for keyword search + blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard.indexArtifact(artifact) + except Blackboard.BlackboardException as ex: + self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + MessageNotifyUtil.Notify.error("Failed to index Tango message artifact for keyword search.", artifact.getDisplayName()) + + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing Tango messages to the blackboard", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + finally: + try: + if resultSet is not None: + resultSet.close() + statement.close() + connection.close() + except Exception as ex: + self._logger.log(Level.SEVERE, "Error closing database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + # take the message string which is wrapped by a certain string, and return the text enclosed. + @staticmethod + def decodeMessage(wrapper, message): + result = "" + decoded = Base64.decodeBase64(message) + try: + Z = String(decoded, "UTF-8") + result = Z.split(wrapper)[1] + except Exception as ex: + self._logger.log(Level.SEVERE, "Error decoding a Tango message", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + return result diff --git a/InternalPythonModules/android/textmessage.py b/InternalPythonModules/android/textmessage.py new file mode 100644 index 0000000000..afca23cdf1 --- /dev/null +++ b/InternalPythonModules/android/textmessage.py @@ -0,0 +1,132 @@ +""" +Autopsy Forensic Browser + +Copyright 2016 Basis Technology Corp. +Contact: carrier <at> sleuthkit <dot> org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from java.io import File +from java.lang import Class +from java.lang import ClassNotFoundException +from java.lang import Integer +from java.lang import Long +from java.sql import Connection +from java.sql import DriverManager +from java.sql import ResultSet +from java.sql import SQLException +from java.sql import Statement +from java.util.logging import Level +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import FileManager +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil +from org.sleuthkit.autopsy.datamodel import ContentUtils +from org.sleuthkit.autopsy.ingest import IngestJobContext +from org.sleuthkit.autopsy.ingest import IngestServices +from org.sleuthkit.autopsy.ingest import ModuleDataEvent +from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import BlackboardArtifact +from org.sleuthkit.datamodel import BlackboardAttribute +from org.sleuthkit.datamodel import Content +from org.sleuthkit.datamodel import TskCoreException + +import traceback +import general + +""" +Finds database with SMS/MMS messages and adds them to blackboard. +""" +class TextMessageAnalyzer(general.AndroidComponentAnalyzer): + + def __init__(self): + self._logger = Logger.getLogger(self.__class__.__name__) + + def analyze(self, dataSource, fileManager, context): + try: + absFiles = fileManager.findFiles(dataSource, "mmssms.db") + for abstractFile in absFiles: + try: + jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName()) + ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled) + self.__findTextsInDB(jFile.toString(), abstractFile) + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing text messages", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Error finding text messages", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + def __findTextsInDB(self, databasePath, abstractFile): + if not databasePath: + return + + bbartifacts = list() + try: + Class.forName("org.sqlite.JDBC") # load JDBC driver + connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) + statement = connection.createStatement() + except (ClassNotFoundException, SQLException) as ex: + self._logger.log(Level.SEVERE, "Error opening database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + return + + try: + resultSet = statement.executeQuery( + "SELECT address, date, read, type, subject, body FROM sms;") + while resultSet.next(): + address = resultSet.getString("address") # may be phone number, or other addresses + date = Long.valueOf(resultSet.getString("date")) / 1000 + read = resultSet.getInt("read") # may be unread = 0, read = 1 + subject = resultSet.getString("subject") # message subject + body = resultSet.getString("body") # message body + 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)) + 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")) + + bbartifacts.append(artifact) + try: + # index the artifact for keyword search + blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard.indexArtifact(artifact) + except Blackboard.BlackboardException as ex: + self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + MessageNotifyUtil.Notify.error("Failed to index text message artifact for keyword search.", artifact.getDisplayName()) + + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing text messages to blackboard", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + finally: + if bbartifacts: + IngestServices.getInstance().fireModuleDataEvent(ModuleDataEvent(general.MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE, bbartifacts)) + + try: + if resultSet is not None: + resultSet.close() + statement.close() + connection.close() + except Exception as ex: + self._logger.log(Level.SEVERE, "Error closing database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) diff --git a/InternalPythonModules/android/wwfmessage.py b/InternalPythonModules/android/wwfmessage.py new file mode 100644 index 0000000000..97f35869ed --- /dev/null +++ b/InternalPythonModules/android/wwfmessage.py @@ -0,0 +1,118 @@ +""" +Autopsy Forensic Browser + +Copyright 2016 Basis Technology Corp. +Contact: carrier <at> sleuthkit <dot> org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from java.io import File +from java.lang import Class +from java.lang import ClassNotFoundException +from java.sql import Connection +from java.sql import DriverManager +from java.sql import ResultSet +from java.sql import SQLException +from java.sql import Statement +from java.util.logging import Level +from org.sleuthkit.autopsy.casemodule import Case +from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import FileManager +from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil +from org.sleuthkit.autopsy.datamodel import ContentUtils +from org.sleuthkit.autopsy.ingest import IngestJobContext +from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import BlackboardArtifact +from org.sleuthkit.datamodel import BlackboardAttribute +from org.sleuthkit.datamodel import Content +from org.sleuthkit.datamodel import TskCoreException + +import traceback +import general + +""" +Analyzes messages from Words With Friends +""" +class WWFMessageAnalyzer(general.AndroidComponentAnalyzer): + + def __init__(self): + self._logger = Logger.getLogger(self.__class__.__name__) + + def analyze(self, dataSource, fileManager, context): + try: + absFiles = fileManager.findFiles(dataSource, "WordsFramework") + for abstractFile in absFiles: + try: + jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName()) + ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled) + self.__findWWFMessagesInDB(jFile.toString(), abstractFile) + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing WWF messages", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Error finding WWF messages", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + + def __findWWFMessagesInDB(self, databasePath, abstractFile): + if not databasePath: + return + + try: + Class.forName("org.sqlite.JDBC"); # load JDBC driver + connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) + statement = connection.createStatement() + except (ClassNotFoundException, SQLException) as ex: + self._logger.log(Level.SEVERE, "Error opening database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + return + + try: + resultSet = statement.executeQuery( + "SELECT message, strftime('%s' ,created_at) as datetime, user_id, game_id FROM chat_messages ORDER BY game_id DESC, created_at DESC;") + + while resultSet.next(): + message = resultSet.getString("message") # WWF Message + created_at = resultSet.getLong("datetime") + 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. + + 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")) + + try: + # index the artifact for keyword search + blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard.indexArtifact(artifact) + except Blackboard.BlackboardException as ex: + self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + MessageNotifyUtil.Notify.error("Failed to index WWF message artifact for keyword search.", artifact.getDisplayName()) + + except Exception as ex: + self._logger.log(Level.SEVERE, "Error parsing WWF messages to the blackboard", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + finally: + try: + if resultSet is not None: + resultSet.close() + statement.close() + connection.close() + except Exception as ex: + self._logger.log(Level.SEVERE, "Error closing database", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java index 7e147c1ba6..47b36de0be 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java @@ -19,6 +19,8 @@ package org.sleuthkit.autopsy.keywordsearch; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import javax.swing.Action; import org.openide.nodes.FilterNode; @@ -26,14 +28,17 @@ import org.openide.nodes.Node; import org.openide.nodes.Node.Property; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.openide.util.lookup.Lookups; import org.openide.util.lookup.ProxyLookup; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.directorytree.HashSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; +import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentVisitor; import org.sleuthkit.datamodel.DerivedFile; @@ -107,16 +112,23 @@ class KeywordSearchFilterNode extends FilterNode { } private List<Action> getFileActions() { - List<Action> actions = new ArrayList<>(); - actions.add(new NewWindowViewAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.viewInNewWinActionLbl"), KeywordSearchFilterNode.this)); - actions.add(new ExternalViewerAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.openExternViewActLbl"), getOriginal())); - actions.add(null); - actions.add(ExtractAction.getInstance()); - actions.add(new HashSearchAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.searchSameMd5"), getOriginal())); - actions.add(null); // creates a menu separator - actions.add(AddContentTagAction.getInstance()); - actions.addAll(ContextMenuExtensionPoint.getActions()); - return actions; + List<Action> actionsList = new ArrayList<>(); + actionsList.add(new NewWindowViewAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.viewInNewWinActionLbl"), KeywordSearchFilterNode.this)); + actionsList.add(new ExternalViewerAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.openExternViewActLbl"), getOriginal())); + actionsList.add(null); + actionsList.add(ExtractAction.getInstance()); + actionsList.add(new HashSearchAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.searchSameMd5"), getOriginal())); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); + + final Collection<AbstractFile> selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; } @Override diff --git a/nbproject/project.properties b/nbproject/project.properties index 8261bedc39..00ab969c89 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -4,7 +4,7 @@ app.title=Autopsy ### lowercase version of above app.name=${branding.token} ### if left unset, version will default to today's date -app.version=4.3.0 +app.version=4.4.0 ### build.type must be one of: DEVELOPMENT, RELEASE #build.type=RELEASE build.type=DEVELOPMENT diff --git a/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py b/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py index 6852a3534b..7a11ed913d 100755 --- a/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py +++ b/pythonExamples/Aug2015DataSourceTutorial/FindContactsDb.py @@ -58,8 +58,7 @@ from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.datamodel import ContentUtils from org.sleuthkit.autopsy.casemodule.services import Services from org.sleuthkit.autopsy.casemodule.services import FileManager -# This will work in 4.0.1 and beyond -# from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import Blackboard @@ -98,25 +97,22 @@ class ContactsDbIngestModule(DataSourceIngestModule): # Where any setup and configuration is done # 'context' is an instance of org.sleuthkit.autopsy.ingest.IngestJobContext. - # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html def startUp(self, context): self.context = context - # Throw an IngestModule.IngestModuleException exception if there was a problem setting up - # raise IngestModuleException("Oh No!") # Where the analysis is done. # The 'dataSource' object being passed in is of type org.sleuthkit.datamodel.Content. - # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.3/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html + # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.4/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html # 'progressBar' is of type org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress - # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html def process(self, dataSource, progressBar): # we don't know how much work there is yet progressBar.switchToIndeterminate() - # This will work in 4.0.1 and beyond # Use blackboard class to index blackboard artifacts for keyword search - # blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard = Case.getCurrentCase().getServices().getBlackboard() # Find files named contacts.db, regardless of parent path fileManager = Case.getCurrentCase().getServices().getFileManager() @@ -124,7 +120,7 @@ class ContactsDbIngestModule(DataSourceIngestModule): numFiles = len(files) progressBar.switchToDeterminate(numFiles) - fileCount = 0; + fileCount = 0 for file in files: # Check if the user pressed cancel while we were busy @@ -176,12 +172,12 @@ class ContactsDbIngestModule(DataSourceIngestModule): art.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), ContactsDbIngestModuleFactory.moduleName, phone)) - # This will work in 4.0.1 and beyond - #try: - # # index the artifact for keyword search - # blackboard.indexArtifact(art) - #except Blackboard.BlackboardException as e: - # self.log(Level.SEVERE, "Error indexing artifact " + art.getDisplayName()) + + try: + # index the artifact for keyword search + blackboard.indexArtifact(art) + except Blackboard.BlackboardException as e: + self.log(Level.SEVERE, "Error indexing artifact " + art.getDisplayName()) # Fire an event to notify the UI and others that there are new artifacts IngestServices.getInstance().fireModuleDataEvent( diff --git a/pythonExamples/Aug2015DataSourceTutorial/RunExe.py b/pythonExamples/Aug2015DataSourceTutorial/RunExe.py index cc63b0d235..14477f06df 100755 --- a/pythonExamples/Aug2015DataSourceTutorial/RunExe.py +++ b/pythonExamples/Aug2015DataSourceTutorial/RunExe.py @@ -95,7 +95,7 @@ class RunExeIngestModule(DataSourceIngestModule): # Where any setup and configuration is done # 'context' is an instance of org.sleuthkit.autopsy.ingest.IngestJobContext. - # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html def startUp(self, context): self.context = context @@ -108,9 +108,9 @@ class RunExeIngestModule(DataSourceIngestModule): # Where the analysis is done. # The 'dataSource' object being passed in is of type org.sleuthkit.datamodel.Content. - # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.3/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html + # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.4/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html # 'progressBar' is of type org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress - # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html def process(self, dataSource, progressBar): # we don't know how much work there will be diff --git a/pythonExamples/July2015FileTutorial_BigRound/FindBigRoundFiles.py b/pythonExamples/July2015FileTutorial_BigRound/FindBigRoundFiles.py index 7aea6ffd3c..fbcf8eaa39 100755 --- a/pythonExamples/July2015FileTutorial_BigRound/FindBigRoundFiles.py +++ b/pythonExamples/July2015FileTutorial_BigRound/FindBigRoundFiles.py @@ -56,8 +56,7 @@ from org.sleuthkit.autopsy.coreutils import Logger from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Services from org.sleuthkit.autopsy.casemodule.services import FileManager -# This will work in 4.0.1 and beyond -# from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import Blackboard # Factory that defines the name and details of the module and allows Autopsy # to create instances of the modules that will do the anlaysis. @@ -93,7 +92,7 @@ class FindBigRoundFilesIngestModule(FileIngestModule): # Where any setup and configuration is done # 'context' is an instance of org.sleuthkit.autopsy.ingest.IngestJobContext. - # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html # TODO: Add any setup code that you need here. def startUp(self, context): self.filesFound = 0 @@ -104,12 +103,11 @@ class FindBigRoundFilesIngestModule(FileIngestModule): # Where the analysis is done. Each file will be passed into here. # The 'file' object being passed in is of type org.sleuthkit.datamodel.AbstractFile. - # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.3/classorg_1_1sleuthkit_1_1datamodel_1_1_abstract_file.html + # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.4/classorg_1_1sleuthkit_1_1datamodel_1_1_abstract_file.html def process(self, file): - # This will work in 4.0.1 and beyond # Use blackboard class to index blackboard artifacts for keyword search - # blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard = Case.getCurrentCase().getServices().getBlackboard() # Skip non-files if ((file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) or @@ -127,21 +125,20 @@ class FindBigRoundFilesIngestModule(FileIngestModule): FindBigRoundFilesIngestModuleFactory.moduleName, "Big and Round Files") art.addAttribute(att) - # This will work in 4.0.1 and beyond - #try: - # # index the artifact for keyword search - # blackboard.indexArtifact(art) - #except Blackboard.BlackboardException as e: - # self.log(Level.SEVERE, "Error indexing artifact " + art.getDisplayName()) + try: + # index the artifact for keyword search + blackboard.indexArtifact(art) + except Blackboard.BlackboardException as e: + self.log(Level.SEVERE, "Error indexing artifact " + art.getDisplayName()) # Fire an event to notify the UI and others that there is a new artifact IngestServices.getInstance().fireModuleDataEvent( ModuleDataEvent(FindBigRoundFilesIngestModuleFactory.moduleName, - BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, None)); + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, None)) return IngestModule.ProcessResult.OK # Where any shutdown code is run and resources are freed. # TODO: Add any shutdown code that you need here. def shutDown(self): - None + None \ No newline at end of file diff --git a/pythonExamples/Sept2015ReportTutorial_CSV/CsvReportModule.py b/pythonExamples/Sept2015ReportTutorial_CSV/CsvReportModule.py index 137af15d26..82a0193aa2 100755 --- a/pythonExamples/Sept2015ReportTutorial_CSV/CsvReportModule.py +++ b/pythonExamples/Sept2015ReportTutorial_CSV/CsvReportModule.py @@ -27,7 +27,7 @@ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. -# See http://sleuthkit.org/autopsy/docs/api-docs/3.1/index.html for documentation +# See http://sleuthkit.org/autopsy/docs/api-docs/4.4/index.html for documentation # Simple report module for Autopsy. # Used as part of Python tutorials from Basis Technology - September 2015 @@ -71,7 +71,7 @@ class CSVReportModule(GeneralReportModuleAdapter): # TODO: Update this method to make a report # The 'baseReportDir' object being passed in is a string with the directory that reports are being stored in. Report should go into baseReportDir + getRelativeFilePath(). # The 'progressBar' object is of type ReportProgressPanel. - # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1report_1_1_report_progress_panel.html + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1report_1_1_report_progress_panel.html def generateReport(self, baseReportDir, progressBar): # Open the output file. diff --git a/pythonExamples/dataSourceIngestModule.py b/pythonExamples/dataSourceIngestModule.py index 596436f6c9..9b75bfd79f 100755 --- a/pythonExamples/dataSourceIngestModule.py +++ b/pythonExamples/dataSourceIngestModule.py @@ -29,7 +29,7 @@ # Simple data source-level ingest module for Autopsy. # Search for TODO for the things that you need to change -# See http://sleuthkit.org/autopsy/docs/api-docs/3.1/index.html for documentation +# See http://sleuthkit.org/autopsy/docs/api-docs/4.4/index.html for documentation import jarray import inspect @@ -51,8 +51,7 @@ from org.sleuthkit.autopsy.coreutils import Logger from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Services from org.sleuthkit.autopsy.casemodule.services import FileManager -# This will work in 4.0.1 and beyond -# from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import Blackboard # Factory that defines the name and details of the module and allows Autopsy @@ -95,32 +94,32 @@ class SampleJythonDataSourceIngestModule(DataSourceIngestModule): # Where any setup and configuration is done # 'context' is an instance of org.sleuthkit.autopsy.ingest.IngestJobContext. - # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html # TODO: Add any setup code that you need here. def startUp(self, context): - self.context = context + # Throw an IngestModule.IngestModuleException exception if there was a problem setting up - # raise IngestModuleException("Oh No!") + # raise IngestModuleException("Oh No!") + self.context = context # Where the analysis is done. # The 'dataSource' object being passed in is of type org.sleuthkit.datamodel.Content. - # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.3/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html + # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.4/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html # 'progressBar' is of type org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress - # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html # TODO: Add your analysis code in here. def process(self, dataSource, progressBar): # we don't know how much work there is yet progressBar.switchToIndeterminate() - # This will work in 4.0.1 and beyond # Use blackboard class to index blackboard artifacts for keyword search - # blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard = Case.getCurrentCase().getServices().getBlackboard() # For our example, we will use FileManager to get all # files with the word "test" # in the name and then count and read them - # FileManager API: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1casemodule_1_1services_1_1_file_manager.html + # FileManager API: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1casemodule_1_1services_1_1_file_manager.html fileManager = Case.getCurrentCase().getServices().getFileManager() files = fileManager.findFiles(dataSource, "%test%") @@ -143,12 +142,11 @@ class SampleJythonDataSourceIngestModule(DataSourceIngestModule): att = BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, SampleJythonDataSourceIngestModuleFactory.moduleName, "Test file") art.addAttribute(att) - # This will work in 4.0.1 and beyond - #try: - # # index the artifact for keyword search - # blackboard.indexArtifact(art) - #except Blackboard.BlackboardException as e: - # self.log(Level.SEVERE, "Error indexing artifact " + art.getDisplayName()) + try: + # index the artifact for keyword search + blackboard.indexArtifact(art) + except Blackboard.BlackboardException as e: + self.log(Level.SEVERE, "Error indexing artifact " + art.getDisplayName()) # To further the example, this code will read the contents of the file and count the number of bytes inputStream = ReadContentInputStream(file) diff --git a/pythonExamples/fileIngestModule.py b/pythonExamples/fileIngestModule.py index df41a140aa..a2fecd2410 100755 --- a/pythonExamples/fileIngestModule.py +++ b/pythonExamples/fileIngestModule.py @@ -29,7 +29,7 @@ # Simple file-level ingest module for Autopsy. # Search for TODO for the things that you need to change -# See http://sleuthkit.org/autopsy/docs/api-docs/3.1/index.html for documentation +# See http://sleuthkit.org/autopsy/docs/api-docs/4.4/index.html for documentation import jarray import inspect @@ -53,8 +53,7 @@ from org.sleuthkit.autopsy.coreutils import Logger from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Services from org.sleuthkit.autopsy.casemodule.services import FileManager -# This will work in 4.0.1 and beyond -# from org.sleuthkit.autopsy.casemodule.services import Blackboard +from org.sleuthkit.autopsy.casemodule.services import Blackboard # Factory that defines the name and details of the module and allows Autopsy # to create instances of the modules that will do the anlaysis. @@ -95,7 +94,7 @@ class SampleJythonFileIngestModule(FileIngestModule): # Where any setup and configuration is done # 'context' is an instance of org.sleuthkit.autopsy.ingest.IngestJobContext. - # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html # TODO: Add any setup code that you need here. def startUp(self, context): self.filesFound = 0 @@ -106,7 +105,7 @@ class SampleJythonFileIngestModule(FileIngestModule): # Where the analysis is done. Each file will be passed into here. # The 'file' object being passed in is of type org.sleuthkit.datamodel.AbstractFile. - # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.3/classorg_1_1sleuthkit_1_1datamodel_1_1_abstract_file.html + # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.4/classorg_1_1sleuthkit_1_1datamodel_1_1_abstract_file.html # TODO: Add your analysis code in here. def process(self, file): # Skip non-files @@ -115,9 +114,8 @@ class SampleJythonFileIngestModule(FileIngestModule): (file.isFile() == False)): return IngestModule.ProcessResult.OK - # This will work in 4.0.1 and beyond # Use blackboard class to index blackboard artifacts for keyword search - # blackboard = Case.getCurrentCase().getServices().getBlackboard() + blackboard = Case.getCurrentCase().getServices().getBlackboard() # For an example, we will flag files with .txt in the name and make a blackboard artifact. if file.getName().lower().endswith(".txt"): @@ -132,12 +130,11 @@ class SampleJythonFileIngestModule(FileIngestModule): SampleJythonFileIngestModuleFactory.moduleName, "Text Files") art.addAttribute(att) - # This will work in 4.0.1 and beyond - #try: - # # index the artifact for keyword search - # blackboard.indexArtifact(art) - #except Blackboard.BlackboardException as e: - # self.log(Level.SEVERE, "Error indexing artifact " + art.getDisplayName()) + try: + # index the artifact for keyword search + blackboard.indexArtifact(art) + except Blackboard.BlackboardException as e: + self.log(Level.SEVERE, "Error indexing artifact " + art.getDisplayName()) # Fire an event to notify the UI and others that there is a new artifact IngestServices.getInstance().fireModuleDataEvent( @@ -171,4 +168,4 @@ class SampleJythonFileIngestModule(FileIngestModule): message = IngestMessage.createMessage( IngestMessage.MessageType.DATA, SampleJythonFileIngestModuleFactory.moduleName, str(self.filesFound) + " files found") - ingestServices = IngestServices.getInstance().postMessage(message) + ingestServices = IngestServices.getInstance().postMessage(message) \ No newline at end of file diff --git a/pythonExamples/fileIngestModuleWithGui.py b/pythonExamples/fileIngestModuleWithGui.py index f34c7289b6..34814ba12b 100755 --- a/pythonExamples/fileIngestModuleWithGui.py +++ b/pythonExamples/fileIngestModuleWithGui.py @@ -35,7 +35,7 @@ # don't need a configuration UI, start with the other sample module. # # Search for TODO for the things that you need to change -# See http://sleuthkit.org/autopsy/docs/api-docs/3.1/index.html for documentation +# See http://sleuthkit.org/autopsy/docs/api-docs/4.4/index.html for documentation import jarray @@ -204,4 +204,3 @@ class SampleFileIngestModuleWithUISettingsPanel(IngestModuleIngestJobSettingsPan # Return the settings used def getSettings(self): return self.local_settings - diff --git a/pythonExamples/reportmodule.py b/pythonExamples/reportmodule.py index 84f6ab4309..8c3c161ee7 100755 --- a/pythonExamples/reportmodule.py +++ b/pythonExamples/reportmodule.py @@ -31,7 +31,7 @@ # Sample report module for Autopsy. Use as a starting point for new modules. # # Search for TODO for the things that you need to change -# See http://sleuthkit.org/autopsy/docs/api-docs/3.1/index.html for documentation +# See http://sleuthkit.org/autopsy/docs/api-docs/4.4/index.html for documentation import os from java.lang import System @@ -69,7 +69,7 @@ class SampleGeneralReportModule(GeneralReportModuleAdapter): # TODO: Update this method to make a report # The 'baseReportDir' object being passed in is a string with the directory that reports are being stored in. Report should go into baseReportDir + getRelativeFilePath(). # The 'progressBar' object is of type ReportProgressPanel. - # See: http://sleuthkit.org/autopsy/docs/api-docs/3.1/classorg_1_1sleuthkit_1_1autopsy_1_1report_1_1_report_progress_panel.html + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1report_1_1_report_progress_panel.html def generateReport(self, baseReportDir, progressBar): # For an example, we write a file with the number of files created in the past 2 weeks