diff --git a/.travis.yml b/.travis.yml index 56c8b7bbbd..dd6b50f34a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,11 @@ language: java sudo: required -dist: bionic -os: - - linux + +jobs: + include: + - os: linux + dist: bionic + - os: osx env: global: @@ -12,6 +15,7 @@ addons: apt: update: true packages: + - testdisk - libafflib-dev - libewf-dev - libpq-dev @@ -29,11 +33,13 @@ addons: update: true packages: - ant - - ant-optional + - wget + - libpq - libewf - gettext - cppunit - afflib + - testdisk python: - "2.7" @@ -43,9 +49,7 @@ before_install: - python setupSleuthkitBranch.py install: - - sudo apt-get install testdisk - - cd sleuthkit/sleuthkit - - ./travis_install_libs.sh + - pushd sleuthkit/sleuthkit && ./travis_install_libs.sh && popd before_script: - if [ $TRAVIS_OS_NAME = linux ]; then @@ -54,13 +58,13 @@ before_script: export PATH=/usr/bin:$PATH; unset JAVA_HOME; fi + - if [ $TRAVIS_OS_NAME = osx ]; then + brew uninstall java --force; + brew cask uninstall java --force; + brew tap homebrew/cask-versions; + brew cask install corretto8; + export JAVA_HOME=/Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home; + fi + - java -version -script: - - set -e - - echo "Building TSK..." - - ./bootstrap && ./configure --prefix=/usr && make - - pushd bindings/java/ && ant -q dist-PostgreSQL && popd - - echo "Building Autopsy..." && echo -en 'travis_fold:start:script.build\\r' - - cd $TRAVIS_BUILD_DIR/ - - ant build - - echo -en 'travis_fold:end:script.build\\r' +script: ./travis_build.sh diff --git a/Core/build.xml b/Core/build.xml index 28e64b83e5..0e5c90ef04 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -137,7 +137,7 @@ - + diff --git a/Core/ivy.xml b/Core/ivy.xml index 9ba4a3c371..5185b58c13 100644 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -42,14 +42,21 @@ + + + - + + + + + - + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 2a952926ba..3c994d58ac 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -18,6 +18,7 @@ file.reference.commons-lang3-3.8.1.jar=release\\modules\\ext\\commons-lang3-3.8. file.reference.commons-pool2-2.4.2.jar=release/modules/ext/commons-pool2-2.4.2.jar file.reference.cxf-rt-rs-client-3.3.0.jar=release\\modules\\ext\\cxf-rt-rs-client-3.3.0.jar file.reference.dec-0.1.2.jar=release\\modules\\ext\\dec-0.1.2.jar +file.reference.decodetect-core-0.3.jar=release\\modules\\ext\\decodetect-core-0.3.jar file.reference.fontbox-2.0.13.jar=release\\modules\\ext\\fontbox-2.0.13.jar file.reference.geoapi-3.0.1.jar=release\\modules\\ext\\geoapi-3.0.1.jar file.reference.grib-4.5.5.jar=release\\modules\\ext\\grib-4.5.5.jar @@ -50,7 +51,10 @@ file.reference.jsoup-1.11.3.jar=release\\modules\\ext\\jsoup-1.11.3.jar file.reference.jul-to-slf4j-1.7.25.jar=release\\modules\\ext\\jul-to-slf4j-1.7.25.jar file.reference.juniversalchardet-1.0.3.jar=release\\modules\\ext\\juniversalchardet-1.0.3.jar file.reference.junrar-2.0.0.jar=release\\modules\\ext\\junrar-2.0.0.jar +file.reference.jutf7-1.0.0.jar=release\\modules\\ext\\jutf7-1.0.0.jar +file.reference.jxmapviewer2-2.4.jar=release/modules/ext/jxmapviewer2-2.4.jar file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone-2.7.0.jar +file.reference.libphonenumber-3.5.jar=release/modules/ext/libphonenumber-3.5.jar file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar file.reference.metadata-extractor-2.11.0.jar=release\\modules\\ext\\metadata-extractor-2.11.0.jar file.reference.netcdf4-4.5.5.jar=release\\modules\\ext\\netcdf4-4.5.5.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index c33dabfbc7..83aefea7c5 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -327,6 +327,7 @@ org.sleuthkit.autopsy.datasourceprocessors org.sleuthkit.autopsy.directorytree org.sleuthkit.autopsy.events + org.sleuthkit.autopsy.exceptions org.sleuthkit.autopsy.filesearch org.sleuthkit.autopsy.guiutils org.sleuthkit.autopsy.healthmonitor @@ -405,6 +406,10 @@ ext/okhttp-2.7.5.jar release/modules/ext/okhttp-2.7.5.jar + + ext/libphonenumber-3.5.jar + release/modules/ext/libphonenumber-3.5.jar + ext/tika-core-1.20.jar release\modules\ext\tika-core-1.20.jar @@ -449,6 +454,10 @@ ext/commons-pool2-2.4.2.jar release/modules/ext/commons-pool2-2.4.2.jar + + ext/jxmapviewer2-2.4.jar + release/modules/ext/jxmapviewer2-2.4.jar + ext/jdom-2.0.5-contrib.jar release/modules/ext/jdom-2.0.5-contrib.jar @@ -517,6 +526,10 @@ ext/google-http-client-1.29.0.jar release/modules/ext/google-http-client-1.29.0.jar + + ext/sleuthkit-postgresql-4.7.0.jar + release/modules/ext/sleuthkit-postgresql-4.7.0.jar + ext/bcpkix-jdk15on-1.60.jar release\modules\ext\bcpkix-jdk15on-1.60.jar @@ -605,10 +618,6 @@ ext/jbig2-imageio-3.0.2.jar release\modules\ext\jbig2-imageio-3.0.2.jar - - ext/sleuthkit-postgresql-4.7.0.jar - release/modules/ext/sleuthkit-postgresql-4.7.0.jar - ext/apache-mime4j-dom-0.8.2.jar release\modules\ext\apache-mime4j-dom-0.8.2.jar @@ -785,6 +794,14 @@ ext/vorbis-java-tika-0.8.jar release\modules\ext\vorbis-java-tika-0.8.jar + + ext/decodetect-core-0.3.jar + release/modules/ext/decodetect-core-0.3.jar + + + ext/jutf7-1.0.0.jar + release/modules/ext/jutf7-1.0.0.jar + diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java index 9015d9a6ad..26d1c7a9bc 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; @@ -72,15 +73,24 @@ public class AddBlackboardArtifactTagAction extends AddTagAction { @Override protected void addTag(TagName tagName, String comment) { - /* - * The documentation for Lookup.lookupAll() explicitly says that the - * collection it returns may contain duplicates. Within this invocation - * of addTag(), we don't want to tag the same BlackboardArtifact more - * than once, so we dedupe the BlackboardArtifacts by stuffing them into - * a HashSet. - */ - final Collection selectedArtifacts = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); - + final Collection selectedArtifacts = new HashSet<>(); + //If the contentToTag is empty look up the selected content + if (getContentToTag().isEmpty()) { + /* + * The documentation for Lookup.lookupAll() explicitly says that the + * collection it returns may contain duplicates. Within this + * invocation of addTag(), we don't want to tag the same + * BlackboardArtifact more than once, so we dedupe the + * BlackboardArtifacts by stuffing them into a HashSet. + */ + selectedArtifacts.addAll(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + } else { + for (Content content : getContentToTag()) { + if (content instanceof BlackboardArtifact) { + selectedArtifacts.add((BlackboardArtifact) content); + } + } + } new Thread(() -> { for (BlackboardArtifact artifact : selectedArtifacts) { try { diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java index 34e7b2a110..c32533b0cd 100644 --- 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-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -76,16 +76,26 @@ public class AddContentTagAction extends AddTagAction { @Override protected void addTag(TagName tagName, String comment) { - /* - * The documentation for Lookup.lookupAll() explicitly says that the - * collection it returns may contain duplicates. Within this invocation - * of addTag(), we don't want to tag the same AbstractFile more than - * once, so we dedupe the AbstractFiles by stuffing them into a HashSet. - * - * We don't want VirtualFile and DerivedFile objects to be tagged. - */ - final Collection selectedFiles = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); - + final Collection selectedFiles = new HashSet<>(); + //If the contentToTag is empty look up the selected content + if (getContentToTag().isEmpty()) { + /* + * The documentation for Lookup.lookupAll() explicitly says that the + * collection it returns may contain duplicates. Within this + * invocation of addTag(), we don't want to tag the same + * AbstractFile more than once, so we dedupe the AbstractFiles by + * stuffing them into a HashSet. + * + * We don't want VirtualFile and DerivedFile objects to be tagged. + */ + selectedFiles.addAll(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + } else { + for (Content content : getContentToTag()) { + if (content instanceof AbstractFile) { + selectedFiles.add((AbstractFile) content); + } + } + } new Thread(() -> { for (AbstractFile file : selectedFiles) { try { diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java index 6606185e24..3709592e62 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,6 +20,9 @@ package org.sleuthkit.autopsy.actions; import java.awt.event.ActionEvent; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -33,6 +36,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -45,6 +49,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { private static final long serialVersionUID = 1L; private static final String NO_COMMENT = ""; + private final Collection content = new HashSet<>(); AddTagAction(String menuText) { super(menuText); @@ -52,6 +57,32 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { @Override public JMenuItem getPopupPresenter() { + content.clear(); + return new TagMenu(); + } + + /** + * Get the collection of content which may have been specified for this + * action. Empty collection returned when no content was specified. + * + * @return The specified content for this action. + */ + Collection getContentToTag() { + return Collections.unmodifiableCollection(content); + } + + /** + * Get the menu for adding tags to the specified collection of Content. + * + * @param contentToTag The collection of Content the menu actions will be + * applied to. + * + * @return The menu which will allow users to choose the tag they want to + * apply to the Content specified. + */ + public JMenuItem getMenuForContent(Collection contentToTag) { + content.clear(); + content.addAll(contentToTag); return new TagMenu(); } @@ -118,26 +149,26 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { tagNameItem.addActionListener((ActionEvent e) -> { getAndAddTag(entry.getKey(), entry.getValue(), NO_COMMENT); }); - - // Show custom tags before predefined tags in the menu + + // Show custom tags before predefined tags in the menu if (standardTagNames.contains(tagDisplayName)) { standardTagMenuitems.add(tagNameItem); } else { add(tagNameItem); } } - } - + } + if (getItemCount() > 0) { addSeparator(); } - + standardTagMenuitems.forEach((menuItem) -> { add(menuItem); }); - + addSeparator(); - + // Create a "Choose Tag and Comment..." menu item. Selecting this item initiates // a dialog that can be used to create or select a tag name with an // optional comment and adds a tag with the resulting name. @@ -150,7 +181,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { } }); add(tagAndCommentItem); - + // Create a "New Tag..." menu item. // Selecting this item initiates a dialog that can be used to create // or select a tag name and adds a tag with the resulting name. @@ -162,7 +193,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { } }); add(newTagMenuItem); - + } /** @@ -194,10 +225,10 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { tagName = openCase.getServices().getTagsManager().getDisplayNamesToTagNamesMap().get(tagDisplayName); } catch (TskCoreException ex1) { Logger.getLogger(AddTagAction.class.getName()).log(Level.SEVERE, tagDisplayName + " already exists in database but an error occurred in retrieving it.", ex1); //NON-NLS - } + } } catch (TskCoreException ex) { Logger.getLogger(AddTagAction.class.getName()).log(Level.SEVERE, "Error adding " + tagDisplayName + " tag name", ex); //NON-NLS - } + } } addTag(tagName, comment); } diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties index 10bd2ad0df..a4a460df69 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle_ja.properties @@ -1,51 +1,97 @@ -GetTagNameDialog.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb +AddBlackboardArtifactTagAction.pluralTagResult=\u7d50\u679c\u30bf\u30b0\u3092\u8ffd\u52a0 +AddBlackboardArtifactTagAction.singularTagResult=\u7d50\u679c\u30bf\u30b0\u3092\u8ffd\u52a0 +AddBlackboardArtifactTagAction.taggingErr=\u30bf\u30b0\u4ed8\u3051\u30a8\u30e9\u30fc +# {0} - artifactName +AddBlackboardArtifactTagAction.unableToTag.msg={0} \u3092\u30bf\u30b0\u4ed8\u3051\u3067\u304d\u307e\u305b\u3093\u3002 +AddContentTagAction.cannotApplyTagErr=\u30bf\u30b0\u3092\u9069\u7528\u3067\u304d\u307e\u305b\u3093 +AddContentTagAction.pluralTagFile=\u30d5\u30a1\u30a4\u30eb\u30bf\u30b0\u3092\u8ffd\u52a0 +AddContentTagAction.singularTagFile=\u30d5\u30a1\u30a4\u30eb\u30bf\u30b0\u3092\u8ffd\u52a0 +# {0} - fileName +# {1} - tagName +AddContentTagAction.tagExists={0} \u304c {1} \u3068\u3057\u3066\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u307e\u3057\u305f\u3002\u540c\u3058\u30bf\u30b0\u3092\u518d\u9069\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +AddContentTagAction.taggingErr=\u30bf\u30b0\u4ed8\u3051\u30a8\u30e9\u30fc +# {0} - fileName +AddContentTagAction.unableToTag.msg={0} \u3092\u30bf\u30b0\u4ed8\u3051\u3067\u304d\u307e\u305b\u3093\u3002\u901a\u5e38\u306e\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +# {0} - fileName +AddContentTagAction.unableToTag.msg2={0} \u3092\u30bf\u30b0\u4ed8\u3051\u3067\u304d\u307e\u305b\u3093\u3002 +CTL_ShowIngestProgressSnapshotAction=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b9\u30c6\u30fc\u30bf\u30b9\u8a73\u7d30 +DeleteBlackboardArtifactTagAction.deleteTag=\u9078\u629e\u3057\u305f\u30bf\u30b0\u3092\u524a\u9664 +DeleteBlackboardArtifactTagAction.tagDelErr=\u30bf\u30b0\u524a\u9664\u30a8\u30e9\u30fc +# {0} - tagName +DeleteBlackboardArtifactTagAction.unableToDelTag.msg={0} \u3092Delete\u30bf\u30b0\u4ed8\u3051\u3067\u304d\u307e\u305b\u3093\u3002 +DeleteContentTagAction.deleteTag=\u9078\u629e\u3057\u305f\u30bf\u30b0\u3092\u524a\u9664 +DeleteContentTagAction.tagDelErr=\u30bf\u30b0\u524a\u9664\u30a8\u30e9\u30fc +# {0} - tagName +DeleteContentTagAction.unableToDelTag.msg={0} \u3092Delete\u30bf\u30b0\u4ed8\u3051\u3067\u304d\u307e\u305b\u3093\u3002 +DeleteFileBlackboardArtifactTagAction.deleteTag=\u7d50\u679c\u30bf\u30b0\u3092\u524a\u9664 +# {0} - artifactID +DeleteFileBlackboardArtifactTagAction.deleteTag.alert=\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c {0} \u3092\u30bf\u30b0\u306a\u3057\u306b\u3067\u304d\u307e\u305b\u3093\u3002 +# {0} - artifactID +DeleteFileBlackboardArtifactTagAction.deleteTags.alert=\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c {0} \u3092\u30bf\u30b0\u306a\u3057\u306b\u3067\u304d\u307e\u305b\u3093\u3002 +DeleteFileContentTagAction.deleteTag=\u30d5\u30a1\u30a4\u30eb\u30bf\u30b0\u3092\u524a\u9664 +# {0} - fileID +DeleteFileContentTagAction.deleteTag.alert=\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c {0} \u3092\u30bf\u30b0\u306a\u3057\u306b\u3067\u304d\u307e\u305b\u3093\u3002 +ExitAction.confirmationDialog.message=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d\u3067\u3059\u3002\u7d42\u4e86\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +ExitAction.confirmationDialog.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d\u3067\u3059 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +ExitAction.messageBox.caseCloseExceptionMessage=\u6b21\u306e\u30b1\u30fc\u30b9\u3092\u9589\u3058\u3066\u3044\u308b\u9593\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +GetTagNameDialog.descriptionLabel.text=\u8a18\u8ff0: +GetTagNameDialog.notableCheckbox.text=\u30bf\u30b0\u306f\u9805\u76ee\u304c\u9855\u8457\u3067\u3042\u308b\u3068\u793a\u5506\u3057\u3066\u3044\u307e\u3059\u3002 +GetTagNameDialog.tagDescriptionIllegalCharacters.message=\u30bf\u30b0\u306e\u8a18\u8ff0\u306b\u30ab\u30f3\u30de(,)\u3084\u30bb\u30df\u30b3\u30ed\u30f3(;)\u3092\u542b\u3081\u3089\u308c\u307e\u305b\u3093 +GetTagNameDialog.tagDescriptionIllegalCharacters.title=\u30bf\u30b0\u306e\u8a18\u8ff0\u306b\u7121\u52b9\u306a\u6587\u5b57\u304c\u3042\u308a\u307e\u3059 +GetTagNameDialog.tagNameAlreadyExists.message=\u30bf\u30b0\u540d\u306f\u4e00\u610f\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002\u3053\u306e\u540d\u524d\u306e\u30bf\u30b0\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 +GetTagNameDialog.tagNameAlreadyExists.title=\u30bf\u30b0\u540d\u3092\u8907\u88fd +GetTagNameDialog.tagNameField.text= +GetTagNameDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 GetTagNameDialog.okButton.text=OK -GetTagNameDialog.preexistingLabel.text=\u65e2\u5b58\u30bf\u30b0\uff1a +GetTagNameDialog.preexistingLabel.text=\u524d\u304b\u3089\u5b58\u5728\u3059\u308b\u30bf\u30b0\u540d: GetTagNameDialog.newTagPanel.border.title=\u65b0\u898f\u30bf\u30b0 -GetTagNameDialog.tagNameLabel.text=\u30bf\u30b0\u540d\uff1a +GetTagNameDialog.tagNameLabel.text=\u30bf\u30b0\u540d: 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.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 +GetTagNameAndCommentDialog.commentText.toolTipText=\u4efb\u610f\u30bf\u30b0\u306e\u30b3\u30e1\u30f3\u30c8\u3092\u5165\u529b\u3059\u308b\u304b\u7a7a\u6b04\u306e\u307e\u307e\u306b\u3059\u308b +GetTagNameAndCommentDialog.commentText.text= +GetTagNameAndCommentDialog.commentLabel.text=\u30b3\u30e1\u30f3\u30c8: +# \u3053\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u5909\u66f4\u3059\u308b\u306b\u306f\u3001[\u30c4\u30fc\u30eb | \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8] \u3092\u9078\u629e\u3057\u3001 +# \u30a8\u30c7\u30a3\u30bf\u30fc\u3067\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u958b\u304d\u307e\u3059\u3002 +GetTagNameAndCommentDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +GetTagNameAndCommentDialog.tagLabel.text=\u30bf\u30b0: +AddTagAction.bookmarkFile=\u30d6\u30c3\u30af\u30de\u30fc\u30af\u30d5\u30a1\u30a4\u30eb 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 +AddTagAction.noTags=\u30bf\u30b0\u306a\u3057 +AddTagAction.newTag=\u65b0\u898f\u30bf\u30b0... +AddTagAction.tagAndComment=\u30bf\u30b0\u3068\u30b3\u30e1\u30f3\u30c8... +AddBookmarkTagAction.bookmark.text=\u30d6\u30c3\u30af\u30de\u30fc\u30af +GetTagNameAndCommentDialog.noTags=\u30bf\u30b0\u306a\u3057 GetTagNameAndCommentDialog.selectTag=\u30bf\u30b0\u3092\u9078\u629e -GetTagNameAndCommentDialog.cancelName=\u30ad\u30e3\u30f3\u30bb\u30eb +GetTagNameAndCommentDialog.cancelName=\u53d6\u308a\u6d88\u3057 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.cancelName=\u53d6\u308a\u6d88\u3057 +GetTagNameDialog.mustSupplyTtagName.msg=\u7d9a\u884c\u3059\u308b\u306b\u306f\u30bf\u30b0\u540d\u3092\u63d0\u4f9b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\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.illegalChars.msg=\u30bf\u30b0\u540d\u306b\u4e0d\u6b63\u306a\u6587\u5b57\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002\n\u6b21\u306e\u8a18\u53f7\u3092\u542b\u3081\u3089\u308c\u307e\u305b\u3093: \\ : * ? " < > | , ; +GetTagNameDialog.illegalCharsErr=\u4e0d\u6b63\u306a\u6587\u5b57 +GetTagNameDialog.unableToAddTagNameToCase.msg={0} \u30bf\u30b0\u540d\u3092\u30b1\u30fc\u30b9\u306b\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\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 +GetTagNameDialog.tagNameAlreadyDef.msg={0} \u30bf\u30b0\u540d\u3092\u3059\u3067\u306b\u5b9a\u7fa9\u6e08\u307f\u3067\u3059\u3002 +GetTagNameDialog.dupTagErr=\u30bf\u30b0\u8907\u88fd\u30a8\u30e9\u30fc +GetTagNameDialog.tagNameExistsTskCore.msg=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u5185\u306b {0} \u30bf\u30b0\u540d\u304c\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u304c\u3001\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +OpenLogFolder.error1=\u30ed\u30b0\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093: {0} +OpenLogFolder.CouldNotOpenLogFolder=\u30ed\u30b0\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f +CTL_OpenLogFolder=\u30ed\u30b0\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u958b\u304f +CTL_OpenOutputFolder=\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u958b\u304f +OpenOutputFolder.error1=\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093: {0} +OpenOutputFolder.noCaseOpen=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u306a\u3044\u305f\u3081\u3001\u73fe\u5728\u306e\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +OpenOutputFolder.CouldNotOpenOutputFolder=\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f +# {0} - \u53e4\u3044\u30bf\u30b0\u540d +# {1} - artifactID +ReplaceBlackboardArtifactTagAction.replaceTag.alert=\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c {1} \u306e\u30bf\u30b0 {0} \u3092\u7f6e\u63db\u3067\u304d\u307e\u305b\u3093\u3002 +# {0} - \u53e4\u3044\u30bf\u30b0\u540d +# {1} - \u30b3\u30f3\u30c6\u30f3\u30c4\u30aa\u30d6\u30b8\u30a7\u30af\u30c8ID +ReplaceContentTagAction.replaceTag.alert={1} \u306e\u30bf\u30b0 {0} \u3092\u7f6e\u63db\u3067\u304d\u307e\u305b\u3093\u3002 +ReplaceTagAction.replaceTag=\u3067\u9078\u629e\u3057\u305f\u30bf\u30b0\u3092\u7f6e\u63db +ShowIngestProgressSnapshotAction.actionName.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u9032\u884c\u72b6\u6cc1\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u3092\u53d6\u5f97 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 +OpenPythonModulesFolderAction.errorMsg.folderNotFound=Python\u30d7\u30e9\u30b0\u30a4\u30f3\u30d5\u30a9\u30eb\u30c0\u30fc\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093: {0} +CTL_OpenPythonModulesFolderAction=Python\u30d7\u30e9\u30b0\u30a4\u30f3 GetTagNameAndCommentDialog.tagCombo.toolTipText=\u4f7f\u7528\u3059\u308b\u30bf\u30b0\u3092\u9078\u629e diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.java index 76f3faa8be..ec846567aa 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileBlackboardArtifactTagAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017-2018 Basis Technology Corp. + * Copyright 2017-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -82,6 +82,20 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem return new TagMenu(); } + /** + * Get the menu for removing tags from the specified collection of + * BlackboardArtifacts. + * + * @param selectedArtifacts The collection of BlackboardArtifacts the menu + * actions will be applied to. + * + * @return The menu which will allow users to remove tags from the specified + * collection of BlackboardArtifacts. + */ + public JMenuItem getMenuForArtifacts(Collection selectedArtifacts) { + return new TagMenu(selectedArtifacts); + } + @Override public void actionPerformed(ActionEvent event) { } @@ -105,7 +119,7 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem Platform.runLater(() -> new Alert(Alert.AlertType.ERROR, Bundle.DeleteFileBlackboardArtifactTagAction_deleteTag_alert(artifactId)).show() ); - return null; + return null; } try { @@ -144,11 +158,11 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem private static final long serialVersionUID = 1L; TagMenu() { + this(new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class))); + } + + TagMenu(Collection selectedBlackboardArtifactsList) { super(getActionDisplayName()); - - final Collection selectedBlackboardArtifactsList - = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); - if (!selectedBlackboardArtifactsList.isEmpty()) { BlackboardArtifact artifact = selectedBlackboardArtifactsList.iterator().next(); @@ -172,7 +186,7 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem try { List existingTagsList = Case.getCurrentCaseThrows().getServices().getTagsManager() - .getBlackboardArtifactTagsByArtifact(artifact); + .getBlackboardArtifactTagsByArtifact(artifact); for (Map.Entry entry : tagNamesMap.entrySet()) { String tagDisplayName = entry.getKey(); @@ -185,7 +199,7 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem tagNameItem.addActionListener((ActionEvent e) -> { deleteTag(tagName, artifactTag, artifact.getArtifactID()); }); - // Show custom tags before predefined tags in the menu + // Show custom tags before predefined tags in the menu if (standardTagNames.contains(tagDisplayName)) { standardTagMenuitems.add(tagNameItem); } else { @@ -200,7 +214,7 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem } } - if ((getItemCount() > 0) && !standardTagMenuitems.isEmpty() ){ + if ((getItemCount() > 0) && !standardTagMenuitems.isEmpty()) { addSeparator(); } standardTagMenuitems.forEach((menuItem) -> { diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteFileContentTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileContentTagAction.java index c8a8a69fdc..f68017de19 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteFileContentTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteFileContentTagAction.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * - * Copyright 2017-2018 Basis Technology Corp. + * + * Copyright 2017-2019 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. @@ -53,7 +53,7 @@ import org.sleuthkit.datamodel.TskData; "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; @@ -81,6 +81,19 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen return new TagMenu(); } + /** + * Get the menu for removing tags from the specified collection of Files. + * + * @param selectedFiles The collection of AbstractFiles the menu actions + * will be applied to. + * + * @return The menu which will allow users to remove tags from the specified + * collection of Files. + */ + public JMenuItem getMenuForFiles(Collection selectedFiles) { + return new TagMenu(selectedFiles); + } + @Override public void actionPerformed(ActionEvent e) { } @@ -102,19 +115,19 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager(); } catch (NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Error untagging file. No open case found.", ex); //NON-NLS - Platform.runLater(() -> - new Alert(Alert.AlertType.ERROR, Bundle.DeleteFileContentTagAction_deleteTag_alert(fileId)).show() + Platform.runLater(() + -> new Alert(Alert.AlertType.ERROR, Bundle.DeleteFileContentTagAction_deleteTag_alert(fileId)).show() ); return null; } - + try { logger.log(Level.INFO, "Removing tag {0} from {1}", new Object[]{tagName.getDisplayName(), contentTag.getContent().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() + Platform.runLater(() + -> new Alert(Alert.AlertType.ERROR, Bundle.DeleteFileContentTagAction_deleteTag_alert(fileId)).show() ); } return null; @@ -141,15 +154,24 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen private static final long serialVersionUID = 1L; + /** + * Construct an TagMenu object using the specified collection of files + * as the files to remove a tag from. + */ TagMenu() { + this(new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class))); + } + + /** + * Construct an TagMenu object using the specified collection of files + * as the files to remove a tag from. + */ + TagMenu(Collection selectedFiles) { super(getActionDisplayName()); - - final Collection selectedAbstractFilesList = - new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); - - if(!selectedAbstractFilesList.isEmpty()) { - AbstractFile file = selectedAbstractFilesList.iterator().next(); - + + if (!selectedFiles.isEmpty()) { + AbstractFile file = selectedFiles.iterator().next(); + Map tagNamesMap = null; List standardTagNames = TagsManager.getStandardTagNames(); List standardTagMenuitems = new ArrayList<>(); @@ -167,22 +189,22 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen // a tag with the associated tag name. if (null != tagNamesMap && !tagNamesMap.isEmpty()) { try { - List existingTagsList = - Case.getCurrentCaseThrows().getServices().getTagsManager() + List existingTagsList + = Case.getCurrentCaseThrows().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())) { + for (ContentTag contentTag : existingTagsList) { + if (tagDisplayName.equals(contentTag.getName().getDisplayName())) { String notableString = tagName.getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : ""; JMenuItem tagNameItem = new JMenuItem(tagDisplayName + notableString); tagNameItem.addActionListener((ActionEvent e) -> { deleteTag(tagName, contentTag, file.getId()); }); - + // Show custom tags before predefined tags in the menu if (standardTagNames.contains(tagDisplayName)) { standardTagMenuitems.add(tagNameItem); @@ -198,14 +220,14 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen } } - if ((getItemCount() > 0) && !standardTagMenuitems.isEmpty() ){ + if ((getItemCount() > 0) && !standardTagMenuitems.isEmpty()) { addSeparator(); } standardTagMenuitems.forEach((menuItem) -> { add(menuItem); }); - - if(getItemCount() == 0) { + + if (getItemCount() == 0) { setEnabled(false); } } diff --git a/Core/src/org/sleuthkit/autopsy/allcasessearch/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/allcasessearch/Bundle_ja.properties new file mode 100644 index 0000000000..08a2809615 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/allcasessearch/Bundle_ja.properties @@ -0,0 +1,46 @@ + +AllCasesSearchAction.getName.text=\u3059\u3079\u3066\u306e\u30b1\u30fc\u30b9\u3092\u691c\u7d22 +# {0} - \u30b1\u30fc\u30b9\u6570 +AllCasesSearchDialog.caseLabel.text=\u73fe\u5728\u306e\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u306f {0} \u30b1\u30fc\u30b9\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.correlationValueTextField.domainExample=\u4f8b: "domain.com" +AllCasesSearchDialog.correlationValueTextField.emailExample=\u4f8b: "user@host.com" +AllCasesSearchDialog.correlationValueTextField.filesExample=\u4f8b: "f0e1d2c3b4a5968778695a4b3c2d1e0f" +AllCasesSearchDialog.correlationValueTextField.iccidExample=\u4f8b: "89 91 19 1299 99 329451 0" +AllCasesSearchDialog.correlationValueTextField.imeiExample=\u4f8b: "351756061523999" +AllCasesSearchDialog.correlationValueTextField.imsiExample=\u4f8b: "310150123456789" +AllCasesSearchDialog.correlationValueTextField.macExample=\u4f8b: "0C-14-F2-01-AF-45" +AllCasesSearchDialog.correlationValueTextField.phoneExample=\u4f8b: "(800)123-4567" +AllCasesSearchDialog.correlationValueTextField.ssidExample=\u4f8b: "WirelessNetwork-5G" +AllCasesSearchDialog.correlationValueTextField.usbExample=\u4f8b: "4&1234567&0" +AllCasesSearchDialog.descriptionLabel.text=\u76f8\u95a2\u5206\u6790\u30d7\u30ed\u30d1\u30c6\u30a3\u304c\u306a\u3044\u304b\u6307\u5b9a\u5024\u3067\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u3092\u691c\u7d22\u3057\u307e\u3059\u3002\u691c\u7d22\u306f\u5927\u6587\u5b57\u5c0f\u6587\u5b57\u3092\u533a\u5225\u3057\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.dialogTitle.text=\u3059\u3079\u3066\u306e\u30b1\u30fc\u30b9\u3092\u691c\u7d22 +AllCasesSearchDialog.emptyNode.text=\u8a72\u5f53\u3059\u308b\u7d50\u679c\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.errorLabel.text=\ +AllCasesSearchDialog.correlationTypeLabel.text=\u76f8\u95a2\u5206\u6790\u30d7\u30ed\u30d1\u30c6\u30a3\u30bf\u30a4\u30d7: +AllCasesSearchDialog.resultsDescription.text=\u3059\u3079\u3066\u306e\u30b1\u30fc\u30b9\u306e\u691c\u7d22 +AllCasesSearchDialog.resultsTitle.text=\u3059\u3079\u3066\u306e\u30b1\u30fc\u30b9 +AllCasesSearchDialog.searchButton.AccessibleContext.accessibleDescription= +AllCasesSearchDialog.searchButton.AccessibleContext.accessibleName=\u691c\u7d22 +AllCasesSearchDialog.searchButton.text=\u691c\u7d22 +AllCasesSearchDialog.correlationValueTextField.text= +AllCasesSearchDialog.correlationValueLabel.text=\u76f8\u95a2\u5206\u6790\u30d7\u30ed\u30d1\u30c6\u30a3\u5024: +AllCasesSearchDialog.casesLabel.text=\ +AllCasesSearchDialog.validation.genericMessage=\u63d0\u4f9b\u3055\u308c\u305f\u5024\u306f\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.validation.invalidDomain=\u63d0\u4f9b\u3055\u308c\u305f\u5024\u306f\u6709\u52b9\u306a\u30c9\u30e1\u30a4\u30f3\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.validation.invalidEmail=\u63d0\u4f9b\u3055\u308c\u305f\u5024\u306f\u6709\u52b9\u306a\u96fb\u5b50\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.validation.invalidHash=\u63d0\u4f9b\u3055\u308c\u305f\u5024\u306f\u6709\u52b9\u306aMD5\u30cf\u30c3\u30b7\u30e5\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.validation.invalidIccid=\u63d0\u4f9b\u3055\u308c\u305f\u5024\u306f\u6709\u52b9\u306aICCID\u756a\u53f7\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.validation.invalidImei=\u63d0\u4f9b\u3055\u308c\u305f\u5024\u306f\u6709\u52b9\u306aIMEI\u756a\u53f7\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.validation.invalidImsi=\u63d0\u4f9b\u3055\u308c\u305f\u5024\u306f\u6709\u52b9\u306aIMSI\u756a\u53f7\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.validation.invalidMac=\u63d0\u4f9b\u3055\u308c\u305f\u5024\u306f\u6709\u52b9\u306aMAC\u30a2\u30c9\u30ec\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.validation.invalidPhone=\u63d0\u4f9b\u3055\u308c\u305f\u5024\u306f\u6709\u52b9\u306a\u96fb\u8a71\u756a\u53f7\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchDialog.validation.invalidSsid=\u63d0\u4f9b\u3055\u308c\u305f\u5024\u306f\u6709\u52b9\u306a\u30ef\u30a4\u30e4\u30ec\u30b9 \u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AllCasesSearchNode.getName.text=\u305d\u306e\u4ed6\u306e\u30b1\u30fc\u30b9\u306e\u691c\u7d22 +CorrelationAttributeInstanceNode.columnName.case=\u30b1\u30fc\u30b9 +CorrelationAttributeInstanceNode.columnName.comment=\u30b3\u30e1\u30f3\u30c8 +CorrelationAttributeInstanceNode.columnName.dataSource=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 +CorrelationAttributeInstanceNode.columnName.device=\u30c7\u30d0\u30a4\u30b9 +CorrelationAttributeInstanceNode.columnName.known=\u65e2\u77e5 +CorrelationAttributeInstanceNode.columnName.name=\u540d\u524d +CorrelationAttributeInstanceNode.columnName.path=\u30d1\u30b9 +CTL_AllCasesSearchAction=\u3059\u3079\u3066\u306e\u30b1\u30fc\u30b9\u3092\u691c\u7d22 diff --git a/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java b/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java index 7ef7e30f01..3e67e6a442 100644 --- a/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java +++ b/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java @@ -50,7 +50,9 @@ public interface AutopsyService { * @param context The case context which includes things such as the case, a * progress indicator for the operation, a cancellation * request flag, etc. - * @throws org.sleuthkit.autopsy.framework.AutopsyService.AutopsyServiceException + * + * @throws + * org.sleuthkit.autopsy.framework.AutopsyService.AutopsyServiceException */ default void openCaseResources(CaseContext context) throws AutopsyServiceException { /* @@ -64,7 +66,9 @@ public interface AutopsyService { * @param context The case context which includes things such as the case, a * progress indicator for the operation, a cancellation * request flag, etc. - * @throws org.sleuthkit.autopsy.framework.AutopsyService.AutopsyServiceException + * + * @throws + * org.sleuthkit.autopsy.framework.AutopsyService.AutopsyServiceException */ default void closeCaseResources(CaseContext context) throws AutopsyServiceException { /* @@ -113,7 +117,9 @@ public interface AutopsyService { /** * Gets the progress indicator for the creation/opening/upgrading of - * case-level resources by a service. + * case-level resources by a service. IMPORTANT: The service should only + * call progress() on the progress indicator. Calling start() and + * finish() are the responsibility of the case providing the context. * * @return The progress indicator. */ diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AccessLimiterUtils.java b/Core/src/org/sleuthkit/autopsy/casemodule/AccessLimiterUtils.java deleted file mode 100644 index 3b2a201e07..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AccessLimiterUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.casemodule; - -import java.io.File; -import java.nio.file.Paths; -import org.sleuthkit.autopsy.coreutils.PlatformUtil; - -/** - * Class for methods to check if access should be limited to a feature - * - */ -final class AccessLimiterUtils { - - private final static String MULTI_USER_ACCESS_FILE_NAME = "mualimit"; // NON-NLS - private final static String MULTI_USER_ACCESS_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), MULTI_USER_ACCESS_FILE_NAME).toString(); - - /** - * Check if privileges regarding multi-user cases should be restricted. - * - * @return True if privileges should be restricted, false otherwise. - */ - static boolean limitMultiUserAccess() { - return new File(MULTI_USER_ACCESS_FILE_PATH).exists(); - } - - /** - * Private constructor for a utility class - */ - private AccessLimiterUtils() { - //private constructer left empty intentionally - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 79488c932f..ca2deebaa4 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -2,14 +2,13 @@ AddImageWizardIngestConfigPanel.name.text=Configure Ingest Modules AddImageWizardSelectDspVisual.multiUserWarning.text=This type of Data Source Processor is not available in multi-user mode # {0} - exception message Case.closeException.couldNotCloseCase=Error closing case: {0} -Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources Case.deleteCaseConfirmationDialog.message=Are you sure you want to close and delete the current case? Case.deleteCaseConfirmationDialog.title=Delete Current Case? # {0} - exception message Case.deleteCaseFailureMessageBox.message=Error deleting case: {0} Case.deleteCaseFailureMessageBox.title=Failed to Delete Case -Case.exceptionMessage.cancelledByUser=Cancelled by user. +Case.exceptionMessage.cancelled=Cancelled. Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first. Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host. Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window @@ -32,10 +31,13 @@ Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0 Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}. # {0} - exception message Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}. +Case.exceptionMessage.dataSourceNotFound=The data source was not found. # {0} - case display name Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled. Case.exceptionMessage.emptyCaseDir=Must specify a case directory path. Case.exceptionMessage.emptyCaseName=Must specify a case name. +Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while removing the data source from the case database. +Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while removing the data source from the text index. Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details. # {0} - exception message Case.exceptionMessage.execExceptionWrapperMessage={0} @@ -50,11 +52,14 @@ Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}. Case.exceptionMessage.metadataUpdateError=Failed to update case metadata # {0} - exception message Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}. +Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case. +Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case. Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User. Case.progressIndicatorCancelButton.label=Cancel Case.progressIndicatorTitle.closingCase=Closing Case Case.progressIndicatorTitle.creatingCase=Creating Case Case.progressIndicatorTitle.deletingCase=Deleting Case +Case.progressIndicatorTitle.deletingDataSource=Removing Data Source Case.progressIndicatorTitle.openingCase=Opening Case Case.progressMessage.cancelling=Cancelling... Case.progressMessage.clearingTempDirectory=Clearing case temp directory... @@ -67,6 +72,7 @@ Case.progressMessage.creatingCaseNodeData=Creating coordination service node dat Case.progressMessage.deletingCaseDatabase=Deleting case database... Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node... Case.progressMessage.deletingCaseDirectory=Deleting case directory... +Case.progressMessage.deletingDataSource=Removing the data source from the case... Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node... Case.progressMessage.deletingTextIndex=Deleting text index... Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case... @@ -74,7 +80,6 @@ Case.progressMessage.openingApplicationServiceResources=Opening application serv Case.progressMessage.openingCaseDatabase=Opening case database... Case.progressMessage.openingCaseLevelServices=Opening case-level services... Case.progressMessage.preparing=Preparing... -Case.progressMessage.preparingToOpenCaseResources=Preparing to open case resources.
This may take time if another user is upgrading the case. Case.progressMessage.removingCaseFromRecentCases=Removing case from Recent Cases menu... Case.progressMessage.savingCaseMetadata=Saving case metadata to file... Case.progressMessage.settingUpNetworkCommunications=Setting up network communications... @@ -118,6 +123,13 @@ CTL_CaseDetailsAction=Case Details CTL_CaseDeleteAction=Delete Case CTL_CaseOpenAction=Open Case CTL_UnpackagePortableCaseAction=Unpack and Open Portable Case +DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the removal. +# {0} - exception message +DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case:\n{0}\nPlease see the application log for details. +# {0} - exception message +DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while removing the data source:\n{0}\nPlease see the application log for details. +DeleteDataSourceAction.ingestRunningWarningDialog.message=Data sources cannot be removed from a case when ingest is running. +DeleteDataSourceAction.name.text=Remove Data Source EditOptionalCasePropertiesPanel.cancelButton.text=Cancel EditOptionalCasePropertiesPanel.saveButton.text=Save GeneralFilter.encaseImageDesc.text=Encase Images (*.e01) @@ -342,6 +354,7 @@ UnpackagePortableCaseProgressDialog.title.text=Unpackage Portable Case Progress UnpackageWorker.doInBackground.canceled=Unpackaging canceled by user UnpackageWorker.doInBackground.errorCompressingCase=Error unpackaging case UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executable +UnpackageWorker.doInBackground.previousSeenCase=Case with name {0} has been previously opened do you want to open it again? UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties index 2b05d5cac8..27d4a47fd7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties @@ -1,198 +1,457 @@ +AddImageWizardIngestConfigPanel.name.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u69cb\u6210 +AddImageWizardSelectDspVisual.multiUserWarning.text=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30d7\u30ed\u30bb\u30c3\u30b5\u30fc\u306f\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30e2\u30fc\u30c9\u3067\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.closeException.couldNotCloseCase=\u6b21\u306e\u30b1\u30fc\u30b9\u3092\u9589\u3058\u3066\u3044\u308b\u9593\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +Case.creationException.couldNotAcquireDirLock=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u306e\u30ed\u30c3\u30af\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +Case.creationException.couldNotAcquireResourcesLock=\u30b1\u30fc\u30b9\u30ea\u30bd\u30fc\u30b9\u306e\u30ed\u30c3\u30af\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +Case.deleteCaseConfirmationDialog.message=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u3092\u9589\u3058\u3066\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +Case.deleteCaseConfirmationDialog.title=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u3092\u524a\u9664\u3057\u307e\u3059\u304b? +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.deleteCaseFailureMessageBox.message=\u6b21\u306e\u30b1\u30fc\u30b9\u306e\u524a\u9664\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +Case.deleteCaseFailureMessageBox.title=\u30b1\u30fc\u30b9\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +Case.exceptionMessage.cancelledByUser=\u30e6\u30fc\u30b6\u30fc\u306b\u3088\u3063\u3066\u53d6\u308a\u6d88\u3055\u308c\u307e\u3057\u305f\u3002 +Case.exceptionMessage.cannotDeleteCurrentCase=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3002\u6700\u521d\u306b\u9589\u3058\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +Case.exceptionMessage.cannotGetLockToDeleteCase=\u5225\u306e\u30e6\u30fc\u30b6\u30fc\u307e\u305f\u306f\u30db\u30b9\u30c8\u304c\u958b\u3044\u3066\u3044\u308b\u305f\u3081\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3002 +Case.exceptionMessage.cannotLocateMainWindow=\u30e1\u30a4\u30f3\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30a6\u30a3\u30f3\u30c9\u30a6\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u8a2d\u5b9a\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093([\u30c4\u30fc\u30eb]\u3001[\u30aa\u30d7\u30b7\u30e7\u30f3]\u3001[\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc] \u30bf\u30d6\u3092\u53c2\u7167)\u3002\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u3092\u958b\u3051\u307e\u305b\u3093\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.couldNotCreatCollaborationMonitor=\u9023\u643a\u30e2\u30cb\u30bf\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.couldNotCreateCaseDatabase=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.couldNotCreateCaseNodeData=\u5ea7\u6a19\u30b5\u30fc\u30d3\u30b9\u30ce\u30fc\u30c9\u30c7\u30fc\u30bf\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.couldNotGetDbServerConnectionInfo=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b5\u30fc\u30d0\u30fc\u306e\u63a5\u7d9a\u60c5\u5831\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.couldNotOpenCaseDatabase=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.couldNotOpenRemoteEventChannel=\u30ea\u30e2\u30fc\u30c8\u30a4\u30d9\u30f3\u30c8\u30c1\u30e3\u30cd\u30eb\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.couldNotSaveCaseMetadata=\u30b1\u30fc\u30b9\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d\u3092\u30b1\u30fc\u30b9\u30e1\u30bf\u30c7\u30fc\u30bf\u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.couldNotUpdateCaseNodeData=\u5ea7\u6a19\u30b5\u30fc\u30d3\u30b9\u30ce\u30fc\u30c9\u30c7\u30fc\u30bf\u3092\u66f4\u65b0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u30b1\u30fc\u30b9\u8868\u793a\u540d +Case.exceptionMessage.deletionInterrupted=\u30b1\u30fc\u30b9 {0} \u306e\u524a\u9664\u304c\u53d6\u308a\u6d88\u3055\u308c\u307e\u3057\u305f\u3002 +Case.exceptionMessage.emptyCaseDir=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u30d1\u30b9\u3092\u6307\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +Case.exceptionMessage.emptyCaseName=\u30b1\u30fc\u30b9\u540d\u3092\u6307\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +Case.exceptionMessage.errorsDeletingCase=\u30b1\u30fc\u30b9\u306e\u524a\u9664\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u8a73\u7d30\u306f\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30ed\u30b0\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.execExceptionWrapperMessage={0} +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.failedToConnectToCoordSvc=\u5ea7\u6a19\u30b5\u30fc\u30d3\u30b9\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}. +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.failedToFetchCoordSvcNodeData=\u5ea7\u6a19\u30b5\u30fc\u30d3\u30b9\u30ce\u30fc\u30c9\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.failedToLockCaseForDeletion=\u524a\u9664\u5bfe\u8c61\u306e\u30b1\u30fc\u30b9\u3092\u6392\u4ed6\u30ed\u30c3\u30af\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.failedToReadMetadata=\u30b1\u30fc\u30b9\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f:\n{0}\u3002 +Case.exceptionMessage.metadataUpdateError=\u30b1\u30fc\u30b9\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u66f4\u65b0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.exceptionMessage.unsupportedSchemaVersionMessage=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b9\u30ad\u30fc\u30de\u30d0\u30fc\u30b8\u30e7\u30f3\u3067\u3059:\n{0}\u3002 +Case.open.exception.multiUserCaseNotEnabled=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u304c\u6709\u52b9\u3067\u306a\u3044\u5834\u5408\u306f\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u3092\u958b\u3051\u307e\u305b\u3093\u3002[\u30c4\u30fc\u30eb]\u3001[\u30aa\u30d7\u30b7\u30e7\u30f3]\u3001[\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc] \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +Case.progressIndicatorCancelButton.label=\u53d6\u308a\u6d88\u3057 +Case.progressIndicatorTitle.closingCase=\u30b1\u30fc\u30b9\u3092\u9589\u3058\u3066\u3044\u307e\u3059 +Case.progressIndicatorTitle.creatingCase=\u30b1\u30fc\u30b9\u3092\u4f5c\u6210\u4e2d\u3067\u3059 +Case.progressIndicatorTitle.deletingCase=\u30b1\u30fc\u30b9\u3092\u524a\u9664\u4e2d\u3067\u3059 +Case.progressIndicatorTitle.openingCase=\u30b1\u30fc\u30b9\u3092\u958b\u3044\u3066\u3044\u307e\u3059 +Case.progressMessage.cancelling=\u53d6\u308a\u6d88\u3057\u4e2d\u3067\u3059... +Case.progressMessage.clearingTempDirectory=\u30b1\u30fc\u30b9\u306e\u4e00\u6642\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u6d88\u53bb\u4e2d\u3067\u3059... +Case.progressMessage.closingApplicationServiceResources=\u30b1\u30fc\u30b9\u56fa\u6709\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30b5\u30fc\u30d3\u30b9\u30ea\u30bd\u30fc\u30b9\u3092\u9589\u3058\u3066\u3044\u307e\u3059... +Case.progressMessage.closingCaseDatabase=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u9589\u3058\u3066\u3044\u307e\u3059... +Case.progressMessage.closingCaseLevelServices=\u30b1\u30fc\u30b9\u30ec\u30d9\u30eb\u306e\u30b5\u30fc\u30d3\u30b9\u3092\u9589\u3058\u3066\u3044\u307e\u3059... +Case.progressMessage.connectingToCoordSvc=\u5ea7\u6a19\u30b5\u30fc\u30d3\u30b9\u306b\u63a5\u7d9a\u4e2d\u3067\u3059... +Case.progressMessage.creatingCaseDatabase=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f5c\u6210\u4e2d\u3067\u3059... +Case.progressMessage.creatingCaseDirectory=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u4e2d\u3067\u3059... +Case.progressMessage.creatingCaseNodeData=\u5ea7\u6a19\u30b5\u30fc\u30d3\u30b9\u30ce\u30fc\u30c9\u30c7\u30fc\u30bf\u306e\u4f5c\u6210\u4e2d\u3067\u3059... +Case.progressMessage.deletingCaseDatabase=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u524a\u9664\u4e2d\u3067\u3059... +Case.progressMessage.deletingCaseDirCoordSvcNode=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u306e\u5ea7\u6a19\u30b5\u30fc\u30d3\u30b9\u30ce\u30fc\u30c9\u30c7\u30fc\u30bf\u3092\u524a\u9664\u4e2d\u3067\u3059... +Case.progressMessage.deletingCaseDirectory=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u524a\u9664\u4e2d\u3067\u3059... +Case.progressMessage.deletingResourcesCoordSvcNode=\u30b1\u30fc\u30b9\u30ea\u30bd\u30fc\u30b9\u306e\u5ea7\u6a19\u30b5\u30fc\u30d3\u30b9\u30ce\u30fc\u30c9\u30c7\u30fc\u30bf\u3092\u524a\u9664\u4e2d\u3067\u3059... +Case.progressMessage.deletingTextIndex=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u3092\u524a\u9664\u4e2d\u3067\u3059... +Case.progressMessage.fetchingCoordSvcNodeData=\u30b1\u30fc\u30b9\u306e\u5ea7\u6a19\u30b5\u30fc\u30d3\u30b9\u30ce\u30fc\u30c9\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\u4e2d\u3067\u3059... +Case.progressMessage.openingApplicationServiceResources=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30b5\u30fc\u30d3\u30b9\u306e\u30b1\u30fc\u30b9\u30ea\u30bd\u30fc\u30b9\u3092\u958b\u3044\u3066\u3044\u307e\u3059... +Case.progressMessage.openingCaseDatabase=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u3044\u3066\u3044\u307e\u3059... +Case.progressMessage.openingCaseLevelServices=\u30b1\u30fc\u30b9\u30ec\u30d9\u30eb\u306e\u30b5\u30fc\u30d3\u30b9\u3092\u958b\u3044\u3066\u3044\u307e\u3059... +Case.progressMessage.preparing=\u6e96\u5099\u4e2d\u3067\u3059... +Case.progressMessage.preparingToOpenCaseResources=\u30b1\u30fc\u30b9\u30ea\u30bd\u30fc\u30b9\u3092\u958b\u304f\u6e96\u5099\u3092\u3057\u3066\u3044\u307e\u3059\u3002
\u5225\u306e\u30e6\u30fc\u30b6\u30fc\u304c\u30b1\u30fc\u30b9\u3092\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u3057\u3066\u3044\u308b\u5834\u5408\u306f\u6642\u9593\u304c\u304b\u304b\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 +Case.progressMessage.removingCaseFromRecentCases=[\u6700\u8fd1\u306e\u30b1\u30fc\u30b9] \u30e1\u30cb\u30e5\u30fc\u304b\u3089\u30b1\u30fc\u30b9\u3092\u524a\u9664\u4e2d\u3067\u3059... +Case.progressMessage.savingCaseMetadata=\u30b1\u30fc\u30b9\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u5b58\u4e2d\u3067\u3059... +Case.progressMessage.settingUpNetworkCommunications=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u901a\u4fe1\u3092\u8a2d\u5b9a\u4e2d\u3067\u3059... +Case.progressMessage.shuttingDownNetworkCommunications=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u901a\u4fe1\u3092\u30b7\u30e3\u30c3\u30c8\u30c0\u30a6\u30f3\u4e2d\u3067\u3059... +Case.progressMessage.switchingLogDirectory=\u30ed\u30b0\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u5207\u308a\u63db\u3048\u4e2d\u3067\u3059... +Case.progressMessage.updatingCaseNodeData=\u5ea7\u6a19\u30b5\u30fc\u30d3\u30b9\u30ce\u30fc\u30c9\u30c7\u30fc\u30bf\u306e\u66f4\u65b0\u4e2d\u3067\u3059... +# {0} - serviceName +Case.serviceCloseResourcesProgressIndicator.title={0} \u30b1\u30fc\u30b9\u30ea\u30bd\u30fc\u30b9\u3092\u9589\u3058\u3066\u3044\u307e\u3059 +# {0} - \u30b5\u30fc\u30d3\u30b9\u540d +Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage={0} \u306b\u3088\u3063\u3066\u30b1\u30fc\u30b9\u30ea\u30bd\u30fc\u30b9\u3092\u958b\u304f\u64cd\u4f5c\u304c\u53d6\u308a\u6d88\u3055\u308c\u307e\u3057\u305f... +# {0} - \u30b5\u30fc\u30d3\u30b9\u540d +Case.serviceOpenCaseResourcesProgressIndicator.title={0} \u30b1\u30fc\u30b9\u30ea\u30bd\u30fc\u30b9\u3092\u958b\u3044\u3066\u3044\u307e\u3059 +# {0} - \u30b5\u30fc\u30d3\u30b9\u540d +Case.servicesException.notificationTitle={0} \u30a8\u30e9\u30fc +# {0} - \u30b5\u30fc\u30d3\u30b9\u540d +# {1} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Case.servicesException.serviceResourcesCloseError={0} \u30b5\u30fc\u30d3\u30b9\u306e\u6b21\u306e\u30b1\u30fc\u30b9\u30ea\u30bd\u30fc\u30b9\u3092\u9589\u3058\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {1} +Case_caseType_multiUser=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9 +Case_caseType_singleUser=\u30b7\u30f3\u30b0\u30eb\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9 +CaseDetailsPanel.casePanel.border.title=\u30b1\u30fc\u30b9 +CaseDetailsPanel.examinerLabel.text=\u540d\u524d: +CaseDetailsPanel.examinerPanel.border.title=\u8abf\u67fb\u54e1 +CaseDetailsPanel.lbCaseUUIDLabel.text=\u30b1\u30fc\u30b9UUID: +CaseDetailsPanel.lbExaminerEmailLabel.text=\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9: +CaseDetailsPanel.lbExaminerPhoneLabel.text=\u96fb\u8a71\u756a\u53f7: +CaseDetailsPanel.lbNotesLabel.text=\u5099\u8003: +CaseDetailsPanel.lbOrganizationNameLabel.text=\u540d\u524d: +CaseDetailsPanel.lbPointOfContactEmailLabel.text=\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9: +CaseDetailsPanel.lbPointOfContactNameLabel.text=\u9023\u7d61\u5148: +CaseDetailsPanel.lbPointOfContactPhoneLabel.text=\u96fb\u8a71\u756a\u53f7: +CaseDetailsPanel.pnOrganization.border.title=\u7d44\u7e54 +CaseInformationPanel.caseDetails.header=\u8a73\u7d30 +CaseInformationPanel.editDetailsButton.text=\u8a73\u7d30\u3092\u7de8\u96c6 +CaseInformationPanel.editDetailsDialog.title=\u30b1\u30fc\u30b9\u8a73\u7d30\u3092\u7de8\u96c6 +CaseOpenAction.msgDlg.cantOpenCase.title=\u30b1\u30fc\u30b9\u3092\u958b\u3044\u3066\u3044\u308b\u9593\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +CTL_AddImage=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0 CTL_AddImageButton=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0 CTL_CaseCloseAct=\u30b1\u30fc\u30b9\u3092\u9589\u3058\u308b CTL_CaseNewAction=\u65b0\u898f\u30b1\u30fc\u30b9 -CTL_CasePropertiesAction=\u30b1\u30fc\u30b9\u30d7\u30ed\u30d1\u30c6\u30a3 +CTL_CaseDetailsAction=\u30b1\u30fc\u30b9\u8a73\u7d30 CTL_CaseDeleteAction=\u30b1\u30fc\u30b9\u3092\u524a\u9664 CTL_CaseOpenAction=\u30b1\u30fc\u30b9\u3092\u958b\u304f -Menu/Case/OpenRecentCase=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f +CTL_UnpackagePortableCaseAction=\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9\u3092\u958b\u5c01 +EditOptionalCasePropertiesPanel.cancelButton.text=\u53d6\u308a\u6d88\u3057 +EditOptionalCasePropertiesPanel.saveButton.text=\u4fdd\u5b58 +GeneralFilter.encaseImageDesc.text=\u30a4\u30e1\u30fc\u30b8(*.e01)\u3092\u5305\u542b +GeneralFilter.executableDesc.text=\u5b9f\u884c\u53ef\u80fd\u30d5\u30a1\u30a4\u30eb(*.exe) +GeneralFilter.graphicImageDesc.text=\u30a4\u30e1\u30fc\u30b8(*.png, *.jpg, *.jpeg, *.gif, *.bmp) +GeneralFilter.rawImageDesc.text=\u30ed\u30fc\u30a4\u30e1\u30fc\u30b8(*.img, *.dd, *.001, *.aa, *.raw, *.bin) +GeneralFilter.virtualMachineImageDesc.text=\u4eee\u60f3\u30de\u30b7\u30f3(*.vmdk, *.vhd) +ImageFilePanel.000.confirmationMessage=\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u306e\u62e1\u5f35\u5b50\u306f .001 \u3067\u3059\u304c\u3001\u30ed\u30fc\u30a4\u30e1\u30fc\u30b8\u306e\u30b7\u30fc\u30b1\u30f3\u30b9\u306b .000\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u307e\u3059\u3002\n\u9078\u629e\u3057\u305f .001\u30d5\u30a1\u30a4\u30eb\u306e\u4ee3\u308f\u308a\u306b\u3001.000\u30d5\u30a1\u30a4\u30eb\u3092\u8d77\u70b9\u3068\u3057\u3066\u4f7f\u7528\u3059\u3079\u304d\u3067\u3059\u304b?\n +ImageFilePanel.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc +ImageFilePanel.moduleErr.msg=ImageFilePanel\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\n +ImageFilePanel.validatePanel.dataSourceOnCDriveError=\u8b66\u544a: \u30de\u30eb\u30c1\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u30d1\u30b9\u306f"C:" \u30c9\u30e9\u30a4\u30d6\u306b\u3042\u308a\u307e\u3059\u3002 +ImageFilePanel.validatePanel.invalidMD5=\u7121\u52b9\u306aMD5\u30cf\u30c3\u30b7\u30e5 +ImageFilePanel.validatePanel.invalidSHA1=\u7121\u52b9\u306aSHA1\u30cf\u30c3\u30b7\u30e5 +ImageFilePanel.validatePanel.invalidSHA256=\u7121\u52b9\u306aSHA256\u30cf\u30c3\u30b7\u30e5 +IngestJobInfoPanel.IngestJobTableModel.EndTime.header=\u7d42\u4e86\u6642\u523b +IngestJobInfoPanel.IngestJobTableModel.IngestStatus.header=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b9\u30c6\u30fc\u30bf\u30b9 +IngestJobInfoPanel.IngestJobTableModel.StartTime.header=\u958b\u59cb\u6642\u523b +IngestJobInfoPanel.IngestModuleTableModel.ModuleName.header=\u30e2\u30b8\u30e5\u30fc\u30eb\u540d +IngestJobInfoPanel.IngestModuleTableModel.ModuleVersion.header=\u30e2\u30b8\u30e5\u30fc\u30eb\u30d0\u30fc\u30b8\u30e7\u30f3 +IngestJobInfoPanel.loadIngestJob.error.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +IngestJobInfoPanel.loadIngestJob.error.title=\u8aad\u307f\u8fbc\u307f\u5931\u6557 +LocalDiskPanel.errorMessage.noOpenCaseBody=LocalDiskPanel\u30ea\u30b9\u30ca\u30fc\u304c\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +LocalDiskPanel.errorMessage.noOpenCaseTitle=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093 +LocalDiskPanel.imageWriterError.directoryNotExist=\u30a8\u30e9\u30fc - \u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +LocalDiskPanel.imageWriterError.emptyPath=\u30a8\u30e9\u30fc - VHD\u306e\u30d1\u30b9\u3092\u5165\u529b +LocalDiskPanel.imageWriterError.fileExists=Error - VHD\u30d1\u30b9\u304c\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059 +LocalDiskPanel.imageWriterError.isDirectory=Error - VHD\u30d1\u30b9\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3067\u3059 +LocalDiskPanel.localDiskMessage.unspecified=\u672a\u6307\u5b9a +LocalDiskPanel.moduleErrorMessage.body=LocalDiskPanel\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +LocalDiskPanel.moduleErrorMessage.title=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc +LocalDiskSelectionDialog.columnName.diskName=\u30c7\u30a3\u30b9\u30af\u540d +LocalDiskSelectionDialog.columnName.diskSize=\u30c7\u30a3\u30b9\u30af\u30b5\u30a4\u30ba +LocalDiskSelectionDialog.errorMessage.disksNotDetected=\u30c7\u30a3\u30b9\u30af\u304c\u691c\u51fa\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u4e00\u90e8\u306e\u30b7\u30b9\u30c6\u30e0\u3067\u306f\u7ba1\u7406\u8005\u6a29\u9650(\u307e\u305f\u306f\u300c\u7ba1\u7406\u8005\u3068\u3057\u3066\u5b9f\u884c\u300d)\u304c\u5fc5\u8981\u3067\u3059\u3002 +LocalDiskSelectionDialog.errorMessage.drivesNotDetected=\u30ed\u30fc\u30ab\u30eb\u30c9\u30e9\u30a4\u30d6\u304c\u691c\u51fa\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u3053\u306eOS\u3067\u306f\u81ea\u52d5\u691c\u51fa\u304c\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u304b\u3001\u7ba1\u7406\u8005\u6a29\u9650\u304c\u5fc5\u8981\u3067\u3059 +LocalDiskSelectionDialog.errorMessage.someDisksNotDetected=\u4e00\u90e8\u306e\u30c7\u30a3\u30b9\u30af\u304c\u691c\u51fa\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u4e00\u90e8\u306e\u30b7\u30b9\u30c6\u30e0\u3067\u306f\u7ba1\u7406\u8005\u6a29\u9650(\u307e\u305f\u306f\u300c\u7ba1\u7406\u8005\u3068\u3057\u3066\u5b9f\u884c\u300d)\u304c\u5fc5\u8981\u3067\u3059\u3002 +LocalDiskSelectionDialog.moduleErrorMessage.body=LocalDiskPanel\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +LocalDiskSelectionDialog.moduleErrorMessage.title=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc +LocalDiskSelectionDialog.tableMessage.loading=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u3067\u3059... +LocalDiskSelectionDialog.tableMessage.noDrives=\u30a2\u30af\u30bb\u30b9\u53ef\u80fd\u306a\u30c9\u30e9\u30a4\u30d6\u306f\u3042\u308a\u307e\u305b\u3093 +LocalFilesDSProcessor.logicalEvidenceFilter.desc=\u8ad6\u7406\u8a3c\u62e0\u30d5\u30a1\u30a4\u30eb(L01) +LocalFilesPanel.pathValidation.dataSourceOnCDriveError=\u8b66\u544a: \u30de\u30eb\u30c1\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u30d1\u30b9\u306f"C:" \u30c9\u30e9\u30a4\u30d6\u306b\u3042\u308a\u307e\u3059\u3002 +LocalFilesPanel.pathValidation.getOpenCase=\u8b66\u544a: \u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306e\u53d6\u5f97\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +LogicalEvidenceFilePanel.moduleErr.msg=LogicalEvidenceFilePanel\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +LogicalEvidenceFilePanel.moduleErr.name=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc +LogicalEvidenceFilePanel.pathValidation.dataSourceOnCDriveError=\u8b66\u544a: \u30de\u30eb\u30c1\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u30d1\u30b9\u306f"C:" \u30c9\u30e9\u30a4\u30d6\u306b\u3042\u308a\u307e\u3059\u3002 +LogicalEvidenceFilePanel.pathValidation.getOpenCase.Error=\u8b66\u544a: \u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306e\u53d6\u5f97\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +LogicalEvidenceFilePanel.validatePanel.nonL01Error.text=\u3053\u3053\u3067\u306f .l01\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u306e\u307f\u304c\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002 +LogicalFilesDspPanel.subTypeComboBox.l01FileOption.text=\u8ad6\u7406\u8a3c\u62e0\u30d5\u30a1\u30a4\u30eb(L01) +LogicalFilesDspPanel.subTypeComboBox.localFilesOption.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u304a\u3088\u3073\u30d5\u30a9\u30eb\u30c0\u30fc +Menu/Case/OpenRecentCase=\u6700\u8fd1\u306e\u30b1\u30fc\u30b9\u3092\u958b\u304f CTL_CaseDeleteAction=\u30b1\u30fc\u30b9\u3092\u524a\u9664 OpenIDE-Module-Name=\u30b1\u30fc\u30b9 -NewCaseVisualPanel1.caseNameLabel.text_1=\u30b1\u30fc\u30b9\u540d\uff1a -NewCaseVisualPanel1.caseDirLabel.text=\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff1a -NewCaseVisualPanel1.caseDirBrowseButton.text=\u95b2\u89a7 -NewCaseVisualPanel1.jLabel2.text_1=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u306f\u6b21\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3059\uff1a +NewCaseVisualPanel1.caseNameLabel.text_1=\u30b1\u30fc\u30b9\u540d: +NewCaseVisualPanel1.caseDirLabel.text=\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc: +NewCaseVisualPanel1.caseDirBrowseButton.text=\u53c2\u7167 +NewCaseVisualPanel1.caseNameTextField.text_1= +NewCaseVisualPanel1.jLabel2.text_1=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u306f\u6b21\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u306b\u683c\u7d0d\u3055\u308c\u307e\u3059: +NewCaseVisualPanel1.caseParentDirTextField.text= +NewCaseVisualPanel1.caseDirTextField.text_1= +CueBannerPanel.autopsyLogo.text= CueBannerPanel.closeButton.text=\u9589\u3058\u308b -OpenRecentCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb -OpenRecentCasePanel.jLabel1.text=\u6700\u8fd1\u958b\u3044\u305f\u30d5\u30a1\u30a4\u30eb +# {0} - caseErrorMessage +OpenMultiUserCaseAction.caseOpeningErrorErrorMsg=\u30b1\u30fc\u30b9\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f: {0} +OpenMultiUserCaseAction.menuItemText=\u30b1\u30fc\u30b9\u3092\u958b\u304f +OpenMultiUserCaseDialog.title=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u3092\u958b\u304f +OpenRecentCasePanel.cancelButton.text=\u53d6\u308a\u6d88\u3057 +OpenRecentCasePanel.jLabel1.text=\u6700\u8fd1\u306e\u30b1\u30fc\u30b9 AddImageErrorsDialog.title=\u30a4\u30e1\u30fc\u30b8\u30ed\u30b0\u3092\u8ffd\u52a0 -AddImageErrorsDialog.copyButton.toolTipText=\u30a8\u30e9\u30fc\u3092\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u306b\u30b3\u30d4\u30fc\u3057\u307e\u3059 +AddImageErrorsDialog.copyButton.toolTipText=\u30a8\u30e9\u30fc\u3092\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u306b\u30b3\u30d4\u30fc AddImageErrorsDialog.copyButton.text=\u30b3\u30d4\u30fc -AddImageErrorsDialog.closeButton.toolTipText=\u3053\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u3092\u9589\u3058\u307e\u3059 +AddImageErrorsDialog.closeButton.toolTipText=\u3053\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u3092\u9589\u3058\u308b AddImageErrorsDialog.closeButton.text=\u9589\u3058\u308b OpenRecentCasePanel.openButton.text=\u958b\u304f -ImageFilePanel.browseButton.text=\u95b2\u89a7 +ImageFilePanel.pathLabel.text=\u30d1\u30b9: +ImageFilePanel.browseButton.text=\u53c2\u7167 +ImageFilePanel.pathTextField.text= MissingImageDialog.selectButton.text=\u30a4\u30e1\u30fc\u30b8\u3092\u9078\u629e -MissingImageDialog.titleLabel.text=\u6b20\u843d\u3057\u305f\u30a4\u30e1\u30fc\u30b8\u306e\u691c\u7d22 -MissingImageDialog.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb -AddImageWizardAddingProgressVisual.statusLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u4e2d\u3067\u3059\u3002 -AddImageWizardAddingProgressVisual.progressLabel.text=\uff1c\u30d7\u30ed\u30b0\u30ec\u30b9\uff1e +MissingImageDialog.titleLabel.text=\u898b\u3064\u304b\u3089\u306a\u3044\u30a4\u30e1\u30fc\u30b8\u3092\u691c\u7d22 +MissingImageDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +AddImageWizardAddingProgressVisual.statusLabel.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u8ffd\u52a0\u3057\u307e\u3057\u305f\u3002\u30d5\u30a1\u30a4\u30eb\u3092\u5206\u6790\u4e2d\u3067\u3059\u3002 +AddImageWizardAddingProgressVisual.progressLabel.text=<\u9032\u6357\u72b6\u6cc1> AddImageWizardAddingProgressVisual.viewLogButton.text=\u30ed\u30b0\u3092\u8868\u793a -AddImageWizardAddingProgressVisual.subTitle1Label.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u60c5\u5831\u3092\u8ffd\u52a0\u4e2d\u3067\u3059\u3002\u3053\u3061\u3089\u304c\u5b8c\u4e86\u6b21\u7b2c\u3001\u30d5\u30a1\u30a4\u30eb\u89e3\u6790\u304c\u59cb\u307e\u308a\u307e\u3059\u3002 -ImageFilePanel.descLabel.text=\uff08\u3088\u308a\u901f\u3044\u7d50\u679c\u3001\u3057\u304b\u3057\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u306f\u691c\u7d22\u3055\u308c\u307e\u305b\u3093\uff09 -LocalDiskPanel.timeZoneLabel.text=\u30a4\u30f3\u30d7\u30c3\u30c8\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u9078\u629e\u3057\u3066\u4e0b\u3055\u3044\uff1a -LocalDiskPanel.descLabel.text=\uff08\u3088\u308a\u901f\u3044\u7d50\u679c\u3001\u3057\u304b\u3057\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u306f\u691c\u7d22\u3055\u308c\u307e\u305b\u3093\uff09 -MissingImageDialog.browseButton.text=\u95b2\u89a7 +AddImageWizardAddingProgressVisual.subTitle1Label.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u51e6\u7406\u3068\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3078\u306e\u8ffd\u52a0\u3092\u884c\u3063\u3066\u3044\u307e\u3059\u3002\u7d42\u4e86\u3059\u308b\u3068\u30d5\u30a1\u30a4\u30eb\u5206\u6790\u304c\u958b\u59cb\u3057\u307e\u3059\u3002 +ImageFilePanel.timeZoneLabel.text=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3: +ImageFilePanel.noFatOrphansCheckbox.text=\u5b64\u7acb\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u7121\u8996 +ImageFilePanel.noFatOrphansCheckbox.toolTipText= +ImageFilePanel.descLabel.text=(\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u691c\u7d22\u3055\u308c\u306a\u304f\u3066\u3082\u3001\u7d50\u679c\u3092\u3088\u308a\u901f\u304f\u53d6\u5f97\u3057\u307e\u3059) +LocalDiskPanel.timeZoneLabel.text=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3: +LocalDiskPanel.noFatOrphansCheckbox.toolTipText= +LocalDiskPanel.noFatOrphansCheckbox.text=\u5b64\u7acb\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u7121\u8996 +LocalDiskPanel.descLabel.text=(\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u691c\u7d22\u3055\u308c\u306a\u304f\u3066\u3082\u3001\u7d50\u679c\u3092\u3088\u308a\u901f\u304f\u53d6\u5f97\u3057\u307e\u3059) +MissingImageDialog.browseButton.text=\u53c2\u7167 +MissingImageDialog.pathNameTextField.text= AddImageWizardAddingProgressVisual.progressTextArea.border.title=\u30b9\u30c6\u30fc\u30bf\u30b9 AddImageAction.wizard.title=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0 -AddImageAction.ingestConfig.ongoingIngest.msg=\u4ed6\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u51e6\u7406\u4e2d\u3067\u3059\u3002\u4eca\u65b0\u898f\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0\u3059\u308b\u3068\u5b9f\u884c\u4e2d\u306e\u51e6\u7406\u304c\u9045\u304f\u306a\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
\u3053\u306e\u307e\u307e\u5b9f\u884c\u3057\u3001\u65b0\u898f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0\u3057\u307e\u3059\u304b\uff1f -AddImageAction.ingestConfig.ongoingIngest.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u4e2d -AddImageTask.run.progress.adding=\u8ffd\u52a0\u4e2d\uff1a{0} +AddImageAction.ingestConfig.ongoingIngest.msg=\u5225\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3067\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u65b0\u898f\u30bd\u30fc\u30b9\u3092\u4eca\u8ffd\u52a0\u3059\u308b\u3068\u3001\u73fe\u5728\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u51e6\u7406\u304c\u9045\u304f\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002
\u7d9a\u884c\u3057\u3066\u65b0\u898f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u4eca\u3059\u3050\u8ffd\u52a0\u3057\u307e\u3059\u304b? +AddImageAction.ingestConfig.ongoingIngest.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059 +AddImageTask.run.progress.adding=\u6b21\u3092\u8ffd\u52a0\u4e2d\u3067\u3059: {0} AddImageTask.interrupt.exception.msg=\u30a4\u30e1\u30fc\u30b8\u8ffd\u52a0\u30d7\u30ed\u30bb\u30b9\u306e\u505c\u6b62\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -AddImageWizardAddingProgressPanel.isValid.focusNext=\u6b21 > -AddImageWizardAddingProgressPanel.stateStarted.progressBarText=*\u5927\u304d\u3044\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u5834\u5408\u3001\u3053\u306e\u30d7\u30ed\u30bb\u30b9\u306f\u6642\u9593\u304c\u304b\u304b\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -AddImageWizardAddingProgressVisual.addingDsComplete.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0 - \u5b8c\u4e86 +AddImageWizardAddingProgressPanel.isValid.focusNext=\u6b21\u3078 > +AddImageWizardAddingProgressPanel.stateStarted.progressBarText=*\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u5927\u304d\u3044\u5834\u5408\u3001\u3053\u306e\u30d7\u30ed\u30bb\u30b9\u306f\u6642\u9593\u304c\u304b\u304b\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 +AddImageWizardAddingProgressVisual.addingDsComplete.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u8ffd\u52a0\u4e2d - \u5b8c\u4e86 AddImageWizardAddingProgressVisual.getName.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0 -AddImageWizardAddingProgressVisual.showErrors.critText=*\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u8ffd\u52a0\u306b\u5931\u6557\u3057\u307e\u3057\u305f\uff08\u81f4\u547d\u7684\u306a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff09\u3002\u4e0b\u8a18\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002 -AddImageWizardAddingProgressVisual.showErrors.nonCritText=*\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u8ffd\u52a0\u306b\u5931\u6557\u3057\u307e\u3057\u305f\uff08\u81f4\u547d\u7684\u3067\u306f\u306a\u3044\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff09\u3002\u4e0b\u8a18\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002 -AddImageWizardChooseDataSourcePanel.moveFocusNext=\u6b21 > -AddImageWizardChooseDataSourceVisual.getName.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u60c5\u5831\u3092\u5165\u529b -AddImageWizardIngestConfigPanel.dsProcDone.noErrs.text=*\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002 +AddImageWizardAddingProgressVisual.showErrors.critText=*\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u8ffd\u52a0\u306b\u5931\u6557\u3057\u307e\u3057\u305f(\u91cd\u5927\u306a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f)\u3002\u6b21\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30ed\u30b0\u3092\u8868\u793a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +AddImageWizardAddingProgressVisual.showErrors.nonCritText=*\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f(\u91cd\u5927\u306a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u305b\u3093\u3067\u3057\u305f)\u3002\u6b21\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30ed\u30b0\u3092\u8868\u793a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +AddImageWizardChooseDataSourcePanel.moveFocusNext=\u6b21\u3078 > +AddImageWizardChooseDataSourceVisual.getName.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u9078\u629e +AddImageWizardIngestConfigPanel.dsProcDone.noErrs.text=*\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0\u3057\u307e\u3057\u305f\u3002 AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -AddImageWizardIngestConfigVisual.getName.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u8a2d\u5b9a -AddImageWizardIterator.stepXofN=\u30b9\u30c6\u30c3\u30d7{0}\uff0f{1} -AddLocalFilesTask.localFileAdd.progress.text=\u8ffd\u52a0\u4e2d\uff1a{0}/{1} -Case.getCurCase.exception.noneOpen=\u4f5c\u696d\u4e2d\u306e\u30b1\u30fc\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\uff1b\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093\uff01 -Case.open.msgDlg.updated.msg=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30b9\u30ad\u30fc\u30de\u3092\u66f4\u65b0\u3057\u307e\u3057\u305f\u3002\n\u6b21\u306e\u30d1\u30b9\u3092\u6301\u3064\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u30b3\u30d4\u30fc\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f\uff1a\n\ - {0} -Case.open.msgDlg.updated.title=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30b9\u30ad\u30fc\u30de\u3092\u66f4\u65b0 -Case.checkImgExist.confDlg.doesntExist.title=\u6b20\u843d\u3057\u3066\u3044\u308b\u30a4\u30e1\u30fc\u30b8 +AddImageWizardIngestConfigVisual.getName.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u69cb\u6210 +AddImageWizardIterator.stepXofN=\u624b\u9806 {0} / {1} +AddLocalFilesTask.localFileAdd.progress.text=\u6b21\u3092\u8ffd\u52a0\u4e2d\u3067\u3059: {0}/{1} +Case.getCurCase.exception.noneOpen=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002\u30b1\u30fc\u30b9\u304c\u958b\u304b\u308c\u3066\u3044\u307e\u305b\u3093\! +Case.open.msgDlg.updated.msg=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b9\u30ad\u30fc\u30de\u3092\u66f4\u65b0\u3057\u307e\u3057\u305f\u3002\n\u6b21\u306e\u30d1\u30b9\u3092\u6301\u3064\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u30b3\u30d4\u30fc\u3092\u4f5c\u6210\u3057\u307e\u3057\u305f:\n {0} +Case.open.msgDlg.updated.title=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b9\u30ad\u30fc\u30de\u306e\u66f4\u65b0 +Case.checkImgExist.confDlg.doesntExist.msg=\u3053\u306e\u30b1\u30fc\u30b9\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\n\ +\u30a4\u30e1\u30fc\u30b8\u306e1\u3064\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002\u4eca\u3059\u3050\u691c\u7d22\u3057\u307e\u3059\u304b?\n\ +\u4ee5\u524d\u3001\u30a4\u30e1\u30fc\u30b8\u306f\u6b21\u306b\u3042\u308a\u307e\u3057\u305f:\n\ +{0}\n\ +\u4eca\u5f8c\u3082\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u53c2\u7167\u3057\u3066\u30ec\u30dd\u30fc\u30c8\u3092\u751f\u6210\u3067\u304d\u307e\u3059\n\ +\u305f\u3060\u3057\u3001[\u3044\u3044\u3048] \u3092\u9078\u629e\u3059\u308b\u3068\u3001\u30d5\u30a1\u30a4\u30eb\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u8868\u793a\u3084\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d7\u30ed\u30bb\u30b9\u306e\u5b9f\u884c\u304c\u884c\u3048\u306a\u304f\u306a\u308a\u307e\u3059\u3002 +Case.checkImgExist.confDlg.doesntExist.title=\u30a4\u30e1\u30fc\u30b8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 Case.addImg.exception.msg=\u30b1\u30fc\u30b9\u306b\u30a4\u30e1\u30fc\u30b8\u3092\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -Case.updateCaseName.exception.msg=\u30b1\u30fc\u30b9\u540d\u3092\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -Case.updateExaminer.exception.msg=\u8abf\u67fb\u62c5\u5f53\u8005\u3092\u66f4\u65b0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -Case.updateCaseNum.exception.msg=\u30b1\u30fc\u30b9\u756a\u53f7\u3092\u66f4\u65b0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -Case.exception.errGetRootObj=\u30eb\u30fc\u30c8\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -Case.createCaseDir.exception.existNotDir=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u65e2\u306b\u5b58\u5728\u3057\u3001\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3067\u306f\u3042\u308a\u307e\u305b\u3093\uff1a{0} -Case.createCaseDir.exception.existCantRW=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u65e2\u306b\u5b58\u5728\u3057\u3001\u8aad\u307f\u53d6\u308a\uff0f\u66f8\u304d\u8fbc\u307f\u304c\u3067\u304d\u307e\u305b\u3093\uff1a{0} -Case.createCaseDir.exception.cantCreateCaseDir=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a {0} -Case.createCaseDir.exception.cantCreateModDir=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} -CaseDeleteAction.closeConfMsg.text=\u3053\u306e\u30b1\u30fc\u30b9\u3092\u672c\u5f53\u306b\u9589\u3058\u3001\u524a\u9664\u3057\u307e\u3059\u304b\uff1f\n\ - \u30b1\u30fc\u30b9\u540d\uff1a {0}\n\ - \u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\: {1} -CaseDeleteAction.closeConfMsg.title=\u8b66\u544a\uff1a\u4f5c\u696d\u4e2d\u306e\u30b1\u30fc\u30b9\u3092\u9589\u3058\u307e\u3059 -CaseDeleteAction.msgDlg.fileInUse.msg=\u5225\u306e\u30d7\u30ed\u30b0\u30e9\u30e0\u3067\u30d5\u30a9\u30eb\u30c0\u307e\u305f\u306f\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u3044\u3066\u3044\u308b\u306e\u3067\u3001\u524a\u9664\u3092\u5b8c\u4e86\u3067\u304d\u307e\u305b\u3093\u3002\n\n\ -\u30d5\u30a9\u30eb\u30c0\u307e\u305f\u306f\u30d5\u30a1\u30a4\u30eb\u3092\u9589\u3058\u3066\u304b\u3089\u518d\u5b9f\u884c\u3059\u308b\u304b\u3001\u624b\u52d5\u3067\u30b1\u30fc\u30b9\u3092\u524a\u9664\u3057\u3066\u4e0b\u3055\u3044\u3002 -CaseDeleteAction.msgDlg.fileInUse.title=\u30a8\u30e9\u30fc\uff1a\u30d5\u30a9\u30eb\u30c0\u304c\u4f7f\u7528\u4e2d -CaseDeleteAction.msgDlg.caseDelete.msg=\u30b1\u30fc\u30b9\u304c\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002 +Case.updateCaseName.exception.msg=\u30b1\u30fc\u30b9\u540d\u306e\u66f4\u65b0\u3092\u8a66\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Case.updateExaminer.exception.msg=\u8abf\u67fb\u54e1\u306e\u66f4\u65b0\u3092\u8a66\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Case.updateCaseNum.exception.msg=\u30b1\u30fc\u30b9\u756a\u53f7\u306e\u66f4\u65b0\u3092\u8a66\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Case.exception.errGetRootObj=\u30eb\u30fc\u30c8\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Case.createCaseDir.exception.existNotDir=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002\u3059\u3067\u306b\u5b58\u5728\u3057\u3001\u6b21\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3067\u306f\u3042\u308a\u307e\u305b\u3093: {0} +Case.createCaseDir.exception.existCantRW=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002\u3059\u3067\u306b\u5b58\u5728\u3057\u3001\u6b21\u306e\u8aad\u307f\u8fbc\u307f/\u66f8\u304d\u8fbc\u307f\u304c\u3067\u304d\u307e\u305b\u3093: {0} +Case.createCaseDir.exception.cantCreate=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u306a\u3044\u304b\u3001\u3059\u3067\u306b\u5b58\u5728\u3057\u3066\u3044\u307e\u3059: {0} +Case.createCaseDir.exception.cantCreateCaseDir=\u6b21\u306e\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +Case.createCaseDir.exception.cantCreateModDir=\u6b21\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u51fa\u529b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +Case.createCaseDir.exception.cantCreateReportsDir=\u6b21\u306e\u30ec\u30dd\u30fc\u30c8\u51fa\u529b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +Case.CollaborationSetup.FailNotify.ErrMsg=\u3053\u306e\u30b1\u30fc\u30b9\u3067\u9023\u643a\u3057\u3066\u3044\u308b\u53ef\u80fd\u6027\u306e\u3042\u308b\u305d\u306e\u4ed6\u306e\u30ce\u30fc\u30c9\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +Case.CollaborationSetup.FailNotify.Title=\u63a5\u7d9a\u5931\u6557 +Case.GetCaseTypeGivenPath.Failure=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093 +Case.metaDataFileCorrupt.exception.msg=\u30b1\u30fc\u30b9\u30e1\u30bf\u30c7\u30fc\u30bf\u30d5\u30a1\u30a4\u30eb(.aut)\u304c\u7834\u640d\u3057\u3066\u3044\u307e\u3059\u3002 +Case.deleteReports.deleteFromDiskException.log.msg=\u30c7\u30a3\u30b9\u30af\u304b\u3089\u30ec\u30dd\u30fc\u30c8\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3002 +Case.deleteReports.deleteFromDiskException.msg=\u30c7\u30a3\u30b9\u30af\u304b\u3089\u30ec\u30dd\u30fc\u30c8 {0} \u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3002\n{1} \u304b\u3089\u624b\u52d5\u3067\u524a\u9664\u3067\u304d\u307e\u3059 +CaseDeleteAction.closeConfMsg.text=\u3053\u306e\u30b1\u30fc\u30b9\u3092\u9589\u3058\u3066\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? \n\ + \u30b1\u30fc\u30b9\u540d: {0}\n\ + \u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc: {1} +CaseDeleteAction.closeConfMsg.title=\u8b66\u544a: \u73fe\u5728\u306e\u30b1\u30fc\u30b9\u3092\u9589\u3058\u3066\u3044\u307e\u3059 +CaseDeleteAction.msgDlg.fileInUse.msg=\u5225\u306e\u30d7\u30ed\u30b0\u30e9\u30e0\u304c\u3053\u306e\u30d5\u30a9\u30eb\u30c0\u30fc\u307e\u305f\u306f\u3053\u306e\u30d5\u30a9\u30eb\u30c0\u30fc\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u3044\u3066\u3044\u308b\u305f\u3081\u3001\u524a\u9664\u64cd\u4f5c\u3092\u5b8c\u5168\u306b\u5b8c\u4e86\u3067\u304d\u307e\u305b\u3093\u3002\n\n\ +\u30d5\u30a9\u30eb\u30c0\u30fc\u3068\u30d5\u30a1\u30a4\u30eb\u3092\u9589\u3058\u3066\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u306b\u306a\u308b\u304b\u3001\u624b\u52d5\u3067\u30b1\u30fc\u30b9\u3092\u524a\u9664\u3067\u304d\u307e\u3059\u3002 +CaseDeleteAction.msgDlg.fileInUse.title=\u30a8\u30e9\u30fc: \u30d5\u30a9\u30eb\u30c0\u30fc\u306f\u4f7f\u7528\u4e2d\u3067\u3059 +CaseDeleteAction.msgDlg.caseDelete.msg=\u30b1\u30fc\u30b9 {0} \u3092\u524a\u9664\u3057\u307e\u3057\u305f\u3002 CaseOpenAction.autFilter.title={0} \u30b1\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb ( {1}) -CaseOpenAction.msgDlg.cantOpenCase.title=\u30b1\u30fc\u30b9\u3092\u958b\u304f\u969b\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -CasePropertiesAction.window.title=\u30b1\u30fc\u30b9\u30d7\u30ed\u30d1\u30c6\u30a3 -CueBannerPanel.title.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f -GeneralFilter.rawImageDesc.text=\u30ed\u30fc\u30a4\u30e1\u30fc\u30b8(*.img, *.dd, *.001, *.aa, *.raw, *.bin) -GeneralFilter.encaseImageDesc.text=\u30a8\u30f3\u30b1\u30fc\u30b9\u30a4\u30e1\u30fc\u30b8(*.e01) -ImageDSProcessor.dsType.text=\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb -ImageDSProcessor.allDesc.text=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u5168\u30bf\u30a4\u30d7 -ImageFilePanel.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -ImageFilePanel.moduleErr.msg=ImageFilePanel\u306e\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 +CaseCreateAction.msgDlg.cantCreateCase.msg=\u30b1\u30fc\u30b9\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093 +IntervalErrorReport.NewIssues=\u65b0\u898f\u554f\u984c +IntervalErrorReport.TotalIssues=\u554f\u984c\u306e\u5408\u8a08 +IntervalErrorReport.ErrorText=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u30a8\u30e9\u30fc +CaseDetailsAction.window.title=\u30b1\u30fc\u30b9\u8a73\u7d30 +CueBannerPanel.title.text=\u6700\u8fd1\u306e\u30b1\u30fc\u30b9\u3092\u958b\u304f +ImageDSProcessor.dsType.text=\u30c7\u30a3\u30b9\u30af\u30a4\u30e1\u30fc\u30b8\u307e\u305f\u306fVM\u30d5\u30a1\u30a4\u30eb +ImageDSProcessor.allDesc.text=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u3059\u3079\u3066\u306e\u30bf\u30a4\u30d7 LocalDiskDSProcessor.dsType.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af -LocalDiskPanel.localDiskModel.loading.msg=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u30ed\u30fc\u30c9\u4e2d\u2026 -LocalDiskPanel.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -LocalDiskPanel.moduleErr.msg=LocalDiskPanel\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -LocalDiskPanel.errLabel.disksNotDetected.text=\u30c7\u30a3\u30b9\u30af\u304c\u691c\u51fa\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u4e00\u90e8\u306e\u30b7\u30b9\u30c6\u30e0\u3067\u306f\u7ba1\u7406\u8005\u6a29\u9650\u304c\u5fc5\u8981\u3067\u3059\uff08\u3082\u3057\u304f\u306f\u300c\u7ba1\u7406\u8005\u3068\u3057\u3066\u5b9f\u884c\u3059\u308b\u300d\u304c\u5fc5\u8981\uff09\u3002 -LocalDiskPanel.errLabel.disksNotDetected.toolTipText=\u30c7\u30a3\u30b9\u30af\u304c\u691c\u51fa\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u4e00\u90e8\u306e\u30b7\u30b9\u30c6\u30e0\u3067\u306f\u7ba1\u7406\u8005\u6a29\u9650\u304c\u5fc5\u8981\u3067\u3059\uff08\u3082\u3057\u304f\u306f\u300c\u7ba1\u7406\u8005\u3068\u3057\u3066\u5b9f\u884c\u3059\u308b\u300d\u304c\u5fc5\u8981\uff09\u3002 -LocalDiskPanel.errLabel.drivesNotDetected.text=\u30ed\u30fc\u30ab\u30eb\u30c9\u30e9\u30a4\u30d6\u304c\u691c\u51fa\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u81ea\u52d5\u691c\u51fa\u306f\u3053\u306eOS\u3067\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u304b\u3001\u7ba1\u7406\u8005\u6a29\u9650\u304c\u5fc5\u8981\u3067\u3059\u3002 -LocalDiskPanel.errLabel.drivesNotDetected.toolTipText=\u30ed\u30fc\u30ab\u30eb\u30c9\u30e9\u30a4\u30d6\u304c\u691c\u51fa\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u81ea\u52d5\u691c\u51fa\u306f\u3053\u306eOS\u3067\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u304b\u3001\u7ba1\u7406\u8005\u6a29\u9650\u304c\u5fc5\u8981\u3067\u3059\u3002 -LocalDiskPanel.errLabel.someDisksNotDetected.text=\u4e00\u90e8\u306e\u30c7\u30a3\u30b9\u30af\u304c\u691c\u51fa\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u4e00\u90e8\u306e\u30b7\u30b9\u30c6\u30e0\u3067\u306f\u7ba1\u7406\u8005\u6a29\u9650\u304c\u5fc5\u8981\u3067\u3059\uff08\u3082\u3057\u304f\u306f\u300c\u7ba1\u7406\u8005\u3068\u3057\u3066\u5b9f\u884c\u3059\u308b\u300d\uff09\u3002 -LocalDiskPanel.errLabel.someDisksNotDetected.toolTipText=\u4e00\u90e8\u306e\u30c7\u30a3\u30b9\u30af\u304c\u691c\u51fa\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u4e00\u90e8\u306e\u30b7\u30b9\u30c6\u30e0\u3067\u306f\u7ba1\u7406\u8005\u6a29\u9650\u304c\u5fc5\u8981\u3067\u3059\uff08\u3082\u3057\u304f\u306f\u300c\u7ba1\u7406\u8005\u3068\u3057\u3066\u5b9f\u884c\u3059\u308b\u300d\uff09\u3002 -LocalFilesDSProcessor.dsType=\u30ed\u30b8\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb -LocalFilesDSProcessor.toString.text=\u30ed\u30b8\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb +LocalFilesDSProcessor.dsType=\u8ad6\u7406\u30d5\u30a1\u30a4\u30eb +LocalFilesDSProcessor.toString.text=\u8ad6\u7406\u30d5\u30a1\u30a4\u30eb LocalFilesPanel.contentType.text=\u30ed\u30fc\u30ab\u30eb LocalFilesPanel.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -LocalFilesPanel.moduleErr.msg=LocalFilesPanel\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -MissingImageDialog.display.title=\u6b20\u843d\u30a4\u30e1\u30fc\u30b8\u3092\u691c\u7d22 -MissingImageDialog.confDlg.noFileSel.msg=\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30e1\u30fc\u30b8\u3092\u898b\u3064\u3051\u308b\n\u524d\u306b\u672c\u5f53\u306b\u7d42\u4e86\u3057\u307e\u3059\u304b\uff1f -MissingImageDialog.confDlg.noFileSel.title=\u6b20\u843d\u30a4\u30e1\u30fc\u30b8 +LocalFilesPanel.moduleErr.msg=LocalFilesPanel\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +MissingImageDialog.display.title=\u898b\u3064\u304b\u3089\u306a\u3044\u30a4\u30e1\u30fc\u30b8\u3092\u691c\u7d22 +MissingImageDialog.confDlg.noFileSel.msg=\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30e1\u30fc\u30b8\u304c\u898b\u3064\u304b\u3089\u306a\u3044\u307e\u307e\n\u7d42\u4e86\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3057\u3087\u3046\u304b? +MissingImageDialog.confDlg.noFileSel.title=\u30a4\u30e1\u30fc\u30b8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +MissingImageDialog.ErrorSettingImage=\u30a4\u30e1\u30fc\u30b8\u30d1\u30b9\u306e\u8a2d\u5b9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 NewCaseVisualPanel1.getName.text=\u30b1\u30fc\u30b9\u60c5\u5831 NewCaseVisualPanel1.caseDirBrowse.selectButton.text=\u9078\u629e -NewCaseVisualPanel2.getName.text=\u4ed8\u52a0\u60c5\u5831 +NewCaseVisualPanel1.badCredentials.text=\u4e0d\u6b63\u306a\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u8a2d\u5b9a(\u30c4\u30fc\u30eb]\u3001[\u30aa\u30d7\u30b7\u30e7\u30f3]\u3001[\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc] \u3092\u53c2\u7167)\u304b\u3001\u30b5\u30fc\u30d3\u30b9\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u307e\u3059\u3002 +NewCaseVisualPanel2.getName.text=\u4efb\u610f\u60c5\u5831 NewCaseWizardAction.newCase.windowTitle.text=\u65b0\u898f\u30b1\u30fc\u30b9\u60c5\u5831 NewCaseWizardAction.getName.text=\u65b0\u898f\u30b1\u30fc\u30b9\u30a6\u30a3\u30b6\u30fc\u30c9 -NewCaseWizardPanel1.validate.errMsg.invalidSymbols=\u30b1\u30fc\u30b9\u540d\u306b\u306f\u6b21\u306e\u8a18\u53f7\u3092\u542b\u3081\u307e\u305b\u3093\uff1a\\ / \: * ? " < > | -NewCaseWizardPanel1.validate.errMsg.dirExists=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea''{0}''\u306f\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 -NewCaseWizardPanel1.validate.confMsg.createDir.msg=\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea''{0}''\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002\n\n\ -\u3053\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3057\u307e\u3059\u304b\uff1f -NewCaseWizardPanel1.validate.confMsg.createDir.title=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210 -NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=\u30a8\u30e9\u30fc\uff1a\u30b1\u30fc\u30b9\u30da\u30a2\u30ec\u30f3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea{0}\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea{0}\u306e\u4f5c\u6210\u3092\u9632\u6b62\u3055\u308c\u307e\u3057\u305f -NewCaseWizardPanel1.validate.errMsg.cantCreateDir=\u30a8\u30e9\u30fc\uff1a\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -NewCaseWizardPanel1.validate.errMsg.invalidBaseDir.msg=\u30a8\u30e9\u30fc\uff1a\u5165\u529b\u3057\u305f\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306f\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\n\u6709\u52b9\u306a\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u5165\u529b\u3057\u3066\u4e0b\u3055\u3044\u3002 -NewCaseWizardPanel1.createDir.errMsg.cantCreateDir.msg=\u30a8\u30e9\u30fc\uff1a\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n\u6709\u52b9\u306a\u30b1\u30fc\u30b9\u540d\u304a\u3088\u3073\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u5165\u529b\u3057\u3066\u4e0b\u3055\u3044\u3002 -NewCaseWizardPanel2.validate.errCreateCase.msg=\u30b1\u30fc\u30b9\u306e\u4f5c\u6210\u30a8\u30e9\u30fc +NewCaseWizardAction.databaseProblem1.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u958b\u3051\u307e\u305b\u3093\u3002\u30b1\u30fc\u30b9\u306e\u4f5c\u6210\u3092\u53d6\u308a\u6d88\u3057\u4e2d\u3067\u3059\u3002 +NewCaseWizardAction.databaseProblem2.text=\u30a8\u30e9\u30fc +NewCaseWizardPanel1.validate.errMsg.invalidSymbols=\u30b1\u30fc\u30b9\u540d\u306b\u6b21\u306e\u8a18\u53f7\u3092\u542b\u3081\u3089\u308c\u307e\u305b\u3093: \\ / : * ? " < > | +NewCaseWizardPanel1.validate.errMsg.dirExists=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc ''{0}'' \u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u3066\u3044\u307e\u3059\u3002 +NewCaseWizardPanel1.validate.confMsg.createDir.msg=\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc "{0}" \u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002\n\n\ + \u305d\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3057\u307e\u3059\u304b? +NewCaseWizardPanel1.validate.confMsg.createDir.title=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210 +NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=\u30a8\u30e9\u30fc: \u30b1\u30fc\u30b9\u89aa\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc {0} \u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc {0} \u306e\u4f5c\u6210\u304c\u963b\u6b62\u3055\u308c\u307e\u3057\u305f +NewCaseWizardPanel1.validate.errMsg.cantCreateDir=\u30a8\u30e9\u30fc: \u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +NewCaseWizardPanel1.validate.errMsg.invalidBaseDir.msg=\u30a8\u30e9\u30fc: \u5165\u529b\u3057\u305f\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u306f\u7121\u52b9\u3067\u3059\u3002\n\u6709\u52b9\u306a\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +NewCaseWizardPanel1.createDir.errMsg.cantCreateDir.msg=\u30a8\u30e9\u30fc: \u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n\u6709\u52b9\u306a\u30b1\u30fc\u30b9\u540d\u3068\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +NewCaseWizardPanel2.validate.errCreateCase.msg=\u30b1\u30fc\u30b9\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f OpenRecentCasePanel.colName.caseName=\u30b1\u30fc\u30b9\u540d OpenRecentCasePanel.colName.path=\u30d1\u30b9 -RecentCases.exception.caseIdxOutOfRange.msg=\u6700\u8fd1\u306e\u30b1\u30fc\u30b9\u30a4\u30f3\u30c7\u30c3\u30af\u30b9{0}\u306f\u7bc4\u56f2\u5916\u3067\u3059\u3002 -RecentCases.getName.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u30af\u30ea\u30a2 -RecentItems.openRecentCase.msgDlg.text=\u30a8\u30e9\u30fc\uff1a\u30b1\u30fc\u30b9{0}\u306f\u3082\u3046\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +OptionalCasePropertiesPanel.bnNewOrganization.text=\u7d44\u7e54\u3092\u7ba1\u7406 +OptionalCasePropertiesPanel.caseDisplayNameLabel.text=\u540d\u524d: +OptionalCasePropertiesPanel.caseNumberLabel.text=\u756a\u53f7: +OptionalCasePropertiesPanel.casePanel.border.title=\u30b1\u30fc\u30b9 +OptionalCasePropertiesPanel.errorDialog.emptyCaseNameMessage=\u30b1\u30fc\u30b9\u540d\u304c\u5165\u529b\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +OptionalCasePropertiesPanel.errorDialog.invalidCaseNameMessage=\u30b1\u30fc\u30b9\u540d\u306b\u6b21\u306e\u8a18\u53f7\u3092\u542b\u3081\u3089\u308c\u307e\u305b\u3093: \\, /, :, *, ?, ", <, >, | +OptionalCasePropertiesPanel.errorDialog.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306e\u53d6\u5f97\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +OptionalCasePropertiesPanel.examinerLabel.text=\u540d\u524d: +OptionalCasePropertiesPanel.examinerPanel.border.title=\u8abf\u67fb\u54e1 +OptionalCasePropertiesPanel.lbExaminerEmailLabel.text=\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9: +OptionalCasePropertiesPanel.lbExaminerPhoneLabel.text=\u96fb\u8a71\u756a\u53f7: +OptionalCasePropertiesPanel.lbNotesLabel.text=\u5099\u8003: +OptionalCasePropertiesPanel.lbOrganizationNameLabel.text=\u6b21\u306b\u95a2\u3059\u308b\u7d44\u7e54\u5206\u6790\u3092\u5b9f\u884c\u4e2d\u3067\u3059: +OptionalCasePropertiesPanel.lbPointOfContactEmailLabel.text=\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9: +OptionalCasePropertiesPanel.lbPointOfContactNameLabel.text=\u9023\u7d61\u5148: +OptionalCasePropertiesPanel.lbPointOfContactPhoneLabel.text=\u96fb\u8a71\u756a\u53f7: +OptionalCasePropertiesPanel.orgainizationPanel.border.title=\u7d44\u7e54 +RecentCases.exception.caseIdxOutOfRange.msg=\u6700\u8fd1\u306e\u30b1\u30fc\u30b9\u7d22\u5f15 {0} \u306f\u7bc4\u56f2\u5916\u3067\u3059\u3002 +RecentCases.getName.text=\u6700\u8fd1\u306e\u30b1\u30fc\u30b9\u3092\u6d88\u53bb +# {0} - \u30b1\u30fc\u30b9\u540d +RecentItems.openRecentCase.msgDlg.text=\u30b1\u30fc\u30b9 {0} \u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +SelectDataSourceProcessorPanel.name.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u30bf\u30a4\u30d7\u3092\u9078\u629e\u3057\u3066\u8ffd\u52a0 StartupWindow.title.text=\u3088\u3046\u3053\u305d -UpdateRecentCases.menuItem.clearRecentCases.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u30af\u30ea\u30a2 -UpdateRecentCases.menuItem.empty=-\u7a7a\u767d- -ImageFilePanel.noFatOrphansCheckbox.text=FAT\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u306e\u30aa\u30fc\u30d5\u30a1\u30f3\u30d5\u30a1\u30a4\u30eb\u306f\u7121\u8996 -LocalDiskPanel.noFatOrphansCheckbox.text=FAT\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u306e\u30aa\u30fc\u30d5\u30a1\u30f3\u30d5\u30a1\u30a4\u30eb\u306f\u7121\u8996 -AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=\u30ad\u30e3\u30f3\u30bb\u30eb -LocalFilesPanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb -ImageFilePanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb -NewCaseVisualPanel1.caseTypeLabel.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\uff1a -Case.open.exception.multiUserCaseNotEnabled=\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u30b1\u30fc\u30b9\u304c\u6709\u52b9\u5316\u3055\u308c\u3066\u3044\u306a\u3044\u3068\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u30b1\u30fc\u30b9\u306f\u958b\u3051\u307e\u305b\u3093\u3002\u30c4\u30fc\u30eb\u3001\u30aa\u30d7\u30b7\u30e7\u30f3\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -Case.createCaseDir.exception.cantCreateReportsDir=\u30ec\u30dd\u30fc\u30c8\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} -Case.CollaborationSetup.FailNotify.ErrMsg=\u3053\u306e\u30b1\u30fc\u30b9\u3067\u4f7f\u308f\u308c\u3066\u3044\u308b\u304b\u3082\u3057\u308c\u306a\u3044\u30ce\u30fc\u30c9\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -Case.CollaborationSetup.FailNotify.Title=\u63a5\u7d9a\u306b\u5931\u6557 -Case.GetCaseTypeGivenPath.Failure=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -Case.metaDataFileCorrupt.exception.msg=\u30b1\u30fc\u30b9\u30e1\u30bf\u30c7\u30fc\u30bf\u30d5\u30a1\u30a4\u30eb(.aut)\u304c\u7834\u640d\u3057\u3066\u3044\u307e\u3059\u3002 -Case.deleteReports.deleteFromDiskException.log.msg=\u30c7\u30a3\u30b9\u30af\u304b\u3089\u30ec\u30dd\u30fc\u30c8\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -Case.deleteReports.deleteFromDiskException.msg=\u30c7\u30a3\u30b9\u30af\u304b\u3089{0}\u30ec\u30dd\u30fc\u30c8\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n{1}\u304b\u3089\u624b\u52d5\u3067\u524a\u9664\u3067\u304d\u307e\u3059\u3002 -CaseCreateAction.msgDlg.cantCreateCase.msg=\u30b1\u30fc\u30b9\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093 -IntervalErrorReport.NewIssues=\u65b0\u898f\u306e\u30a4\u30b7\u30e5\u30fc -IntervalErrorReport.TotalIssues=\u5168\u30a4\u30b7\u30e5\u30fc -IntervalErrorReport.ErrorText=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u63a5\u7d9a\u30a8\u30e9\u30fc -GeneralFilter.virtualMachineImageDesc.text=\u4eee\u60f3\u30de\u30b7\u30f3(*.vmdk, *.vhd) -LocalDiskPanel.localDiskModel.nodrives.msg=\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u30c9\u30e9\u30a4\u30d6\u304c\u3042\u308a\u307e\u305b\u3093 -MissingImageDialog.ErrorSettingImage=\u30a4\u30e1\u30fc\u30b8\u30d1\u30b9\u3092\u8a2d\u5b9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u518d\u5ea6\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -NewCaseVisualPanel1.badCredentials.text=\u4f7f\u3048\u306a\u3044\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u8a2d\u5b9a\uff08\u30c4\u30fc\u30eb\u3001\u30aa\u30d7\u30b7\u30e7\u30f3\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\uff09\u307e\u305f\u306f\u30b5\u30fc\u30d3\u30b9\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u307e\u3059\u3002 -NewCaseWizardAction.databaseProblem1.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u3051\u307e\u305b\u3093\u3002\u30b1\u30fc\u30b9\u4f5c\u6210\u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u3057\u3066\u3044\u307e\u3059\u3002 -NewCaseWizardAction.databaseProblem2.text=\u30a8\u30e9\u30fc -DataSourceOnCDriveError.text=\u8b66\u544a\uff1a\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3078\u306e\u30d1\u30b9\u306f"C\:"\u30c9\u30e9\u30a4\u30d6\u306b\u3042\u308a\u307e\u3059 -NewCaseVisualPanel1.CaseFolderOnCDriveError.text=\u8b66\u544a\uff1a\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u30b1\u30fc\u30b9\u30d5\u30a9\u30eb\u30c0\u3078\u306e\u30d1\u30b9\u306f"C\:"\u30c9\u30e9\u30a4\u30d6\u306b\u3042\u308a\u307e\u3059 -CollaborationMonitor.addingDataSourceStatus.msg={0}\u304c\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u8ffd\u52a0\u4e2d -CollaborationMonitor.analyzingDataSourceStatus.msg={0}\u304c{1}\u3092\u89e3\u6790\u4e2d -NewCaseVisualPanel1.multiUserCaseRadioButton.text=\u8907\u6570\u30e6\u30fc\u30b6\u30fc -NewCaseVisualPanel1.singleUserCaseRadioButton.text=\u5358\u6570\u30e6\u30fc\u30b6\u30fc -SingleUserCaseConverter.BadDatabaseFileName=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\uff01 -SingleUserCaseConverter.AlreadyMultiUser=\u30b1\u30fc\u30b9\u306f\u65e2\u306b\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u3067\u3059\uff01 -SingleUserCaseConverter.NonUniqueDatabaseName=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d\u304c\u30e6\u30cb\u30fc\u30af\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 -SingleUserCaseConverter.UnableToCopySourceImages=\u30bd\u30fc\u30b9\u30a4\u30e1\u30fc\u30b8\u3092\u30b3\u30d4\u30fc\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -SingleUserCaseConverter.CanNotOpenDatabase=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f -CloseCaseWhileIngesting.Warning=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b9f\u884c\u4e2d\u3067\u3059\u3002\u3053\u306e\u30b1\u30fc\u30b9\u3092\u672c\u5f53\u306b\u9589\u3058\u307e\u3059\u304b\uff1f -CloseCaseWhileIngesting.Warning.title=\u8b66\u544a\uff1a\u3053\u308c\u3092\u5b9f\u884c\u3059\u308c\u3070\u4f5c\u696d\u4e2d\u306e\u30b1\u30fc\u30b9\u3092\u9589\u3058\u307e\u3059 -Case_caseType_multiUser=\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9 -Case_caseType_singleUser=\u5358\u6570\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9 -CasePropertiesForm.imagesTable.columnModel.title0=\u30d1\u30b9 +UnpackagePortableCaseDialog.title.text=\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9\u3092\u958b\u5c01 +UnpackagePortableCaseDialog.UnpackagePortableCaseDialog.extensions=\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9\u30d1\u30c3\u30b1\u30fc\u30b8(.zip, .zip.001) +UnpackagePortableCaseDialog.validatePaths.badExtension=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u306f .zip \u307e\u305f\u306f .zip.001 \u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093 +# {0} - \u30b1\u30fc\u30b9\u30d5\u30a9\u30eb\u30c0\u30fc +UnpackagePortableCaseDialog.validatePaths.caseFolderExists=\u30d5\u30a9\u30eb\u30c0\u30fc {0} \u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059 +UnpackagePortableCaseDialog.validatePaths.caseIsNotFile=\u9078\u629e\u3057\u305f\u30d1\u30b9\u306f\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u3042\u308a\u307e\u305b\u3093 +UnpackagePortableCaseDialog.validatePaths.caseNotFound=\u30d5\u30a1\u30a4\u30eb\u306f\u5b58\u5728\u3057\u307e\u305b\u3093 +UnpackagePortableCaseDialog.validatePaths.folderNotFound=\u30d5\u30a9\u30eb\u30c0\u30fc\u306f\u5b58\u5728\u3057\u307e\u305b\u3093 +UnpackagePortableCaseDialog.validatePaths.notAFolder=\u51fa\u529b\u5834\u6240\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3067\u306f\u3042\u308a\u307e\u305b\u3093 +UnpackagePortableCaseProgressDialog.propertyChange.success=\u30b1\u30fc\u30b9\u3092\u6b63\u5e38\u306b\u958b\u5c01\u3057\u307e\u3057\u305f +UnpackagePortableCaseProgressDialog.title.text=\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9\u306e\u958b\u5c01\u72b6\u6cc1 +UnpackageWorker.doInBackground.canceled=\u30e6\u30fc\u30b6\u30fc\u306b\u3088\u3063\u3066\u958b\u5c01\u304c\u53d6\u308a\u6d88\u3055\u308c\u307e\u3057\u305f +UnpackageWorker.doInBackground.errorCompressingCase=\u30b1\u30fc\u30b9\u306e\u958b\u5c01\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +UnpackageWorker.doInBackground.errorFinding7zip=7-Zip\u5b9f\u884c\u53ef\u80fd\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f +UpdateRecentCases.menuItem.clearRecentCases.text=\u6700\u8fd1\u306e\u30b1\u30fc\u30b9\u3092\u6d88\u53bb +UpdateRecentCases.menuItem.empty=-\u7a7a(\u672a\u5165\u529b)- +AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=\u53d6\u308a\u6d88\u3057 +NewCaseVisualPanel1.CaseFolderOnCDriveError.text=\u8b66\u544a: \u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u30d5\u30a9\u30eb\u30c0\u30fc\u306e\u30d1\u30b9\u306f \"C:\" \u30c9\u30e9\u30a4\u30d6\u306b\u3042\u308a\u307e\u3059 +NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=\u8b66\u544a: \u30b1\u30fc\u30b9\u30d5\u30a9\u30eb\u30c0\u30fc\u306e\u30d1\u30b9\u306f \"C:\" \u30c9\u30e9\u30a4\u30d6\u306b\u3042\u308a\u307e\u3059\u3002\u30b1\u30fc\u30b9\u30d5\u30a9\u30eb\u30c0\u30fc\u306f\u30bf\u30fc\u30b2\u30c3\u30c8\u30b7\u30b9\u30c6\u30e0\u4e0a\u306b\u4f5c\u6210\u3055\u308c\u307e\u3059 +NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=\u8b66\u544a: \u30b1\u30fc\u30b9\u30d5\u30a9\u30eb\u30c0\u30fc\u306e\u30d1\u30b9\u306f\u30bf\u30fc\u30b2\u30c3\u30c8\u30b7\u30b9\u30c6\u30e0\u4e0a\u306b\u3042\u308a\u307e\u3059\u3002\u30de\u30a6\u30f3\u30c8\u3055\u308c\u305f\u30c9\u30e9\u30a4\u30d6\u5185\u306b\u30b1\u30fc\u30b9\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002 +CollaborationMonitor.addingDataSourceStatus.msg={0} \u304c\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0\u4e2d\u3067\u3059 +CollaborationMonitor.analyzingDataSourceStatus.msg={0} \u5206\u6790\u4e2d {1} +MissingImageDialog.lbWarning.text= +MissingImageDialog.lbWarning.toolTipText= +NewCaseVisualPanel1.caseParentDirWarningLabel.text= +NewCaseVisualPanel1.multiUserCaseRadioButton.text=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc +NewCaseVisualPanel1.singleUserCaseRadioButton.text=\u30b7\u30f3\u30b0\u30eb\u30e6\u30fc\u30b6\u30fc +NewCaseVisualPanel1.caseTypeLabel.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7: +SingleUserCaseConverter.BadDatabaseFileName=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u306f\u5b58\u5728\u3057\u307e\u305b\u3093! +SingleUserCaseConverter.AlreadyMultiUser=\u30b1\u30fc\u30b9\u306f\u3059\u3067\u306b\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u3067\u3059! +SingleUserCaseConverter.NonUniqueDatabaseName=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d\u304c\u4e00\u610f\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +SingleUserCaseConverter.UnableToCopySourceImages=\u30bd\u30fc\u30b9\u30a4\u30e1\u30fc\u30b8\u3092\u30b3\u30d4\u30fc\u3067\u304d\u307e\u305b\u3093 +SingleUserCaseConverter.CanNotOpenDatabase=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u3051\u307e\u305b\u3093 +CloseCaseWhileIngesting.Warning=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d\u3067\u3059\u3002\u30b1\u30fc\u30b9\u3092\u9589\u3058\u3066\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +CloseCaseWhileIngesting.Warning.title=\u8b66\u544a: \u3053\u308c\u306b\u3088\u308a\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u304c\u9589\u3058\u307e\u3059\u3002 CasePropertiesForm.imagesTable.columnModel.title1=\u524a\u9664 -OptionalCasePropertiesPanel.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a -OptionalCasePropertiesPanel.caseDisplayNameLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a -CueBannerPanel.openRecentCaseLabel.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f -CueBannerPanel.openAutoIngestCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f -CueBannerPanel.newCaseLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5c\u6210 -CueBannerPanel.openCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f -ImageFilePanel.sectorSizeLabel.text=\u30a4\u30f3\u30d7\u30c3\u30c8\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u9078\u629e\u3057\u3066\u4e0b\u3055\u3044\uff1a +CasePropertiesForm.imagesTable.columnModel.title0=\u30d1\u30b9 +IngestJobInfoPanel.jLabel1.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb +IngestJobInfoPanel.jLabel2.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6 +CaseInformationPanel.closeButton.text=\u9589\u3058\u308b +LocalDiskPanel.copyImageCheckbox.text=\u30c9\u30e9\u30a4\u30d6\u306e\u5206\u6790\u4e2d\u306bVHD\u30a4\u30e1\u30fc\u30b8\u3092\u4f5c\u6210 +LocalDiskPanel.jLabel1.text=\u5b8c\u5168\u306a\u30b3\u30d4\u30fc\u3092\u4f5c\u6210\u3059\u308b\u306b\u306f\u3001\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c\u3057\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093 +LocalDiskPanel.pathTextField.text= +LocalDiskPanel.browseButton.text=\u53c2\u7167 +LocalDiskPanel.changeDatabasePathCheckbox.text=\u5b8c\u4e86\u6642\u306bVHD\u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u7528\u3059\u308b\u305f\u3081\u306b\u30b1\u30fc\u30b9\u3092\u66f4\u65b0 +CueBannerPanel.openRecentCaseButton.text= +CueBannerPanel.openRecentCaseLabel.text=\u6700\u8fd1\u306e\u30b1\u30fc\u30b9\u3092\u958b\u304f +ReviewModeCasePanel.cannotOpenCase=\u30b1\u30fc\u30b9\u3092\u958b\u3051\u307e\u305b\u3093 +ReviewModeCasePanel.casePathNotFound=\u30b1\u30fc\u30b9\u30d1\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +ReviewModeCasePanel.caseIsLocked=\u30b7\u30f3\u30b0\u30eb\u30e6\u30fc\u30b6\u30fc\u304c\u30ed\u30c3\u30af\u3055\u308c\u3066\u3044\u307e\u3059\u3002 +ReviewModeCasePanel.CaseHeaderText=\u30b1\u30fc\u30b9 +ReviewModeCasePanel.CreatedTimeHeaderText=\u4f5c\u6210\u6e08\u307f\u6642\u523b +ReviewModeCasePanel.StatusIconHeaderText=\u30b9\u30c6\u30fc\u30bf\u30b9 +ReviewModeCasePanel.OutputFolderHeaderText=\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc +ReviewModeCasePanel.LastAccessedTimeHeaderText=\u6700\u7d42\u30a2\u30af\u30bb\u30b9\u6642\u523b +ReviewModeCasePanel.MetadataFileHeaderText=\u30e1\u30bf\u30c7\u30fc\u30bf\u30d5\u30a1\u30a4\u30eb +CueBannerPanel.newCaseLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9 +CueBannerPanel.openCaseButton.text= +CueBannerPanel.openCaseLabel.text=\u30b1\u30fc\u30b9\u3092\u958b\u304f +CueBannerPanel.newCaseButton.text= +ImageFilePanel.sectorSizeLabel.text=\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30ba: +LocalDiskPanel.sectorSizeLabel.text=\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30ba: +LocalFilesPanel.displayNameLabel.text=\u8ad6\u7406\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u8868\u793a\u540d: \u30c7\u30d5\u30a9\u30eb\u30c8 LocalFilesPanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb -LocalFilesPanel.clearButton.toolTipText=\u73fe\u5728\u9078\u629e\u3055\u308c\u3066\u3044\u308b\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u304c\u30af\u30ea\u30a2\u3055\u308c\u307e\u3059 -LocalFilesPanel.clearButton.text=\u30af\u30ea\u30a2 +LocalFilesPanel.selectedPaths.toolTipText= +LocalFilesPanel.clearButton.toolTipText=\u73fe\u5728\u9078\u629e\u3055\u308c\u3066\u3044\u308b\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u3092\u6d88\u53bb +LocalFilesPanel.clearButton.text=\u6d88\u53bb LocalFilesPanel.selectButton.actionCommand=\u8ffd\u52a0 -LocalFilesPanel.selectButton.toolTipText=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u304a\u3088\u3073\u30d5\u30a9\u30eb\u30c0\u3092\u30ed\u30b8\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u3068\u3057\u3066\u8ffd\u52a0\u3057\u307e\u3059 +LocalFilesPanel.selectButton.toolTipText=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u304a\u3088\u3073\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u8ad6\u7406\u30d5\u30a1\u30a4\u30eb\u3068\u3057\u3066\u8ffd\u52a0 LocalFilesPanel.selectButton.text=\u8ffd\u52a0 -LocalFilesPanel.localFileChooser.dialogTitle=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306f\u30d5\u30a9\u30eb\u30c0\u3092\u9078\u629e +LocalFilesPanel.localFileChooser.dialogTitle=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306f\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u9078\u629e +LocalFilesPanel.localFileChooser.approveButtonToolTipText= LocalFilesPanel.localFileChooser.approveButtonText=\u9078\u629e LogicalEvidenceFilePanel.selectButton.actionCommand=\u8ffd\u52a0 -LogicalEvidenceFilePanel.selectButton.toolTipText=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u304a\u3088\u3073\u30d5\u30a9\u30eb\u30c0\u3092\u30ed\u30b8\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u3068\u3057\u3066\u8ffd\u52a0\u3057\u307e\u3059 -LogicalEvidenceFilePanel.selectButton.text=\u8ffd\u52a0 +LogicalEvidenceFilePanel.selectButton.toolTipText=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u304a\u3088\u3073\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u8ad6\u7406\u30d5\u30a1\u30a4\u30eb\u3068\u3057\u3066\u8ffd\u52a0 +LogicalEvidenceFilePanel.selectButton.text=\u9078\u629e LogicalEvidenceFilePanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb -LogicalEvidenceFilePanel.logicalEvidenceFileChooser.dialogTitle=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306f\u30d5\u30a9\u30eb\u30c0\u3092\u9078\u629e +LogicalEvidenceFilePanel.logicalEvidenceFileChooser.dialogTitle=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306f\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u9078\u629e +LogicalEvidenceFilePanel.logicalEvidenceFileChooser.approveButtonToolTipText= LogicalEvidenceFilePanel.logicalEvidenceFileChooser.approveButtonText=\u9078\u629e +LogicalEvidenceFilePanel.logicalEvidencePathField.text= +LocalFilesPanel.changeNameButton.text=\u5909\u66f4 +LocalDiskPanel.selectDiskButton.text=\u30c7\u30a3\u30b9\u30af\u3092\u9078\u629e +LocalDiskSelectionDialog.refreshLocalDisksButton.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u66f4\u65b0 LocalDiskSelectionDialog.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb -LocalDiskSelectionDialog.selectLocalDiskLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u9078\u629e\uff1a -CaseDetailsPanel.crDateLabel.text=\u4f5c\u6210\u65e5\uff1a -CaseDetailsPanel.caseDirLabel.text=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff1a -CaseDetailsPanel.caseNumberLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a -CaseDetailsPanel.lbDbName.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d\uff1a -CaseDetailsPanel.lbDbType.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\uff1a -CaseDetailsPanel.caseNameLabel.text=\u30b1\u30fc\u30b9\u540d\uff1a -OpenMultiUserCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb +LocalDiskSelectionDialog.selectLocalDiskLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u9078\u629e: +LocalDiskSelectionDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +LocalDiskSelectionDialog.okButton.text=OK +LocalDiskPanel.localDiskLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af: +LocalDiskPanel.imageWriterErrorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb +LocalDiskSelectionDialog.title=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u9078\u629e +ImageFilePanel.md5HashLabel.text=MD5: +ImageFilePanel.sha1HashLabel.text=SHA-1: +ImageFilePanel.sha256HashLabel.text=SHA-256: +ImageFilePanel.sha256HashTextField.text= +ImageFilePanel.sha1HashTextField.text= +ImageFilePanel.md5HashTextField.text= +ImageFilePanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb +ImageFilePanel.hashValuesNoteLabel.text=\u6ce8: \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u8ffd\u52a0\u6642\u306b\u3053\u308c\u3089\u306e\u5024\u306f\u691c\u8a3c\u3055\u308c\u307e\u305b\u3093\u3002 +ImageFilePanel.hashValuesLabel.text=\u30cf\u30c3\u30b7\u30e5\u5024(\u4efb\u610f): +CaseDetailsPanel.crDateLabel.text=\u4f5c\u6210\u65e5: +CaseDetailsPanel.caseDirLabel.text=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc: +CaseDetailsPanel.caseNumberLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7: +CaseDetailsPanel.lbDbName.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d: +CaseDetailsPanel.lbDbType.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7: +CaseDetailsPanel.caseNameLabel.text=\u30b1\u30fc\u30b9\u540d: +OpenMultiUserCasePanel.searchLabel.text=\u4efb\u610f\u306e\u30b1\u30fc\u30b9\u3092\u9078\u629e\u3057\u3001\u5165\u529b\u3092\u958b\u59cb\u3057\u3066\u30b1\u30fc\u30b9\u540d\u3067\u691c\u7d22 +OpenMultiUserCasePanel.cancelButton.text=\u53d6\u308a\u6d88\u3057 +OpenMultiUserCasePanel.openSingleUserCaseButton.tex=\u30b7\u30f3\u30b0\u30eb\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u3092\u958b\u304f... +OpenMultiUserCasePanel.openSelectedCaseButton.text=\u9078\u629e\u3057\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f +OpenMultiUserCasePanel.searchLabel.text=\u4efb\u610f\u306e\u30b1\u30fc\u30b9\u3092\u9078\u629e\u3057\u3001\u5165\u529b\u3092\u958b\u59cb\u3057\u3066\u30b1\u30fc\u30b9\u540d\u3067\u691c\u7d22 +UnpackagePortableCaseDialog.desc2Label.text=\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9\u30ec\u30dd\u30fc\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3002 +UnpackagePortableCaseDialog.desc1Label.text=\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9\u3092\u958b\u5c01\u3057\u3066Autopsy\u3067\u958b\u3051\u308b\u3088\u3046\u306b\u3057\u307e\u3059\u3002\u7d4c\u7531\u3067\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9\u3092\u4f5c\u6210\u3057\u307e\u3059 +UnpackagePortableCaseDialog.exitButton.text=\u7d42\u4e86 +UnpackagePortableCaseDialog.unpackageButton.text=\u958b\u5c01 +UnpackagePortableCaseDialog.caseSelectButton.text=\u53c2\u7167 +UnpackagePortableCaseDialog.outputSelectButton.text=\u53c2\u7167 +UnpackagePortableCaseDialog.caseTextField.text= +UnpackagePortableCaseDialog.outputTextField.text= +UnpackagePortableCaseDialog.caseErrorLabel.text=jLabel1 +UnpackagePortableCaseDialog.outputErrorLabel.text=jLabel1 +UnpackagePortableCaseProgressDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +UnpackagePortableCaseProgressDialog.okButton.text=OK +UnpackagePortableCaseProgressDialog.resultLabel.text=resultLabel +UnpackagePortableCaseDialog.extractLabel.text=\u30d5\u30a9\u30eb\u30c0\u30fc\u306e\u62bd\u51fa\u5148: +UnpackagePortableCaseDialog.caseLabel.text=\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9: diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 0a4586dd42..f63cfa1a81 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.casemodule; +import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils; import com.google.common.annotations.Beta; import com.google.common.eventbus.Subscribe; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData; @@ -77,6 +78,7 @@ import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent; import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException; @@ -104,6 +106,7 @@ import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEventException; import org.sleuthkit.autopsy.events.AutopsyEventPublisher; +import org.sleuthkit.autopsy.filequery.OpenFileDiscoveryAction; import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestServices; @@ -121,11 +124,14 @@ import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.CaseDbConnectionInfo; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Report; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TimelineManager; +import org.sleuthkit.datamodel.SleuthkitCaseAdminUtil; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException; /** @@ -133,8 +139,8 @@ import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException; */ public class Case { - private static final int DIR_LOCK_TIMOUT_HOURS = 12; - private static final int RESOURCES_LOCK_TIMOUT_HOURS = 12; + private static final int CASE_LOCK_TIMEOUT_MINS = 1; + private static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS = 1; 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 @@ -153,13 +159,12 @@ public class Case { private static volatile Frame mainFrame; private static volatile Case currentCase; private final CaseMetadata metadata; - private volatile ExecutorService caseLockingExecutor; - private CoordinationService.Lock caseDirLock; + private volatile ExecutorService caseActionExecutor; + private CoordinationService.Lock caseLock; private SleuthkitCase caseDb; + private final SleuthkitEventListener sleuthkitEventListener; private CollaborationMonitor collaborationMonitor; private Services caseServices; - private boolean hasDataSources; - private final TSKCaseRepublisher tskEventForwarder = new TSKCaseRepublisher(); /* * Get a reference to the main window of the desktop application to use to @@ -167,11 +172,8 @@ public class Case { * changing the main window title. */ static { - WindowManager.getDefault().invokeWhenUIReady(new Runnable() { - @Override - public void run() { - mainFrame = WindowManager.getDefault().getMainWindow(); - } + WindowManager.getDefault().invokeWhenUIReady(() -> { + mainFrame = WindowManager.getDefault().getMainWindow(); }); } @@ -398,30 +400,37 @@ public class Case { * TimelineEvent that was added. */ TIMELINE_EVENT_ADDED, - /* An item in the central repository has had its comment - * modified. The old value is null, the new value is string for current - * comment. + /* + * An item in the central repository has had its comment modified. The + * old value is null, the new value is string for current comment. */ CR_COMMENT_CHANGED; }; - private final class TSKCaseRepublisher { + /** + * An instance of this class is registered as a listener on the event bus + * associated with the case database so that selected SleuthKit layer + * application events can be published as Autopsy application events. + */ + private final class SleuthkitEventListener { @Subscribe - public void rebroadcastTimelineEventCreated(TimelineManager.TimelineEventAddedEvent event) { + public void publishTimelineEventAddedEvent(TimelineManager.TimelineEventAddedEvent event) { eventPublisher.publish(new TimelineEventAddedEvent(event)); } @SuppressWarnings("deprecation") @Subscribe - public void rebroadcastArtifactsPosted(Blackboard.ArtifactsPostedEvent event) { + public void publishArtifactsPostedEvent(Blackboard.ArtifactsPostedEvent event) { for (BlackboardArtifact.Type artifactType : event.getArtifactTypes()) { /* - * fireModuleDataEvent is deprecated so module writers don't use - * it (they should use Blackboard.postArtifact(s) instead), but - * we still need a way to rebroadcast the ArtifactsPostedEvent - * as a ModuleDataEvent. + * IngestServices.fireModuleDataEvent is deprecated to + * discourage ingest module writers from using it (they should + * use org.sleuthkit.datamodel.Blackboard.postArtifact(s) + * instead), but a way to publish + * Blackboard.ArtifactsPostedEvents from the SleuthKit layer as + * Autopsy ModuleDataEvents is still needed. */ IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent( event.getModuleName(), @@ -535,8 +544,8 @@ public class Case { */ public static boolean isValidName(String caseName) { return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":") - || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"") - || caseName.contains("<") || caseName.contains(">") || caseName.contains("|")); + || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"") + || caseName.contains("<") || caseName.contains(">") || caseName.contains("|")); } /** @@ -668,14 +677,15 @@ public class Case { * method should put all operations in an exception firewall with a try and * catch-all block to handle the possibility of bad timing. * - * TODO (JIRA-3825): Introduce a reference counting scheme for this get case - * method. - * * @return The current case. * * @throws NoCurrentCaseException if there is no current case. */ public static Case getCurrentCaseThrows() throws NoCurrentCaseException { + /* + * TODO (JIRA-3825): Introduce a reference counting scheme for this get + * case method. + */ Case openCase = currentCase; if (openCase == null) { throw new NoCurrentCaseException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen")); @@ -706,7 +716,7 @@ public class Case { eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null)); logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS currentCase = null; - closedCase.close(); + closedCase.doCloseCaseAction(); logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS } catch (CaseActionException ex) { logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS @@ -738,6 +748,46 @@ public class Case { } } + /** + * Deletes a data source from the current case. + * + * @param dataSourceObjectID The object ID of the data source to delete. + * + * @throws CaseActionException If there is a problem deleting the case. The + * exception will have a user-friendly message + * and may be a wrapper for a lower-level + * exception. + */ + @Messages({ + "Case.progressIndicatorTitle.deletingDataSource=Removing Data Source" + }) + public static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException { + synchronized (caseActionSerializationLock) { + if (null == currentCase) { + return; + } + + /* + * Close the current case to release the shared case lock. + */ + CaseMetadata caseMetadata = currentCase.getMetadata(); + closeCurrentCase(); + + /* + * Re-open the case with an exclusive case lock, delete the data + * source, and close the case again, releasing the exclusive case + * lock. + */ + Case theCase = new Case(caseMetadata); + theCase.doOpenCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), theCase::deleteDataSource, CaseLockType.EXCLUSIVE, false, dataSourceObjectID); + + /* + * Re-open the case with a shared case lock. + */ + openAsCurrentCase(new Case(caseMetadata), false); + } + } + /** * Deletes a case. The case to be deleted must not be the "current case." * Deleting the current case must be done by calling Case.deleteCurrentCase. @@ -799,6 +849,8 @@ public class Case { * @throws CaseActionCancelledException If creating the case is cancelled. */ @Messages({ + "Case.progressIndicatorTitle.creatingCase=Creating Case", + "Case.progressIndicatorTitle.openingCase=Opening Case", "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window" }) private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException { @@ -816,7 +868,16 @@ public class Case { } try { logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS - newCurrentCase.open(isNewCase); + String progressIndicatorTitle; + CaseAction openCaseAction; + if (isNewCase) { + progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_creatingCase(); + openCaseAction = newCurrentCase::create; + } else { + progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase(); + openCaseAction = newCurrentCase::open; + } + newCurrentCase.doOpenCaseAction(progressIndicatorTitle, openCaseAction, CaseLockType.SHARED, true, null); currentCase = newCurrentCase; logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS if (RuntimeProperties.runningWithGUI()) { @@ -978,14 +1039,14 @@ public class Case { @Messages({ "Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources" }) - private static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir) throws CaseActionException { + private static CoordinationService.Lock acquireCaseResourcesLock(String caseDir) throws CaseActionException { try { Path caseDirPath = Paths.get(caseDir); String resourcesNodeName = CoordinationServiceUtils.getCaseResourcesNodePath(caseDirPath); - Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, RESOURCES_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); + Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, CASE_RESOURCES_LOCK_TIMEOUT_HOURS, TimeUnit.HOURS); return lock; } catch (InterruptedException ex) { - throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); + throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled()); } catch (CoordinationServiceException ex) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex); } @@ -1049,7 +1110,7 @@ public class Case { /* * Enable the case-specific actions. */ - CallableSystemAction.get(AddImageAction.class).setEnabled(Case.getCurrentCase().getMetadata().getCaseType() == CaseType.SINGLE_USER_CASE || !AccessLimiterUtils.limitMultiUserAccess()); + CallableSystemAction.get(AddImageAction.class).setEnabled(FeatureAccessUtils.canAddDataSources()); CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true); CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true); @@ -1058,6 +1119,7 @@ public class Case { CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true); CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true); CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false); + CallableSystemAction.get(OpenFileDiscoveryAction.class).setEnabled(true); /* * Add the case to the recent cases tracker that supplies a list @@ -1112,6 +1174,7 @@ public class Case { CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false); CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false); CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false); + CallableSystemAction.get(OpenFileDiscoveryAction.class).setEnabled(false); /* * Clear the notifications in the notfier component in the lower @@ -1366,16 +1429,14 @@ public class Case { /** * Gets the data sources for the case. * - * @return A list of data sources. + * @return A list of data sources, possibly empty. * * @throws org.sleuthkit.datamodel.TskCoreException if there is a problem * querying the case * database. */ public List getDataSources() throws TskCoreException { - List list = caseDb.getRootObjects(); - hasDataSources = (list.size() > 0); - return list; + return caseDb.getRootObjects(); } /** @@ -1416,12 +1477,11 @@ public class Case { * @return True or false. */ public boolean hasData() { - if (!hasDataSources) { - try { - hasDataSources = (getDataSources().size() > 0); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS - } + boolean hasDataSources = false; + try { + hasDataSources = (getDataSources().size() > 0); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS } return hasDataSources; } @@ -1699,7 +1759,7 @@ public class Case { * */ private Case(CaseType caseType, String caseDir, CaseDetails caseDetails) { - metadata = new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails); + this(new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails)); } /** @@ -1709,91 +1769,91 @@ public class Case { */ private Case(CaseMetadata caseMetaData) { metadata = caseMetaData; + sleuthkitEventListener = new SleuthkitEventListener(); } /** - * Opens this case by creating a task running in the same non-UI thread that - * will be used to close the case. If the case is a single-user case, this - * supports cancelling creation of the case by cancelling the task. If the - * case is a multi-user case, this ensures ensures that case directory lock - * held as long as the case is open is released in the same thread in which - * it was acquired, as is required by the coordination service. + * Performs a case action that involves creating or opening a case. If the + * case is a multi-user case, the action is done after acquiring a + * coordination service case lock. This case lock must be released in the + * same thread in which it was acquired, as required by the coordination + * service. A single-threaded executor is therefore created to do the case + * opening action, and is saved for an eventual case closing action. * - * @param isNewCase True for a new case, false otherwise. + * IMPORTANT: If an open case action for a multi-user case is terminated + * because an exception is thrown or the action is cancelled, the action is + * responsible for releasing the case lock while still running in the case + * action executor's thread. This method assumes this has been done and + * performs an orderly shut down of the case action executor. * - * @throws CaseActionException If there is a problem creating the case. The - * exception will have a user-friendly message - * and may be a wrapper for a lower-level - * exception. + * @param progressIndicatorTitle A title for the progress indicator for the + * case action. + * @param caseAction The case action method. + * @param caseLockType The type of case lock required for the case + * action. + * @param allowCancellation Whether or not to allow the action to be + * cancelled. + * @param additionalParams An Object that holds any additional + * parameters for a case action. + * + * @throws CaseActionException If there is a problem completing the action. + * The exception will have a user-friendly + * message and may be a wrapper for a + * lower-level exception. */ @Messages({ - "Case.progressIndicatorTitle.creatingCase=Creating Case", - "Case.progressIndicatorTitle.openingCase=Opening Case", "Case.progressIndicatorCancelButton.label=Cancel", "Case.progressMessage.preparing=Preparing...", - "Case.progressMessage.preparingToOpenCaseResources=Preparing to open case resources.
This may take time if another user is upgrading the case.", "Case.progressMessage.cancelling=Cancelling...", - "Case.exceptionMessage.cancelledByUser=Cancelled by user.", + "Case.exceptionMessage.cancelled=Cancelled.", "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}" }) - private void open(boolean isNewCase) throws CaseActionException { + private void doOpenCaseAction(String progressIndicatorTitle, CaseAction caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams) throws CaseActionException { /* - * Create and start either a GUI progress indicator with a Cancel button - * or a logging progress indicator. + * Create and start either a GUI progress indicator (with or without a + * cancel button) or a logging progress indicator. */ CancelButtonListener cancelButtonListener = null; ProgressIndicator progressIndicator; if (RuntimeProperties.runningWithGUI()) { - cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling()); - String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase(); - progressIndicator = new ModalDialogProgressIndicator( - mainFrame, - progressIndicatorTitle, - new String[]{Bundle.Case_progressIndicatorCancelButton_label()}, - Bundle.Case_progressIndicatorCancelButton_label(), - cancelButtonListener); + if (allowCancellation) { + cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling()); + progressIndicator = new ModalDialogProgressIndicator( + mainFrame, + progressIndicatorTitle, + new String[]{Bundle.Case_progressIndicatorCancelButton_label()}, + Bundle.Case_progressIndicatorCancelButton_label(), + cancelButtonListener); + } else { + progressIndicator = new ModalDialogProgressIndicator( + mainFrame, + progressIndicatorTitle); + } } else { progressIndicator = new LoggingProgressIndicator(); } progressIndicator.start(Bundle.Case_progressMessage_preparing()); /* - * Creating/opening a case is always done by creating a task running in - * the same non-UI thread that will be used to close the case, so a - * single-threaded executor service is created here and saved as case - * state (must be volatile for cancellation to work). - * - * --- If the case is a single-user case, this supports cancelling - * opening of the case by cancelling the task. - * - * --- If the case is a multi-user case, this still supports - * cancellation, but it also makes it possible for the shared case - * directory lock held as long as the case is open to be released in the - * same thread in which it was acquired, as is required by the - * coordination service. + * Do the case action in the single thread in the case action executor. + * If the case is a multi-user case, a case lock is acquired and held + * until explictly released and an exclusive case resources lock is + * aquired and held for the duration of the action. */ TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName())); - caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory); - Future future = caseLockingExecutor.submit(() -> { + caseActionExecutor = Executors.newSingleThreadExecutor(threadFactory); + Future future = caseActionExecutor.submit(() -> { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { - open(isNewCase, progressIndicator); + caseAction.execute(progressIndicator, additionalParams); } else { - /* - * First, acquire a shared case directory lock that will be held - * as long as this node has this case open. This will prevent - * deletion of the case by another node. Next, acquire an - * exclusive case resources lock to ensure only one node at a - * time can create/open/upgrade/close the case resources. - */ - progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources()); - acquireSharedCaseDirLock(metadata.getCaseDirectory()); - try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { + acquireCaseLock(caseLockType); + try (CoordinationService.Lock resourcesLock = acquireCaseResourcesLock(metadata.getCaseDirectory())) { if (null == resourcesLock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock()); } - open(isNewCase, progressIndicator); + caseAction.execute(progressIndicator, additionalParams); } catch (CaseActionException ex) { - releaseSharedCaseDirLock(getMetadata().getCaseDirectory()); + releaseCaseLock(); throw ex; } } @@ -1804,44 +1864,31 @@ public class Case { } /* - * Wait for the case creation/opening task to finish. + * Wait for the case action task to finish. */ try { future.get(); } catch (InterruptedException discarded) { /* - * The thread this method is running in has been interrupted. Cancel - * the create/open task, wait for it to finish, and shut down the - * executor. This can be done safely because if the task is - * completed with a cancellation condition, the case will have been - * closed and the case directory lock released will have been - * released. + * The thread this method is running in has been interrupted. */ if (null != cancelButtonListener) { cancelButtonListener.actionPerformed(null); } else { future.cancel(true); } - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); + ThreadUtils.shutDownTaskExecutor(caseActionExecutor); } catch (CancellationException discarded) { /* - * The create/open task has been cancelled. Wait for it to finish, - * and shut down the executor. This can be done safely because if - * the task is completed with a cancellation condition, the case - * will have been closed and the case directory lock released will - * have been released. + * The case action has been cancelled. */ - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); - throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); + ThreadUtils.shutDownTaskExecutor(caseActionExecutor); + throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled()); } catch (ExecutionException ex) { /* - * The create/open task has thrown an exception. Wait for it to - * finish, and shut down the executor. This can be done safely - * because if the task is completed with an execution condition, the - * case will have been closed and the case directory lock released - * will have been released. + * The case action has thrown an exception. */ - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); + ThreadUtils.shutDownTaskExecutor(caseActionExecutor); throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex); } finally { progressIndicator.finish(); @@ -1849,55 +1896,48 @@ public class Case { } /** - * Opens the case database and services for this case. + * A case action (interface CaseAction) that creates the case + * directory and case database and opens the application services for this + * case. * - * @param isNewCase True for a new case, false otherwise. * @param progressIndicator A progress indicator. + * @param additionalParams An Object that holds any additional parameters + * for a case action. For this action, this is + * null. * - * @throws CaseActionException If there is a problem creating the case. The - * exception will have a user-friendly message - * and may be a wrapper for a lower-level - * exception. + * @throws CaseActionException If there is a problem completing the action. + * The exception will have a user-friendly + * message and may be a wrapper for a + * lower-level exception. */ - private void open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException { + private Void create(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { + assert (additionalParams == null); try { - checkForUserCancellation(); + checkForCancellation(); createCaseDirectoryIfDoesNotExist(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); switchLoggingToCaseLogsDirectory(progressIndicator); - checkForUserCancellation(); - if (isNewCase) { - saveCaseMetadataToFile(progressIndicator); - } - checkForUserCancellation(); - if (isNewCase) { - createCaseNodeData(progressIndicator); - } else { - updateCaseNodeData(progressIndicator); - } - checkForUserCancellation(); - if (!isNewCase) { - deleteTempfilesFromCaseDirectory(progressIndicator); - } - checkForUserCancellation(); - if (isNewCase) { - createCaseDatabase(progressIndicator); - } else { - openCaseDataBase(progressIndicator); - } - checkForUserCancellation(); + checkForCancellation(); + saveCaseMetadataToFile(progressIndicator); + checkForCancellation(); + createCaseNodeData(progressIndicator); + checkForCancellation(); + checkForCancellation(); + createCaseDatabase(progressIndicator); + checkForCancellation(); openCaseLevelServices(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); openAppServiceCaseResources(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); openCommunicationChannels(progressIndicator); + return null; } catch (CaseActionException ex) { /* - * Cancellation or failure. Clean up by calling the close method. - * The sleep is a little hack to clear the interrupted flag for this - * thread if this is a cancellation scenario, so that the clean up - * can run to completion in the current thread. + * Cancellation or failure. The sleep is a little hack to clear the + * interrupted flag for this thread if this is a cancellation + * scenario, so that the clean up can run to completion in the + * current thread. */ try { Thread.sleep(1); @@ -1908,6 +1948,102 @@ public class Case { } } + /** + * A case action (interface CaseAction) that opens the case + * database and application services for this case. + * + * @param progressIndicator A progress indicator. + * @param additionalParams An Object that holds any additional parameters + * for a case action. For this action, this is + * null. + * + * @throws CaseActionException If there is a problem completing the action. + * The exception will have a user-friendly + * message and may be a wrapper for a + * lower-level exception. + */ + private Void open(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { + assert (additionalParams == null); + try { + checkForCancellation(); + switchLoggingToCaseLogsDirectory(progressIndicator); + checkForCancellation(); + updateCaseNodeData(progressIndicator); + checkForCancellation(); + deleteTempfilesFromCaseDirectory(progressIndicator); + checkForCancellation(); + openCaseDataBase(progressIndicator); + checkForCancellation(); + openCaseLevelServices(progressIndicator); + checkForCancellation(); + openAppServiceCaseResources(progressIndicator); + checkForCancellation(); + openCommunicationChannels(progressIndicator); + return null; + + } catch (CaseActionException ex) { + /* + * Cancellation or failure. The sleep is a little hack to clear the + * interrupted flag for this thread if this is a cancellation + * scenario, so that the clean up can run to completion in the + * current thread. + */ + try { + Thread.sleep(1); + } catch (InterruptedException discarded) { + } + close(progressIndicator); + throw ex; + } + } + + /** + * A case action (interface CaseAction) that opens a case, deletes + * a data source from the case, and closes the case. + * + * @param progressIndicator A progress indicator. + * @param additionalParams An Object that holds any additional parameters + * for a case action. For this action, this the + * object ID of the data source to be deleted. + * + * @throws CaseActionException If there is a problem completing the action. + * The exception will have a user-friendly + * message and may be a wrapper for a + * lower-level exception. + */ + @Messages({ + "Case.progressMessage.deletingDataSource=Removing the data source from the case...", + "Case.exceptionMessage.dataSourceNotFound=The data source was not found.", + "Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while removing the data source from the case database.", + "Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while removing the data source from the text index.",}) + Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { + assert (additionalParams instanceof Long); + open(progressIndicator, null); + try { + progressIndicator.progress(Bundle.Case_progressMessage_deletingDataSource()); + Long dataSourceObjectID = (Long) additionalParams; + try { + DataSource dataSource = this.caseDb.getDataSource(dataSourceObjectID); + if (dataSource == null) { + throw new CaseActionException(Bundle.Case_exceptionMessage_dataSourceNotFound()); + } + SleuthkitCaseAdminUtil.deleteDataSource(this.caseDb, dataSourceObjectID); + } catch (TskDataException | TskCoreException ex) { + throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromCaseDb(), ex); + } + try { + this.caseServices.getKeywordSearchService().deleteDataSource(dataSourceObjectID); + } catch (KeywordSearchServiceException ex) { + throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromTextIndex(), ex); + } + eventPublisher.publish(new DataSourceDeletedEvent(dataSourceObjectID)); + return null; + } finally { + close(progressIndicator); + releaseCaseLock(); + } + } + /** * Create an empty portable case from the current case * @@ -1954,18 +2090,15 @@ public class Case { * interrupted, assumes interrupt was * due to a user action. */ - private static void checkForUserCancellation() throws CaseActionCancelledException { + private static void checkForCancellation() throws CaseActionCancelledException { if (Thread.currentThread().isInterrupted()) { - throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); + throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled()); } } /** * Creates the case directory, if it does not already exist. * - * TODO (JIRA-2180): Always create the case directory as part of the case - * creation process. - * * @param progressIndicator A progress indicator. * * @throws CaseActionException If there is a problem completing the @@ -1977,6 +2110,10 @@ public class Case { "Case.progressMessage.creatingCaseDirectory=Creating case directory..." }) private void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator) throws CaseActionException { + /* + * TODO (JIRA-2180): Always create the case directory as part of the + * case creation process. + */ progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory()); if (new File(metadata.getCaseDirectory()).exists() == false) { progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory()); @@ -2186,8 +2323,12 @@ public class Case { private void openCaseLevelServices(ProgressIndicator progressIndicator) { progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices()); this.caseServices = new Services(caseDb); - - caseDb.registerForEvents(tskEventForwarder); + /* + * RC Note: JM put this initialization here. I'm not sure why. However, + * my attempt to put it in the openCaseDatabase method seems to lead to + * intermittent unchecked exceptions concerning a missing subscriber. + */ + caseDb.registerForEvents(sleuthkitEventListener); } /** @@ -2217,7 +2358,9 @@ public class Case { * one starts by awaiting termination of the executor service. */ progressIndicator.progress(Bundle.Case_progressMessage_openingApplicationServiceResources()); - for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) { + + for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class + )) { /* * Create a progress indicator for the task and start the task. If * running with a GUI, the progress indicator will be a dialog box @@ -2295,7 +2438,7 @@ public class Case { ThreadUtils.shutDownTaskExecutor(executor); appServiceProgressIndicator.finish(); } - checkForUserCancellation(); + checkForCancellation(); } } @@ -2320,7 +2463,7 @@ public class Case { progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications()); try { eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName())); - checkForUserCancellation(); + checkForCancellation(); collaborationMonitor = new CollaborationMonitor(metadata.getCaseName()); } catch (AutopsyEventException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex); @@ -2331,14 +2474,20 @@ public class Case { } /** - * Closes the case. + * Performs a case action that involves closing a case opened by calling + * doOpenCaseAction. If the case is a multi-user case, the coordination + * service case lock acquired by the call to doOpenCaseAction is released. + * This case lock must be released in the same thread in which it was + * acquired, as required by the coordination service. The single-threaded + * executor saved during the case opening action is therefore used to do the + * case closing action. * - * @throws CaseActionException If there is a problem completing the - * operation. The exception will have a - * user-friendly message and may be a wrapper - * for a lower-level exception. + * @throws CaseActionException If there is a problem completing the action. + * The exception will have a user-friendly + * message and may be a wrapper for a + * lower-level exception. */ - private void close() throws CaseActionException { + private void doCloseCaseAction() throws CaseActionException { /* * Set up either a GUI progress indicator without a Cancel button or a * logging progress indicator. @@ -2356,11 +2505,11 @@ public class Case { /* * Closing a case is always done in the same non-UI thread that * opened/created the case. If the case is a multi-user case, this - * ensures that case directory lock that is held as long as the case is - * open is released in the same thread in which it was acquired, as is - * required by the coordination service. + * ensures that case lock that is held as long as the case is open is + * released in the same thread in which it was acquired, as is required + * by the coordination service. */ - Future future = caseLockingExecutor.submit(() -> { + Future future = caseActionExecutor.submit(() -> { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { close(progressIndicator); } else { @@ -2370,7 +2519,7 @@ public class Case { * resources. */ progressIndicator.progress(Bundle.Case_progressMessage_preparing()); - try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { + try (CoordinationService.Lock resourcesLock = acquireCaseResourcesLock(metadata.getCaseDirectory())) { if (null == resourcesLock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock()); } @@ -2380,7 +2529,7 @@ public class Case { * Always release the case directory lock that was acquired * when the case was opened. */ - releaseSharedCaseDirLock(metadata.getCaseName()); + releaseCaseLock(); } } return null; @@ -2392,13 +2541,13 @@ public class Case { /* * The wait has been interrupted by interrupting the thread running * this method. Not allowing cancellation of case closing, so ignore - * the interrupt. Likewsie, cancellation of the case closing task is + * the interrupt. Likewise, cancellation of the case closing task is * not supported. */ } catch (ExecutionException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex); } finally { - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); + ThreadUtils.shutDownTaskExecutor(caseActionExecutor); progressIndicator.finish(); } } @@ -2440,7 +2589,7 @@ public class Case { */ if (null != caseDb) { progressIndicator.progress(Bundle.Case_progressMessage_closingCaseDatabase()); - caseDb.unregisterForEvents(tskEventForwarder); + caseDb.unregisterForEvents(sleuthkitEventListener); caseDb.close(); } @@ -2464,7 +2613,8 @@ public class Case { * Each service gets its own independently cancellable task, and thus * its own task progress indicator. */ - for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) { + for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class + )) { ProgressIndicator progressIndicator; if (RuntimeProperties.runningWithGUI()) { progressIndicator = new ModalDialogProgressIndicator( @@ -2504,37 +2654,47 @@ public class Case { } /** - * Acquires a shared case directory lock for the current case. + * Acquires a case (case directory) lock for the current case. * - * @param caseDir The full path of the case directory. - * - * @throws CaseActionException with a user-friendly message if the lock - * cannot be acquired. + * @throws CaseActionException If the lock cannot be acquired. */ - @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"}) - private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException { + @Messages({ + "Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case.", + "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case." + }) + private void acquireCaseLock(CaseLockType lockType) throws CaseActionException { + String caseDir = metadata.getCaseDirectory(); try { - caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); - if (null == caseDirLock) { - throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock()); + CoordinationService coordinationService = CoordinationService.getInstance(); + caseLock = lockType == CaseLockType.SHARED + ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES) + : coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES); + if (caseLock == null) { + if (lockType == CaseLockType.SHARED) { + throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock()); + } else { + throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock()); + } } } catch (InterruptedException | CoordinationServiceException ex) { - throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock(), ex); + if (lockType == CaseLockType.SHARED) { + throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock(), ex); + } else { + throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock(), ex); + } } } /** - * Releases a shared case directory lock for the current case. - * - * @param caseDir The full path of the case directory. + * Releases a case (case directory) lock for the current case. */ - private void releaseSharedCaseDirLock(String caseDir) { - if (caseDirLock != null) { + private void releaseCaseLock() { + if (caseLock != null) { try { - caseDirLock.release(); - caseDirLock = null; + caseLock.release(); + caseLock = null; } catch (CoordinationService.CoordinationServiceException ex) { - logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", caseDir), ex); + logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", getMetadata().getCaseDirectory()), ex); } } } @@ -2807,7 +2967,9 @@ public class Case { }) private static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator) throws KeywordSearchServiceException { progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex()); - for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class)) { + + for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class + )) { searchService.deleteTextIndex(metadata); } } @@ -2908,9 +3070,41 @@ public class Case { CaseNodeData.writeCaseNodeData(caseNodeData); } catch (CaseNodeDataException ex) { logger.log(Level.SEVERE, String.format("Error updating deleted item flag %s for %s (%s) in %s", flag.name(), caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex); + } } + /** + * Defines the signature for case action methods that can be passed as + * arguments to the doCaseAction method. + * + * @param A ProgressIndicator + * @param The optional parameters stored in an Object. + * @param The return type of Void. + */ + private interface CaseAction { + + /** + * The signature for a case action method. + * + * @param progressIndicator A ProgressIndicator. + * @param additionalParams The optional parameters stored in an Object. + * + * @return A Void object (null). + * + * @throws CaseActionException + */ + R execute(T progressIndicator, V additionalParams) throws CaseActionException; + } + + /** + * The choices for the case (case directory) coordination service lock used + * for multi-user cases. + */ + private enum CaseLockType { + SHARED, EXCLUSIVE; + } + /** * A case operation Cancel button listener for use with a * ModalDialogProgressIndicator when running with a GUI. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java index 19dc651f9a..960634d8f9 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -50,7 +50,7 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.casemodule.CaseCloseAction") @ActionRegistration(displayName = "#CTL_CaseCloseAct", lazy = false) @ActionReferences(value = { - @ActionReference(path = "Toolbars/Case", position = 104)}) + @ActionReference(path = "Toolbars/Case", position = 105)}) public final class CaseCloseAction extends CallableSystemAction implements Presenter.Toolbar { private static final long serialVersionUID = 1L; @@ -95,7 +95,7 @@ public final class CaseCloseAction extends CallableSystemAction implements Prese logger.log(Level.SEVERE, "Unexpected interrupt closing the current case", ex); } catch (ExecutionException ex) { logger.log(Level.SEVERE, "Error closing the current case", ex); - MessageNotifyUtil.Message.error(Bundle.Case_closeException_couldNotCloseCase(ex.getMessage())); + MessageNotifyUtil.Message.error(Bundle.Case_closeException_couldNotCloseCase(ex.getMessage())); } WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); StartupWindowProvider.getInstance().open(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java index 20dfabd93c..5f7ffe9ea7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java @@ -24,6 +24,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.text.DateFormat; @@ -427,9 +428,10 @@ public final class CaseMetadata { transformer.transform(source, streamResult); /* - * Write the DOM to the metadata file. + * Write the DOM to the metadata file. Add UTF-8 Characterset so it writes to the file + * correctly for non-latin characters */ - try (BufferedWriter fileWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(metadataFilePath.toFile())))) { + try (BufferedWriter fileWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(metadataFilePath.toFile()), StandardCharsets.UTF_8))) { fileWriter.write(stringWriter.toString()); fileWriter.flush(); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java new file mode 100644 index 0000000000..e603b4045f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -0,0 +1,152 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule; + +import java.awt.event.ActionEvent; +import java.nio.file.Path; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javax.swing.AbstractAction; +import javax.swing.SwingWorker; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils; +import org.sleuthkit.autopsy.ingest.IngestManager; + +/** + * An Action that allows a user to remove a data source from the current case. + */ +public final class DeleteDataSourceAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); + private long dataSourceObjectID; + private Path caseMetadataFilePath; + + /** + * Constructs an Action that allows a user to remove a data source from a + * case. + * + * @param dataSourceObjectID The object ID of the data source to be removed. + */ + @NbBundle.Messages({ + "DeleteDataSourceAction.name.text=Remove Data Source" + }) + public DeleteDataSourceAction(Long dataSourceObjectID) { + super(Bundle.DeleteDataSourceAction_name_text()); + this.dataSourceObjectID = dataSourceObjectID; + this.setEnabled(FeatureAccessUtils.canDeleteDataSources()); + } + + @NbBundle.Messages({ + "DeleteDataSourceAction.ingestRunningWarningDialog.message=Data sources cannot be removed from a case when ingest is running.", + "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the removal.", + "# {0} - exception message", "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while removing the data source:\n{0}\nPlease see the application log for details.", + "# {0} - exception message", "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case:\n{0}\nPlease see the application log for details." + }) + @Override + public void actionPerformed(ActionEvent event) { + if (IngestManager.getInstance().isIngestRunning()) { + MessageNotifyUtil.Message.warn(Bundle.DeleteDataSourceAction_ingestRunningWarningDialog_message()); + return; + } + + if (MessageNotifyUtil.Message.confirm(Bundle.DeleteDataSourceAction_confirmationDialog_message())) { + new DataSourceDeletionWorker().execute(); + } + } + + /** + * A SwingWorker to do the data source deletion. + */ + private class DataSourceDeletionWorker extends SwingWorker { + + @Override + protected Void doInBackground() throws Exception { + /* + * Save the case metadata file path so the case can be reopened if + * something goes wrong and the case ends up closed. + */ + caseMetadataFilePath = Case.getCurrentCase().getMetadata().getFilePath(); + Case.deleteDataSourceFromCurrentCase(dataSourceObjectID); + return null; + } + + @Override + protected void done() { + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, String.format("Error deleting data source (obj_id=%d)", dataSourceObjectID), ex); + MessageNotifyUtil.Message.show(Bundle.DeleteDataSourceAction_exceptionMessage_dataSourceDeletionError(ex.getLocalizedMessage()), MessageNotifyUtil.MessageType.ERROR); + if (!Case.isCaseOpen()) { + new CaseReopeningWorker().execute(); + } + } + } + + } + + /** + * A SwingWorker to attempt to re-open the case after a data source deletion + * exception. + */ + private class CaseReopeningWorker extends SwingWorker { + + @Override + protected Void doInBackground() throws Exception { + Case.openAsCurrentCase(caseMetadataFilePath.toString()); + return null; + } + + @Override + protected void done() { + try { + get(); + } catch (InterruptedException ex) { + logger.log(Level.WARNING, String.format("Interrupted reopening case after error deleting data source (obj_id=%d)", dataSourceObjectID), ex); + + } catch (ExecutionException ex) { + logger.log(Level.SEVERE, String.format("Error reopening case after error deleting data source (obj_id=%d)", dataSourceObjectID), ex); + MessageNotifyUtil.Message.show(Bundle.DeleteDataSourceAction_exceptionMessage_dataSourceDeletionError(ex.getCause().getLocalizedMessage()), MessageNotifyUtil.MessageType.ERROR); + StartupWindowProvider.getInstance().open(); + } + } + + } + + @Override + public DeleteDataSourceAction clone() throws CloneNotSupportedException { + DeleteDataSourceAction clonedObject = ((DeleteDataSourceAction) super.clone()); + clonedObject.setDataSourceID(this.dataSourceObjectID); + return clonedObject; + } + + /** + * Allows the setting of the data source object ID field of a clone of this + * action. + * + * @param dataSourceObjectID The data source object ID. + */ + private void setDataSourceID(long dataSourceObjectID) { + this.dataSourceObjectID = dataSourceObjectID; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index 789913d78a..41c1697a76 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.casemodule; +import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils; import java.awt.Component; import org.openide.util.NbBundle; @@ -61,7 +62,7 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { */ void readSettings() { caseNameTextField.setText(""); - if (UserPreferences.getIsMultiUserModeEnabled() && !AccessLimiterUtils.limitMultiUserAccess()) { + if (FeatureAccessUtils.canCreateMultiUserCases()) { multiUserCaseRadioButton.setEnabled(true); multiUserCaseRadioButton.setSelected(true); } else { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/UnpackagePortableCaseProgressDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/UnpackagePortableCaseProgressDialog.java index 656b376432..fe26843e28 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/UnpackagePortableCaseProgressDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/UnpackagePortableCaseProgressDialog.java @@ -26,6 +26,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Paths; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import javax.swing.JFrame; @@ -36,7 +37,10 @@ import org.openide.modules.InstalledFileLocator; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.coreutils.TimeStampUtils; import org.sleuthkit.datamodel.TskCoreException; /** @@ -46,6 +50,9 @@ import org.sleuthkit.datamodel.TskCoreException; class UnpackagePortableCaseProgressDialog extends javax.swing.JDialog implements PropertyChangeListener { private UnpackageWorker worker; + private final static String CASES_OPENED_LOG_FILE = "portable_cases_opened"; //NON-NLS + private final static String PORTABLE_CASE_NAME = "portable_case_name"; //NON-NLS + private final static String PORTABLE_CASE_DIR = "portable_case_dir_opened"; //NON-NLS /** * Creates new form UnpackagePortableCaseProgressDialog @@ -142,10 +149,23 @@ class UnpackagePortableCaseProgressDialog extends javax.swing.JDialog implements @NbBundle.Messages({ "UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executable", "UnpackageWorker.doInBackground.errorCompressingCase=Error unpackaging case", - "UnpackageWorker.doInBackground.canceled=Unpackaging canceled by user",}) + "UnpackageWorker.doInBackground.canceled=Unpackaging canceled by user", + "UnpackageWorker.doInBackground.previousSeenCase=Case with name {0} has been previously opened do you want to open it again?"}) @Override protected Void doInBackground() throws Exception { + // Check to see if this case has been already opened before + String caseUnpackedBefore = getCaseIfUnpackedBefore(packagedCase); + if ((!caseUnpackedBefore.isEmpty()) + && (MessageNotifyUtil.Message.confirm(Bundle.UnpackageWorker_doInBackground_previousSeenCase(packagedCase)))) { + try { + Case.openAsCurrentCase(caseUnpackedBefore); + success.set(true); + return null; + } catch (CaseActionException ex) { + throw new TskCoreException("Error opening case after unpacking it.", ex); // NON-NLS + } + } // Find 7-Zip File sevenZipExe = locate7ZipExecutable(); if (sevenZipExe == null) { @@ -191,16 +211,27 @@ class UnpackagePortableCaseProgressDialog extends javax.swing.JDialog implements setDisplayError(Bundle.UnpackageWorker_doInBackground_errorCompressingCase()); throw new TskCoreException("Error unpackaging case", ex); // NON-NLS } - + try { String caseFileDirectory = FilenameUtils.getBaseName(packagedCase); String caseDirectory = StringUtils.substringBefore(caseFileDirectory, ".zip"); - Case.openAsCurrentCase(outputFolder + File.separator + caseDirectory + File.separator + caseDirectory + ".aut"); // NON-NLS + String caseFileToOpen = outputFolder + File.separator + caseDirectory + File.separator + caseDirectory + ".aut"; + Case.openAsCurrentCase(caseFileToOpen); // NON-NLS + String timestampFileOpened = TimeStampUtils.createTimeStamp(); + if (ModuleSettings.configExists(CASES_OPENED_LOG_FILE)) { + ModuleSettings.setConfigSetting(CASES_OPENED_LOG_FILE, timestampFileOpened + "-" + PORTABLE_CASE_NAME, packagedCase); + ModuleSettings.setConfigSetting(CASES_OPENED_LOG_FILE, timestampFileOpened + "-" + PORTABLE_CASE_DIR, caseFileToOpen); + } else { + ModuleSettings.makeConfigFile(CASES_OPENED_LOG_FILE); + ModuleSettings.setConfigSetting(CASES_OPENED_LOG_FILE, timestampFileOpened + "-" + PORTABLE_CASE_NAME, packagedCase); + ModuleSettings.setConfigSetting(CASES_OPENED_LOG_FILE, timestampFileOpened + "-" + PORTABLE_CASE_DIR, caseFileToOpen); + } } catch (CaseActionException ex) { throw new TskCoreException("Error opening case after unpacking it.", ex); // NON-NLS } - + success.set(true); + return null; } @@ -261,6 +292,33 @@ class UnpackagePortableCaseProgressDialog extends javax.swing.JDialog implements return exeFile; } + + /** + * Check to see if the case has been unpacked before + */ + private String getCaseIfUnpackedBefore(String packedCaseName) { + if (!ModuleSettings.configExists(CASES_OPENED_LOG_FILE)) { + return ""; + } + + Map configEntries = ModuleSettings.getConfigSettings(CASES_OPENED_LOG_FILE); + + for (Map.Entry entries : configEntries.entrySet()) { + if (entries.getValue().contains(packedCaseName)) { + String entryFound = entries.getKey().substring(0, entries.getKey().indexOf('-')); + String caseFileName = ModuleSettings.getConfigSetting(CASES_OPENED_LOG_FILE, entryFound + "-" + PORTABLE_CASE_DIR); + File caseFile = new File(caseFileName); + if (caseFile.exists()) { + return caseFileName; + } else { + return ""; + } + } + } + + return ""; + } + } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle_ja.properties new file mode 100644 index 0000000000..99fd24c7ef --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle_ja.properties @@ -0,0 +1,73 @@ +CTL_DataSourceSummaryAction=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30b5\u30de\u30ea\u30fc +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=\u3059\u3079\u3066 +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=\u5272\u308a\u5f53\u3066\u6e08\u307f +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=\u30ab\u30a6\u30f3\u30c8 +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=\u672a\u4f7f\u7528 +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=\u97f3\u58f0 +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.count.header=\u30ab\u30a6\u30f3\u30c8 +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=\u6587\u66f8 +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=\u5b9f\u884c\u53ef\u80fd\u30d5\u30a1\u30a4\u30eb +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=\u30a4\u30e1\u30fc\u30b8 +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=\u52d5\u753b +DataSourceSummaryDetailsPanel.getDataSources.error.text=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30ea\u30b9\u30c8\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +DataSourceSummaryDetailsPanel.getDataSources.error.title=\u8aad\u307f\u8fbc\u307f\u5931\u6557 +DataSourceSummaryDetailsPanel.units.bytes=\ \u30d0\u30a4\u30c8 +DataSourceSummaryDetailsPanel.units.gigabytes=\ GB +DataSourceSummaryDetailsPanel.units.kilobytes=\ kB +DataSourceSummaryDetailsPanel.units.megabytes=\ MB +DataSourceSummaryDetailsPanel.units.petabytes=\ PB +DataSourceSummaryDetailsPanel.units.terabytes=\ TB +DataSourceSummaryDialog.closeButton.text=\u9589\u3058\u308b +DataSourceSummaryDetailsPanel.displayNameLabel.text=\u8868\u793a\u540d: +DataSourceSummaryDetailsPanel.originalNameLabel.text=\u540d\u524d: +DataSourceSummaryDetailsPanel.deviceIdLabel.text=\u30c7\u30d0\u30a4\u30b9ID: +DataSourceSummaryDetailsPanel.operatingSystemLabel.text=OS: +DataSourceSummaryDetailsPanel.dataSourceUsageLabel.text=\u4f7f\u7528\u91cf: +DataSourceSummaryDetailsPanel.timeZoneLabel.text=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3: +DataSourceSummaryDetailsPanel.imageTypeLabel.text=\u30a4\u30e1\u30fc\u30b8\u30bf\u30a4\u30d7: +DataSourceSummaryDetailsPanel.sizeLabel.text=\u30b5\u30a4\u30ba: +DataSourceSummaryDetailsPanel.sectorSizeLabel.text=\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30ba: +DataSourceSummaryDetailsPanel.md5HashLabel.text=MD5: +DataSourceSummaryDetailsPanel.sha1HashLabel.text=SHA1: +DataSourceSummaryDetailsPanel.sha256HashLabel.text=SHA256: +DataSourceSummaryDetailsPanel.filePathsLabel.text=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9: +DataSourceSummaryDetailsPanel.displayNameValue.text= +DataSourceSummaryDetailsPanel.originalNameValue.text= +DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText= +DataSourceSummaryDetailsPanel.deviceIdValue.text= +DataSourceSummaryDetailsPanel.dataSourceUsageValue.text= +DataSourceSummaryDetailsPanel.operatingSystemValue.toolTipText= +DataSourceSummaryDetailsPanel.operatingSystemValue.text= +DataSourceSummaryDetailsPanel.timeZoneValue.text= +DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText= +DataSourceSummaryDetailsPanel.imageTypeValue.text= +DataSourceSummaryDetailsPanel.sizeValue.text= +DataSourceSummaryDetailsPanel.sectorSizeValue.text= +DataSourceSummaryDetailsPanel.md5HashValue.toolTipText= +DataSourceSummaryDetailsPanel.md5HashValue.text= +DataSourceSummaryDetailsPanel.sha1HashValue.text= +DataSourceSummaryDetailsPanel.sha256HashValue.text= +DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0= +DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text= +DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=\u53d6\u5f97\u306e\u8a73\u7d30: +DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=\u672a\u4f7f\u7528\u9818\u57df: +DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= +DataSourceSummaryCountsPanel.byMimeTypeLabel.text=MIME\u30bf\u30a4\u30d7\u5225\u30d5\u30a1\u30a4\u30eb +DataSourceSummaryCountsPanel.byCategoryLabel.text=\u30ab\u30c6\u30b4\u30ea\u30fc\u5225\u30d5\u30a1\u30a4\u30eb +DataSourceSummaryCountsPanel.jLabel1.text=\u30bf\u30a4\u30d7\u5225\u7d50\u679c +DataSourceSummaryDialog.countsTab.title=\u30ab\u30a6\u30f3\u30c8 +DataSourceSummaryDialog.detailsTab.title=\u8a73\u7d30 +DataSourceSummaryDialog.ingestHistoryTab.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5c65\u6b74 +DataSourceSummaryDialog.window.title=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30b5\u30de\u30ea\u30fc +DataSourceSummaryNode.column.dataSourceName.header=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d +DataSourceSummaryNode.column.files.header=\u30d5\u30a1\u30a4\u30eb +DataSourceSummaryNode.column.results.header=\u7d50\u679c +DataSourceSummaryNode.column.status.header=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b9\u30c6\u30fc\u30bf\u30b9 +DataSourceSummaryNode.column.tags.header=\u30bf\u30b0 +DataSourceSummaryNode.column.type.header=\u30bf\u30a4\u30d7 +DataSourceSummaryNode.viewDataSourceAction.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u79fb\u52d5 +ViewSummaryInformationAction.name.text=\u30b5\u30de\u30ea\u30fc\u60c5\u5831\u3092\u8868\u793a diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java new file mode 100644 index 0000000000..3a0372fa94 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java @@ -0,0 +1,53 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule.events; + +import java.io.Serializable; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.events.AutopsyEvent; + +/** + * An application event that is published when a data source has been deleted. + */ +public class DataSourceDeletedEvent extends AutopsyEvent implements Serializable { + + private static final long serialVersionUID = 1L; + private final long dataSourceID; + + /** + * Constructs an application event that is published when a data source has + * been deleted. + * + * @param dataSourceId The object ID of the data source that was deleted. + */ + public DataSourceDeletedEvent(Long dataSourceId) { + super(Case.Events.DATA_SOURCE_DELETED.toString(), dataSourceId, null); + this.dataSourceID = dataSourceId; + } + + /** + * Gets the object ID of the data source that was deleted. + * + * @return The data source id. + */ + public long getDataSourceId() { + return dataSourceID; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercasesbrowser/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercasesbrowser/Bundle_ja.properties new file mode 100644 index 0000000000..c1f8d6b162 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercasesbrowser/Bundle_ja.properties @@ -0,0 +1,12 @@ +MultiUserCaseBrowserCustomizer.column.caseDbDeleteStatus=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u524a\u9664\u3057\u307e\u3057\u305f +MultiUserCaseBrowserCustomizer.column.caseDirDeleteStatus=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u524a\u9664\u3057\u307e\u3057\u305f +MultiUserCaseBrowserCustomizer.column.createTime=\u6642\u523b\u3092\u4f5c\u6210 +MultiUserCaseBrowserCustomizer.column.dataSourcesDeleteStatus=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u524a\u9664\u3057\u307e\u3057\u305f +MultiUserCaseBrowserCustomizer.column.directory=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc +MultiUserCaseBrowserCustomizer.column.displayName=\u540d\u524d +MultiUserCaseBrowserCustomizer.column.lastAccessTime=\u6700\u7d42\u30a2\u30af\u30bb\u30b9\u6642\u523b +MultiUserCaseBrowserCustomizer.column.manifestFileZNodesDeleteStatus=\u30de\u30cb\u30d5\u30a7\u30b9\u30c8Znode\u3092\u524a\u9664\u3057\u307e\u3057\u305f +MultiUserCaseBrowserCustomizer.column.textIndexDeleteStatus=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u3092\u524a\u9664\u3057\u307e\u3057\u305f +MultiUserCaseNode.column.createTime=False +MultiUserCaseNode.columnValue.true=True +MultiUserCasesBrowserPanel.waitNode.message=\u304a\u5f85\u3061\u304f\u3060\u3055\u3044... diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties index 41e871691b..c173a9e7de 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle_ja.properties @@ -1,3 +1,31 @@ -TagsManager.predefTagNames.bookmark.text=\u30d6\u30c3\u30af\u30de\u30fc\u30af -Blackboard.unableToIndexArtifact.error.msg=blackboard\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8{0}\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -TagOptionsPanel.ingestRunningWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u4e2d\u306b\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093\uff01 +OptionsCategory_Name_TagNamesOptions=\u30bf\u30b0 +OptionsCategory_TagNames=TagNames +TagNameDefinition.predefTagNames.bookmark.text=\u30d6\u30c3\u30af\u30de\u30fc\u30af +TagNameDefinition.predefTagNames.followUp.text=\u30d5\u30a9\u30ed\u30fc\u30a2\u30c3\u30d7 +TagNameDefinition.predefTagNames.notableItem.text=\u9855\u8457\u306a\u9805\u76ee +TagNameDialog.descriptionLabel.text=\u8a18\u8ff0: +TagNameDialog.editTitle.text=\u30bf\u30b0\u3092\u7de8\u96c6 +TagNameDialog.JOptionPane.tagDescriptionIllegalCharacters.message=\u30bf\u30b0\u306e\u8a18\u8ff0\u306b\u30ab\u30f3\u30de(,)\u3084\u30bb\u30df\u30b3\u30ed\u30f3(;)\u3092\u542b\u3081\u3089\u308c\u307e\u305b\u3093 +TagNameDialog.JOptionPane.tagDescriptionIllegalCharacters.title=\u30bf\u30b0\u306e\u8a18\u8ff0\u306b\u7121\u52b9\u306a\u6587\u5b57\u304c\u3042\u308a\u307e\u3059 +TagNameDialog.notableCheckbox.text=\u30bf\u30b0\u306f\u9805\u76ee\u304c\u9855\u8457\u3067\u3042\u308b\u3068\u793a\u5506\u3057\u3066\u3044\u307e\u3059\u3002 +TagNameDialog.title.text=\u65b0\u898f\u30bf\u30b0 +TagNameDialog.JOptionPane.tagNameIllegalCharacters.message=\u30bf\u30b0\u540d\u306b\u6b21\u306e\u8a18\u53f7\u3092\u542b\u3081\u3089\u308c\u307e\u305b\u3093: \\ : * ? " < > | , ; +TagNameDialog.JOptionPane.tagNameIllegalCharacters.title=\u30bf\u30b0\u540d\u306b\u7121\u52b9\u306a\u6587\u5b57\u304c\u3042\u308a\u307e\u3059 +TagNameDialog.JOptionPane.tagNameEmpty.message=\u30bf\u30b0\u540d\u306f\u7a7a(\u672a\u5165\u529b)\u306e\u72b6\u614b\u306b\u3067\u304d\u307e\u305b\u3093\u3002 +TagNameDialog.JOptionPane.tagNameEmpty.title=\u7a7a(\u672a\u5165\u529b)\u306e\u72b6\u614b\u306e\u30bf\u30b0\u540d +TagOptionsPanel.descriptionLabel.text=\u30bf\u30b0\u306e\u8a18\u8ff0: +TagOptionsPanel.editTagNameButton.text=\u30bf\u30b0\u3092\u7de8\u96c6 +TagOptionsPanel.ingestRunningWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5b9f\u884c\u4e2d\u306f\u65e2\u5b58\u306e\u30bf\u30b0\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093! +TagOptionsPanel.isNotableLabel.text=\u30bf\u30b0\u306f\u9805\u76ee\u304c\u9855\u8457\u3067\u3042\u308b\u3068\u793a\u5506\u3057\u3066\u3044\u307e\u3059: +TagOptionsPanel.notableYesOrNoLabel.text= +TagOptionsPanel.panelDescriptionTextArea.text=\u30bf\u30b0\u3092\u4f5c\u6210\u3057\u3066\u7ba1\u7406\u3057\u307e\u3059\u3002\u30bf\u30b0\u3092\u30b1\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u3068\u7d50\u679c\u306b\u9069\u7528\u3067\u304d\u307e\u3059\u3002\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306e\u4f7f\u7528\u4e2d\u3001\u9855\u8457\u306a\u30bf\u30b0\u3067\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u9805\u76ee\u306b\u9855\u8457\u3068\u30d5\u30e9\u30b0\u304c\u7acb\u3066\u3089\u308c\u307e\u3059\u3002\u30bf\u30b0\u306e\u30b9\u30c6\u30fc\u30bf\u30b9\u5909\u66f4\u306f\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306e\u9805\u76ee\u306b\u306e\u307f\u5f71\u97ff\u3057\u307e\u3059\u3002 +TagOptionsPanel.TagNameDialog.tagNameAlreadyExists.message=\u30bf\u30b0\u540d\u306f\u4e00\u610f\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002\u3053\u306e\u540d\u524d\u306e\u30bf\u30b0\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 +TagOptionsPanel.TagNameDialog.tagNameAlreadyExists.title=\u30bf\u30b0\u540d\u3092\u8907\u88fd +TagOptionsPanel.tagTypesListLabel.text=\u30bf\u30b0\u540d: +TagOptionsPanel.deleteTagNameButton.text=\u30bf\u30b0\u3092\u524a\u9664 +TagOptionsPanel.newTagNameButton.text=\u65b0\u898f\u30bf\u30b0 +TagNameDialog.okButton.text=OK +TagNameDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +TagNameDialog.tagNameTextField.text= +TagNameDialog.newTagNameLabel.text=\u540d\u524d: +TagsManager.notableTagEnding.text=\ (\u9855\u8457) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java index 81bc6a643c..8cbe25e345 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java @@ -103,8 +103,8 @@ public class Services implements Closeable { /** * Closes the services for the current case. * - * @throws IOException if there is a problem closing the services. - * @deprecated Do not use. + * @throws IOException if there is a problem closing the services. + * @deprecated Do not use. */ @Deprecated @Override diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle_ja.properties new file mode 100644 index 0000000000..ed1195288f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle_ja.properties @@ -0,0 +1,53 @@ +DataContentViewerOtherCases.caseDetailsDialog.noCaseNameError=\u30a8\u30e9\u30fc +DataContentViewerOtherCases.caseDetailsDialog.noDetails=\u3053\u306e\u30b1\u30fc\u30b9\u306e\u8a73\u7d30\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +DataContentViewerOtherCases.caseDetailsDialog.noDetailsReference=\u30b0\u30ed\u30fc\u30d0\u30eb\u30ec\u30d5\u30a1\u30ec\u30f3\u30b9\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u30b1\u30fc\u30b9\u8a73\u7d30\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +DataContentViewerOtherCases.caseDetailsDialog.notSelected=\u884c\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +# {0} - \u5171\u6709\u6027\u306e\u5272\u5408 +# {1} - \u76f8\u95a2\u5206\u6790\u30bf\u30a4\u30d7 +# {2} - \u76f8\u95a2\u5206\u6790\u5024 +DataContentViewerOtherCases.correlatedArtifacts.byType=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e {0}% \u306b {2} \u304c\u3042\u308a\u307e\u3059(\u30bf\u30a4\u30d7: {1})\n +DataContentViewerOtherCases.correlatedArtifacts.failed=\u983b\u5ea6\u306e\u8a73\u7d30\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +DataContentViewerOtherCases.correlatedArtifacts.isEmpty=\u76f8\u95a2\u5206\u6790\u3059\u308b\u305f\u3081\u306e\u30d5\u30a1\u30a4\u30eb\u3084\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +DataContentViewerOtherCases.correlatedArtifacts.title=\u5c5e\u6027\u983b\u5ea6 +DataContentViewerOtherCases.dataSources.header.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d +DataContentViewerOtherCases.earliestCaseNotAvailable=\ \u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u305b\u3093\u3002 +DataContentViewerOtherCases.foundIn.text=%d \u306e\u30b1\u30fc\u30b9\u3068 %d \u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5185\u306b %d \u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002 +DataContentViewerOtherCases.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +DataContentViewerOtherCases.showCaseDetailsMenuItem.text=\u30b1\u30fc\u30b9\u8a73\u7d30\u3092\u8868\u793a +DataContentViewerOtherCases.table.noArtifacts=\u9805\u76ee\u306b\u691c\u7d22\u306b\u5229\u7528\u3067\u304d\u308b\u5c5e\u6027\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +DataContentViewerOtherCases.table.noResultsFound=\u8a72\u5f53\u3059\u308b\u7d50\u679c\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +DataContentViewerOtherCases.table.toolTip.text=\u5217\u540d\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30bd\u30fc\u30c8\u3057\u307e\u3059\u3002\u30c6\u30fc\u30d6\u30eb\u3092\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3066\u3055\u3089\u306a\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +DataContentViewerOtherCases.exportToCSVMenuItem.text=\u305d\u306e\u4ed6\u3059\u3079\u3066\u306e\u767a\u751f\u3092CSV\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +DataContentViewerOtherCases.showCommonalityMenuItem.text=\u983b\u5ea6\u3092\u8868\u793a +DataContentViewerOtherCases.earliestCaseDate.text=\u6700\u3082\u53e4\u3044\u30b1\u30fc\u30b9\u65e5\u4ed8 +DataContentViewerOtherCases.earliestCaseLabel.toolTipText= +DataContentViewerOtherCases.earliestCaseLabel.text=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u958b\u59cb\u65e5: +DataContentViewerOtherCases.foundInLabel.text= +DataContentViewerOtherCases.title=\u305d\u306e\u4ed6\u306e\u767a\u751f +DataContentViewerOtherCases.toolTip=\u305d\u306e\u4ed6\u306e\u767a\u751f\u304b\u3089\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb/\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +DataContentViewerOtherCasesModel.csvHeader.attribute=\u4e00\u81f4\u3057\u305f\u5c5e\u6027 +DataContentViewerOtherCasesModel.csvHeader.case=\u30b1\u30fc\u30b9 +DataContentViewerOtherCasesModel.csvHeader.comment=\u30b3\u30e1\u30f3\u30c8 +DataContentViewerOtherCasesModel.csvHeader.dataSource=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 +DataContentViewerOtherCasesModel.csvHeader.device=\u30c7\u30d0\u30a4\u30b9 +DataContentViewerOtherCasesModel.csvHeader.known=\u65e2\u77e5 +DataContentViewerOtherCasesModel.csvHeader.path=\u30d1\u30b9 +DataContentViewerOtherCasesModel.csvHeader.value=\u5c5e\u6027\u5024 +OccurrencePanel.caseCreatedDateLabel.text=\u4f5c\u6210\u65e5: +OccurrencePanel.caseDetails.text=\u30b1\u30fc\u30b9\u8a73\u7d30 +OccurrencePanel.caseNameLabel.text=\u540d\u524d: +OccurrencePanel.commonProperties.text=\u5171\u901a\u306e\u30d7\u30ed\u30d1\u30c6\u30a3 +OccurrencePanel.commonPropertyCommentLabel.text=\u30b3\u30e1\u30f3\u30c8: +OccurrencePanel.commonPropertyKnownStatusLabel.text=\u65e2\u77e5\u306e\u30b9\u30c6\u30fc\u30bf\u30b9: +OccurrencePanel.commonPropertyTypeLabel.text=\u30bf\u30a4\u30d7: +OccurrencePanel.commonPropertyValueLabel.text=\u5024: +OccurrencePanel.dataSourceDetails.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u8a73\u7d30 +OccurrencePanel.dataSourceNameLabel.text=\u540d\u524d: +OccurrencePanel.fileDetails.text=\u30d5\u30a1\u30a4\u30eb\u8a73\u7d30 +OccurrencePanel.filePathLabel.text=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9: +OtherOccurrencesCasesTableModel.case=\u30b1\u30fc\u30b9 +OtherOccurrencesCasesTableModel.noData=\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +OtherOccurrencesDataSourcesTableModel.dataSourceName=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d +OtherOccurrencesDataSourcesTableModel.noData=\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +OtherOccurrencesFilesTableModel.fileName=\u30d5\u30a1\u30a4\u30eb\u540d +OtherOccurrencesFilesTableModel.noData=\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index 6e77c317a7..fee297880c 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -2269,6 +2269,45 @@ abstract class AbstractSqlEamDb implements EamDb { EamDbUtil.closeConnection(conn); } } + + /** + * Process a SELECT query + * + * @param selectClause query string to execute + * @param instanceTableCallback callback to process the instance + * + * @throws EamDbException + */ + @Override + public void processSelectClause(String selectClause, InstanceTableCallback instanceTableCallback) throws EamDbException { + + if (instanceTableCallback == null) { + throw new EamDbException("Callback interface is null"); + } + + if (selectClause == null) { + throw new EamDbException("Select clause is null"); + } + + Connection conn = connect(); + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + StringBuilder sql = new StringBuilder(300); + sql.append("select ") + .append(selectClause); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + resultSet = preparedStatement.executeQuery(); + instanceTableCallback.process(resultSet); + } catch (SQLException ex) { + throw new EamDbException("Error running query", ex); + } finally { + EamDbUtil.closeStatement(preparedStatement); + EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeConnection(conn); + } + } @Override public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException { @@ -3298,10 +3337,10 @@ abstract class AbstractSqlEamDb implements EamDb { try { minorVersion = Integer.parseInt(minorVersionStr); } catch (NumberFormatException ex) { - throw new EamDbException(Bundle.AbstractSqlEamDb_badMinorSchema_message(minorVersionStr), ex); + throw new EamDbException("Bad value for schema minor version (" + minorVersionStr + ") - database is corrupt", Bundle.AbstractSqlEamDb_badMinorSchema_message(minorVersionStr), ex); } } else { - throw new EamDbException(Bundle.AbstractSqlEamDb_failedToReadMinorVersion_message()); + throw new EamDbException("Failed to read schema minor version from db_info table", Bundle.AbstractSqlEamDb_failedToReadMinorVersion_message()); } int majorVersion = 0; @@ -3312,10 +3351,10 @@ abstract class AbstractSqlEamDb implements EamDb { try { majorVersion = Integer.parseInt(majorVersionStr); } catch (NumberFormatException ex) { - throw new EamDbException(Bundle.AbstractSqlEamDb_badMajorSchema_message(majorVersionStr), ex); + throw new EamDbException("Bad value for schema version (" + majorVersionStr + ") - database is corrupt", Bundle.AbstractSqlEamDb_badMajorSchema_message(majorVersionStr), ex); } } else { - throw new EamDbException(Bundle.AbstractSqlEamDb_failedToReadMajorVersion_message()); + throw new EamDbException("Failed to read schema major version from db_info table", Bundle.AbstractSqlEamDb_failedToReadMajorVersion_message()); } /* @@ -3393,7 +3432,7 @@ abstract class AbstractSqlEamDb implements EamDb { addObjectIdIndexTemplate = SqliteEamDbSettings.getAddObjectIdIndexTemplate(); break; default: - throw new EamDbException(Bundle.AbstractSqlEamDb_cannotUpgrage_message(selectedPlatform.name())); + throw new EamDbException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded.", Bundle.AbstractSqlEamDb_cannotUpgrage_message(selectedPlatform.name())); } final String dataSourcesTableName = "data_sources"; final String dataSourceObjectIdColumnName = "datasource_obj_id"; @@ -3553,7 +3592,7 @@ abstract class AbstractSqlEamDb implements EamDb { statement.execute("DROP TABLE old_data_sources"); break; default: - throw new EamDbException(Bundle.AbstractSqlEamDb_cannotUpgrage_message(selectedPlatform.name())); + throw new EamDbException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded.", Bundle.AbstractSqlEamDb_cannotUpgrage_message(selectedPlatform.name())); } } updateSchemaVersion(conn); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Bundle.properties-MERGED index c94c442588..9b43833ee2 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Bundle.properties-MERGED @@ -39,6 +39,7 @@ EamDbUtil.centralRepoUpgradeFailed.message=Failed to upgrade Central Repository. EamDbUtil.exclusiveLockAquisitionFailure.message=Unable to acquire exclusive lock for Central Repository. PostgresEamDb.centralRepoDisabled.message=Central Repository module is not enabled. PostgresEamDb.connectionFailed.message=Error getting connection to database. +PostgresEamDb.multiUserLockError.message=Error acquiring database lock SqliteEamDb.centralRepositoryDisabled.message=Central Repository module is not enabled. SqliteEamDb.connectionFailedMessage.message=Error getting connection to database. SqliteEamDb.databaseMissing.message=Central repository database missing diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index 3cbc510e4c..727b5ecb0b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -277,10 +277,10 @@ public class CorrelationAttributeInstance implements Serializable { * @param enabled Is this Type currently enabled. */ @Messages({"CorrelationAttributeInstance.nullName.message=Database name is null.", - "CorrelationAttributeInstance.invalidName.message=Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."}) + "CorrelationAttributeInstance.invalidName.message=Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."}) public Type(int typeId, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { if (dbTableName == null) { - throw new EamDbException(Bundle.CorrelationAttributeInstance_nullName_message()); + throw new EamDbException("dbTableName is null", Bundle.CorrelationAttributeInstance_nullName_message()); } this.typeId = typeId; this.displayName = displayName; @@ -288,7 +288,7 @@ public class CorrelationAttributeInstance implements Serializable { this.supported = supported; this.enabled = enabled; if (!Pattern.matches(DB_NAMES_REGEX, dbTableName)) { - throw new EamDbException(Bundle.CorrelationAttributeInstance_invalidName_message()); // NON-NLS + throw new EamDbException("Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'.", Bundle.CorrelationAttributeInstance_invalidName_message()); // NON-NLS } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java index 5317fcffe8..c28c0537f7 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java @@ -794,4 +794,13 @@ public interface EamDb { */ void processInstanceTableWhere(CorrelationAttributeInstance.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException; + /** + * Process a SELECT query + * + * @param selectClause query string to execute + * @param instanceTableCallback callback to process the instance + * + * @throws EamDbException + */ + public void processSelectClause(String selectClause, InstanceTableCallback instanceTableCallback) throws EamDbException; } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbException.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbException.java index a410f7f182..262b550f5c 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbException.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbException.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,10 +18,12 @@ */ package org.sleuthkit.autopsy.centralrepository.datamodel; -/* +import org.sleuthkit.autopsy.exceptions.AutopsyException; + +/** * An exception to be thrown by an artifact manager. */ -public class EamDbException extends Exception { +public class EamDbException extends AutopsyException { private static final long serialVersionUID = 1L; @@ -34,6 +36,28 @@ public class EamDbException extends Exception { super(message); } + /** + * Constructs an exception to be thrown by an artifact manager with a user exception. + * + * @param message Exception message. + * @param userMessage the user friendly message to include in this exception + */ + public EamDbException(String message, String userMessage) { + super(message, userMessage); + } + + /** + * Constructs an exception to be thrown by an artifact manager with a user + * exception. + * + * @param message Exception message. + * @param userMessage the user friendly message to include in this exception + * @param cause Exception cause. + */ + public EamDbException(String message, String userMessage, Throwable cause) { + super(message, userMessage, cause); + } + /** * Constructs an exception to be thrown by an artifact manager. * diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java index ad198b1744..c287990d79 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java @@ -181,32 +181,39 @@ public class EamDbUtil { } EamDb db = null; CoordinationService.Lock lock = null; - String messageForDialog = ""; + //get connection try { - db = EamDb.getInstance(); - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error updating central repository, unable to make connection", ex); - messageForDialog = Bundle.EamDbUtil_centralRepoConnectionFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(); - } - //get lock necessary for upgrade - if (db != null) { try { - // This may return null if locking isn't supported, which is fine. It will - // throw an exception if locking is supported but we can't get the lock - // (meaning the database is in use by another user) - lock = db.getExclusiveMultiUserDbLock(); - //perform upgrade + db = EamDb.getInstance(); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error updating central repository, unable to make connection", ex); + throw new EamDbException("Error updating central repository, unable to make connection", Bundle.EamDbUtil_centralRepoConnectionFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex); + } + //get lock necessary for upgrade + if (db != null) { + try { + // This may return null if locking isn't supported, which is fine. It will + // throw an exception if locking is supported but we can't get the lock + // (meaning the database is in use by another user) + lock = db.getExclusiveMultiUserDbLock(); + //perform upgrade + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error updating central repository, unable to acquire exclusive lock", ex); + throw new EamDbException("Error updating central repository, unable to acquire exclusive lock", Bundle.EamDbUtil_exclusiveLockAquisitionFailure_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex); + } + try { db.upgradeSchema(); - } catch (EamDbException | SQLException | IncompatibleCentralRepoException ex) { + } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error updating central repository", ex); - messageForDialog = Bundle.EamDbUtil_centralRepoUpgradeFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(); - if (ex instanceof IncompatibleCentralRepoException) { - messageForDialog = ex.getMessage() + "\n\n" + messageForDialog; - } else if (ex instanceof EamDbException) { - messageForDialog = ex.getMessage() + Bundle.EamDbUtil_centralRepoDisabled_message(); - } + throw new EamDbException("Error updating central repository", ex.getUserMessage() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex); + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Error updating central repository", ex); + throw new EamDbException("Error updating central repository", Bundle.EamDbUtil_centralRepoUpgradeFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex); + } catch (IncompatibleCentralRepoException ex) { + LOGGER.log(Level.SEVERE, "Error updating central repository", ex); + throw new EamDbException("Error updating central repository", ex.getMessage() + "\n\n" + Bundle.EamDbUtil_centralRepoUpgradeFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex); } finally { if (lock != null) { try { @@ -216,16 +223,11 @@ public class EamDbUtil { } } } - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error updating central repository, unable to acquire exclusive lock", ex); - messageForDialog = Bundle.EamDbUtil_exclusiveLockAquisitionFailure_message() + Bundle.EamDbUtil_centralRepoDisabled_message(); + } else { + throw new EamDbException("Unable to connect to database", Bundle.EamDbUtil_centralRepoConnectionFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message()); } - - } else { - messageForDialog = Bundle.EamDbUtil_centralRepoConnectionFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(); - } - // Disable the central repo and clear the current settings. - if (!messageForDialog.isEmpty()) { + } catch (EamDbException ex) { + // Disable the central repo and clear the current settings. try { if (null != EamDb.getInstance()) { EamDb.getInstance().shutdownConnections(); @@ -235,8 +237,7 @@ public class EamDbUtil { } EamDbPlatformEnum.setSelectedPlatform(EamDbPlatformEnum.DISABLED.name()); EamDbPlatformEnum.saveSelectedPlatform(); - - throw new EamDbException(messageForDialog); + throw ex; } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java index c6b0d15669..dabfeba7a1 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java @@ -190,7 +190,7 @@ final class PostgresEamDb extends AbstractSqlEamDb { protected Connection connect() throws EamDbException { synchronized (this) { if (!EamDb.isEnabled()) { - throw new EamDbException(Bundle.PostgresEamDb_centralRepoDisabled_message()); // NON-NLS + throw new EamDbException("Central Repository module is not enabled", Bundle.PostgresEamDb_centralRepoDisabled_message()); // NON-NLS } if (connectionPool == null) { @@ -200,7 +200,7 @@ final class PostgresEamDb extends AbstractSqlEamDb { try { return connectionPool.getConnection(); } catch (SQLException ex) { - throw new EamDbException(Bundle.PostgresEamDb_connectionFailed_message(), ex); // NON-NLS + throw new EamDbException("Error getting connection from connection pool.", Bundle.PostgresEamDb_connectionFailed_message(), ex); // NON-NLS } } @@ -221,6 +221,7 @@ final class PostgresEamDb extends AbstractSqlEamDb { * to get the lock */ @Override + @Messages({"PostgresEamDb.multiUserLockError.message=Error acquiring database lock"}) public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException { try { // First check if multi user mode is enabled - if not there's no point trying to get a lock @@ -234,9 +235,9 @@ final class PostgresEamDb extends AbstractSqlEamDb { if (lock != null) { return lock; } - throw new EamDbException("Error acquiring database lock"); + throw new EamDbException("Error acquiring database lock", Bundle.PostgresEamDb_multiUserLockError_message()); } catch (InterruptedException ex) { - throw new EamDbException("Error acquiring database lock"); + throw new EamDbException("Error acquiring database lock", Bundle.PostgresEamDb_multiUserLockError_message(), ex); } catch (CoordinationService.CoordinationServiceException ex) { // This likely just means the coordination service isn't running, which is ok return null; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java index b2df5f0fd9..28374a1311 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java @@ -158,7 +158,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { private void setupConnectionPool(boolean foreignKeysEnabled) throws EamDbException { if (dbSettings.dbFileExists() == false) { - throw new EamDbException(Bundle.SqliteEamDb_databaseMissing_message()); + throw new EamDbException("Central repository database missing", Bundle.SqliteEamDb_databaseMissing_message()); } connectionPool = new BasicDataSource(); @@ -194,7 +194,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { protected Connection connect(boolean foreignKeys) throws EamDbException { synchronized (this) { if (!EamDb.isEnabled()) { - throw new EamDbException(Bundle.SqliteEamDb_centralRepositoryDisabled_message()); // NON-NLS + throw new EamDbException("Central repository database missing", Bundle.SqliteEamDb_centralRepositoryDisabled_message()); // NON-NLS } if (connectionPool == null) { setupConnectionPool(foreignKeys); @@ -202,7 +202,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { try { return connectionPool.getConnection(); } catch (SQLException ex) { - throw new EamDbException(Bundle.SqliteEamDb_connectionFailedMessage_message(), ex); // NON-NLS + throw new EamDbException("Error getting connection from connection pool.", Bundle.SqliteEamDb_connectionFailedMessage_message(), ex); // NON-NLS } } } @@ -810,6 +810,24 @@ final class SqliteEamDb extends AbstractSqlEamDb { } } + /** + * Process a SELECT query + * + * @param selectClause query string to execute + * @param instanceTableCallback callback to process the instance + * + * @throws EamDbException + */ + @Override + public void processSelectClause(String selectClause, InstanceTableCallback instanceTableCallback) throws EamDbException { + try { + acquireSharedLock(); + super.processSelectClause(selectClause, instanceTableCallback); + } finally { + releaseSharedLock(); + } + } + /** * Check whether a reference set with the given name/version is in the * central repo. Used to check for name collisions when creating reference diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index 282e225135..355c7c8dbc 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -224,6 +224,7 @@ public class IngestEventsListener { * in the central repository. * * @param originalArtifact the artifact to create the interesting item for + * @param caseDisplayNames the case names the artifact was previously seen in */ @NbBundle.Messages({"IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository)", "# {0} - typeName", diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java index a96f09fa78..690fb9189d 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java @@ -64,7 +64,7 @@ public class Installer extends ModuleInstall { if (RuntimeProperties.runningWithGUI()) { WindowManager.getDefault().invokeWhenUIReady(() -> { JOptionPane.showMessageDialog(null, - ex.getMessage(), + ex.getUserMessage(), NbBundle.getMessage(this.getClass(), "Installer.centralRepoUpgradeFailed.title"), JOptionPane.ERROR_MESSAGE); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties-MERGED index 2e709858d8..96a73954fa 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties-MERGED @@ -1,13 +1,12 @@ +CentralRepoIngestModel_name_header=Name:
+CentralRepoIngestModel_previous_case_header=
Previous Cases:
CentralRepoIngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Correlation Engine ingest module. CentralRepoIngestModule.notfyBubble.title=Central Repository Not Initialized -CentralRepoIngestModule.postToBB.eamHit=Central Repository -CentralRepoIngestModule.postToBB.fileName=File Name -CentralRepoIngestModule.postToBB.hashSetSource=Source of Hash -# {0} - Name of file that is Notable -CentralRepoIngestModule.postToBB.knownBadMsg=Notable: {0} -CentralRepoIngestModule.postToBB.md5Hash=MD5 Hash CentralRepoIngestModule.prevCaseComment.text=Previous Case: CentralRepoIngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository) +CentralRepoIngestModule_notable_message_header=A file in this data source was previously seen and tagged as Notable.
+# {0} - Name of file that is Notable +CentralRepoIngestModule_postToBB_knownBadMsg=Notable: {0} CentralRepoIngestModuleFactory.ingestmodule.desc=Saves properties to the central repository for later correlation CentralRepoIngestModuleFactory.ingestmodule.name=Correlation Engine IngestSettingsPanel.ingestSettingsLabel.text=Ingest Settings diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java index 792a3f2ebb..bea9b801b8 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.centralrepository.ingestmodule; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -54,7 +53,6 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME; import org.sleuthkit.datamodel.HashUtility; -import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -276,7 +274,7 @@ final class CentralRepoIngestModule implements FileIngestModule { // Don't allow sqlite central repo databases to be used for multi user cases if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) - && (EamDbPlatformEnum.getSelectedPlatform() == EamDbPlatformEnum.SQLITE)) { + && (EamDbPlatformEnum.getSelectedPlatform() == EamDbPlatformEnum.SQLITE)) { logger.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository."); throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS } @@ -302,7 +300,7 @@ final class CentralRepoIngestModule implements FileIngestModule { } catch (EamDbException ex) { throw new IngestModuleException("Unable to get case from central repository database ", ex); } - + try { eamDataSource = CorrelationDataSource.fromTSKDataSource(eamCase, context.getDataSource()); } catch (EamDbException ex) { @@ -313,7 +311,7 @@ final class CentralRepoIngestModule implements FileIngestModule { // if we are the first thread / module for this job, then make sure the case // and image exist in the DB before we associate artifacts with it. if (refCounter.incrementAndGet(jobId) - == 1) { + == 1) { // ensure we have this data source in the EAM DB try { if (null == centralRepoDb.getDataSource(eamCase, eamDataSource.getDataSourceObjectID())) { @@ -355,7 +353,7 @@ final class CentralRepoIngestModule implements FileIngestModule { logger.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS } // send inbox message - sendBadFileInboxMessage(tifArtifact, abstractFile.getName(), abstractFile.getMd5Hash()); + sendBadFileInboxMessage(tifArtifact, abstractFile.getName(), abstractFile.getMd5Hash(), caseDisplayNames); } } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS @@ -364,50 +362,32 @@ final class CentralRepoIngestModule implements FileIngestModule { } } + @Messages({ + "CentralRepoIngestModule_notable_message_header=A file in this data source was previously seen and tagged as Notable.
", + "CentralRepoIngestModel_name_header=Name:
", + "CentralRepoIngestModel_previous_case_header=
Previous Cases:
", + "# {0} - Name of file that is Notable", + "CentralRepoIngestModule_postToBB_knownBadMsg=Notable: {0}" + }) + /** * Post a message to the ingest inbox alerting the user that a bad file was * found. * - * @param artifact badFile Blackboard Artifact - * @param name badFile's name - * @param md5Hash badFile's md5 hash + * @param artifact badFile Blackboard Artifact + * @param name badFile's name + * @param md5Hash badFile's md5 hash + * @param caseDisplayNames List of cases that the artifact appears in. */ - @Messages({"CentralRepoIngestModule.postToBB.fileName=File Name", - "CentralRepoIngestModule.postToBB.md5Hash=MD5 Hash", - "CentralRepoIngestModule.postToBB.hashSetSource=Source of Hash", - "CentralRepoIngestModule.postToBB.eamHit=Central Repository", - "# {0} - Name of file that is Notable", - "CentralRepoIngestModule.postToBB.knownBadMsg=Notable: {0}"}) - public void sendBadFileInboxMessage(BlackboardArtifact artifact, String name, String md5Hash) { - StringBuilder detailsSb = new StringBuilder(); - //details - detailsSb.append(""); //NON-NLS - //hit - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - - detailsSb.append("
") //NON-NLS - .append(Bundle.CentralRepoIngestModule_postToBB_fileName()) - .append("") //NON-NLS - .append(name) - .append("
") //NON-NLS - .append(Bundle.CentralRepoIngestModule_postToBB_md5Hash()) - .append("").append(md5Hash).append("
") //NON-NLS - .append(Bundle.CentralRepoIngestModule_postToBB_hashSetSource()) - .append("").append(Bundle.CentralRepoIngestModule_postToBB_eamHit()).append("
"); //NON-NLS + private void sendBadFileInboxMessage(BlackboardArtifact artifact, String name, String md5Hash, List caseDisplayNames) { + StringBuilder detailsSb = new StringBuilder(1024); + detailsSb.append(Bundle.CentralRepoIngestModule_notable_message_header()).append(Bundle.CentralRepoIngestModel_name_header()); + detailsSb.append(name).append(Bundle.CentralRepoIngestModel_previous_case_header()); + for (String str : caseDisplayNames) { + detailsSb.append(str).append("
"); + } + detailsSb.append(""); services.postMessage(IngestMessage.createDataMessage(CentralRepoIngestModuleFactory.getModuleName(), Bundle.CentralRepoIngestModule_postToBB_knownBadMsg(name), detailsSb.toString(), diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED index dc42aa3b68..7277910e7d 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED @@ -33,7 +33,7 @@ EamDbSettingsDialog.validation.finished=Click OK to save your database settings EamDbSettingsDialog.validation.incompleteFields=Fill in all values for the selected database. EamOptionsController.moduleErr=Error processing value changes. EamOptionsController.moduleErr.msg=Value change processing failed. -GlobalSettingsPanel.updateFailed.title=Central repository upgrade failed +GlobalSettingsPanel.updateFailed.title=Central repository disabled GlobalSettingsPanel.validationErrMsg.ingestRunning=You cannot change settings while ingest is running. GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module. ManageCasesDialog.title.text=Manage Cases diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle_ja.properties new file mode 100644 index 0000000000..96e0634149 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle_ja.properties @@ -0,0 +1,145 @@ +AddNewOrganizationDialog.addNewOrg.msg=\u65b0\u898f\u7d44\u7e54\u3092\u8ffd\u52a0 +AddNewOrganizationDialog.bnOk.addFailed.text=\u65b0\u898f\u7d44\u7e54\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +AddNewOrganizationDialog.validation.incompleteFields=\u7d44\u7e54\u540d\u304c\u5fc5\u8981\u3067\u3059\u3002 +CasesTableModel.case=\u30b1\u30fc\u30b9\u540d +CasesTableModel.creationDate=\u4f5c\u6210\u65e5 +CasesTableModel.noData=\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093 +DataSourcesTableModel.dataSource=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d +DataSourcesTableModel.deviceId=\u30c7\u30d0\u30a4\u30b9ID +DataSourcesTableModel.noData=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093 +EamDbSettingsDialog.chooserPath.failedToGetDbPathMsg=\u9078\u629e\u3057\u305f\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u7121\u52b9\u3067\u3059\u3002\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 +EamDbSettingsDialog.fcDatabasePath.title=central_repository.db\u306e\u5834\u6240\u3092\u9078\u629e +EamDbSettingsDialog.lbDatabaseType.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30bf\u30a4\u30d7 : +EamDbSettingsDialog.lbSingleUserSqLite.text=SQLite\u306f\u4e00\u5ea6\u306b1\u4eba\u306e\u8abf\u67fb\u54e1\u306e\u307f\u304c\u4f7f\u7528\u3057\u307e\u3059\u3002 +EamDbSettingsDialog.okButton.connectionErrorMsg.text=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u30fb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +EamDbSettingsDialog.okButton.corruptDatabaseExists.message=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u5b58\u5728\u3057\u307e\u3059\u304c\u3001\u6b63\u3057\u3044\u5f62\u5f0f\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u624b\u52d5\u3067\u524a\u9664\u3059\u308b\u304b\u5225\u306e\u30d1\u30b9\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044(\u5fc5\u8981\u306b\u5fdc\u3058\u3066)\u3002 +EamDbSettingsDialog.okButton.corruptDatabaseExists.title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +EamDbSettingsDialog.okButton.createDbDialog.message=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002\u4f5c\u6210\u3057\u307e\u3059\u304b? +EamDbSettingsDialog.okButton.createDbDialog.title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u5b58\u5728\u3057\u307e\u305b\u3093 +EamDbSettingsDialog.okButton.createDbError.title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093 +EamDbSettingsDialog.okButton.createPostgresDbError.message=Postgres\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002Postgres\u30b5\u30fc\u30d0\u30fc\u306e\u30a2\u30c9\u30ec\u30b9\u3001\u30dd\u30fc\u30c8\u3001\u30ed\u30b0\u30a4\u30f3\u8cc7\u683c\u60c5\u5831\u304c\u6b63\u3057\u3044\u304b\u78ba\u8a8d\u3057\u3066\u304b\u3089\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 +EamDbSettingsDialog.okButton.createSQLiteDbError.message=SQLite\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002\u5834\u6240\u304c\u5b58\u5728\u3057\u3001\u66f8\u304d\u8fbc\u307f\u6a29\u9650\u304c\u3042\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u304b\u3089\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 +EamDbSettingsDialog.okButton.databaseConnectionFailed.message=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3002\u8a2d\u5b9a\u3092\u78ba\u8a8d\u3057\u3066\u304b\u3089\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 +EamDbSettingsDialog.okButton.databaseConnectionFailed.title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f +EamDbSettingsDialog.okButton.errorMsg.text=Autopsy\u3092\u518d\u8d77\u52d5\u3057\u3066\u3001\u65b0\u898f\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u306e\u4f7f\u7528\u3092\u958b\u59cb\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +EamDbSettingsDialog.okButton.errorTitle.text=\u518d\u8d77\u52d5\u304c\u5fc5\u8981\u3067\u3059\u3002 +EamDbSettingsDialog.textPrompt.dbName=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d +EamDbSettingsDialog.textPrompt.hostnameOrIP=\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9 +EamDbSettingsDialog.textPrompt.password=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30e6\u30fc\u30b6\u30fc\u306e\u30d1\u30b9\u30ef\u30fc\u30c9 +EamDbSettingsDialog.textPrompt.port=\u30dd\u30fc\u30c8\u756a\u53f7 +EamDbSettingsDialog.textPrompt.user=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30e6\u30fc\u30b6\u30fc +EamDbSettingsDialog.title.text=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u30fb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u69cb\u6210 +EamDbSettingsDialog.validation.finished=[OK] \u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u3001[\u30aa\u30d7\u30b7\u30e7\u30f3] \u306b\u623b\u308a\u307e\u3059\u3002\u3042\u308b\u3044\u306f\u3001\u5225\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30bf\u30a4\u30d7\u3092\u9078\u629e\u3057\u307e\u3059\u3002 +EamDbSettingsDialog.validation.incompleteFields=\u9078\u629e\u3057\u305f\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u3059\u3079\u3066\u306e\u5024\u3092\u5165\u529b\u3057\u307e\u3059\u3002 +EamOptionsController.moduleErr=\u5024\u306e\u5909\u66f4\u3092\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +EamOptionsController.moduleErr.msg=\u5024\u306e\u5909\u66f4\u306e\u51e6\u7406\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 +GlobalSettingsPanel.updateFailed.title=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306e\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f +GlobalSettingsPanel.validationErrMsg.ingestRunning=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5b9f\u884c\u4e2d\u306f\u8a2d\u5b9a\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093\u3002 +GlobalSettingsPanel.validationerrMsg.mustConfigure=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u69cb\u6210\u3057\u3066\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u6709\u52b9\u5316\u3057\u307e\u3059\u3002 +ManageCasesDialog.title.text=\u30b1\u30fc\u30b9\u3092\u7ba1\u7406 +ManageCorrelationPropertiesDialog.noneSelected=\u76f8\u95a2\u5206\u6790\u30d7\u30ed\u30d1\u30c6\u30a3\u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u305b\u3093\u3002 +ManageCorrelationPropertiesDialog.okbutton.failure=\u66f4\u65b0\u3057\u305f\u9078\u629e\u306e\u4fdd\u5b58\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ManageCorrelationPropertiesDialog.title=\u76f8\u95a2\u5206\u6790\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u7ba1\u7406 +ManageOrganizationsDialog.closeButton.text=\u9589\u3058\u308b +ManageOrganizationsDialog.confirmDeletion.message=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u304b\u3089\u9078\u629e\u3057\u305f\u7d44\u7e54\u3092\u524a\u9664\u3057\u3066\u3082\u672c\u5f53\u306b\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +ManageOrganizationsDialog.confirmDeletion.title=\u524a\u9664\u3092\u78ba\u5b9a +ManageOrganizationsDialog.deleteButton.text=\u524a\u9664 +ManageOrganizationsDialog.editButton.text=\u7de8\u96c6 +ManageOrganizationsDialog.newButton.text=\u65b0\u898f +ManageOrganizationsDialog.orgDescriptionTextArea.text=\u7d44\u7e54\u306f\u3001\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u8ffd\u52a0\u306e\u9023\u7d61\u5148\u60c5\u5831\u3092\u63d0\u4f9b\u3059\u308b\u969b\u306b\u4f7f\u7528\u3057\u307e\u3059\u3002 +ManageOrganizationsDialog.orgDetailsLabel.text=\u7d44\u7e54\u8a73\u7d30 +ManageOrganizationsDialog.orgListLabel.text=\u7d44\u7e54 +ManageOrganizationsDialog.orgNameLabel.text=\u7d44\u7e54\u540d: +ManageOrganizationsDialog.pocEmailLabel.text=\u9023\u7d61\u5148\u306e\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9: +ManageOrganizationsDialog.pocNameLabel.text=\u9023\u7d61\u5148\u540d: +ManageOrganizationsDialog.pocPhoneLabel.text=\u9023\u7d61\u5148\u306e\u96fb\u8a71\u756a\u53f7: +ManageOrganizationsDialog.title.text=\u7d44\u7e54\u3092\u7ba1\u7406 +ManageOrganizationsDialog.unableToDeleteOrg.message=\u9078\u629e\u3057\u305f\u7d44\u7e54\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3002 +ManageOrganizationsDialog.unableToDeleteOrg.title=\u524a\u9664\u3067\u304d\u307e\u305b\u3093 +OptionsCategory_Name_Central_Repository_Options=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc +OptionsCategory_Keywords_Central_Repository_Options=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u8a2d\u5b9a +ImportHashDatabaseDialog.known.text=\u65e2\u77e5 +ImportHashDatabaseDialog.knownBad.text=\u9855\u8457 +EamPostgresSettingsDialog.lbUserPassword.text=\u30e6\u30fc\u30b6\u30fc\u30d1\u30b9\u30ef\u30fc\u30c9 : +EamPostgresSettingsDialog.lbUserName.text=\u30e6\u30fc\u30b6\u30fc\u540d : +EamPostgresSettingsDialog.bnCancel.text=\u53d6\u308a\u6d88\u3057 +EamPostgresSettingsDialog.lbPort.text=\u30dd\u30fc\u30c8 : +EamPostgresSettingsDialog.lbHostName.text=\u30db\u30b9\u30c8\u540d / IP : +EamPostgresSettingsDialog.bnTestConnection.text=\u30c6\u30b9\u30c8\u63a5\u7d9a +EamPostgresSettingsDialog.lbDatabaseName.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d : +EamSqliteSettingsDialog.bnCancel.text=\u53d6\u308a\u6d88\u3057 +EamSqliteSettingsDialog.lbTestDatabase.text= +EamSqliteSettingsDialog.bnTestDatabase.text=\u30c6\u30b9\u30c8\u63a5\u7d9a +EamSqliteSettingsDialog.lbTestDatabaseWarning.text= +EamSqliteSettingsDialog.bnDatabasePathFileOpen.text=\u53c2\u7167... +EamSqliteSettingsDialog.tfDatabasePath.toolTipText=SQLite db\u30d5\u30a1\u30a4\u30eb\u306b\u683c\u7d0d\u3059\u308b\u30d5\u30a1\u30a4\u30eb\u540d\u3068\u30d1\u30b9 +EamSqliteSettingsDialog.tfDatabasePath.text= +EamSqliteSettingsDialog.lbDatabasePath.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9 : +ImportHashDatabaseDialog.tfDatabaseName.tooltip=\u3053\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u540d\u524d +ImportHashDatabaseDialog.tfDatabaseVersion.tooltip.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u756a\u53f7 +ImportHashDatabaseDialog.tfDatabaseName.tooltip=\u3053\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u540d\u524d +ImportHashDatabaseDialog.tfDatabaseVersion.tooltip.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u756a\u53f7 +GlobalSettingsPanel.tbOops.text= +GlobalSettingsPanel.lbDatabaseSettings.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8a2d\u5b9a +GlobalSettingsPanel.bnImportDatabase.label=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 +AddNewOrganizationDialog.lbPocPhone.text=\u96fb\u8a71\u756a\u53f7: +AddNewOrganizationDialog.lbPocEmail.text=\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9: +AddNewOrganizationDialog.lbPocName.text=\u540d\u524d: +AddNewOrganizationDialog.lbPocHeading.text=\u9023\u7d61\u5148: +AddNewOrganizationDialog.lbOrganizationName.text=\u7d44\u7e54\u540d: +AddNewOrganizationDialog.bnCancel.text=\u53d6\u308a\u6d88\u3057 +AddNewOrganizationDialog.bnOK.text=OK +AddNewOrganizationDialog.tfName.tooltip=POC\u540d +ManageTagsDialog.okButton.text=OK +ManageTagsDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +ManageArtifactTypesDialog.taInstructionsMsg.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306e\u76f8\u95a2\u5206\u6790\u306b\u4f7f\u7528\u3059\u308b\u76f8\u95a2\u5206\u6790\u30d7\u30ed\u30d1\u30c6\u30a3\u30921\u3064\u4ee5\u4e0a\u6709\u52b9\u5316\u3057\u307e\u3059\u3002\u3053\u308c\u3089\u306e\u30d7\u30ed\u30d1\u30c6\u30a3\u306f\u30b0\u30ed\u30fc\u30d0\u30eb\u3067\u3042\u308b\u305f\u3081\u3001\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306e\u3059\u3079\u3066\u306e\u30e6\u30fc\u30b6\u30fc\u306b\u5f71\u97ff\u3057\u307e\u3059\u3002 +EamSqliteSettingsDialog.bnOk.text=OK +EamPostgresSettingsDialog.bnSave.text=\u4fdd\u5b58 +EamDbSettingsDialog.bnDatabasePathFileOpen.text=\u53c2\u7167... +EamDbSettingsDialog.tfDatabasePath.toolTipText=SQLite db\u30d5\u30a1\u30a4\u30eb\u306b\u683c\u7d0d\u3059\u308b\u30d5\u30a1\u30a4\u30eb\u540d\u3068\u30d1\u30b9 +EamDbSettingsDialog.tfDatabasePath.text= +EamDbSettingsDialog.lbDatabasePath.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9 : +EamDbSettingsDialog.bnCancel.text=\u53d6\u308a\u6d88\u3057 +EamDbSettingsDialog.bnOk.text=OK +EamDbSettingsDialog.lbHostName.text=\u30db\u30b9\u30c8\u540d / IP : +EamDbSettingsDialog.lbUserPassword.text=\u30e6\u30fc\u30b6\u30fc\u30d1\u30b9\u30ef\u30fc\u30c9 : +EamDbSettingsDialog.lbUserName.text=\u30e6\u30fc\u30b6\u30fc\u540d : +EamDbSettingsDialog.lbPort.text=\u30dd\u30fc\u30c8 : +GlobalSettingsPanel.pnDatabaseConfiguration.title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u69cb\u6210 +GlobalSettingsPanel.lbDbPlatformTypeLabel.text=\u30bf\u30a4\u30d7: +GlobalSettingsPanel.lbDbNameLabel.text=\u540d\u524d: +GlobalSettingsPanel.bnDbConfigure.text=\u69cb\u6210 +GlobalSettingsPanel.lbDbLocationLabel.text=\u5834\u6240: +ManageCorrelationPropertiesDialog.lbWarningMsg.text=\u8b66\u544a\u30e1\u30c3\u30bb\u30fc\u30b8 +ManageCorrelationPropertiesDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +ManageCorrelationPropertiesDialog.okButton.text=OK +GlobalSettingsPanel.bnManageProperties.text=\u76f8\u95a2\u5206\u6790\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u7ba1\u7406 +EamDbSettingsDialog.lbDatabaseDesc.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb: +EamDbSettingsDialog.lbFullDbPath.text= +GlobalSettingsPanel.cbUseCentralRepo.text=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u3092\u4f7f\u7528 +GlobalSettingsPanel.organizationTextArea.text=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u5185\u3067\u7d44\u7e54\u60c5\u5831\u3092\u8ffd\u8de1\u3067\u304d\u307e\u3059\u3002 +GlobalSettingsPanel.manageOrganizationButton.text=\u7d44\u7e54\u3092\u7ba1\u7406 +GlobalSettingsPanel.lbCentralRepository.text=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u30b1\u30fc\u30b9\u9593\u306e\u30d5\u30a1\u30a4\u30eb\u3068\u7d50\u679c\u3092\u76f8\u95a2\u5206\u6790\u3067\u304d\u307e\u3059\u3002\u30b1\u30fc\u30b9\u304c\u958b\u3044\u3066\u3044\u308b\u9593\u306f\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u69cb\u6210\u3092\u4fee\u6b63\u3067\u304d\u307e\u305b\u3093\u3002 +GlobalSettingsPanel.pnCorrelationProperties.border.title=\u76f8\u95a2\u5206\u6790\u30d7\u30ed\u30d1\u30c6\u30a3 +GlobalSettingsPanel.organizationPanel.border.title=\u7d44\u7e54 +GlobalSettingsPanel.casesPanel.border.title=\u30b1\u30fc\u30b9\u8a73\u7d30 +GlobalSettingsPanel.showCasesButton.text=\u30b1\u30fc\u30b9\u3092\u7ba1\u7406 +ShowCasesDialog.closeButton.AccessibleContext.accessibleName=\u9589\u3058\u308b +ShowCasesDialog.closeButton.actionCommand=\u9589\u3058\u308b +ShowCasesDialog.closeButton.text=\u9589\u3058\u308b +ShowCasesDialog.caseDetailsTable.toolTipText=\u5217\u540d\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30bd\u30fc\u30c8\u3057\u307e\u3059\u3002\u30c6\u30fc\u30d6\u30eb\u3092\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3066\u3055\u3089\u306a\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +ShowCasesDialog.title=\u30b1\u30fc\u30b9\u8a73\u7d30 +GlobalSettingsPanel.Case\ Details.AccessibleContext.accessibleName=\u30b1\u30fc\u30b9\u8a73\u7d30 +ShowCasesDialog.caseDetailsTable.AccessibleContext.accessibleDescription=\u5217\u540d\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30bd\u30fc\u30c8\u3057\u307e\u3059\u3002 +GlobalSettingsPanel.casesTextArea.text=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306e\u30b1\u30fc\u30b9\u8a73\u7d30\u3092\u4e00\u89a7\u8868\u793a\u3059\u308b\u30c6\u30fc\u30d6\u30eb\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +GlobalSettingsPanel.ingestRunningWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5b9f\u884c\u4e2d\u306f\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u8a2d\u5b9a\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093! +GlobalSettingsPanel.correlationPropertiesTextArea.text=\u5f8c\u3067\u76f8\u95a2\u5206\u6790\u3067\u304d\u308b\u3088\u3046\u3001\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u3069\u306e\u30d5\u30a1\u30a4\u30eb\u3068\u7d50\u679c\u306e\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u4fdd\u7ba1\u3059\u308b\u304b\u9078\u629e\u3057\u307e\u3059\u3002\n +ManageCasesDialog.examinerPhoneLabel.text=\u8abf\u67fb\u54e1\u306e\u96fb\u8a71\u756a\u53f7: +ManageCasesDialog.examinerNameLabel.text=\u8abf\u67fb\u54e1\u540d: +ManageCasesDialog.examinerEmailLabel.text=\u8abf\u67fb\u54e1\u306e\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9: +ManageCasesDialog.caseNumberLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7: +ManageCasesDialog.orgLabel.text=\u7d44\u7e54: +ManageCasesDialog.closeButton.text=\u9589\u3058\u308b +ManageCasesDialog.notesLabel.text=\u5099\u8003: +ManageCasesDialog.dataSourcesLabel.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9: +ManageCasesDialog.caseInfoLabel.text=\u30b1\u30fc\u30b9\u60c5\u5831: diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java index c388d081a9..37718a8d7c 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java @@ -77,7 +77,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i ingestStateUpdated(Case.isCaseOpen()); } - @Messages({"GlobalSettingsPanel.updateFailed.title=Central repository upgrade failed"}) + @Messages({"GlobalSettingsPanel.updateFailed.title=Central repository disabled"}) private void updateDatabase() { if (EamDbPlatformEnum.getSelectedPlatform().equals(DISABLED)) { @@ -91,7 +91,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i } catch (EamDbException ex) { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); JOptionPane.showMessageDialog(this, - ex.getMessage(), + ex.getUserMessage(), NbBundle.getMessage(this.getClass(), "GlobalSettingsPanel.updateFailed.title"), JOptionPane.WARNING_MESSAGE); diff --git a/Core/src/org/sleuthkit/autopsy/commandlineingest/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/commandlineingest/Bundle_ja.properties new file mode 100644 index 0000000000..62c8f29e53 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commandlineingest/Bundle_ja.properties @@ -0,0 +1,17 @@ +OpenIDE-Module-Name=CommandLineAutopsy +OptionsCategory_Keywords_Command_Line_Ingest_Settings=\u30b3\u30de\u30f3\u30c9\u30e9\u30a4\u30f3\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u8a2d\u5b9a +OptionsCategory_Keywords_General=\u30aa\u30d7\u30b7\u30e7\u30f3 +OptionsCategory_Name_Command_Line_Ingest=\u30b3\u30de\u30f3\u30c9\u30e9\u30a4\u30f3\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 +CommandLineIngestSettingsPanel.ResultsDirectoryUnspecified=\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059 +AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.invalidPath.text=\u30d1\u30b9\u306f\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093 +CommandLineIngestSettingsPanel.CannotAccess=\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u305b\u3093 +CommandLineIngestSettingsPanel.CheckPermissions=\u30a2\u30af\u30bb\u30b9\u6a29\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text=\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u9078\u629e: +CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text=jLabelInvalidOutputFolder +CommandLineIngestSettingsPanel.outputPathTextField.toolTipText=\u30b3\u30de\u30f3\u30c9\u30e9\u30a4\u30f3\u51e6\u7406\u7528\u306e\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc(\u30b3\u30de\u30f3\u30c9\u30e9\u30a4\u30f3\u51e6\u7406\u30e2\u30fc\u30c9\u3067\u30b1\u30fc\u30b9\u30d5\u30a9\u30eb\u30c0\u30fc\u304c\u4f5c\u6210\u3055\u308c\u308b\u5834\u6240)\u3067\u3059\u3002 +CommandLineIngestSettingsPanel.outputPathTextField.text= +CommandLineIngestSettingsPanel.browseOutputFolderButton.text=\u53c2\u7167 +CommandLineIngestSettingsPanel.bnEditIngestSettings.toolTipText=\u30b3\u30de\u30f3\u30c9\u30e9\u30a4\u30f3\u51e6\u7406\u30e2\u30fc\u30c9\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a\u3067\u3059\u3002 +CommandLineIngestSettingsPanel.bnEditIngestSettings.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u8a2d\u5b9a +CommandLinePanel.jLabel1.text=\u30b3\u30de\u30f3\u30c9\u30e9\u30a4\u30f3\u304b\u3089\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u4e2d\u3067\u3059 +CommandLineStartupWindow.title.text=\u30b3\u30de\u30f3\u30c9\u30e9\u30a4\u30f3\u30e2\u30fc\u30c9\u3067\u5b9f\u884c\u4e2d\u3067\u3059 diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle_ja.properties new file mode 100644 index 0000000000..9247f0d4d0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle_ja.properties @@ -0,0 +1,116 @@ +AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.all=\u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u30ab\u30c6\u30b4\u30ea\u30fc +AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.doc=\u6587\u66f8 +AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.media=\u30e1\u30c7\u30a3\u30a2 +# {0} - \u3057\u304d\u3044\u5024\u306e\u30d1\u30fc\u30bb\u30f3\u30c6\u30fc\u30b8 +AbstractCommonFilesMetadataBuilder.getPercentFilter.thresholdPercent=, \u3057\u304d\u3044\u5024 {0}% +# {0} - \u5c5e\u6027\u30bf\u30a4\u30d7 +# {1} - \u3057\u304d\u3044\u5024\u306e\u6587\u5b57\u5217 +AllInterCaseCommonAttributeSearcher.buildTabTitle.titleInterAll=\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3(\u3059\u3079\u3066\u306e\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u30b1\u30fc\u30b9, {0}{1}) +# {0} - \u30d3\u30eb\u30c9\u30ab\u30c6\u30b4\u30ea\u30fc +# {1} - \u3057\u304d\u3044\u5024\u306e\u6587\u5b57\u5217 +AllIntraCaseCommonAttributeSearcher.buildTabTitle.titleIntraAll=\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3(\u3059\u3079\u3066\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9, {0}{1}) +# {0} - \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u6570 +CommonAttributePanel.dataSourcesLabel.text=\u73fe\u5728\u306e\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u306f {0} \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002 +CommonAttributePanel.exception=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u4e88\u671f\u305b\u306c\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +CommonAttributePanel.frame.title=\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u691c\u7d22 +CommonAttributePanel.incompleteResults.continueText=\n\n \u305d\u308c\u3067\u3082\u3053\u306e\u691c\u7d22\u3092\u7d9a\u884c\u3057\u307e\u3059\u304b? +CommonAttributePanel.incompleteResults.introText=\u7d50\u679c\u306f\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306e\u3059\u3079\u3066\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u73fe\u5728\u306e\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3055\u308c\u305f\u308f\u3051\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u6b21\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u51e6\u7406\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f: +CommonAttributePanel.incompleteResults.title=\u691c\u7d22\u306f\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059 +CommonAttributePanel.intraCasePanel.title=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u30aa\u30d7\u30b7\u30e7\u30f3 +CommonAttributePanel.search.done.exception=[\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u691c\u7d22] \u306e\u5b9f\u884c\u4e2d\u306b\u4e88\u671f\u305b\u306c\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +CommonAttributePanel.search.done.interupted=\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u691c\u7d22\u4e2d\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +CommonAttributePanel.search.done.noCurrentCaseException=\u30b1\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u3051\u307e\u305b\u3093\u3002 +CommonAttributePanel.search.done.noResults=\u8a72\u5f53\u3059\u308b\u7d50\u679c\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +CommonAttributePanel.search.done.searchProgressDisplay=\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u691c\u7d22\u7d50\u679c\u3092\u8868\u793a\u4e2d\u3067\u3059\u3002 +CommonAttributePanel.search.done.searchProgressGathering=\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u691c\u7d22\u7d50\u679c\u3092\u53ce\u96c6\u4e2d\u3067\u3059\u3002 +CommonAttributePanel.search.done.sqlException=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30af\u30a8\u30ea\u3057\u3066\u30d7\u30ed\u30d1\u30c6\u30a3\u307e\u305f\u306f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u691c\u7d22\u3067\u304d\u307e\u305b\u3093\u3002 +CommonAttributePanel.search.done.tskCoreException=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u5bfe\u3057\u3066\u30af\u30a8\u30ea\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 +CommonAttributePanel.search.results.pathText=\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u691c\u7d22\u7d50\u679c +CommonAttributePanel.setupCases.done.exeutionException=\u30b1\u30fc\u30b9\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u4e88\u671f\u305b\u306c\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +CommonAttributePanel.setupCases.done.interruptedException=[\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u691c\u7d22] \u30c0\u30a4\u30a2\u30ed\u30b0\u30dc\u30c3\u30af\u30b9\u306e\u4f5c\u6210\u4e2d\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +CommonAttributePanel.setupDataSources.done.exception=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u4e88\u671f\u305b\u306c\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +CommonAttributePanel.setupDataSources.done.interupted=[\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u691c\u7d22] \u30c0\u30a4\u30a2\u30ed\u30b0\u30dc\u30c3\u30af\u30b9\u306e\u4f5c\u6210\u4e2d\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +CommonAttributePanel.setupDataSources.done.noCurrentCaseException=\u30b1\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u3051\u307e\u305b\u3093\u3002 +CommonAttributePanel.setupDataSources.done.sqlException=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30af\u30a8\u30ea\u3057\u3066\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u691c\u7d22\u3067\u304d\u307e\u305b\u3093\u3002 +CommonAttributePanel.setupDataSources.done.tskCoreException=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u5bfe\u3057\u3066\u30af\u30a8\u30ea\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 +CommonAttributePanel.setupDataSources.updateUi.noDataSources=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +CommonAttributePanel.title=[\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3] \u30d1\u30cd\u30eb +CommonAttributeSearchAction.getName.text=\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u691c\u7d22 +CommonAttributeSearchAction.openPanel.caseNotInCentralRepo=\n - \u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306f\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +CommonAttributeSearchAction.openPanel.centralRepoDisabled=\n - \u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306f\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u3059\u3002 +CommonAttributeSearchAction.openPanel.centralRepoInvalid=\n - \u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306e\u69cb\u6210\u306f\u7121\u52b9\u3067\u3059\u3002 +CommonAttributeSearchAction.openPanel.intro=\u6b21\u306e\u7406\u7531\u306b\u3088\u308a\u3001\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3\u691c\u7d22\u6a5f\u80fd\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093: +CommonAttributeSearchAction.openPanel.noCaseOpen=\n - \u30b1\u30fc\u30b9\u306f\u958b\u304b\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +CommonAttributeSearchAction.openPanel.notEnoughCases=\n - \u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b2\u3064\u4ee5\u4e0b\u306e\u30b1\u30fc\u30b9\u304c\u5b58\u5728\u3057\u307e\u3059\u3002 +CommonAttributeSearchAction.openPanel.notEnoughDataSources=\n - \u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306b\u8907\u6570\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +CommonAttributeSearchAction.openPanel.resolution=\n\n\u3053\u308c\u3089\u306e\u554f\u984c\u306e1\u3064\u306b\u5bfe\u51e6\u3057\u3066\u3001\u3053\u306e\u6a5f\u80fd\u3092\u6709\u52b9\u5316\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +CommonAttributeValueNode.CommonAttributeValueNode.format=\u5024: %s +CommonFilesNode.getName.text=\u5171\u901a\u30d5\u30a1\u30a4\u30eb +CommonFilesPanel.commonFilesSearchLabel.text=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306e\u8907\u6570\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22\u3057\u307e\u3059\u3002 +CommonFilesPanel.text=\u91cd\u8907\u306e\u691c\u7d22\u4e2d\u306b\u3069\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u691c\u8a0e\u3059\u3079\u304d\u304b\u3092\u793a\u3057\u307e\u3059: +CommonFilesPanel.jRadioButton1.text=jRadioButton1 +CommonFilesPanel.jRadioButton2.text=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u3068\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306e\u30b1\u30fc\u30b9\u9593 +CommonFilesPanel.intraCaseRadio.label=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u3067\u306e\u307f\u76f8\u95a2\u5206\u6790 +CommonFilesPanel.interCaseRadio.label=\u65e2\u77e5\u306e\u30b1\u30fc\u30b9\u9593\u3067\u76f8\u95a2(\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u3092\u4f7f\u7528) +CommonFilesSearchResultsViewerTable.caseColLbl=\u30b1\u30fc\u30b9 +CommonFilesSearchResultsViewerTable.dataSourceColLbl=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 +CommonFilesSearchResultsViewerTable.instancesColLbl=\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9 +CommonFilesSearchResultsViewerTable.localPath=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306e\u89aa\u30d1\u30b9 +CommonFilesSearchResultsViewerTable.noDescText=\ +CommonFilesSearchResultsViewerTable.pathColLbl=\u89aa\u30d1\u30b9 +CommonFilesSearchResultsViewerTable.valueColLbl=\u5024 +InstanceCountNode.createSheet.noDescription=\ +InstanceCountNode.displayName=%s \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9(%s)\u306b\u5b58\u5728 +IntraCasePanel.selectDataSourceComboBox.actionCommand= +CommonAttributePanel.jCheckBox1.text=\u6b21\u306e\u7bc4\u56f2\u3067\u898b\u3064\u304b\u3063\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u975e\u8868\u793a +CommonAttributePanel.jLabel1.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e% \u304c\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 +CommonAttributePanel.percentageThresholdCheck.text_1=\u6b21\u306e\u7bc4\u56f2\u3067\u898b\u3064\u304b\u3063\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u975e\u8868\u793a +CommonAttributePanel.jCheckBox1.text=\u6b21\u306e\u7bc4\u56f2\u3067\u898b\u3064\u304b\u3063\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u975e\u8868\u793a +CommonAttributePanel.jLabel1.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e% \u304c\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 +CommonAttributePanel.percentageThreshold.text=20 +CommonAttributePanel.jLabel1.text_1=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e% \u304c\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 +InterCasePanel.correlationTypeComboBox.toolTipText=\u9078\u629e\u3057\u305f\u76f8\u95a2\u5206\u6790\u30bf\u30a4\u30d7 +IntraCasePanel.selectedFileCategoriesButton.text=\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u307f: +IntraCasePanel.categoriesLabel.text=\u8868\u793a\u3059\u308b\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7: +IntraCasePanel.allFileCategoriesRadioButton.toolTipText=\u7d50\u679c\u306b\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0\u304c\u9069\u7528\u3055\u308c\u3066\u3044\u307e\u305b\u3093... +IntraCasePanel.allFileCategoriesRadioButton.text=\u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 +IntraCasePanel.documentsCheckbox.text=\u6587\u66f8 +IntraCasePanel.pictureVideoCheckbox.text=\u5199\u771f\u3068\u52d5\u753b +IntraCasePanel.selectedFileCategoriesButton.toolTipText=\u4ee5\u4e0b\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u304b\u3089\u9078\u629e... +CommonAttributePanel.percentageThresholdTextTwo.text_1=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e% \u304c\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 +CommonAttributePanel.percentageThresholdCheck.text_1_1=\u6b21\u306e\u7bc4\u56f2\u3067\u898b\u3064\u304b\u3063\u305f\u9805\u76ee\u3092\u975e\u8868\u793a +CommonAttributePanel.intraCaseRadio.text=\u73fe\u5728\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u9593 +CommonAttributePanel.errorText.text=\u691c\u7d22\u3059\u308b\u306b\u306f\u3001\u30d5\u30a1\u30a4\u30eb\u30ab\u30c6\u30b4\u30ea\u30fc\u3092\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +CommonAttributePanel.searchButton.text=\u691c\u7d22 +InterCasePanel.categoriesLabel.text=\u8868\u793a\u3059\u308b\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7: +InterCasePanel.documentsCheckbox.text=\u6587\u66f8 +InterCasePanel.pictureVideoCheckbox.text=\u5199\u771f\u3068\u52d5\u753b +InterCasePanel.selectedFileCategoriesButton.toolTipText=\u4ee5\u4e0b\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u304b\u3089\u9078\u629e... +InterCasePanel.selectedFileCategoriesButton.text=\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u307f: +InterCasePanel.allFileCategoriesRadioButton.toolTipText=\u7d50\u679c\u306b\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0\u304c\u9069\u7528\u3055\u308c\u3066\u3044\u307e\u305b\u3093... +InterCasePanel.allFileCategoriesRadioButton.text=\u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 +InterCasePanel.specificCentralRepoCaseCheckbox.text=\u5171\u901a\u9805\u76ee\u306f\u7279\u5b9a\u306e\u30b1\u30fc\u30b9\u306b\u5b58\u5728\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059: +IntraCasePanel.onlySpecificDataSourceCheckbox.text=\u5171\u901a\u9805\u76ee\u306f\u7279\u5b9a\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u5b58\u5728\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059: +CommonAttributePanel.interCasePanel.border.title=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u30aa\u30d7\u30b7\u30e7\u30f3 +CommonAttributePanel.intraCasePanel.border.title=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u30aa\u30d7\u30b7\u30e7\u30f3 +CommonAttributePanel.commonItemSearchDescription.text=\u8907\u6570\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u307e\u305f\u306f\u30b1\u30fc\u30b9\u306b\u5b58\u5728\u3059\u308b\u9805\u76ee\u3092\u691c\u7d22 +CommonAttributePanel.scopeLabel.text=\u691c\u7d22\u7bc4\u56f2: +InterCasePanel.correlationComboBoxLabel.text=\u4e00\u81f4\u3059\u308b\u30d7\u30ed\u30d1\u30c6\u30a3\u30bf\u30a4\u30d7: +CommonAttributePanel.percentageThresholdInputBox.text=20 +CommonAttributePanel.resultsDisplayLabel.text_2=\u6b21\u306b\u3088\u3063\u3066\u6574\u7406\u3055\u308c\u305f\u7d50\u679c\u3092\u8868\u793a: +CommonAttributePanel.organizeByCaseRadio.text=\u30b1\u30fc\u30b9 +CommonAttributePanel.organizeByCountRadio.text=\u767a\u751f\u6570 +CommonAttributePanel.caseResultsRadioButton.text=\u30b1\u30fc\u30b9 +CommonAttributePanel.countResultsRadioButton.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u6570 +CommonAttributePanel.displayResultsLabel.text_2=\u6b21\u306b\u3088\u3063\u3066\u6574\u7406\u3055\u308c\u305f\u7d50\u679c\u3092\u8868\u793a: +# {0} - \u30b1\u30fc\u30b9\u540d +# {1} - \u5c5e\u6027\u30bf\u30a4\u30d7 +# {2} - \u3057\u304d\u3044\u5024\u306e\u6587\u5b57\u5217 +SingleInterCaseCommonAttributeSearcher.buildTabTitle.titleInterSingle=\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3(\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u30b1\u30fc\u30b9: {0}, {1}{2}) +# {0} - \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d +# {1} - \u30d3\u30eb\u30c9\u30ab\u30c6\u30b4\u30ea\u30fc +# {2} - \u3057\u304d\u3044\u5024\u306e\u6587\u5b57\u5217 +SingleIntraCaseCommonAttributeSearcher.buildTabTitle.titleIntraSingle=\u5171\u901a\u30d7\u30ed\u30d1\u30c6\u30a3(\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9: {0}, {1}{2}) +UserInputErrorManager.categories=\u691c\u7d22\u306b\u30d5\u30a1\u30a4\u30eb\u30ab\u30c6\u30b4\u30ea\u30fc\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +UserInputErrorManager.frequency=\u7121\u52b9\u306a\u983b\u5ea6\u306e\u5272\u5408: 0 < % < 100\u3002 +ValueNode.createSheet.noDescription=\ diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle_ja.properties new file mode 100644 index 0000000000..728fa8179f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle_ja.properties @@ -0,0 +1,97 @@ +AccountNode.accountName=\u30a2\u30ab\u30a6\u30f3\u30c8 +AccountNode.accountType=\u30bf\u30a4\u30d7 +AccountNode.device=\u30c7\u30d0\u30a4\u30b9 +AccountNode.messageCount=\u9805\u76ee +applyText=\u9069\u7528 +CTL_OpenCVTAction=\u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3 +CVTTopComponent.name=\ \u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u53ef\u8996\u5316 +CVTTopComponent.TabConstraints.tabTitle=\u53ef\u8996\u5316 +CVTTopComponent.accountsBrowser.TabConstraints.tabTitle=\u53c2\u7167 +FiltersPanel.applyFiltersButton.text=\u9069\u7528 +FiltersPanel.devicesLabel.text=\u30c7\u30d0\u30a4\u30b9: +FiltersPanel.accountTypesLabel.text=\u30a2\u30ab\u30a6\u30f3\u30c8\u30bf\u30a4\u30d7: +FiltersPanel.filtersTitleLabel.text=\u30d5\u30a3\u30eb\u30bf\u30fc +FiltersPanel.unCheckAllAccountTypesButton.text=\u3059\u3079\u3066\u306e\u30c1\u30a7\u30c3\u30af\u3092\u5916\u3059 +FiltersPanel.checkAllAccountTypesButton.text=\u3059\u3079\u3066\u306e\u30c1\u30a7\u30c3\u30af +FiltersPanel.unCheckAllDevicesButton.text=\u3059\u3079\u3066\u306e\u30c1\u30a7\u30c3\u30af\u3092\u5916\u3059 +FiltersPanel.checkAllDevicesButton.text=\u3059\u3079\u3066\u306e\u30c1\u30a7\u30c3\u30af +FiltersPanel.dateRangeLabel.text=\u65e5\u4ed8\u7bc4\u56f2: +FiltersPanel.startCheckBox.text=\u958b\u59cb: +FiltersPanel.endCheckBox.text=\u7d42\u4e86: +FiltersPanel.refreshButton.text=\u66f4\u65b0 +FiltersPanel.deviceRequiredLabel.text=\u5c11\u306a\u304f\u3068\u30821\u3064\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +FiltersPanel.accountTypeRequiredLabel.text=\u5c11\u306a\u304f\u3068\u30821\u3064\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +FiltersPanel.needsRefreshLabel.text=\u8868\u793a\u3055\u308c\u3066\u3044\u308b\u30c7\u30fc\u30bf\u306f\u53e4\u3044\u3067\u3059\u3002[\u66f4\u65b0] \u3092\u62bc\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +OpenCVTAction.displayName=\u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3 +PinAccountsAction.pluralText=\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u53ef\u8996\u5316\u306b\u8ffd\u52a0 +PinAccountsAction.singularText=\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u53ef\u8996\u5316\u306b\u8ffd\u52a0 +refreshText=\u7d50\u679c\u3092\u66f4\u65b0 +ResetAndPinAccountsAction.pluralText=\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u307f\u3092\u53ef\u8996\u5316 +ResetAndPinAccountsAction.singularText=\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u307f\u3092\u53ef\u8996\u5316 +UnpinAccountsAction.pluralText=\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u524a\u9664 +UnpinAccountsAction.singularText=\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u524a\u9664 +VisalizationPanel.paintingError=\u53ef\u8996\u5316\u306e\u63cf\u753b\u4e2d\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +# {0} - \u30c7\u30d5\u30a9\u30eb\u30c8\u540d +VisualizationPane_accept_defaultName=\u30ec\u30dd\u30fc\u30c8\u540d\u304c\u7a7a(\u672a\u5165\u529b)\u3067\u3057\u305f\u3002[OK] \u3092\u62bc\u3057\u3066\u6b21\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30ec\u30dd\u30fc\u30c8\u540d\u3092\u8a31\u53ef\u3057\u3066\u304f\u3060\u3055\u3044: {0} +VisualizationPane_blank_report_title=\u7a7a(\u672a\u5165\u529b)\u306e\u72b6\u614b\u306e\u30ec\u30dd\u30fc\u30c8\u540d +VisualizationPane_DisplayName=\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u304f +VisualizationPane_fileName_prompt=\u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3\u30fb\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u30fb\u30ec\u30dd\u30fc\u30c8\u306e\u540d\u524d\u3092\u5165\u529b: +VisualizationPane_MessageBoxTitle=\u30ec\u30dd\u30fc\u30c8\u30aa\u30fc\u30d7\u30f3\u5931\u6557 +VisualizationPane_MissingReportFileMessage=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +VisualizationPane_NoAssociatedEditorMessage=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u30ec\u30dd\u30fc\u30c8\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30a8\u30c7\u30a3\u30bf\u30fc\u304c\u306a\u3044\u304b\u3001\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +VisualizationPane_NoOpenInEditorSupportMessage=\u3053\u306e\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0(\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0)\u306f\u3053\u306e\u65b9\u6cd5\u3067\u30a8\u30c7\u30a3\u30bf\u30fc\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u304f\u3053\u3068\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u305b\u3093\u3002 +VisualizationPane_Open_Report=\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u304f +# {0} - \u30ec\u30dd\u30fc\u30c8\u540d +VisualizationPane_overrite_exiting=\u65e2\u5b58\u306e\u30ec\u30dd\u30fc\u30c8\u3092\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b?\n{0} +VisualizationPane_Report_OK_Button=OK +# {0} - \u30ec\u30dd\u30fc\u30c8\u30d1\u30b9 +VisualizationPane_Report_Success=\u6b21\u306e\u5834\u6240\u306b\u30ec\u30dd\u30fc\u30c8\u304c\u6b63\u5e38\u306b\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f:\n{0} +VisualizationPane_ReportFileOpenPermissionDeniedMessage=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u304f\u8a31\u53ef\u304c\u62d2\u5426\u3055\u308c\u307e\u3057\u305f\u3002 +VisualizationPane_reportName=\u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3\u30fb\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8 +VisualizationPanel.cancelButton.text=\u53d6\u308a\u6d88\u3057 +VisualizationPanel.computingLayout=\u30ec\u30a4\u30a2\u30a6\u30c8\u3092\u8a08\u7b97\u4e2d\u3067\u3059 +VisualizationPanel.jButton1.text=\u8fc5\u901f\u304b\u3064\u6709\u6a5f\u7684 +CVTTopComponent.vizPanel.TabConstraints.tabTitle=\u53ef\u8996\u5316 +CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=\u53c2\u7167 +CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=\u53ef\u8996\u5316 +CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=\u53ef\u8996\u5316 +VisualizationPanel.fitGraphButton.text= +VisualizationPanel.jTextArea1.text=[\u30a2\u30ab\u30a6\u30f3\u30c8\u53c2\u7167] \u30c6\u30fc\u30d6\u30eb\u3067\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3001[\u53ef\u8996\u5316] \u3092\u9078\u629e\u3057\u3066\u958b\u59cb\u3057\u307e\u3059\u3002 +# {0} - \u30ec\u30a4\u30a2\u30a6\u30c8\u540d +VisualizationPanel.layoutFail.text={0} \u30ec\u30a4\u30a2\u30a6\u30c8\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u5225\u306e\u30ec\u30a4\u30a2\u30a6\u30c8\u3092\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 +# {0} - \u30ec\u30a4\u30a2\u30a6\u30c8\u540d +VisualizationPanel.layoutFailWithLockedVertices.text=\u9802\u70b9\u304c\u30ed\u30c3\u30af\u3055\u308c\u305f {0} \u30ec\u30a4\u30a2\u30a6\u30c8\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u4e00\u90e8\u306e\u9802\u70b9\u306e\u30ed\u30c3\u30af\u3092\u89e3\u9664\u3059\u308b\u304b\u3001\u5225\u306e\u30ec\u30a4\u30a2\u30a6\u30c8\u3092\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 +VisualizationPanel.lockAction.pluralText=\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u30ed\u30c3\u30af +VisualizationPanel.lockAction.singularText=\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u30ed\u30c3\u30af +VisualizationPanel.unlockAction.pluralText=\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u30ed\u30c3\u30af\u3092\u89e3\u9664 +VisualizationPanel.unlockAction.singularText=\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u30ed\u30c3\u30af +VisualizationPanel.zoomLabel.text=100% +VisualizationPanel.jLabel2.text=\u30ba\u30fc\u30e0: +VisualizationPanel.fitZoomButton.toolTipText=\u53ef\u8996\u5316\u3092\u9069\u5408 +VisualizationPanel.fitZoomButton.text= +VisualizationPanel.zoomActualButton.toolTipText=\u30ba\u30fc\u30e0\u3092\u30ea\u30bb\u30c3\u30c8 +VisualizationPanel.zoomActualButton.text= +VisualizationPanel.zoomInButton.toolTipText=\u30ba\u30fc\u30e0\u30a4\u30f3 +VisualizationPanel.zoomInButton.text= +VisualizationPanel.zoomOutButton.toolTipText=\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8 +VisualizationPanel.zoomOutButton.text= +VisualizationPanel.fastOrganicLayoutButton.text= +VisualizationPanel.backButton.text_1= +VisualizationPanel.circleLayoutButton.text=\u5186\u5f62 +VisualizationPanel.organicLayoutButton.text=\u6709\u6a5f\u7684 +VisualizationPanel.hierarchyLayoutButton.text=\u968e\u5c64\u7684 +VisualizationPanel.clearVizButton.text_1= +VisualizationPanel.snapshotButton.text_1=\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u30fb\u30ec\u30dd\u30fc\u30c8 +VisualizationPanel.clearVizButton.actionCommand= +VisualizationPanel.backButton.toolTipText=\u30af\u30ea\u30c3\u30af\u3057\u3066\u623b\u308b +VisualizationPanel.forwardButton.toolTipText=\u30af\u30ea\u30c3\u30af\u3057\u3066\u9032\u3080 +VisualizationPanel.fastOrganicLayoutButton.toolTipText=\u30af\u30ea\u30c3\u30af\u3057\u3066\u30c1\u30e3\u30fc\u30c8\u3092\u518d\u63cf\u753b +VisualizationPanel.clearVizButton.toolTipText=\u30af\u30ea\u30c3\u30af\u3057\u3066\u30c1\u30e3\u30fc\u30c8\u3092\u6d88\u53bb +FiltersPanel.limitHeaderLabel.text=\u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3\u9650\u754c\u5024: +FiltersPanel.mostRecentLabel.text=\u6700\u65b0: +FiltersPanel.limitErrorMsgLabel.text=\u7121\u52b9\u306a\u6574\u6570\u5024\u3067\u3059\u3002 +VisualizationPanel.forwardButton.text= +VisualizationPanel_action_dialogs_title=\u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3 +VisualizationPanel_action_name_text=\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u30fb\u30ec\u30dd\u30fc\u30c8 +VisualizationPanel_module_name=\u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3 +VisualizationPanel_snapshot_report_failure=\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u30fb\u30ec\u30dd\u30fc\u30c8\u304c\u4f5c\u6210\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentThumbnailsChildren.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentThumbnailsChildren.java new file mode 100755 index 0000000000..816eb05d75 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentThumbnailsChildren.java @@ -0,0 +1,153 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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 obt ain 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.communications.relationships; + +import com.google.gson.Gson; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.function.Consumer; +import java.util.logging.Level; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.nodes.Sheet; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; +import org.sleuthkit.autopsy.datamodel.FileNode; +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; +import org.sleuthkit.datamodel.blackboardutils.FileAttachment; +import org.sleuthkit.datamodel.blackboardutils.MessageAttachments; + +/** + * Factory for creating thumbnail children nodes. + */ +final class AttachmentThumbnailsChildren extends Children.Keys { + + private static final Logger LOGGER = Logger.getLogger(AttachmentThumbnailsChildren.class.getName()); + + private final Set artifacts; + + /* + * Creates the list of thumbnails from the given list of + * BlackboardArtifacts. + * + * The thumbnails will be initialls sorted by size, then name so that they + * appear sorted by size by default. + */ + AttachmentThumbnailsChildren(Set artifacts) { + super(false); + + this.artifacts = artifacts; + + } + + @Override + protected Node[] createNodes(AbstractFile t) { + return new Node[]{new AttachementThumbnailNode(t)}; + } + + @Override + protected void addNotify() { + super.addNotify(); + + Set thumbnails = new TreeSet<>((AbstractFile file1, AbstractFile file2) -> { + int result = Long.compare(file1.getSize(), file2.getSize()); + if (result == 0) { + result = file1.getName().compareTo(file2.getName()); + } + + return result; + }); + + artifacts.forEach(new Consumer() { + @Override + public void accept(BlackboardArtifact bba) { + try { + // Get the attachments from TSK_ATTACHMENTS attribute. + BlackboardAttribute attachmentsAttr = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS)); + if (attachmentsAttr != null) { + + String jsonVal = attachmentsAttr.getValueString(); + MessageAttachments msgAttachments = new Gson().fromJson(jsonVal, MessageAttachments.class); + + Collection fileAttachments = msgAttachments.getFileAttachments(); + for (FileAttachment fileAttachment : fileAttachments) { + long attachedFileObjId = fileAttachment.getObjectId(); + if (attachedFileObjId >= 0) { + AbstractFile attachedFile = bba.getSleuthkitCase().getAbstractFileById(attachedFileObjId); + thumbnails.add(attachedFile); + } + } + } else { // backward compatibility - email message attachments are derived files, children of the message. + for (Content childContent : bba.getChildren()) { + if (childContent instanceof AbstractFile) { + thumbnails.add((AbstractFile) childContent); + } + } + } + + } catch (TskCoreException ex) { + LOGGER.log(Level.WARNING, "Unable to get children from artifact.", ex); //NON-NLS + } + } + }); + + setKeys(thumbnails); + } + + /** + * A node for representing a thumbnail. + */ + static class AttachementThumbnailNode extends FileNode { + + AttachementThumbnailNode(AbstractFile file) { + super(file, false); + } + + @Override + protected Sheet createSheet() { + Sheet sheet = super.createSheet(); + Set keepProps = new HashSet<>(Arrays.asList( + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.sizeColLbl"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.knownColLbl"))); + + //Remove all other props except for the ones above + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + for (Node.Property p : sheetSet.getProperties()) { + if (!keepProps.contains(p.getName())) { + sheetSet.remove(p.getName()); + } + } + + return sheet; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties index 01d5316454..b14d8a2688 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties @@ -26,3 +26,4 @@ SummaryViewer.attachmentsLabel.text=Total Attachments: SummaryViewer.referencesLabel.text=Communication References: SummaryViewer.referencesDataLabel.text= SummaryViewer.contactsLabel.text=Book Entries: +SummaryViewer.accountCountry.text= diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index 3d6fbef0cb..f1530d9f89 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -46,6 +46,7 @@ SummaryViewer_Account_Description=This account represents a device in the case. SummaryViewer_Account_Description_MuliSelect=Summary information is not available when multiple accounts are selected. SummaryViewer_CaseRefNameColumn_Title=Case Name SummaryViewer_CentralRepository_Message= +SummaryViewer_Country_Code=Country: SummaryViewer_Creation_Date_Title=Creation Date SummaryViewer_Device_Account_Description=This account was referenced by a device in the case. SummaryViewer_FileRef_Message=", "SummaryViewer_Device_Account_Description=This account was referenced by a device in the case.", "SummaryViewer_Account_Description=This account represents a device in the case.", - "SummaryViewer_Account_Description_MuliSelect=Summary information is not available when multiple accounts are selected." + "SummaryViewer_Account_Description_MuliSelect=Summary information is not available when multiple accounts are selected.", + "SummaryViewer_Country_Code=Country: " }) /** @@ -110,7 +112,16 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi Account[] accountArray = info.getAccounts().toArray(new Account[1]); Account account = accountArray[0]; - accountLabel.setText(account.getTypeSpecificID()); + if (account.getAccountType().getTypeName().contains("PHONE")) { + String countryCode = PhoneNumUtil.getCountryCode(account.getTypeSpecificID()); + accountLabel.setText(PhoneNumUtil.convertToInternational(account.getTypeSpecificID())); + accountCountry.setText(Bundle.SummaryViewer_Country_Code() + countryCode); + accountCountry.setEnabled(true); + } else { + accountLabel.setText(account.getTypeSpecificID()); + accountCountry.setText(""); + accountCountry.setEnabled(false); + } if (account.getAccountType().equals(Account.Type.DEVICE)) { accoutDescriptionLabel.setText(Bundle.SummaryViewer_Account_Description()); @@ -173,6 +184,7 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi accountLabel.setText(""); accoutDescriptionLabel.setText(""); referencesDataLabel.setText(""); + accountCountry.setText(""); fileReferencesPanel.setNode(new AbstractNode(Children.LEAF)); caseReferencesPanel.setNode(new AbstractNode(Children.LEAF)); @@ -210,6 +222,7 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi summaryPanel = new javax.swing.JPanel(); accountLabel = new javax.swing.JLabel(); + accountCountry = new javax.swing.JLabel(); accoutDescriptionLabel = new javax.swing.JLabel(); countsPanel = new javax.swing.JPanel(); messagesLabel = new javax.swing.JLabel(); @@ -220,13 +233,15 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi callLogsDataLabel = new javax.swing.JLabel(); attachmentsLabel = new javax.swing.JLabel(); attachmentDataLabel = new javax.swing.JLabel(); - fileReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); - caseReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); contanctsPanel = new javax.swing.JPanel(); contactsLabel = new javax.swing.JLabel(); contactsDataLabel = new javax.swing.JLabel(); referencesLabel = new javax.swing.JLabel(); referencesDataLabel = new javax.swing.JLabel(); + fileReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); + caseReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); + + setLayout(new java.awt.GridBagLayout()); summaryPanel.setLayout(new java.awt.GridBagLayout()); @@ -238,15 +253,27 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi gridBagConstraints.insets = new java.awt.Insets(15, 9, 0, 9); summaryPanel.add(accountLabel, gridBagConstraints); - org.openide.awt.Mnemonics.setLocalizedText(accoutDescriptionLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.accoutDescriptionLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(accountCountry, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.accountCountry.text")); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 9, 0, 9); + summaryPanel.add(accountCountry, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(accoutDescriptionLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.accoutDescriptionLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(15, 9, 15, 9); summaryPanel.add(accoutDescriptionLabel, gridBagConstraints); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + add(summaryPanel, gridBagConstraints); + countsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.countsPanel.border.title"))); // NOI18N countsPanel.setLayout(new java.awt.GridBagLayout()); @@ -315,31 +342,12 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 15); countsPanel.add(attachmentDataLabel, gridBagConstraints); - summaryPanel.add(countsPanel, new java.awt.GridBagConstraints()); - - setLayout(new java.awt.GridBagLayout()); - - fileReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.fileReferencesPanel.border.title"))); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; + gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); - add(fileReferencesPanel, gridBagConstraints); - - caseReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.caseReferencesPanel.border.title"))); // NOI18N - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); - add(caseReferencesPanel, gridBagConstraints); + add(countsPanel, gridBagConstraints); contanctsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.contanctsPanel.border.title"))); // NOI18N contanctsPanel.setLayout(new java.awt.GridBagLayout()); @@ -381,14 +389,37 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; add(contanctsPanel, gridBagConstraints); + + fileReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.fileReferencesPanel.border.title"))); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); + add(fileReferencesPanel, gridBagConstraints); + + caseReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.caseReferencesPanel.border.title"))); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); + add(caseReferencesPanel, gridBagConstraints); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel accountCountry; private javax.swing.JLabel accountLabel; private javax.swing.JLabel accoutDescriptionLabel; private javax.swing.JLabel attachmentDataLabel; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index b3a384ce48..f8119b88da 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -93,3 +93,7 @@ MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors MediaPlayerPanel.VolumeIcon.text=Volume MediaPlayerPanel.playBackSpeedLabel.text=Speed: +ContextViewer.jSourceGoToResultButton.text=Go to Result +ContextViewer.jSourceNameLabel.text=jSourceNameLabel +ContextViewer.jSourceTextLabel.text=jLabel2 +ContextViewer.jSourceLabel.text=Source diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index ba39d1d419..ceec760a69 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -10,6 +10,17 @@ AnnotationsContentViewer.title=Annotations AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. ApplicationContentViewer.title=Application ApplicationContentViewer.toolTip=Displays file contents. +ContextViewer.attachmentSource=Attached to: +ContextViewer.downloadedOn=On +ContextViewer.downloadSource=Downloaded from: +ContextViewer.downloadURL=URL +ContextViewer.email=Email +ContextViewer.message=Message +ContextViewer.messageFrom=From +ContextViewer.messageOn=On +ContextViewer.messageTo=From +ContextViewer.title=Context Viewer +ContextViewer.toolTip=Displays context for selected file. FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video. FXVideoPanel.progress.bufferingCancelled=media buffering was canceled FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted @@ -23,6 +34,7 @@ GstVideoPanel.noOpenCase.errMsg=No open case available. Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML. HtmlPanel_showImagesToggleButton_hide=Hide Images HtmlPanel_showImagesToggleButton_show=Download Images +HtmlViewer_encoding_error=This file has unsupported encoding HtmlViewer_file_error=This file is missing or unreadable. MediaFileViewer.initGst.gstException.msg=Error initializing gstreamer for audio/video viewing and frame extraction capabilities. Video and audio viewing will be disabled. GstVideoPanel.setupVideo.infoLabel.text=Playback of deleted videos is not supported, use an external player. @@ -163,6 +175,10 @@ MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors MediaPlayerPanel.VolumeIcon.text=Volume MediaPlayerPanel.playBackSpeedLabel.text=Speed: +ContextViewer.jSourceGoToResultButton.text=Go to Result +ContextViewer.jSourceNameLabel.text=jSourceNameLabel +ContextViewer.jSourceTextLabel.text=jLabel2 +ContextViewer.jSourceLabel.text=Source # {0} - tableName SQLiteViewer.readTable.errorText=Error getting rows for table: {0} # {0} - tableName diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties index 0be94476e6..f7b7844e60 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties @@ -1,36 +1,81 @@ -FXVideoPanel.pauseButton.infoLabel.playbackErr=\u30d3\u30c7\u30aa\u3092\u518d\u751f\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -FXVideoPanel.progress.bufferingCancelled=\u30e1\u30c7\u30a3\u30a2\u306e\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u304c\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u307e\u3057\u305f -FXVideoPanel.progress.bufferingInterrupted=\u30e1\u30c7\u30a3\u30a2\u306e\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u304c\u4e2d\u65ad\u3055\u308c\u307e\u3057\u305f -FXVideoPanel.progress.errorWritingVideoToDisk=\u30d3\u30c7\u30aa\u3092\u30c7\u30a3\u30b9\u30af\u3078\u66f8\u304d\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -FXVideoPanel.mediaPane.infoLabel=\u524a\u9664\u3055\u308c\u305f\u30d3\u30c7\u30aa\u306e\u518d\u751f\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u5916\u90e8\u30d7\u30ec\u30fc\u30e4\u30fc\u3092\u4f7f\u7528\u3057\u3066\u4e0b\u3055\u3044\u3002 -FXVideoPanel.progress.bufferingFile={0}\u3092\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0 -FXVideoPanel.progressLabel.buffering=\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u4e2d\u2026 +AnnotationsContentViewer.centralRepositoryEntryDataLabel.case=\u30b1\u30fc\u30b9: +AnnotationsContentViewer.centralRepositoryEntryDataLabel.comment=\u30b3\u30e1\u30f3\u30c8: +AnnotationsContentViewer.centralRepositoryEntryDataLabel.path=\u30d1\u30b9: +AnnotationsContentViewer.centralRepositoryEntryDataLabel.type=\u30bf\u30a4\u30d7: +AnnotationsContentViewer.tagEntryDataLabel.comment=\u30b3\u30e1\u30f3\u30c8: +AnnotationsContentViewer.tagEntryDataLabel.tag=\u30bf\u30b0: +AnnotationsContentViewer.tagEntryDataLabel.tagUser=\u30bf\u30b0\u30e6\u30fc\u30b6\u30fc: +AnnotationsContentViewer.title=\u30a2\u30ce\u30c6\u30fc\u30b7\u30e7\u30f3 +AnnotationsContentViewer.toolTip=\u9078\u629e\u3057\u305f\u30b3\u30f3\u30c6\u30f3\u30c4\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30bf\u30b0\u3068\u30b3\u30e1\u30f3\u30c8\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +ApplicationContentViewer.title=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 +ApplicationContentViewer.toolTip=\u30d5\u30a1\u30a4\u30eb\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +FXVideoPanel.pauseButton.infoLabel.playbackErr=\u52d5\u753b\u3092\u518d\u751f\u3067\u304d\u307e\u305b\u3093\u3002 +FXVideoPanel.progress.bufferingCancelled=\u30e1\u30c7\u30a3\u30a2\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u304c\u53d6\u308a\u6d88\u3055\u308c\u307e\u3057\u305f\u3002 +FXVideoPanel.progress.bufferingInterrupted=\u30e1\u30c7\u30a3\u30a2\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u304c\u4e2d\u65ad\u3055\u308c\u307e\u3057\u305f\u3002 +FXVideoPanel.progress.errorWritingVideoToDisk=\u52d5\u753b\u3092\u30c7\u30a3\u30b9\u30af\u306b\u66f8\u304d\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +FXVideoPanel.mediaPane.infoLabel=\u524a\u9664\u3057\u305f\u52d5\u753b\u306e\u518d\u751f\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u5916\u90e8\u30d7\u30ec\u30fc\u30e4\u30fc\u3092\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +FXVideoPanel.progress.bufferingFile={0} \u3092\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u4e2d\u3067\u3059 +FXVideoPanel.progressLabel.buffering=\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u4e2d\u3067\u3059... FXVideoPanel.media.unsupportedFormat=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u3059\u3002 -GstVideoPanel.cannotProcFile.err=\u30e1\u30c7\u30a4\u30a2\u30d7\u30ec\u30fc\u30e4\u30fc\u3067\u306f\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u3067\u304d\u307e\u305b\u3093\u3002 -GstVideoPanel.initGst.gstException.msg=\u30aa\u30fc\u30c7\u30a3\u30aa\uff0f\u30d3\u30c7\u30aa\u306e\u518d\u751f\u304a\u3088\u3073\u30d5\u30ec\u30fc\u30e0\u306e\u62bd\u51fa\u306b\u4f7f\u7528\u3059\u308bGStreamer\u306e\u521d\u671f\u5316\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30d3\u30c7\u30aa\u304a\u3088\u3073\u30aa\u30fc\u30c7\u30a3\u30aa\u518d\u751f\u304c\u7121\u52b9\u5316\u3055\u308c\u307e\u3059\u3002 -GstVideoPanel.setupVideo.infoLabel.text=\u524a\u9664\u3055\u308c\u305f\u30d3\u30c7\u30aa\u306e\u518d\u751f\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u5916\u90e8\u30d7\u30ec\u30fc\u30e4\u30fc\u3092\u4f7f\u7528\u3057\u3066\u4e0b\u3055\u3044\u3002 -GstVideoPanel.exception.problemFile.msg=\u30d5\u30a1\u30a4\u30eb({0})\u304b\u3089\u30d5\u30ec\u30fc\u30e0\u3092\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -GstVideoPanel.exception.problemPlay.msg=\u30d3\u30c7\u30aa\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u9577\u3055\u3092\u78ba\u8a8d\u4e2d\u306b\u518d\u751f\u3092\u3057\u3088\u3046\u3068\u3057\u305f\u969b\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -GstVideoPanel.exception.problemPause.msg=\u30d3\u30c7\u30aa\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u9577\u3055\u3092\u78ba\u8a8d\u4e2d\u306b\u4e00\u6642\u505c\u6b62\u3092\u3057\u3088\u3046\u3068\u3057\u305f\u969b\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -GstVideoPanel.exception.problemPauseCaptFrame.msg=\u30d3\u30c7\u30aa\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30d5\u30ec\u30fc\u30e0\u306e\u62bd\u51fa\u4e2d\u306b\u4e00\u6642\u505c\u6b62\u3092\u3057\u3088\u3046\u3068\u3057\u305f\u969b\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -GstVideoPanel.exception.problemPlayCaptFrame.msg=\u30d3\u30c7\u30aa\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30d5\u30ec\u30fc\u30e0\u306e\u62bd\u51fa\u4e2d\u306b\u518d\u751f\u3057\u3088\u3046\u3068\u3057\u305f\u969b\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -GstVideoPanel.exception.problemStopCaptFrame.msg=\u30d3\u30c7\u30aa\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30d5\u30ec\u30fc\u30e0\u306e\u62bd\u51fa\u4e2d\u306b\u505c\u6b62\u3057\u3088\u3046\u3068\u3057\u305f\u969b\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -GstVideoPanel.progress.buffering=\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u4e2d\u2026 -GstVideoPanel.progressLabel.bufferingErr=\u30d5\u30a1\u30a4\u30eb\u306e\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u30a8\u30e9\u30fc -GstVideoPanel.progress.infoLabel.updateErr=\u30d3\u30c7\u30aa\u30d7\u30ed\u30b0\u30ec\u30b9\u306e\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a {0} -GstVideoPanel.ExtractMedia.progress.buffering={0}\u3092\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u4e2d +GstVideoPanel.cannotProcFile.err=\u30e1\u30c7\u30a3\u30a2\u30d7\u30ec\u30a4\u30e4\u30fc\u306f\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u3067\u304d\u307e\u305b\u3093\u3002 +GstVideoPanel.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +Html_text_display_error=HTML\u30c6\u30ad\u30b9\u30c8\u3092\u8868\u793a\u3067\u304d\u307e\u305b\u3093\u3002 \u6b63\u3057\u304f\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3055\u308c\u305fHTML\u3067\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +HtmlPanel_showImagesToggleButton_hide=\u30a4\u30e1\u30fc\u30b8\u3092\u975e\u8868\u793a\u306b\u3059\u308b +HtmlPanel_showImagesToggleButton_show=\u30a4\u30e1\u30fc\u30b8\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9 +HtmlViewer_file_error=\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u306f\u898b\u3064\u304b\u3089\u306a\u3044\u304b\u3001\u89e3\u8aad\u3067\u304d\u307e\u305b\u3093\u3002 +MediaFileViewer.initGst.gstException.msg=\u30aa\u30fc\u30c7\u30a3\u30aa/\u52d5\u753b\u8996\u8074\u6a5f\u80fd\u3068\u30d5\u30ec\u30fc\u30e0\u62bd\u51fa\u6a5f\u80fd\u3092\u4f7f\u7528\u3059\u308b\u305f\u3081\u306bGstreamer\u3092\u521d\u671f\u5316\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 \u30aa\u30fc\u30c7\u30a3\u30aa/\u52d5\u753b\u8996\u8074\u304c\u7121\u52b9\u5316\u3055\u308c\u307e\u3059\u3002 +GstVideoPanel.setupVideo.infoLabel.text=\u524a\u9664\u3057\u305f\u52d5\u753b\u306e\u518d\u751f\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u5916\u90e8\u30d7\u30ec\u30fc\u30e4\u30fc\u3092\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +GstVideoPanel.exception.problemFile.msg=\u3053\u306e\u30d5\u30a1\u30a4\u30eb({0})\u304b\u3089\u30d5\u30ec\u30fc\u30e0\u3092\u30ad\u30e3\u30d7\u30c1\u30e3\u30fc\u3067\u304d\u307e\u305b\u3093\u3002 +GstVideoPanel.exception.problemPlay.msg=\u52d5\u753b\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3059\u3002\u7dcf\u518d\u751f\u6642\u9593\u306e\u53d6\u5f97\u4e2d\u306b\u518d\u751f\u3092\u8a66\u307f\u308b\u3068\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3059\u3002 +GstVideoPanel.exception.problemPause.msg=\u52d5\u753b\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3059\u3002\u7dcf\u518d\u751f\u6642\u9593\u306e\u53d6\u5f97\u4e2d\u306b\u4e00\u6642\u505c\u6b62\u3092\u8a66\u307f\u308b\u3068\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3059\u3002 +GstVideoPanel.exception.problemPauseCaptFrame.msg=\u52d5\u753b\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3059\u3002\u30d5\u30ec\u30fc\u30e0\u306e\u30ad\u30e3\u30d7\u30c1\u30e3\u30fc\u4e2d\u306b\u4e00\u6642\u505c\u6b62\u3092\u8a66\u307f\u308b\u3068\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3059\u3002 +GstVideoPanel.exception.problemPlayCaptFrame.msg=\u52d5\u753b\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3059\u3002\u30d5\u30ec\u30fc\u30e0\u306e\u30ad\u30e3\u30d7\u30c1\u30e3\u30fc\u4e2d\u306b\u518d\u751f\u3092\u8a66\u307f\u308b\u3068\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3059\u3002 +GstVideoPanel.exception.problemStopCaptFrame.msg=\u52d5\u753b\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3059\u3002\u30d5\u30ec\u30fc\u30e0\u306e\u30ad\u30e3\u30d7\u30c1\u30e3\u30fc\u4e2d\u306b\u505c\u6b62\u3092\u8a66\u307f\u308b\u3068\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3059\u3002 +GstVideoPanel.progress.buffering=\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u4e2d\u3067\u3059... +GstVideoPanel.progressLabel.bufferingErr=\u30d5\u30a1\u30a4\u30eb\u306e\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +GstVideoPanel.progress.infoLabel.updateErr=\u6b21\u306e\u52d5\u753b\u306e\u9032\u6357\u72b6\u6cc1\u3092\u66f4\u65b0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +GstVideoPanel.ExtractMedia.progress.buffering={0} \u3092\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u4e2d\u3067\u3059 +MediaFileViewer.AccessibleContext.accessibleDescription= MediaFileViewer.title=\u30e1\u30c7\u30a3\u30a2 -MediaFileViewer.toolTip=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u30de\u30eb\u30c1\u30e1\u30c7\u30a3\u30a2\u30d5\u30a1\u30a4\u30eb\uff08\u30a4\u30e1\u30fc\u30b8\u3001\u30d3\u30c7\u30aa\u3001\u30aa\u30fc\u30c7\u30a3\u30aa\uff09\u3092\u8868\u793a\u3057\u307e\u3059\u3002 -MediaViewImagePanel.imgFileTooLarge.msg=\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\uff08\u5927\u304d\u3059\u304e\u3067\u3059\uff09\uff1a {0} +MediaFileViewer.toolTip=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u30de\u30eb\u30c1\u30e1\u30c7\u30a3\u30a2\u30d5\u30a1\u30a4\u30eb(\u30a4\u30e1\u30fc\u30b8\u3001\u52d5\u753b\u3001\u30aa\u30fc\u30c7\u30a3\u30aa)\u3092\u8868\u793a\u3057\u307e\u3059 +MediaPlayerPanel.noSupport=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u30d5\u30a1\u30a4\u30eb\u3067\u3059\u3002 +MediaPlayerPanel.timeFormat=%02d:%02d:%02d +MediaPlayerPanel.unknownTime=\u4e0d\u660e +MediaViewImagePanel.createTagOption=\u4f5c\u6210 +MediaViewImagePanel.deleteTagOption=\u524a\u9664 +MediaViewImagePanel.errorLabel.OOMText=\u30e1\u30c7\u30a3\u30a2\u30d3\u30e5\u30fc\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30e1\u30e2\u30ea\u30fc\u304c\u4e0d\u8db3\u3057\u3066\u3044\u307e\u3059\u3002 +MediaViewImagePanel.errorLabel.text=\u30e1\u30c7\u30a3\u30a2\u30d3\u30e5\u30fc\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +MediaViewImagePanel.exportSaveText=\u4fdd\u5b58 +MediaViewImagePanel.exportTagOption=\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +MediaViewImagePanel.externalViewerButton.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f Ctrl+E +MediaViewImagePanel.fileChooserTitle=\u4fdd\u5b58\u5834\u6240\u3092\u9078\u629e +MediaViewImagePanel.hideTagOption=\u975e\u8868\u793a +MediaViewImagePanel.successfulExport=\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u30a4\u30e1\u30fc\u30b8\u304c\u6b63\u5e38\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3057\u305f\u3002 +MediaViewImagePanel.unsuccessfulExport=\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u30a4\u30e1\u30fc\u30b8\u3092\u30c7\u30a3\u30b9\u30af\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3067\u304d\u307e\u305b\u3093\u3002 MediaViewVideoPanel.pauseButton.text=\u25ba -MediaViewVideoPanel.progressLabel.text=00\:00 -MediaViewVideoPanel.infoLabel.text=\u60c5\u5831 +MediaViewVideoPanel.progressLabel.text=00:00 +MediaViewVideoPanel.infoLabel.text=\u4fdd\u5b58 +MediaViewImagePanel.imgFileTooLarge.msg=\u6b21\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f(\u5927\u304d\u3059\u304e\u3066): {0} +MessageContentViewer.AtrachmentsPanel.title=\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb +MessageContentViewer.title=\u30e1\u30c3\u30bb\u30fc\u30b8 +MessageContentViewer.toolTip=\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +Metadata.nodeText.none=\u306a\u3057 +Metadata.nodeText.truncated=(\u4e00\u62ec\u524a\u9664\u3055\u308c\u305f\u7d50\u679c) +Metadata.nodeText.unknown=\u4e0d\u660e +Metadata.tableRowTitle.acquisitionDetails=\u53d6\u5f97\u306e\u8a73\u7d30 +Metadata.tableRowTitle.deviceId=\u30c7\u30d0\u30a4\u30b9ID +Metadata.tableRowTitle.downloadSource=\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u5143 +Metadata.tableRowTitle.imageType=\u30bf\u30a4\u30d7 +Metadata.tableRowTitle.mimeType=MIME\u30bf\u30a4\u30d7 Metadata.tableRowTitle.name=\u540d\u524d +Metadata.tableRowTitle.sectorSize=\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30ba +Metadata.tableRowTitle.sha1=SHA1 +Metadata.tableRowTitle.sha256=SHA256 Metadata.tableRowTitle.size=\u30b5\u30a4\u30ba -Metadata.tableRowTitle.fileNameAlloc=\u30d5\u30a1\u30a4\u30eb\u540d\u30a2\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3 -Metadata.tableRowTitle.metadataAlloc=\u30e1\u30bf\u30c7\u30fc\u30bf\u30a2\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3 +Metadata.tableRowTitle.fileNameAlloc=\u30d5\u30a1\u30a4\u30eb\u540d\u306e\u5272\u308a\u5f53\u3066 +Metadata.tableRowTitle.metadataAlloc=\u30e1\u30bf\u30c7\u30fc\u30bf\u306e\u5272\u308a\u5f53\u3066 Metadata.tableRowTitle.modified=\u4fee\u6b63\u6e08\u307f Metadata.tableRowTitle.accessed=\u30a2\u30af\u30bb\u30b9\u6e08\u307f Metadata.tableRowTitle.created=\u4f5c\u6210\u6e08\u307f @@ -40,9 +85,82 @@ Metadata.tableRowTitle.md5=MD5 Metadata.tableRowTitle.hashLookupResults=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u7d50\u679c Metadata.tableRowTitle.internalid=\u5185\u90e8ID Metadata.tableRowTitle.localPath=\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9 -Metadata.title=\u30d5\u30a1\u30a4\u30eb\u30e1\u30bf\u30c7\u30fc\u30bf -Metadata.toolTip=\u30d5\u30a1\u30a4\u30eb\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +Metadata.tableRowTitle.timezone=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 Metadata.tableRowTitle.type=\u30bf\u30a4\u30d7 -Metadata.nodeText.exceptionNotice.text=\u30d5\u30a1\u30a4\u30eb\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a -Metadata.nodeText.text=Sleuth Kit istat\u30c4\u30fc\u30eb\u304b\u3089\uff1a -Metadata.nodeText.nonFilePassedIn=\u5165\u529b\u3055\u308c\u305f\u3082\u306e\u306f\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u3042\u308a\u307e\u305b\u3093 +Metadata.title=\u30d5\u30a1\u30a4\u30eb\u306e\u30e1\u30bf\u30c7\u30fc\u30bf +Metadata.toolTip=\u30d5\u30a1\u30a4\u30eb\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +Metadata.nodeText.nonFilePassedIn=\u30d5\u30a1\u30a4\u30eb\u4ee5\u5916\u306e\u3082\u306e\u304c\u901a\u904e\u3057\u307e\u3057\u305f +Metadata.nodeText.text=\u9001\u4fe1\u5143\u306eSleuth Kit\u306eistat\u30c4\u30fc\u30eb: +Metadata.nodeText.exceptionNotice.text=\u30d5\u30a1\u30a4\u30eb\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: +MessageContentViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF +MessageContentViewer.headersScrollPane.TabConstraints.tabTitle=\u30d8\u30c3\u30c0\u30fc +MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle=\u30c6\u30ad\u30b9\u30c8 +MessageContentViewer.htmlPane.TabConstraints.tabTitle=HTML +MessageContentViewer.fromText.text=\u9001\u4fe1\u5143\u30a2\u30c9\u30ec\u30b9\u3092\u3053\u3053\u306b\u8868\u793a +MessageContentViewer.fromLabel.text=\u5dee\u51fa\u4eba: +MessageContentViewer.datetimeText.text=\u65e5\u4ed8\u3092\u3053\u3053\u306b\u8868\u793a +MessageContentViewer.toText.text=\u5b9b\u5148\u306eTO\u30ea\u30b9\u30c8\u3092\u3053\u3053\u306b\u8868\u793a +MessageContentViewer.toLabel.text=\u5b9b\u5148: +MessageContentViewer.ccText.text=\u5b9b\u5148\u306eCC\u30ea\u30b9\u30c8\u3092\u3053\u3053\u306b\u8868\u793a +MessageContentViewer.subjectLabel.text=\u4ef6\u540d: +MessageContentViewer.subjectText.text=\u4ef6\u540d\u3092\u3053\u3053\u306b\u8868\u793a +MessageContentViewer.directionText.text=\u9001\u53d7\u4fe1\u306e\u7a2e\u5225 +MessageContentViewer.ccLabel.text=CC: +MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle=\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb +MessageContentViewer.viewInNewWindowButton.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a +JPEGViewerDummy.jLabel1.text=JPEG\u30d5\u30a1\u30a4\u30eb\u304c\u8868\u793a\u3055\u308c\u3066\u3044\u307e\u3059: +JPEGViewerDummy.jTextField1.text=jTextField1 +PListNode.KeyCol=\u30ad\u30fc +PListNode.TypeCol=\u30bf\u30a4\u30d7 +PListNode.ValueCol=\u5024 +PListViewer.DataType.message=\u30d0\u30a4\u30ca\u30ea\u30fc\u30c7\u30fc\u30bf\u5024\u304c\u8868\u793a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +PListViewer.ExportFailed.message=Plist\u30d5\u30a1\u30a4\u30eb\u306e\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 +PListViewer.ExportSuccess.message=Plist\u30d5\u30a1\u30a4\u30eb\u304c\u6b63\u5e38\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u307e\u3057\u305f +PListViewer.processPlist.errorMessage=plist\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790/\u8868\u793a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +PListViewer.processPlist.interruptedMessage=plist\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790/\u8868\u793a\u4e2d\u306b\u4e2d\u65ad\u3055\u308c\u307e\u3057\u305f\u3002 +SQLiteTableView.DisplayAs.text=\u5225\u540d\u3067\u8868\u793a +SQLiteTableView.DisplayAsMenuItem.Date=\u65e5\u4ed8 +SQLiteTableView.DisplayAsMenuItem.RawData=\u30ed\u30fc\u30c7\u30fc\u30bf +SQLiteViewer.comboBox.noTableEntry=\u30c6\u30fc\u30d6\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +SQLiteViewer.csvExport.confirm.msg=\u65e2\u5b58\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b? +SQLiteViewer.csvExport.fileName.empty=\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u30d5\u30a1\u30a4\u30eb\u540d\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +SQLiteViewer.csvExport.title=CSV\u30d5\u30a1\u30a4\u30eb\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +SQLiteViewer.errorMessage.failedToExtractFile=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304b\u3089\u30d5\u30a1\u30a4\u30eb\u3092\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +SQLiteViewer.errorMessage.failedToinitJDBCDriver=SQLite\u306eJDBC\u30c9\u30e9\u30a4\u30d0\u30fc\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +SQLiteViewer.errorMessage.failedToQueryDatabase=\u30d5\u30a1\u30a4\u30eb\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30c6\u30fc\u30d6\u30eb\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +SQLiteViewer.errorMessage.interrupted=\u30d5\u30a1\u30a4\u30eb\u306e\u51e6\u7406\u304c\u4e2d\u65ad\u3055\u308c\u307e\u3057\u305f\u3002 +SQLiteViewer.errorMessage.noCurrentCase=\u30b1\u30fc\u30b9\u304c\u9589\u3058\u3089\u308c\u307e\u3057\u305f\u3002 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +SQLiteViewer.errorMessage.unexpectedError=\u4e88\u671f\u305b\u306c\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f:\n{0)\u3002 +SQLiteViewer.exportTableToCsv.FileName=\u30d5\u30a1\u30a4\u30eb\u540d: +SQLiteViewer.exportTableToCsv.TableName=\u30c6\u30fc\u30d6\u30eb\u540d: +SQLiteViewer.exportTableToCsv.write.errText=\u30c6\u30fc\u30d6\u30eb\u30b3\u30f3\u30c6\u30f3\u30c4\u3092CSV\u30d5\u30a1\u30a4\u30eb\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +SQLiteViewer.nextPageButton.text= +SQLiteViewer.prevPageButton.text= +SQLiteViewer.numPagesLabel.text=N +SQLiteViewer.jLabel3.text=/ +SQLiteViewer.currPageLabel.text=x +SQLiteViewer.jLabel2.text=\u30da\u30fc\u30b8 +SQLiteViewer.numEntriesField.text=num Entries +SQLiteViewer.jLabel1.text=\u30c6\u30fc\u30d6\u30eb +PListViewer.exportButton.text=\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +SQLiteViewer.exportCsvButton.text=CSV\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +MediaViewImagePanel.rotateLeftButton.text= +MediaViewImagePanel.zoomInButton.text= +MediaViewImagePanel.rotateRightButton.text= +MediaViewImagePanel.zoomOutButton.text= +MediaViewImagePanel.zoomResetButton.text=\u30ea\u30bb\u30c3\u30c8 +MediaViewImagePanel.zoomTextField.text= +MediaViewImagePanel.rotationTextField.text= +MediaViewImagePanel.rotateLeftButton.toolTipText= +HtmlPanel.showImagesToggleButton.text=\u30a4\u30e1\u30fc\u30b8\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9 +MediaPlayerPanel.audioSlider.toolTipText= +MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ \u30dc\u30ea\u30e5\u30fc\u30e0 +MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00 +MediaPlayerPanel.playButton.text=\u25ba +MediaPlayerPanel.infoLabel.text=\u30a8\u30e9\u30fc\u306a\u3057 +MediaViewImagePanel.tagsMenu.text_1=\u30bf\u30b0\u30e1\u30cb\u30e5\u30fc +# {0} - tableName +SQLiteViewer.readTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +# {0} - tableName +SQLiteViewer.selectTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u30ab\u30a6\u30f3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form new file mode 100644 index 0000000000..e1f171ad36 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form @@ -0,0 +1,108 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java new file mode 100644 index 0000000000..212321e178 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java @@ -0,0 +1,442 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.contentviewers; + +import java.awt.Component; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.apache.commons.lang.StringUtils; +import org.openide.nodes.Node; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Displays additional context for the selected file, such as its source, and + * usage, if known. + * + */ +@ServiceProvider(service = DataContentViewer.class, position = 7) +public final class ContextViewer extends javax.swing.JPanel implements DataContentViewer { + + private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(ContextViewer.class.getName()); + private static final int ARTIFACT_STR_MAX_LEN = 1024; + private static final int ATTRIBUTE_STR_MAX_LEN = 200; + + // defines a list of artifacts that provide context for a file + private static final List SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>(); + + static { + SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT); + } + + private BlackboardArtifact sourceContextArtifact; + + /** + * Creates new form ContextViewer + */ + public ContextViewer() { + + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JButton jSourceGoToResultButton = new javax.swing.JButton(); + javax.swing.JLabel jSourceLabel = new javax.swing.JLabel(); + jSourceNameLabel = new javax.swing.JLabel(); + jSourceTextLabel = new javax.swing.JLabel(); + + setBackground(new java.awt.Color(255, 255, 255)); + + org.openide.awt.Mnemonics.setLocalizedText(jSourceGoToResultButton, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceGoToResultButton.text")); // NOI18N + jSourceGoToResultButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jSourceGoToResultButtonActionPerformed(evt); + } + }); + + jSourceLabel.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jSourceLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jSourceNameLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceNameLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jSourceTextLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceTextLabel.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jSourceLabel) + .addGroup(layout.createSequentialGroup() + .addGap(6, 6, 6) + .addComponent(jSourceNameLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jSourceTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE))) + .addGap(36, 36, 36)) + .addGroup(layout.createSequentialGroup() + .addComponent(jSourceGoToResultButton) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jSourceLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jSourceNameLabel) + .addComponent(jSourceTextLabel)) + .addGap(18, 18, 18) + .addComponent(jSourceGoToResultButton) + .addGap(0, 203, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void jSourceGoToResultButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jSourceGoToResultButtonActionPerformed + + final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); + + // Navigate to the source context artifact. + if (sourceContextArtifact != null) { + dtc.viewArtifact(sourceContextArtifact); + } + + }//GEN-LAST:event_jSourceGoToResultButtonActionPerformed + + @Override + public void setNode(Node selectedNode) { + if ((selectedNode == null) || (!isSupported(selectedNode))) { + resetComponent(); + return; + } + + AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class); + try { + populateSourceContextData(file); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Exception displaying context for file %s", file.getName()), ex); //NON-NLS + } + } + + @NbBundle.Messages({ + "ContextViewer.title=Context", + "ContextViewer.toolTip=Displays context for selected file." + }) + + @Override + public String getTitle() { + return Bundle.ContextViewer_title(); + } + + @Override + public String getToolTip() { + return Bundle.ContextViewer_toolTip(); + } + + @Override + public DataContentViewer createInstance() { + return new ContextViewer(); + } + + @Override + public Component getComponent() { + return this; + } + + @Override + public void resetComponent() { + setSourceName(""); + setSourceText(""); + } + + @Override + public boolean isSupported(Node node) { + + // check if the node has an abstract file and the file has any context defining artifacts. + if (node.getLookup().lookup(AbstractFile.class) != null) { + AbstractFile abstractFile = node.getLookup().lookup(AbstractFile.class); + for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) { + List artifactsList; + try { + artifactsList = abstractFile.getArtifacts(artifactType); + if (!artifactsList.isEmpty()) { + return true; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Exception while looking up context artifacts for file %s", abstractFile), ex); //NON-NLS + } + } + + } + + return false; + } + + @Override + public int isPreferred(Node node) { + // this is a low preference viewer. + return 1; + } + + /** + * Looks for context providing artifacts for the given file and populates + * the source context. + * + * @param sourceFile File for which to show the context. + * + * @throws NoCurrentCaseException + * @throws TskCoreException + */ + private void populateSourceContextData(AbstractFile sourceFile) throws NoCurrentCaseException, TskCoreException { + + SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + + // Check for all context artifacts + boolean foundASource = false; + for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) { + List artifactsList = tskCase.getBlackboardArtifacts(artifactType, sourceFile.getId()); + + foundASource = !artifactsList.isEmpty(); + for (BlackboardArtifact contextArtifact : artifactsList) { + addSourceEntry(contextArtifact); + } + } + if (foundASource == false) { + setSourceName("Unknown"); + showSourceText(false); + } + } + + + + /** + * Adds a source context entry for the selected file based on the given context + * providing artifact. + * + * @param artifact Artifact that may provide context. + * + * @throws NoCurrentCaseException + * @throws TskCoreException + */ + private void addSourceEntry(BlackboardArtifact artifact) throws TskCoreException { + if (BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID() == artifact.getArtifactTypeID()) { + BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); + if (associatedArtifactAttribute != null) { + long artifactId = associatedArtifactAttribute.getValueLong(); + BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId); + + //save the artifact for "Go to Result" button + sourceContextArtifact = associatedArtifact; + + setSourceFields(associatedArtifact); + } + } + } + + /** + * Sets the source label and text fields based on the given associated + * artifact. + * + * @param associatedArtifact - associated artifact + * + * @throws TskCoreException + */ + @NbBundle.Messages({ + "ContextViewer.attachmentSource=Attached to: ", + "ContextViewer.downloadSource=Downloaded from: " + }) + private void setSourceFields(BlackboardArtifact associatedArtifact) throws TskCoreException { + if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID() + || BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) { + + setSourceName(Bundle.ContextViewer_attachmentSource()); + setSourceText(msgArtifactToAbbreviatedString(associatedArtifact)); + + } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID() + || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) { + + setSourceName(Bundle.ContextViewer_downloadSource()); + setSourceText(webDownloadArtifactToString(associatedArtifact)); + } + } + + /** + * Sets the source label string. + * + * @param nameLabel String value for source label. + */ + private void setSourceName(String nameLabel) { + jSourceNameLabel.setText(nameLabel); + } + + /** + * Sets the source text string. + * + * @param nameLabel String value for source text. + */ + private void setSourceText(String text) { + jSourceTextLabel.setText(text); + showSourceText(true); + } + + private void showSourceText(boolean isVisible) { + jSourceTextLabel.setVisible(isVisible); + } + + /** + * Returns a display string with download source URL from the given + * artifact. + * + * @param artifact artifact to get download source URL from. + * + * @return Display string with download URL and date/time. + * + * @throws TskCoreException + */ + @NbBundle.Messages({ + "ContextViewer.downloadURL=URL", + "ContextViewer.downloadedOn=On" + }) + private String webDownloadArtifactToString(BlackboardArtifact artifact) throws TskCoreException { + StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN); + Map attributesMap = getAttributesMap(artifact); + + if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == artifact.getArtifactTypeID() + || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == artifact.getArtifactTypeID()) { + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, attributesMap, Bundle.ContextViewer_downloadURL()); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, attributesMap, Bundle.ContextViewer_downloadedOn()); + } + return sb.toString(); + } + + /** + * Returns a abbreviated display string for a message artifact. + * + * @param artifact artifact to get download source URL from. + * + * @return Display string for message artifact. + * + * @throws TskCoreException + */ + @NbBundle.Messages({ + "ContextViewer.message=Message", + "ContextViewer.email=Email", + "ContextViewer.messageFrom=From", + "ContextViewer.messageTo=To", + "ContextViewer.messageOn=On", + }) + private String msgArtifactToAbbreviatedString(BlackboardArtifact artifact) throws TskCoreException { + + StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN); + Map attributesMap = getAttributesMap(artifact); + + if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == artifact.getArtifactTypeID()) { + sb.append(Bundle.ContextViewer_message()).append(' '); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, attributesMap, Bundle.ContextViewer_messageFrom()); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, attributesMap, Bundle.ContextViewer_messageTo()); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, attributesMap, Bundle.ContextViewer_messageOn()); + } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == artifact.getArtifactTypeID()) { + sb.append(Bundle.ContextViewer_email()).append(' '); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM, attributesMap, Bundle.ContextViewer_messageFrom()); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO, attributesMap, Bundle.ContextViewer_messageTo()); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT, attributesMap, Bundle.ContextViewer_messageOn()); + } + return sb.toString(); + } + + /** + * Looks up specified attribute in the given map and, if found, appends its + * value to the given string builder. + * + * @param sb String builder to append to. + * @param attribType Attribute type to look for. + * @param attributesMap Attributes map. + * @param prependStr Optional string that is prepended before the attribute + * value. + */ + private void appendAttributeString(StringBuilder sb, BlackboardAttribute.ATTRIBUTE_TYPE attribType, + Map attributesMap, String prependStr) { + + BlackboardAttribute attribute = attributesMap.get(attribType); + if (attribute != null) { + String attrVal = attribute.getDisplayString(); + if (!StringUtils.isEmpty(attrVal)) { + if (!StringUtils.isEmpty(prependStr)) { + sb.append(prependStr).append(' '); + } + sb.append(StringUtils.abbreviate(attrVal, ATTRIBUTE_STR_MAX_LEN)).append(' '); + } + } + } + + /** + * Gets all attributes for the given artifact, and returns a map of + * attributes keyed by attribute type. + * + * @param artifact Artifact for which to get the attributes. + * + * @return Map of attribute type and value. + * + * @throws TskCoreException + */ + private Map getAttributesMap(BlackboardArtifact artifact) throws TskCoreException { + Map attributeMap = new HashMap<>(); + + List attributeList = artifact.getAttributes(); + for (BlackboardAttribute attribute : attributeList) { + BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID()); + attributeMap.put(type, attribute); + } + + return attributeMap; + } + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jSourceNameLabel; + private javax.swing.JLabel jSourceTextLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java index fb88ed9312..138e70ae47 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java @@ -20,9 +20,12 @@ package org.sleuthkit.autopsy.contentviewers; import java.awt.Component; import java.awt.Cursor; +import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.List; import java.util.logging.Level; +import org.apache.tika.parser.txt.CharsetDetector; +import org.apache.tika.parser.txt.CharsetMatch; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.coreutils.Logger; @@ -59,17 +62,27 @@ final class HtmlViewer extends javax.swing.JPanel implements FileTypeViewer { * @return The text content of the file. */ @NbBundle.Messages({ - "HtmlViewer_file_error=This file is missing or unreadable.",}) + "HtmlViewer_file_error=This file is missing or unreadable.", + "HtmlViewer_encoding_error=This file has unsupported encoding"}) private String getHtmlText(AbstractFile abstractFile) { try { int fileSize = (int) abstractFile.getSize(); byte[] buffer = new byte[fileSize]; abstractFile.read(buffer, 0, fileSize); - return new String(buffer); + CharsetMatch match = new CharsetDetector().setText(buffer).detect(); + if (match != null) { + return new String(buffer, match.getName()); + } else { + return new String(buffer); + } } catch (TskCoreException ex) { logger.log(Level.SEVERE, String.format("Unable to read from file '%s' (id=%d).", abstractFile.getName(), abstractFile.getId()), ex); return String.format("

%s

", Bundle.HtmlViewer_file_error()); + } catch (UnsupportedEncodingException ex) { + logger.log(Level.SEVERE, String.format("Unsupported encoding for file '%s' (id=%d).", + abstractFile.getName(), abstractFile.getId()), ex); + return String.format("

%s

", Bundle.HtmlViewer_encoding_error()); } } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java index 1f02708f64..84f7da6c36 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java @@ -637,7 +637,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie * thumb at the given width and height. It also paints the track blue as * the thumb progresses. * - * @param b JSlider component + * @param slider JSlider component * @param config Configuration object. Contains info about thumb * dimensions and colors. */ diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 0f36c84b9e..35f9efeddb 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -18,15 +18,17 @@ */ package org.sleuthkit.autopsy.contentviewers; +import org.sleuthkit.autopsy.datamodel.AttachmentNode; +import com.google.gson.Gson; import java.awt.Color; import java.awt.Component; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.logging.Level; -import java.util.stream.Collectors; import javax.swing.text.JTextComponent; import org.apache.commons.lang3.StringUtils; import org.jsoup.Jsoup; @@ -35,15 +37,12 @@ import org.openide.explorer.ExplorerManager; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; -import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; -import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.datamodel.AbstractFile; @@ -67,7 +66,12 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.blackboardutils.FileAttachment; +import org.sleuthkit.datamodel.blackboardutils.MessageAttachments; +import org.sleuthkit.datamodel.blackboardutils.Attachment; +import org.sleuthkit.datamodel.blackboardutils.URLAttachment; /** * Shows SMS/MMS/EMail messages @@ -541,11 +545,34 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont } private void configureAttachments() throws TskCoreException { - //TODO: Replace this with code to get the actual attachements! - final Set attachments = artifact.getChildren().stream() - .filter(AbstractFile.class::isInstance) - .map(AbstractFile.class::cast) - .collect(Collectors.toSet()); + + final Set attachments; + + // Attachments are specified in an attribute TSK_ATTACHMENTS as JSON attribute + BlackboardAttribute attachmentsAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS)); + if(attachmentsAttr != null) { + + attachments = new HashSet<>(); + String jsonVal = attachmentsAttr.getValueString(); + MessageAttachments msgAttachments = new Gson().fromJson(jsonVal, MessageAttachments.class); + + Collection fileAttachments = msgAttachments.getFileAttachments(); + for (FileAttachment fileAttachment: fileAttachments) { + attachments.add(fileAttachment); + } + Collection urlAttachments = msgAttachments.getUrlAttachments(); + for (URLAttachment urlAttachment: urlAttachments) { + attachments.add(urlAttachment); + } + } else { // For backward compatibility - email attachements are derived files and children of the email message artifact + attachments = new HashSet<>(); + for (Content child: artifact.getChildren()) { + if (child instanceof AbstractFile) { + attachments.add(new FileAttachment((AbstractFile)child)); + } + } + } + final int numberOfAttachments = attachments.size(); msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0); @@ -633,16 +660,20 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont return doc.html(); } - private static class AttachmentsChildren extends Children.Keys { + + /** + * Creates child nodes for message attachments. + */ + private static class AttachmentsChildren extends Children.Keys { - private final Set attachments; + private final Set attachments; - AttachmentsChildren(Set attachments) { + AttachmentsChildren(Set attachments) { this.attachments = attachments; } @Override - protected Node[] createNodes(AbstractFile t) { + protected Node[] createNodes(Attachment t) { return new Node[]{new AttachmentNode(t)}; } @@ -652,40 +683,4 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont setKeys(attachments); } } - - /** - * Extension of FileNode customized for viewing attachments in the - * MessageContentViewer. It overrides createSheet() to customize what - * properties are shown in the table, and could also override getActions(), - * getPreferedAction(), etc. - */ - private static class AttachmentNode extends FileNode { - - AttachmentNode(AbstractFile file) { - super(file, false); - } - - @Override - protected Sheet createSheet() { - Sheet sheet = super.createSheet(); - Set keepProps = new HashSet<>(Arrays.asList( - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.sizeColLbl"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.knownColLbl"))); - - //Remove all other props except for the ones above - Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); - for (Property p : sheetSet.getProperties()) { - if (!keepProps.contains(p.getName())) { - sheetSet.remove(p.getName()); - } - } - - return sheet; - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index 126299f120..796386a5b3 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -191,12 +191,14 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { } try { - List sourceArtifacts = file.getArtifacts(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - if (!sourceArtifacts.isEmpty()) { - BlackboardArtifact artifact = sourceArtifacts.get(0); - BlackboardAttribute urlAttr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL)); - if (urlAttr != null) { - addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.downloadSource"), urlAttr.getValueString()); + List associatedObjectArtifacts = file.getArtifacts(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + if (!associatedObjectArtifacts.isEmpty()) { + BlackboardArtifact artifact = associatedObjectArtifacts.get(0); + BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); + if (associatedArtifactAttribute != null) { + long artifactId = associatedArtifactAttribute.getValueLong(); + BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId); + addDownloadSourceRow(sb, associatedArtifact); } } } catch (TskCoreException ex) { @@ -292,6 +294,26 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { this.setCursor(null); } + /** + * Adds a row for download source from the given associated artifact, + * if the associated artifacts specifies a source. + * + * @param sb string builder. + * @param associatedArtifact + * + * @throws TskCoreException if there is an error + */ + private void addDownloadSourceRow(StringBuilder sb, BlackboardArtifact associatedArtifact ) throws TskCoreException { + if (associatedArtifact != null && + ((associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()) || + (associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID())) ) { + BlackboardAttribute urlAttr = associatedArtifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL)); + if (urlAttr != null) { + addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.downloadSource"), urlAttr.getValueString()); + } + } + } + /** * Add the acquisition details to the results (if applicable) * diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle_ja.properties index f139f20a19..8ce5b3ba54 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle_ja.properties @@ -1,15 +1,23 @@ -StringsTextViewer.goToPageTextField.msgDlg=\uff11\u304b\u3089{0}\u306e\u9593\u306e\u6709\u52b9\u306a\u30da\u30fc\u30b8\u6570\u3092\u5165\u529b\u3057\u3066\u4e0b\u3055\u3044 -StringsTextViewer.goToPageTextField.err=\u7121\u52b9\u306a\u30da\u30fc\u30b8\u6570 -StringsTextViewer.setDataView.errorText=\uff08\u30aa\u30d5\u30bb\u30c3\u30c8{0}-{1}\u306f\u8aad\u307f\u53d6\u308c\u307e\u305b\u3093\u3067\u3057\u305f\uff09 -StringsTextViewer.title=\u30b9\u30c8\u30ea\u30f3\u30b0 -StringsTextViewer.toolTip=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u62bd\u51fa\u3055\u308c\u305fASCII\u304a\u3088\u3073\u30e6\u30cb\u30b3\u30fc\u30c9\u306e\u30b9\u30c8\u30ea\u30f3\u30b0\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +StringsTextViewer.goToPageTextField.msgDlg=1 \u304b\u3089 {0} \u307e\u3067\u306e\u6709\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044 +StringsTextViewer.goToPageTextField.err=\u7121\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3067\u3059 +StringsTextViewer.setDataView.errorText=(\u30aa\u30d5\u30bb\u30c3\u30c8 {0}-{1} \u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f) +StringsTextViewer.setDataView.errorNoText=(\u30aa\u30d5\u30bb\u30c3\u30c8 {0}-{1} \u306b\u306f\u30c6\u30ad\u30b9\u30c8\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u305b\u3093) +StringsTextViewer.title=\u6587\u5b57\u5217 +StringsTextViewer.toolTip=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u62bd\u51fa\u3057\u305fASCII\u6587\u5b57\u5217\u3068Unicode\u6587\u5b57\u5217\u3092\u8868\u793a StringsContentPanel.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e StringsContentPanel.currentPageLabel.text_1=1 StringsContentPanel.copyMenuItem.text=\u30b3\u30d4\u30fc -StringsContentPanel.ofLabel.text_1=of +StringsContentPanel.ofLabel.text_1=/ StringsContentPanel.totalPageLabel.text_1=100 -StringsContentPanel.languageLabel.text=\u30b9\u30af\u30ea\u30d7\u30c8\uff1a -StringsContentPanel.languageCombo.toolTipText=\u30d0\u30a4\u30ca\u30ea\u30b9\u30c8\u30ea\u30f3\u30b0\u306e\u51e6\u7406\uff08\u62bd\u51fa\u304a\u3088\u3073\u30c7\u30b3\u30fc\u30c9\uff09\u306b\u4f7f\u7528\u3059\u308b\u8a00\u8a9e -StringsContentPanel.goToPageLabel.text=\u6b21\u306e\u30da\u30fc\u30b8\u3078\u79fb\u52d5\uff1a +StringsContentPanel.languageLabel.toolTipText= +StringsContentPanel.languageLabel.text=\u30b9\u30af\u30ea\u30d7\u30c8: +StringsContentPanel.languageCombo.toolTipText=\u30d0\u30a4\u30ca\u30ea\u30fc\u30c7\u30fc\u30bf\u306e\u6587\u5b57\u5217\u306e\u89e3\u91c8(\u62bd\u51fa\u304a\u3088\u3073\u30c7\u30b3\u30fc\u30c9)\u4e2d\u306b\u8a66\u3059\u8a00\u8a9e +StringsContentPanel.goToPageTextField.text= +StringsContentPanel.goToPageLabel.text=\u30da\u30fc\u30b8\u306b\u79fb\u52d5: +StringsContentPanel.prevPageButton.text= StringsContentPanel.pageLabel2.text=\u30da\u30fc\u30b8 -StringsContentPanel.pageLabel.text_1=\u30da\u30fc\u30b8\uff1a +StringsContentPanel.nextPageButton.text= +StringsContentPanel.pageLabel.text_1=\u30da\u30fc\u30b8: +TextContentViewer.title=\u30c6\u30ad\u30b9\u30c8 +TextContentViewer.tooltip=\u9078\u629e\u3057\u305f\u9805\u76ee\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30c6\u30ad\u30b9\u30c8\u3092\u8868\u793a +TextContentViewerPanel.defaultName=\u30c6\u30ad\u30b9\u30c8 diff --git a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties index db8e9e9808..4fccbcb6fa 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties @@ -11,7 +11,7 @@ OpenIDE-Module-Short-Description=Autopsy Core Module org_sleuthkit_autopsy_core_update_center=http://sleuthkit.org/autopsy/updates.xml Services/AutoupdateType/org_sleuthkit_autopsy_core_update_center.settings=Autopsy Update Center Installer.errorInitJavafx.msg=Error initializing JavaFX. -Installer.errorInitJavafx.details=\ Some features will not be available. Check that you have the right JRE installed (Oracle JRE > 1.7.10). +Installer.errorInitJavafx.details=\ Some features will not be available. Check that you have JavaFX installed (OpenJFX 8). ServicesMonitor.failedService.notify.title=Service Is Down ServicesMonitor.failedService.notify.msg=Connection to {0} is down ServicesMonitor.restoredService.notify.title=Service Is Up diff --git a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED index 087eaec314..c84f1f1b86 100755 --- a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED @@ -3,19 +3,13 @@ Installer.closing.confirmationDialog.title=Ingest is Running # {0} - exception message Installer.closing.messageBox.caseCloseExceptionMessage=Error closing case: {0} OpenIDE-Module-Display-Category=Infrastructure -OpenIDE-Module-Long-Description=\ - This is the core Autopsy module.\n\n\ - The module contains the core components needed for the bare application to run; the RCP platform, windowing GUI, sleuthkit bindings, datamodel / storage, explorer, result viewers, content viewers, ingest framework, reporting, and core tools, such as the file search.\n\n\ - The framework included in the module contains APIs for developing modules for ingest, viewers and reporting. \ - The modules can be deployed as Plugins using the Autopsy plugin installer.\n\ - This module should not be uninstalled - without it, Autopsy will not run.\n\n\ - For more information, see http://www.sleuthkit.org/autopsy/ +OpenIDE-Module-Long-Description=This is the core Autopsy module.\n\nThe module contains the core components needed for the bare application to run; the RCP platform, windowing GUI, sleuthkit bindings, datamodel / storage, explorer, result viewers, content viewers, ingest framework, reporting, and core tools, such as the file search.\n\nThe framework included in the module contains APIs for developing modules for ingest, viewers and reporting. The modules can be deployed as Plugins using the Autopsy plugin installer.\nThis module should not be uninstalled - without it, Autopsy will not run.\n\nFor more information, see http://www.sleuthkit.org/autopsy/ OpenIDE-Module-Name=Autopsy-Core OpenIDE-Module-Short-Description=Autopsy Core Module org_sleuthkit_autopsy_core_update_center=http://sleuthkit.org/autopsy/updates.xml Services/AutoupdateType/org_sleuthkit_autopsy_core_update_center.settings=Autopsy Update Center Installer.errorInitJavafx.msg=Error initializing JavaFX. -Installer.errorInitJavafx.details=\ Some features will not be available. Check that you have the right JRE installed (Oracle JRE > 1.7.10). +Installer.errorInitJavafx.details=\ Some features will not be available. Check that you have JavaFX installed (OpenJFX 8). ServicesMonitor.failedService.notify.title=Service Is Down ServicesMonitor.failedService.notify.msg=Connection to {0} is down ServicesMonitor.restoredService.notify.title=Service Is Up diff --git a/Core/src/org/sleuthkit/autopsy/core/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/core/Bundle_ja.properties index 171ced6172..3bda55e29c 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/core/Bundle_ja.properties @@ -1,31 +1,33 @@ -OpenIDE-Module-Display-Category=\u30A4\u30F3\u30D5\u30E9\u30B9\u30C8\u30E9\u30AF\u30C1\u30E3\u30FC +Installer.closing.confirmationDialog.message=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d\u3067\u3059\u3002\u7d42\u4e86\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +Installer.closing.confirmationDialog.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d\u3067\u3059 +# {0} - \u4f8b\u5916\u30e1\u30c3\u30bb\u30fc\u30b8 +Installer.closing.messageBox.caseCloseExceptionMessage=\u6b21\u306e\u30b1\u30fc\u30b9\u3092\u9589\u3058\u3066\u3044\u308b\u9593\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +OpenIDE-Module-Display-Category=\u30a4\u30f3\u30d5\u30e9\u30b9\u30c8\u30e9\u30af\u30c1\u30e3\u30fc OpenIDE-Module-Long-Description=\ - \u3053\u308C\u304CAutopsy\u306E\u30B3\u30A2\u30E2\u30B8\u30E5\u30FC\u30EB\u3067\u3059\u3002\n\n\ - \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u307F\u3067\u5B9F\u884C\u3059\u308B\u306E\u306B\u5FC5\u8981\u306A\u4E3B\u8981\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\uFF1ARCP\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u3001\u30A6\u30A3\u30F3\u30C9\u30A6\u30A4\u30F3\u30B0GUI\u3001Sleuth Kit\u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u3001\u30C7\u30FC\u30BF\u30E2\u30C7\u30EB\uFF0F\u30B9\u30C8\u30EC\u30FC\u30B8\u3001\u30A8\u30AF\u30B9\u30D7\u30ED\u30FC\u30E9\u3001\u7D50\u679C\u30D3\u30E5\u30FC\u30A2\u3001\u30B3\u30F3\u30C6\u30F3\u30C4\u30D3\u30E5\u30FC\u30A2\u3001\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u7528\u30D5\u30EC\u30FC\u30E0\u30EF\u30FC\u30AF\u3001\u30EC\u30DD\u30FC\u30C8\u751F\u6210\u3001\u30D5\u30A1\u30A4\u30EB\u691C\u7D22\u7B49\u306E\u4E3B\u8981\u30C4\u30FC\u30EB\u3002\n\n\ - \u30E2\u30B8\u30E5\u30FC\u30EB\u5185\u306E\u30D5\u30EC\u30FC\u30E0\u30EF\u30FC\u30AF\u306B\u306F\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u3001\u30D3\u30E5\u30FC\u30A2\u3001\u30EC\u30DD\u30FC\u30C8\u751F\u6210\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u958B\u767A\u7528\u306EAPI\u304C\u542B\u307E\u308C\u307E\u3059\u3002\ - \u30E2\u30B8\u30E5\u30FC\u30EB\u306FAutopsy\u30D7\u30E9\u30B0\u30A4\u30F3\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u30FC\u3092\u4F7F\u7528\u3057\u3001\u30D7\u30E9\u30B0\u30A4\u30F3\u3068\u3057\u3066\u5B9F\u88C5\u3067\u304D\u307E\u3059\u3002\n\ - \u3053\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u306F\u30A2\u30F3\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u3066\u3044\u306A\u3051\u308C\u3070\u3001Autopsy\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\n\n\ - \u8A73\u7D30\u306F\u3053\u3061\u3089\u3067\u3054\u78BA\u8A8D\u304F\u3060\u3055\u3044\u3002http\://www.sleuthkit.org/autopsy/ -OpenIDE-Module-Name=Autopsy-\u30B3\u30A2 -OpenIDE-Module-Short-Description=Autopsy\u30B3\u30A2\u30E2\u30B8\u30E5\u30FC\u30EB -org_sleuthkit_autopsy_core_update_center=http\://sleuthkit.org/autopsy/updates_ja.xml -Services/AutoupdateType/org_sleuthkit_autopsy_core_update_center.settings=Autopsy\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u30BB\u30F3\u30BF\u30FC -Installer.errorInitJavafx.msg=JavaFX\u521D\u671F\u5316\u30A8\u30E9\u30FC -Installer.errorInitJavafx.details=\u4E00\u90E8\u306E\u6A5F\u80FD\u304C\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u6B63\u3057\u3044JRE\u304C\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u3066\u3044\u308B\u304B\u3092\u78BA\u8A8D\u3057\u3066\u4E0B\u3055\u3044\u3002\uFF08Oracle JRE > 1.7.10\uFF09 -ServicesMonitor.failedService.notify.title=\u30B5\u30FC\u30D3\u30B9\u304C\u505C\u6B62\u3057\u3066\u3044\u307E\u3059 -ServicesMonitor.failedService.notify.msg={0}\u3078\u306E\u63A5\u7D9A\u304C\u30C0\u30A6\u30F3\u3057\u3066\u3044\u307E\u3059 -ServicesMonitor.restoredService.notify.title=\u30B5\u30FC\u30D3\u30B9\u304C\u7A3C\u50CD\u4E2D\u3067\u3059 -ServicesMonitor.restoredService.notify.msg={0}\u3078\u306E\u63A5\u7D9A\u304C\u5229\u7528\u3067\u304D\u307E\u3059 -ServicesMonitor.statusChange.notify.title=\u30B5\u30FC\u30D3\u30B9\u30B9\u30C6\u30FC\u30BF\u30B9\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8 -ServicesMonitor.statusChange.notify.msg={0}\u306E\u30B9\u30C6\u30FC\u30BF\u30B9\u306F{1} -ServicesMonitor.nullServiceName.excepton.txt=\u30EA\u30AF\u30A8\u30B9\u30C8\u3055\u308C\u305F\u30B5\u30FC\u30D3\u30B9\u306F\u30CC\u30EB\u3067\u3059 -ServicesMonitor.unknownServiceName.excepton.txt=\u30EA\u30AF\u30A8\u30B9\u30C8\u3055\u308C\u305F\u30B5\u30FC\u30D3\u30B9{0}\u306F\u4E0D\u660E\u3067\u3059 -TextConverter.convert.exception.txt=\u30C6\u30AD\u30B9\u30C8{0}\u3092hex\u30C6\u30AD\u30B9\u30C8\u306B\u5909\u63DB\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F -TextConverter.convertFromHex.exception.txt=hex\u30C6\u30AD\u30B9\u30C8\u3092\u30C6\u30AD\u30B9\u30C8\u306B\u5909\u63DB\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F -ServicesMonitor.KeywordSearchNull=\u30AD\u30FC\u30EF\u30FC\u30C9\u691C\u7D22\u30B5\u30FC\u30D3\u30B9\u3092\u898B\u3064\u3051\u308C\u307E\u305B\u3093\u3067\u3057\u305F -ServicesMonitor.InvalidPortNumber=\u7121\u52B9\u306A\u30DD\u30FC\u30C8\u756A\u53F7 -ServicesMonitor.remoteCaseDatabase.displayName.text=\u8907\u6570\u306E\u30E6\u30FC\u30B6\u30FC\u306E\u30B1\u30FC\u30B9\u306E\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u30B5\u30FC\u30D3\u30B9 -ServicesMonitor.remoteKeywordSearch.displayName.text=\u8907\u6570\u306E\u30E6\u30FC\u30B6\u30FC\u306E\u30AD\u30FC\u30EF\u30FC\u30C9\u691C\u7D22\u30B5\u30FC\u30D3\u30B9 -ServicesMonitor.messaging.displayName.text=\u30E1\u30C3\u30BB\u30FC\u30B8\u30B5\u30FC\u30D3\u30B9 -ServicesMonitor.databaseConnectionInfo.error.msg=\u30B1\u30FC\u30B9\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u63A5\u7D9A\u60C5\u5831\u3092\u5165\u624B\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ServicesMonitor.messagingService.connErr.text=\u30E1\u30C3\u30BB\u30FC\u30B8\u30B5\u30FC\u30D3\u30B9\u306E\u63A5\u7D9A\u60C5\u5831\u3092\u5165\u624B\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F \ No newline at end of file + \u3053\u308c\u306fAutopsy\u306e\u30b3\u30a2\u30e2\u30b8\u30e5\u30fc\u30eb\u3067\u3059\u3002\n\n\ + \u30e2\u30b8\u30e5\u30fc\u30eb\u306b\u306f\u3001RCP\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u3001\u30a6\u30a3\u30f3\u30c9\u30a6\u4f5c\u6210GUI\u3001sleuthkit\u30d0\u30a4\u30f3\u30c7\u30a3\u30f3\u30b0\u3001\u30c7\u30fc\u30bf\u30e2\u30c7\u30eb / \u30b9\u30c8\u30ec\u30fc\u30b8\u3001\u30a8\u30af\u30b9\u30d7\u30ed\u30fc\u30e9\u30fc\u3001\u7d50\u679c\u30d3\u30e5\u30fc\u30ef\u30fc\u3001\u30b3\u30f3\u30c6\u30f3\u30c4\u30d3\u30e5\u30fc\u30ef\u30fc\u3001\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3001\u30ec\u30dd\u30fc\u30c6\u30a3\u30f3\u30b0\u3001\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u306a\u3069\u306e\u30b3\u30a2\u30c4\u30fc\u30eb\u3068\u3044\u3063\u305f\u3001\u30d9\u30a2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u5b9f\u884c\u306b\u5fc5\u8981\u306a\u30b3\u30a2\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002\n\n\ + \u30e2\u30b8\u30e5\u30fc\u30eb\u306b\u542b\u307e\u308c\u308b\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u306b\u306f\u3001\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3001\u30d3\u30e5\u30fc\u30ef\u30fc\u3001\u30ec\u30dd\u30fc\u30c6\u30a3\u30f3\u30b0\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u4f5c\u6210\u7528API\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002 \ + \u30e2\u30b8\u30e5\u30fc\u30eb\u306fAutopsy\u30d7\u30e9\u30b0\u30a4\u30f3\u30a4\u30f3\u30b9\u30c8\u30fc\u30e9\u30fc\u3092\u7528\u3044\u308b\u30d7\u30e9\u30b0\u30a4\u30f3\u3068\u3057\u3066\u5c55\u958b\u3067\u304d\u307e\u3059\u3002\n\ + \u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u30a2\u30f3\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u306a\u3044\u3067\u304f\u3060\u3055\u3044 - \u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u306a\u3044\u3068Autopsy\u304c\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002\n\n\ + \u8a73\u7d30\u306f\u3001http://www.sleuthkit.org/autopsy/ \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044 +OpenIDE-Module-Name=Autopsy-Core +OpenIDE-Module-Short-Description=Autopsy\u30b3\u30a2\u30e2\u30b8\u30e5\u30fc\u30eb +org_sleuthkit_autopsy_core_update_center=http://sleuthkit.org/autopsy/updates.xml +Services/AutoupdateType/org_sleuthkit_autopsy_core_update_center.settings=Autopsy\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30bb\u30f3\u30bf\u30fc +Installer.errorInitJavafx.msg=JavaFX\u306e\u521d\u671f\u5316\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Installer.errorInitJavafx.details=\ \u4e00\u90e8\u306e\u6a5f\u80fd\u304c\u5229\u7528\u3067\u304d\u306a\u304f\u306a\u308a\u307e\u3059\u3002 \u9069\u5207\u306aJRE\u304c\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3055\u308c\u3066\u3044\u308b\u304b\u78ba\u8a8d\u304f\u3060\u3055\u3044(Oracle JRE 1.7.10\u4ee5\u964d)\u3002 +ServicesMonitor.failedService.notify.title=\u30b5\u30fc\u30d3\u30b9\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u307e\u3059 +ServicesMonitor.failedService.notify.msg={0} \u3078\u306e\u63a5\u7d9a\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u307e\u3059 +ServicesMonitor.restoredService.notify.title=\u30b5\u30fc\u30d3\u30b9\u304c\u5b9f\u884c\u4e2d\u3067\u3059 +ServicesMonitor.restoredService.notify.msg={0} \u3078\u306e\u63a5\u7d9a\u304c\u5b9f\u884c\u4e2d\u3067\u3059 +ServicesMonitor.statusChange.notify.title=\u30b5\u30fc\u30d3\u30b9\u30b9\u30c6\u30fc\u30bf\u30b9\u66f4\u65b0 +ServicesMonitor.statusChange.notify.msg={0} \u306e\u30b9\u30c6\u30fc\u30bf\u30b9\u306f {1} \u3067\u3059 +ServicesMonitor.nullServiceName.excepton.txt=\u30ea\u30af\u30a8\u30b9\u30c8\u3055\u308c\u305f\u30b5\u30fc\u30d3\u30b9\u540d\u306fnull\u3067\u3059 +ServicesMonitor.unknownServiceName.excepton.txt=\u30ea\u30af\u30a8\u30b9\u30c8\u3055\u308c\u305f\u30b5\u30fc\u30d3\u30b9\u540d {0} \u306f\u4e0d\u660e\u3067\u3059 +ServicesMonitor.KeywordSearchNull=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d3\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +ServicesMonitor.InvalidPortNumber=\u7121\u52b9\u306a\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u3059\u3002 +ServicesMonitor.remoteCaseDatabase.displayName.text=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b5\u30fc\u30d3\u30b9 +ServicesMonitor.remoteKeywordSearch.displayName.text=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d3\u30b9 +ServicesMonitor.messaging.displayName.text=\u30e1\u30c3\u30bb\u30fc\u30b8\u30f3\u30b0\u30b5\u30fc\u30d3\u30b9 +ServicesMonitor.databaseConnectionInfo.error.msg=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u60c5\u5831\u306b\u30a2\u30af\u30bb\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ServicesMonitor.messagingService.connErr.text=\u30e1\u30c3\u30bb\u30fc\u30b8\u30f3\u30b0\u30b5\u30fc\u30d3\u30b9\u63a5\u7d9a\u60c5\u5831\u306b\u30a2\u30af\u30bb\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java index 0d2bfa90d3..409e27f080 100644 --- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java @@ -78,6 +78,10 @@ public final class UserPreferences { public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath"; public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize"; public static final String RESULTS_TABLE_PAGE_SIZE = "ResultsTablePageSize"; + private static final String GEO_TILE_OPTION = "GeolocationTileOption"; + private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath"; + private static final String GEO_OSM_SERVER_ADDRESS = "GeolocationOsmServerAddress"; + private static final String GEO_MBTILES_FILE_PATH = "GeolcoationMBTilesFilePath"; // Prevent instantiation. private UserPreferences() { @@ -534,4 +538,77 @@ public final class UserPreferences { public static String getExternalHexEditorPath() { return preferences.get(EXTERNAL_HEX_EDITOR_PATH, Paths.get("C:", "Program Files", "HxD", "HxD.exe").toString()); } + + /** + * Set the geolocation tile server option. + * + * @param option + */ + public static void setGeolocationTileOption(int option) { + preferences.putInt(GEO_TILE_OPTION, option); + } + + /** + * Retrieves the Geolocation tile option. If not found, the value will + * default to 0. + * @return + */ + public static int getGeolocationtTileOption() { + return preferences.getInt(GEO_TILE_OPTION, 0); + } + + /** + * Sets the path to the OSM tile zip file. + * + * @param absolutePath + */ + public static void setGeolocationOsmZipPath(String absolutePath) { + preferences.put(GEO_OSM_TILE_ZIP_PATH, absolutePath); + } + + /** + * Retrieves the path for the OSM tile zip file or returns empty string if + * none was found. + * + * @return Path to zip file + */ + public static String getGeolocationOsmZipPath() { + return preferences.get(GEO_OSM_TILE_ZIP_PATH, ""); + } + + /** + * Sets the address of geolocation window user defined OSM server data source. + * + * @param address + */ + public static void setGeolocationOsmServerAddress(String address) { + preferences.put(GEO_OSM_SERVER_ADDRESS, address); + } + + /** + * Retrieves the address to the OSM server or null if one was not found. + * + * @return Address of OSM server + */ + public static String getGeolocationOsmServerAddress() { + return preferences.get(GEO_OSM_SERVER_ADDRESS, ""); + } + + /** + * Sets the path for Geolocation MBTiles data source file. + * + * @param absolutePath + */ + public static void setGeolocationMBTilesFilePath(String absolutePath) { + preferences.put(GEO_MBTILES_FILE_PATH, absolutePath); + } + + /** + * Retrieves the path for the Geolocation MBTiles data source file. + * + * @return Absolute path to MBTiles file or empty string if none was found. + */ + public static String getGeolocationMBTilesFilePath() { + return preferences.get(GEO_MBTILES_FILE_PATH, ""); + } } diff --git a/Core/src/org/sleuthkit/autopsy/core/discoveryWsmode.xml b/Core/src/org/sleuthkit/autopsy/core/discoveryWsmode.xml new file mode 100644 index 0000000000..ef5da81277 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/core/discoveryWsmode.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/core/geolocationWsmode.xml b/Core/src/org/sleuthkit/autopsy/core/geolocationWsmode.xml new file mode 100755 index 0000000000..4e29bb3e2d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/core/geolocationWsmode.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/core/layer.xml b/Core/src/org/sleuthkit/autopsy/core/layer.xml index cf17a31c12..2b54236d77 100644 --- a/Core/src/org/sleuthkit/autopsy/core/layer.xml +++ b/Core/src/org/sleuthkit/autopsy/core/layer.xml @@ -448,6 +448,8 @@ + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/Bundle_ja.properties index a2727c5700..91f27d6210 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/Bundle_ja.properties @@ -1,3 +1,3 @@ -OpenIDE-Module-Name=\u4E3B\u8981\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u30A4\u30F3\u30BF\u30FC\u30D5\u30A7\u30A4\u30B9 -CoreComponentControl.CTL_DirectoryTreeTopComponent=\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30C4\u30EA\u30FC -CoreComponentControl.CTL_FavoritesTopComponent=\u304A\u6C17\u306B\u5165\u308A \ No newline at end of file +OpenIDE-Module-Name=\u4e3b\u8981\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30a4\u30b9 +CoreComponentControl.CTL_DirectoryTreeTopComponent=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c4\u30ea\u30fc +CoreComponentControl.CTL_FavoritesTopComponent=\u304a\u6c17\u306b\u5165\u308a diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index 9f363b7723..f252420726 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -63,9 +63,9 @@ DataContentViewerHex.totalPageLabel.text_1=100 DataContentViewerHex.pageLabel2.text=Page # Product Information panel -LBL_Description=
\n Product Version: {0} ({9})
Sleuth Kit Version: {7}
Netbeans RCP Build: {8}
Java: {1}; {2}
System: {3}; {4}; {5}
Userdir: {6}
+LBL_Description=
\n Product Version: {0} ({9})
Sleuth Kit Version: {7}
Netbeans RCP Build: {8}
Java: {1}; {2}
System: {3}; {4}; {5}
Userdir: {6}
Format_OperatingSystem_Value={0} version {1} running on {2} -LBL_Copyright=
Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools.
Copyright © 2003-2018.
+LBL_Copyright=
Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools.
Copyright © 2003-2018.
SortChooser.dialogTitle=Choose Sort Criteria ThumbnailViewChildren.progress.cancelling=(Cancelling) # {0} - file name @@ -95,7 +95,7 @@ DataResultViewerThumbnail.pageNextButton.text= DataResultViewerThumbnail.imagesLabel.text=Images: DataResultViewerThumbnail.imagesRangeLabel.text=- DataResultViewerThumbnail.pageNumLabel.text=- -DataResultViewerThumbnail.filePathLabel.text=\ \ \ +DataResultViewerThumbnail.filePathLabel.text=\ DataResultViewerThumbnail.goToPageLabel.text=Go to Page: DataResultViewerThumbnail.goToPageField.text= AdvancedConfigurationDialog.cancelButton.text=Cancel diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties index 3f1c8c2ac5..6f941632c8 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties @@ -1,117 +1,274 @@ -CTL_DataContentAction=\u30c7\u30fc\u30bf\u30b3\u30f3\u30c6\u30f3\u30c4 -OptionsCategory_Keywords_General=Autopsy\u30aa\u30d7\u30b7\u30e7\u30f3 -CTL_CustomAboutAction=Autopsy\u306b\u3064\u3044\u3066 +AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.invalidImageSpecified.text=\u7121\u52b9\u306e\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u304c\u6307\u5b9a\u3055\u308c\u307e\u3057\u305f\u3002 +AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.invalidPath.text=\u30d1\u30b9\u306f\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.pathNotSet.text=\u30a8\u30fc\u30b8\u30a7\u30f3\u30b7\u30fc\u30ed\u30b4\u30d1\u30b9\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +AutopsyOptionsPanel.invalidImageFile.msg=\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u30a8\u30fc\u30b8\u30a7\u30f3\u30b7\u30fc\u30ed\u30b4\u3068\u3057\u3066\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +AutopsyOptionsPanel.invalidImageFile.title=\u7121\u52b9\u306a\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u3067\u3059 +AutopsyOptionsPanel.logNumAlert.invalidInput.text=\u3053\u3053\u3067\u306f\u6b63\u306e\u6574\u6570\u304c\u5fc5\u8981\u3067\u3059\u3002 +AutopsyOptionsPanel.memFieldValidationLabel.developerMode.text=\u958b\u767a\u8005\u30e2\u30fc\u30c9\u3067\u5b9f\u884c\u4e2d\u306f\u30e1\u30e2\u30ea\u30fc\u8a2d\u5b9a\u304c\u5229\u7528\u3067\u304d\u307e\u305b\u3093 +AutopsyOptionsPanel.memFieldValidationLabel.invalidCharacters.text=\u7121\u52b9\u306a\u6587\u5b57\u3067\u3059\u3002\u5024\u306f\u6b63\u306e\u6574\u6570\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093 +AutopsyOptionsPanel.memFieldValidationLabel.not64BitInstall.text=JVM\u30e1\u30e2\u30ea\u30fc\u8a2d\u5b9a\u306f64\u30d3\u30c3\u30c8\u7248\u306b\u306e\u307f\u6709\u52b9\u3067\u3059 +AutopsyOptionsPanel.memFieldValidationLabel.noValueEntered.text=\u5024\u304c\u5165\u529b\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +# {0} - systemMemory +AutopsyOptionsPanel.memFieldValidationLabel.overMaxMemory.text=\u5024\u306f\u5408\u8a08\u30b7\u30b9\u30c6\u30e0\u30e1\u30e2\u30ea\u30fc\u306e {0}GB\u4ee5\u4e0b\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093 +# {0} - minimumMemory +AutopsyOptionsPanel.memFieldValidationLabel.underMinMemory.text=\u5024\u306f\u5c11\u306a\u304f\u3068\u3082 {0}GB\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059 +CTL_DataContentAction=DataContent CTL_DataContentTopComponent=\u30c7\u30fc\u30bf\u30b3\u30f3\u30c6\u30f3\u30c4 -HINT_DataContentTopComponent=\u3053\u308c\u306f\u30c7\u30fc\u30bf\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u3059 -HINT_NodeTableTopComponent=\u3053\u308c\u306f\u30c7\u30fc\u30bf\u7d50\u679c\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u3059 -OpenIDE-Module-Name=\u4e3b\u8981\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8 -DataContentViewerHex.pageLabel.text_1=\u30da\u30fc\u30b8\uff1a +CTL_CustomAboutAction=\u6982\u8981 +CTL_OfflineHelpAction=Autopsy\u30aa\u30d5\u30e9\u30a4\u30f3\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8 +CTL_OnlineHelpAction=Autopsy\u30aa\u30f3\u30e9\u30a4\u30f3\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8 +DataContentViewerArtifact.attrsTableHeader.sources=\u30bd\u30fc\u30b9 +DataContentViewerArtifact.attrsTableHeader.type=\u30bf\u30a4\u30d7 +DataContentViewerArtifact.attrsTableHeader.value=\u5024 +DataContentViewerArtifact.failedToGetAttributes.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u4e00\u90e8\u307e\u305f\u306f\u3059\u3079\u3066\u306e\u5c5e\u6027\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +DataContentViewerArtifact.failedToGetSourcePath.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +DataContentViewerHex.copyingFile=HxD\u3067\u958b\u304f\u30d5\u30a1\u30a4\u30eb\u3092\u30b3\u30d4\u30fc\u4e2d\u3067\u3059... +DataContentViewerHex.launchError=HxD\u30a8\u30c7\u30a3\u30bf\u30fc\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3002[\u30c4\u30fc\u30eb] -> [\u30aa\u30d7\u30b7\u30e7\u30f3] -> [\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc] \u3067HxD\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u5834\u6240\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044 +DataResultViewerTable.commentRender.name=C +DataResultViewerTable.commentRender.toolTip=C(\u30b3\u30e1\u30f3\u30c8)\u306f\u9805\u76ee\u306b\u30b3\u30e1\u30f3\u30c8\u304c\u3042\u308b\u304b\u3069\u3046\u304b\u3092\u793a\u3057\u3066\u3044\u307e\u3059 +DataResultViewerTable.commentRenderer.crAndTagComment.toolTip=\u30b3\u30e1\u30f3\u30c8\u304c\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30bf\u30b0\u306e\u4e21\u65b9\u306b\u5b58\u5728\u3057\u307e\u3059 +DataResultViewerTable.commentRenderer.crComment.toolTip=\u30b3\u30e1\u30f3\u30c8\u304c\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u5b58\u5728\u3057\u307e\u3059 +DataResultViewerTable.commentRenderer.noComment.toolTip=\u30b3\u30e1\u30f3\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +DataResultViewerTable.commentRenderer.tagComment.toolTip=\u30b3\u30e1\u30f3\u30c8\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30bf\u30b0\u306b\u5b58\u5728\u3057\u307e\u3059 +DataResultViewerTable.countRender.name=O +DataResultViewerTable.countRender.toolTip=O(\u767a\u751f)\u306f\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306e\u9805\u76ee\u3092\u542b\u3080\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u6570\u3092\u793a\u3057\u3066\u3044\u307e\u3059 +DataResultViewerTable.exportCSVButtonActionPerformed.empty=\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u30c7\u30fc\u30bf\u306f\u3042\u308a\u307e\u305b\u3093 +DataResultViewerTable.firstColLbl=\u540d\u524d +DataResultViewerTable.goToPageTextField.err=\u7121\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3067\u3059 +# {0} - totalPages +DataResultViewerTable.goToPageTextField.msgDlg=1 \u304b\u3089 {0} \u307e\u3067\u306e\u6709\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044 +# {0} - currentPage +# {1} - totalPages +DataResultViewerTable.pageNumbers.curOfTotal={0} / {1} +DataResultViewerTable.scoreRender.name=S +DataResultViewerTable.scoreRender.toolTip=S(\u30b9\u30b3\u30a2)\u306f\u9805\u76ee\u304c\u8208\u5473\u6df1\u3044\u304b\u9855\u8457\u306a\u3082\u306e\u3067\u3042\u308b\u304b\u3069\u3046\u304b\u3092\u793a\u3057\u3066\u3044\u307e\u3059 +DataResultViewerTable.title=\u30c6\u30fc\u30d6\u30eb +DataResultViewerThumbnail.sortLabel.text=\u6b21\u3067\u30bd\u30fc\u30c8: --- +# {0} - \u30bd\u30fc\u30c8\u6761\u4ef6 +DataResultViewerThumbnail.sortLabel.textTemplate=\u6b21\u3067\u30bd\u30fc\u30c8: {0} +DataResultViewerThumbnail.thumbnailSizeComboBox.large=\u5927\u30b5\u30a4\u30ba\u306e\u30b5\u30e0\u30cd\u30a4\u30eb +DataResultViewerThumbnail.thumbnailSizeComboBox.medium=\u4e2d\u30b5\u30a4\u30ba\u306e\u30b5\u30e0\u30cd\u30a4\u30eb +DataResultViewerThumbnail.thumbnailSizeComboBox.small=\u5c0f\u30b5\u30a4\u30ba\u306e\u30b5\u30e0\u30cd\u30a4\u30eb +OptionsCategory_Name_General=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 +OptionsCategory_Keywords_General=Autopsy\u30aa\u30d7\u30b7\u30e7\u30f3 +HINT_DataContentTopComponent=\u3053\u308c\u306fDataContent\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u3059 +HINT_NodeTableTopComponent=\u3053\u308c\u306fDataResult\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u3059 +OpenIDE-Module-Name=CoreComponents +DataContentViewerHex.prevPageButton.text= +DataContentViewerHex.nextPageButton.text= +DataContentViewerHex.pageLabel.text_1=\u30da\u30fc\u30b8: DataContentViewerHex.currentPageLabel.text_1=1 +DataContentViewerHex.ofLabel.text_1=/ DataContentViewerHex.totalPageLabel.text_1=100 DataContentViewerHex.pageLabel2.text=\u30da\u30fc\u30b8 -Format_OperatingSystem_Value={0} \u30d0\u30fc\u30b8\u30e7\u30f3 {1} \u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3 {2} + +# \u88fd\u54c1\u60c5\u5831\u30d1\u30cd\u30eb +LBL_Description=
\n \u88fd\u54c1\u30d0\u30fc\u30b8\u30e7\u30f3: {0} ({9})
Sleuth Kit\u30d0\u30fc\u30b8\u30e7\u30f3: {7}
Netbeans RCP\u30d3\u30eb\u30c9: {8}
Java: {1}; {2}
\u30b7\u30b9\u30c6\u30e0: {3}; {4}; {5}
Userdir: {6}
+Format_OperatingSystem_Value={2} \u4e0a\u3067 {0} \u30d0\u30fc\u30b8\u30e7\u30f3 {1} \u304c\u5b9f\u884c\u4e2d\u3067\u3059 +LBL_Copyright=
Autopsy™ \u306fSleuth Kit&trade\u3068\u305d\u306e\u4ed6\u30c4\u30fc\u30eb\u3092\u30d9\u30fc\u30b9\u3068\u3057\u305f\u30c7\u30b8\u30bf\u30eb\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30fb\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u3067\u3059\u3002
\u8457\u4f5c\u6a29 © 2003-2018.
+SortChooser.dialogTitle=\u30bd\u30fc\u30c8\u6761\u4ef6\u3092\u9078\u629e +ThumbnailViewChildren.progress.cancelling=(\u53d6\u308a\u6d88\u3057\u4e2d) +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +ThumbnailViewNode.progressHandle.text={0} \u306e\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u4f5c\u6210\u4e2d\u3067\u3059 +URL_ON_IMG=http://www.sleuthkit.org/ +FILE_FOR_LOCAL_HELP=file:/// +INDEX_FOR_LOCAL_HELP=/docs/index.html LBL_Close=\u9589\u3058\u308b DataContentViewerHex.copyMenuItem.text=\u30b3\u30d4\u30fc -DataContentViewerHex.selectAllMenuItem.text=\u5168\u3066\u9078\u629e +DataContentViewerHex.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e DataContentViewerArtifact.totalPageLabel.text=100 +DataContentViewerArtifact.prevPageButton.text= DataContentViewerArtifact.pageLabel2.text=\u7d50\u679c +DataContentViewerArtifact.nextPageButton.text= DataContentViewerArtifact.currentPageLabel.text=1 +DataContentViewerArtifact.ofLabel.text=/ DataContentViewerArtifact.copyMenuItem.text=\u30b3\u30d4\u30fc -DataContentViewerArtifact.selectAllMenuItem.text=\u5168\u3066\u9078\u629e -DataContentViewerArtifact.pageLabel.text=\u7d50\u679c\uff1a +DataContentViewerArtifact.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e +DataContentViewerArtifact.pageLabel.text=\u7d50\u679c: AdvancedConfigurationDialog.applyButton.text=OK -DataContentViewerHex.goToPageLabel.text=\u6b21\u306e\u30da\u30fc\u30b8\u3078\u79fb\u52d5\uff1a -DataResultViewerThumbnail.pageLabel.text=\u30da\u30fc\u30b8\uff1a -DataResultViewerThumbnail.pagesLabel.text=\u30da\u30fc\u30b8\uff1a -DataResultViewerThumbnail.imagesLabel.text=\u30a4\u30e1\u30fc\u30b8\uff1a +DataContentViewerHex.goToPageTextField.text= +DataContentViewerHex.goToPageLabel.text=\u30da\u30fc\u30b8\u306b\u79fb\u52d5: +DataResultViewerThumbnail.pageLabel.text=\u30da\u30fc\u30b8: +DataResultViewerThumbnail.pagesLabel.text=\u30da\u30fc\u30b8: +DataResultViewerThumbnail.pagePrevButton.text= +DataResultViewerThumbnail.pageNextButton.text= +DataResultViewerThumbnail.imagesLabel.text=\u30a4\u30e1\u30fc\u30b8: DataResultViewerThumbnail.imagesRangeLabel.text=- DataResultViewerThumbnail.pageNumLabel.text=- -DataResultViewerThumbnail.goToPageLabel.text=\u6b21\u306e\u30da\u30fc\u30b8\u306b\u79fb\u52d5\uff1a -AdvancedConfigurationDialog.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb -DataContentViewerArtifact.waitText=\u30c7\u30fc\u30bf\u3092\u53d6\u8fbc\u307f\u304a\u3088\u3073\u6e96\u5099\u4e2d\u3002\u3057\u3070\u3089\u304f\u304a\u5f85\u3061\u4e0b\u3055\u3044... -DataContentViewerArtifact.errorText=\u7d50\u679c\u3092\u53d6\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +DataResultViewerThumbnail.filePathLabel.text=\ \ \ +DataResultViewerThumbnail.goToPageLabel.text=\u30da\u30fc\u30b8\u306b\u79fb\u52d5: +DataResultViewerThumbnail.goToPageField.text= +AdvancedConfigurationDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +DataContentViewerArtifact.waitText=\u30c7\u30fc\u30bf\u3092\u691c\u7d22\u3057\u3066\u6e96\u5099\u4e2d\u3067\u3059\u3002\u304a\u5f85\u3061\u304f\u3060\u3055\u3044... +DataContentViewerArtifact.errorText=\u7d50\u679c\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f DataContentViewerArtifact.title=\u7d50\u679c -DataContentViewerArtifact.toolTip=\u30d5\u30a1\u30a4\u30eb\u306b\u95a2\u9023\u3059\u308b\u7d50\u679c\u3092\u8868\u793a\u3057\u307e\u3059 -DataContentViewerHex.goToPageTextField.msgDlg=\uff11\u304b\u3089 {0}\u306e\u9593\u306e\u6709\u52b9\u306a\u30da\u30fc\u30b8\u6570\u3092\u5165\u529b\u3057\u3066\u4e0b\u3055\u3044 -DataContentViewerHex.goToPageTextField.err=\u7121\u52b9\u306a\u30da\u30fc\u30b8\u6570 -DataContentViewerHex.setDataView.errorText=\uff08\u30aa\u30d5\u30bb\u30c3\u30c8{0}-{1}\u306f\u8aad\u307f\u53d6\u308c\u307e\u305b\u3093\u3067\u3057\u305f\uff09 -DataContentViewerHex.title=HEX -DataContentViewerHex.toolTip=\u30d0\u30a4\u30ca\u30ea\u30b3\u30f3\u30c6\u30f3\u30c4\u3092HEX\u30d5\u30a1\u30a4\u30eb\u3068\u3057\u3066\u8868\u793a\u3057\u3001ASCII\u3068\u3057\u3066\u8868\u793a\u3067\u304d\u308b\u30d0\u30a4\u30c8\u306f\u53f3\u5074\u306b\u8868\u793a\u3057\u307e\u3059\u3002 -DataResultPanel.pleasewaitNodeDisplayName=\u3057\u3070\u3089\u304f\u304a\u5f85\u3061\u304f\u3060\u3055\u3044\u2026 -DataResultViewerTable.firstColLbl=\u540d\u524d -DataResultViewerTable.illegalArgExc.noChildFromParent=\u6307\u5b9a\u3055\u308c\u305f\u30da\u30a2\u30ec\u30f3\u30c8\u304b\u3089\u30c1\u30e3\u30a4\u30eb\u30c9\u30ce\u30fc\u30c9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -DataResultViewerTable.illegalArgExc.childWithoutPropertySet=\u30c1\u30e3\u30a4\u30eb\u30c9\u30ce\u30fc\u30c9\u306f\u901a\u5e38\u306ePropertySet\u3092\u6301\u3063\u3066\u3044\u307e\u305b\u3093\u3002 -DataResultViewerTable.title=\u30c6\u30fc\u30d6\u30eb -DataResultViewerTable.pleasewaitNodeDisplayName=\u3057\u3070\u3089\u304f\u304a\u5f85\u3061\u304f\u3060\u3055\u3044\u2026 +DataContentViewerArtifact.toolTip=\u30d5\u30a1\u30a4\u30eb\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u7d50\u679c\u3092\u8868\u793a +DataContentViewerHex.goToPageTextField.msgDlg=1 \u304b\u3089 {0} \u307e\u3067\u306e\u6709\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044 +DataContentViewerHex.goToPageTextField.err=\u7121\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3067\u3059 +DataContentViewerHex.setDataView.errorText=(\u30aa\u30d5\u30bb\u30c3\u30c8 {0}-{1} \u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f) +DataContentViewerHex.title=16\u9032\u6570 +DataContentViewerHex.toolTip=\u30d5\u30a1\u30a4\u30eb\u306e\u30d0\u30a4\u30ca\u30ea\u30fc\u30b3\u30f3\u30c6\u30f3\u30c4\u309216\u9032\u6570\u3068\u3057\u3066\u8868\u793a\u3057\u307e\u3059\u3002\u53f3\u5074\u306bASCII\u6587\u5b57\u3068\u3057\u3066\u8868\u793a\u53ef\u80fd\u306a\u30d0\u30a4\u30c8\u304c\u793a\u3055\u308c\u307e\u3059\u3002 +DataResultPanel.pleasewaitNodeDisplayName=\u304a\u5f85\u3061\u304f\u3060\u3055\u3044... +DataResultViewerTable.illegalArgExc.noChildFromParent=\u6307\u5b9a\u3057\u305f\u89aa\u304b\u3089\u5b50\u30ce\u30fc\u30c9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +DataResultViewerTable.illegalArgExc.childWithoutPropertySet=\u5b50\u30ce\u30fc\u30c9\u306b\u306f\u6b63\u898f\u306ePropertySet\u304c\u3042\u308a\u307e\u305b\u3093\u3002 DataResultViewerThumbnail.title=\u30b5\u30e0\u30cd\u30a4\u30eb -DataResultViewerThumbnail.goToPageTextField.msgDlg=\uff11\u304b\u3089{0}\u306e\u9593\u306e\u6709\u52b9\u306a\u30da\u30fc\u30b8\u6570\u3092\u5165\u529b\u3057\u3066\u4e0b\u3055\u3044 -DataResultViewerThumbnail.goToPageTextField.err=\u7121\u52b9\u306a\u30da\u30fc\u30b8\u6570 -DataResultViewerThumbnail.genThumbs=\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u4f5c\u6210\u4e2d\u2026 -DataResultViewerThumbnail.pageNumbers.curOfTotal={0}\uff0f{1}\u76ee +DataResultViewerThumbnail.goToPageTextField.msgDlg=1 \u304b\u3089 {0} \u307e\u3067\u306e\u6709\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044 +DataResultViewerThumbnail.goToPageTextField.err=\u7121\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3067\u3059 +DataResultViewerThumbnail.genThumbs=\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u4f5c\u6210\u4e2d\u3067\u3059... +DataResultViewerThumbnail.pageNumbers.curOfTotal={0} / {1} GeneralOptionsPanelController.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -GeneralOptionsPanelController.moduleErr.msg=GeneralOptionsPanelController\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -LBL_Description=
\n \u88fd\u54c1\u30d0\u30fc\u30b8\u30e7\u30f3\uff1a {0} ({9})
Sleuth Kit\u30d0\u30fc\u30b8\u30e7\u30f3\uff1a {7}
Netbeans RCP\u30d3\u30eb\u30c9\: {8}
Java\: {1}; {2}
\u30b7\u30b9\u30c6\u30e0\uff1a {3}; {4}; {5}
\u30e6\u30fc\u30b6\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u540d {6}
-LBL_Copyright=
Autopsy™\u306fSleuth Kit™\u3084\u305d\u306e\u4ed6\u30c4\u30fc\u30eb\u3092\u57fa\u306b\u3057\u305f\u30c7\u30b8\u30bf\u30eb\u30fb\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30fb\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u3067\u3059\u3002

Copyright © 2003-2013. \u8a73\u7d30\u306f\u4e0b\u8a18\u3092\u3054\u89a7\u4e0b\u3055\u3044\u3002 http\://www.sleuthkit.org.
-ProductInformationPanel.verbLoggingEnabled.text=Verbose\u30ed\u30b0\u304c\u6709\u52b9\u3067\u3059 +GeneralOptionsPanelController.moduleErr.msg=GeneralOptionsPanelController\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +ProductInformationPanel.verbLoggingEnabled.text=\u8a73\u7d30\u30ed\u30ae\u30f3\u30b0\u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u3059 ProductInformationPanel.propertyUnknown.text=\u4e0d\u660e ProductInformationPanel.getVMValue.text={0} {1} TableFilterNode.displayName.text=\u540d\u524d -DataContentViewerHex.ofLabel.text_1=of -DataContentViewerArtifact.ofLabel.text=of -DataContentViewerString.setDataView.errorNoText=\uff08\u30aa\u30d5\u30bb\u30c3\u30c8{0}-{1}\u306b\u306f\u30c6\u30ad\u30b9\u30c8\u304c\u3042\u308a\u307e\u305b\u3093\uff09 -DataResultViewerThumbnail.comboBox.smallThumbnails=\u30b5\u30e0\u30cd\u30a4\u30eb\uff08\u5c0f\uff09 -DataResultViewerThumbnail.comboBox.mediumThumbnails=\u30b5\u30e0\u30cd\u30a4\u30eb\uff08\u4e2d\uff09 -DataResultViewerThumbnail.comboBox.largeThumbnails=\u30b5\u30e0\u30cd\u30a4\u30eb\uff08\u5927\uff09 -DataResultViewerThumbnail.switchPage.done.errMsg=\u30b5\u30e0\u30cd\u30a4\u30eb\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a {0} -AboutWindowPanel.actVerboseLogging.text=Verbose\u30ed\u30b0\u3092\u30a2\u30af\u30c6\u30a3\u30d9\u30fc\u30c8 -OptionsCategory_Name_Multi_User_Settings=\u8907\u6570\u306e\u30e6\u30fc\u30b6\u30fc -OptionsCategory_Keywords_Multi_User_Options=\u8907\u6570\u306e\u30e6\u30fc\u30b6\u30fc\u30aa\u30d7\u30b7\u30e7\u30f3 +DataResultViewerThumbnail.comboBox.smallThumbnails=\u5c0f\u30b5\u30a4\u30ba\u306e\u30b5\u30e0\u30cd\u30a4\u30eb +DataResultViewerThumbnail.comboBox.mediumThumbnails=\u4e2d\u30b5\u30a4\u30ba\u306e\u30b5\u30e0\u30cd\u30a4\u30eb +DataResultViewerThumbnail.comboBox.largeThumbnails=\u5927\u30b5\u30a4\u30ba\u306e\u30b5\u30e0\u30cd\u30a4\u30eb +DataResultViewerThumbnail.switchPage.done.errMsg=\u6b21\u306e\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +AboutWindowPanel.actVerboseLogging.text=\u8a73\u7d30\u30ed\u30ae\u30f3\u30b0\u3092\u30a2\u30af\u30c6\u30a3\u30d6\u5316 +OptionsCategory_Name_Multi_User_Settings=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc +OptionsCategory_Keywords_Multi_User_Options=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u8a2d\u5b9a MultiUserSettingsPanel.lbSolrSettings.text=Solr\u8a2d\u5b9a -MultiUserSettingsPanel.cbEnableMultiUser.text=\u8907\u6570\u306e\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u3092\u6709\u52b9\u5316 +MultiUserSettingsPanel.cbEnableMultiUser.text=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u3092\u6709\u52b9\u5316 MultiUserSettingsPanel.lbDatabaseSettings.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8a2d\u5b9a -MultiUserSettingsPanel.validationErrMsg.incomplete=\u5168\u3066\u306e\u30d0\u30ea\u30e5\u30fc\u3092\u5165\u529b -MultiUserSettingsPanel.nonWindowsOs.msg=Windows\u3067\u3057\u304b\u8907\u6570\u306e\u30e6\u30fc\u30b6\u30fc\u7248\u306f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093 -MultiUserSettingsPanel.validationErrMsg.invalidDatabasePort=\u7121\u52b9\u306a\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30dd\u30fc\u30c8\u756a\u53f7 -MultiUserSettingsPanel.validationErrMsg.invalidMessageServicePort=\u7121\u52b9\u306a\u30e1\u30c3\u30bb\u30fc\u30b8\u30b5\u30fc\u30d3\u30b9\u30dd\u30fc\u30c8\u756a\u53f7 -MultiUserSettingsPanel.validationErrMsg.invalidIndexingServerPort=\u7121\u52b9\u306aSolr\u30b5\u30fc\u30d0\u30fc\u30dd\u30fc\u30c8\u756a\u53f7 -MultiUserSettingsPanel.validationErrMsg.invalidMessgeServiceURI=\u7121\u52b9\u306a\u30e1\u30c3\u30bb\u30fc\u30b8\u30b5\u30fc\u30d3\u30b9\u30db\u30b9\u30c8\u3084\u30dd\u30fc\u30c8\u756a\u53f7 +MultiUserSettingsPanel.validationErrMsg.incomplete=\u3059\u3079\u3066\u306e\u5024\u3092\u5165\u529b +MultiUserSettingsPanel.nonWindowsOs.msg=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u306fWindows\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u4e0a\u3067\u306e\u307f\u5229\u7528\u3067\u304d\u307e\u3059 +MultiUserSettingsPanel.validationErrMsg.invalidDatabasePort=\u7121\u52b9\u306a\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u3059 +MultiUserSettingsPanel.validationErrMsg.invalidMessageServicePort=\u7121\u52b9\u306a\u30e1\u30c3\u30bb\u30fc\u30b8\u30b5\u30fc\u30d3\u30b9\u306e\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u3059 +MultiUserSettingsPanel.validationErrMsg.invalidIndexingServerPort=\u7121\u52b9\u306aSolr \u30b5\u30fc\u30d0\u30fc\u306e\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u3059 +MultiUserSettingsPanel.validationErrMsg.invalidMessgeServiceURI=\u6709\u52b9\u3067\u306f\u306a\u3044\u30e1\u30c3\u30bb\u30fc\u30b8\u30b5\u30fc\u30d3\u30b9\u30db\u30b9\u30c8\u304a\u3088\u3073/\u307e\u305f\u306f\u30dd\u30fc\u30c8\u3067\u3059 DataContentViewerHex.goToOffsetLabel.text=\u30aa\u30d5\u30bb\u30c3\u30c8\u306b\u30b8\u30e3\u30f3\u30d7 -DataContentViewerHex.goToOffsetTextField.msgDlg=\u7121\u52b9\u306a\u30aa\u30d5\u30bb\u30c3\u30c8\uff1a{0} -DataContentViewerHex.setDataView.invalidOffset.negativeOffsetValue=\u8a08\u7b97\u3055\u308c\u305f\u30aa\u30d5\u30bb\u30c3\u30c8\u306b\u30b8\u30e3\u30f3\u30d7\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +DataContentViewerHex.goToOffsetTextField.text= +DataContentViewerHex.goToOffsetTextField.msgDlg=\u7121\u52b9\u306a\u30aa\u30d5\u30bb\u30c3\u30c8: {0} +DataContentViewerHex.setDataView.invalidOffset.negativeOffsetValue=\u7d50\u679c\u3068\u3057\u3066\u751f\u3058\u305f\u30aa\u30d5\u30bb\u30c3\u30c8\u306b\u30b8\u30e3\u30f3\u30d7\u3067\u304d\u307e\u305b\u3093 +MultiUserSettingsPanel.tbOops.text= +MultiUserSettingsPanel.lbTestDatabase.text= MultiUserSettingsPanel.bnTestDatabase.text=\u30c6\u30b9\u30c8 MultiUserSettingsPanel.tbDbHostname.toolTipText=\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9 +MultiUserSettingsPanel.tbDbHostname.text= MultiUserSettingsPanel.tbDbPort.toolTipText=\u30dd\u30fc\u30c8\u756a\u53f7 -MultiUserSettingsPanel.tbDbUsername.toolTipText=\u30e6\u30fc\u30b6\u30fc\u540d +MultiUserSettingsPanel.tbDbPort.text= +MultiUserSettingsPanel.tbDbUsername.toolTipText=\u30e6\u30fc\u30b6\u30fc\u540d +MultiUserSettingsPanel.tbDbUsername.text= MultiUserSettingsPanel.tbDbPassword.toolTipText=\u30d1\u30b9\u30ef\u30fc\u30c9 +MultiUserSettingsPanel.tbDbPassword.text= +MultiUserSettingsPanel.lbTestSolr.text= MultiUserSettingsPanel.bnTestSolr.text=\u30c6\u30b9\u30c8 MultiUserSettingsPanel.tbSolrHostname.toolTipText=\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9 MultiUserSettingsPanel.tbSolrPort.toolTipText=\u30dd\u30fc\u30c8\u756a\u53f7 +MultiUserSettingsPanel.lbTestMessageService.text= MultiUserSettingsPanel.bnTestMessageService.text=\u30c6\u30b9\u30c8 MultiUserSettingsPanel.lbMessageServiceSettings.text=ActiveMQ\u30e1\u30c3\u30bb\u30fc\u30b8\u30b5\u30fc\u30d3\u30b9\u8a2d\u5b9a MultiUserSettingsPanel.tbMsgPort.toolTipText=\u30dd\u30fc\u30c8\u756a\u53f7 -MultiUserSettingsPanel.tbMsgUsername.toolTipText=\u30e6\u30fc\u30b6\u30fc\u540d\uff08\u30aa\u30d7\u30b7\u30e7\u30ca\u30eb\uff09 -MultiUserSettingsPanel.tbMsgPassword.toolTipText=\u30d1\u30b9\u30ef\u30fc\u30c9\uff08\u30aa\u30d7\u30b7\u30e7\u30ca\u30eb\uff09 +MultiUserSettingsPanel.tbMsgPort.text= +MultiUserSettingsPanel.tbMsgUsername.toolTipText=\u30e6\u30fc\u30b6\u30fc\u540d(\u4efb\u610f) +MultiUserSettingsPanel.tbMsgUsername.text= +MultiUserSettingsPanel.tbMsgPassword.toolTipText=\u30d1\u30b9\u30ef\u30fc\u30c9(\u4efb\u610f) +MultiUserSettingsPanel.tbMsgPassword.text= MultiUserSettingsPanel.tbMsgHostname.toolTipText=\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9 -MultiUserSettingsPanel.KeywordSearchNull=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d3\u30b9\u3092\u898b\u3064\u3051\u308c\u3089\u308c\u307e\u305b\u3093 -MultiUserSettingsPanel.InvalidPortNumber=\u7121\u52b9\u306a\u30dd\u30fc\u30c8\u756a\u53f7 -CTL_OfflineHelpAction=\u30aa\u30d5\u30e9\u30a4\u30f3Autopsy\u30c9\u30ad\u30e5\u30e1\u30f3\u30c6\u30fc\u30b7\u30e7\u30f3 -CTL_OnlineHelpAction=\u30aa\u30f3\u30e9\u30a4\u30f3Autopsy\u30c9\u30ad\u30e5\u30e1\u30f3\u30c6\u30fc\u30b7\u30e7\u30f3 -DataResultViewerThumbnail.thumbnailSizeComboBox.large=\u30b5\u30e0\u30cd\u30a4\u30eb\uff08\u5927\uff09 -DataResultViewerThumbnail.thumbnailSizeComboBox.medium=\u30b5\u30e0\u30cd\u30a4\u30eb\uff08\u4e2d\uff09 -DataResultViewerThumbnail.thumbnailSizeComboBox.small=\u30b5\u30e0\u30cd\u30a4\u30eb\uff08\u5c0f\uff09 -MediaViewImagePanel.errorLabel.OOMText=\u30d5\u30a1\u30a4\u30eb\u3092\u30e1\u30c7\u30a3\u30a2\u30d3\u30e5\u30fc\u306b\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\uff1a\u30e1\u30e2\u30ea\u4e0d\u8db3\u3002 -MediaViewImagePanel.errorLabel.text=\u30d5\u30a1\u30a4\u30eb\u3092\u30e1\u30c7\u30a3\u30a2\u30d3\u30e5\u30fc\u306b\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -MediaViewImagePanel.externalViewerButton.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u30fc\u3067\u958b\u304f +MultiUserSettingsPanel.tbMsgHostname.text= +MultiUserSettingsPanel.lbTestMessageWarning.text= +MultiUserSettingsPanel.lbTestSolrWarning.text= +MultiUserSettingsPanel.lbTestDbWarning.text= +MultiUserSettingsPanel.KeywordSearchNull=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d3\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +MultiUserSettingsPanel.InvalidPortNumber=\u7121\u52b9\u306a\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u3059 +AutopsyOptionsPanel.agencyLogoImageLabel.toolTipText= +AutopsyOptionsPanel.agencyLogoPathField.text= +SortChooserDialog.label=\u524a\u9664 +SortChooser.addCriteriaButton.text=\u30bd\u30fc\u30c8\u6761\u4ef6\u3092\u8ffd\u52a0 +DataResultViewerThumbnail.sortButton.text=\u30bd\u30fc\u30c8 +CriterionChooser.ascendingRadio.text=\u25b2 \u6607\u9806\n +CriterionChooser.removeButton.text=\u524a\u9664 +CriterionChooser.descendingRadio.text=\u25bc \u964d\u9806 +AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text= +AutopsyOptionsPanel.logNumAlert.text= +AutopsyOptionsPanel.totalMemoryLabel.text=\u5408\u8a08\u30b7\u30b9\u30c6\u30e0\u30e1\u30e2\u30ea\u30fc: +AutopsyOptionsPanel.maxMemoryLabel.text=\u6700\u5927JVM\u30e1\u30e2\u30ea\u30fc: +AutopsyOptionsPanel.maxLogFileCount.text=\u6700\u5927\u30ed\u30b0\u30d5\u30a1\u30a4\u30eb\u6570: +AutopsyOptionsPanel.maxMemoryUnitsLabel.text=GB +AutopsyOptionsPanel.restartNecessaryWarning.text=\u30e1\u30e2\u30ea\u30fc\u5909\u66f4\u3092\u6709\u52b9\u306b\u3059\u308b\u306b\u306f\u518d\u8d77\u52d5\u304c\u5fc5\u8981\u3067\u3059\u3002 +AutopsyOptionsPanel.browseLogosButton.text=\u53c2\u7167 +AutopsyOptionsPanel.defaultLogoRB.text=\u30c7\u30d5\u30a9\u30eb\u30c8\u3092\u4f7f\u7528 +AutopsyOptionsPanel.specifyLogoRB.text=\u30ed\u30b4\u3092\u6307\u5b9a +AutopsyOptionsPanel.agencyLogoPreview.text=
\u30ed\u30b4\u304c\u3042\u308a\u307e\u305b\u3093
selected
+AutopsyOptionsPanel.logoPanel.border.title=\u30ed\u30b4 +AutopsyOptionsPanel.runtimePanel.border.title=\u30e9\u30f3\u30bf\u30a4\u30e0 DataResultPanel.matchLabel.text=\u7d50\u679c DataResultPanel.numberOfChildNodesLabel.text=0 -DataResultPanel.descriptionLabel.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30d1\u30b9 +DataResultPanel.descriptionLabel.text=directoryPath +ViewOptionsController.moduleErr=\u5024\u306e\u5909\u66f4\u3092\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ViewOptionsController.moduleErr.msg=\u5024\u306e\u5909\u66f4\u306e\u51e6\u7406\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 +ViewPreferencesPanel.groupByDataSourceCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5225\u306b\u30b0\u30eb\u30fc\u30d7\u5316 +ViewPreferencesPanel.currentCaseSettingsPanel.border.title=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u8a2d\u5b9a +OptionsCategory_Name_View=\u8868\u793a +OptionsCategory_Keywords_View=\u8868\u793a +ViewPreferencesPanel.currentSessionSettingsPanel.border.title=\u73fe\u5728\u306e\u30bb\u30c3\u30b7\u30e7\u30f3\u8a2d\u5b9a +ViewPreferencesPanel.hideRejectedResultsCheckbox.text=\u62d2\u5426\u3055\u308c\u305f\u7d50\u679c\u3092\u975e\u8868\u793a\u306b\u3059\u308b +ViewPreferencesPanel.selectFileLabel.text=\u30d5\u30a1\u30a4\u30eb\u9078\u629e\u6642: +ViewPreferencesPanel.globalSettingsPanel.border.title=\u30b0\u30ed\u30fc\u30d0\u30eb\u8a2d\u5b9a +ViewPreferencesPanel.translateTextLabel.text=\u30c6\u30ad\u30b9\u30c8\u3092\u7ffb\u8a33: +ViewPreferencesPanel.hideOtherUsersTagsLabel.text=\u6b21\u306e\u305d\u306e\u4ed6\u306e\u30e6\u30fc\u30b6\u30fc\u306e\u30bf\u30b0\u3092\u975e\u8868\u793a\u306b\u3059\u308b: +ViewPreferencesPanel.hideOtherUsersTagsCheckbox.text=\u30c4\u30ea\u30fc\u5185\u306e\u30bf\u30b0\u9818\u57df +ViewPreferencesPanel.useAnotherTimeRadioButton.text=\u5225\u306e\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u4f7f\u7528 ViewPreferencesPanel.useLocalTimeRadioButton.text=\u30ed\u30fc\u30ab\u30eb\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u4f7f\u7528 -ViewPreferencesPanel.displayTimeLabel.text=\u6642\u9593\u3092\u8868\u793a\u3059\u308b\u5834\u5408\uff1a -ViewPreferencesPanel.viewsHideKnownCheckbox.text=\u30d3\u30e5\u30fc\u30a8\u30ea\u30a2 -ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30a8\u30ea\u30a2\uff08\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u968e\u5c64\uff09 -ViewPreferencesPanel.hideKnownFilesLabel.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\uff08NIST NSRL\u5185\u306e\uff09\u3092\u6b21\u306b\u96a0\u3059\uff1a -ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=\u4f8b\u3048\u3070\u3001JPEG\u304c\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u306b\u305d\u306e\u307e\u307eHEX\u30d3\u30e5\u30fc\u3092\u4f7f\u7528\u3002 -ViewPreferencesPanel.keepCurrentViewerRadioButton.text=\u305d\u306e\u307e\u307e\u540c\u3058\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u30a2\u3092\u4f7f\u7528 -ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=\u4f8b\u3048\u3070\u3001JPEG\u304c\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u306b\u306fHEX\u304b\u3089\u30e1\u30c7\u30a3\u30a2\u306b\u5909\u66f4\u3059\u308b\u3002 -ViewPreferencesPanel.useBestViewerRadioButton.text=\u6700\u3082\u5c02\u9580\u7684\u306a\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u30a2\u306b\u5909\u66f4 -ViewPreferencesPanel.selectFileLabel.text=\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e\u3059\u308b\u5834\u5408\uff1a +ViewPreferencesPanel.displayTimeLabel.text=\u6642\u523b\u8868\u793a\u6642: +ViewPreferencesPanel.viewsHideSlackCheckbox.text=\u30d3\u30e5\u30fc\u9818\u57df +ViewPreferencesPanel.dataSourcesHideSlackCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u9818\u57df(\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u968e\u5c64) +ViewPreferencesPanel.hideSlackFilesLabel.text=\u6b21\u306e\u30b9\u30e9\u30c3\u30af\u30d5\u30a1\u30a4\u30eb\u3092\u975e\u8868\u793a\u306b\u3059\u308b: +ViewPreferencesPanel.viewsHideKnownCheckbox.text=\u30d3\u30e5\u30fc\u9818\u57df +ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u9818\u57df(\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u968e\u5c64) +ViewPreferencesPanel.hideKnownFilesLabel.text=\u6b21\u306e\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb(NIST NSRL\u5185\u306e\u30d5\u30a1\u30a4\u30eb)\u3092\u975e\u8868\u793a\u306b\u3059\u308b: +ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=\u305f\u3068\u3048\u3070\u3001JPEG\u9078\u629e\u6642\u306f16\u9032\u30d3\u30e5\u30fc\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002 +ViewPreferencesPanel.keepCurrentViewerRadioButton.text=\u540c\u3058\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u306e\u307e\u307e\u306b\u3059\u308b +ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=\u305f\u3068\u3048\u3070\u3001JPEG\u9078\u629e\u6642\u306f16\u9032\u304b\u3089\u30e1\u30c7\u30a3\u30a2\u306b\u5207\u308a\u66ff\u3048\u307e\u3059\u3002 +ViewPreferencesPanel.useBestViewerRadioButton.text=\u6700\u3082\u56fa\u6709\u306e\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u306b\u5207\u308a\u66ff\u3048\u308b +ViewPreferencesPanel.fileNameTranslationColumnCheckbox.text=\u7d50\u679c\u30d3\u30e5\u30fc\u30ef\u30fc\u306b\u30d5\u30a1\u30a4\u30eb\u540d\u7ffb\u8a33\u7528\u5217\u3092\u8ffd\u52a0 +DataContentViewerHex.launchHxDButton.text=HxD\u3067\u8d77\u52d5 +ExternalViewerGlobalSettingsPanel.jButton2.text=jButton2 +ExternalViewerGlobalSettingsPanel.newRuleButton1.text=\u65b0\u898f\u30eb\u30fc\u30eb +ExternalViewerGlobalSettingsPanel.newRuleButton.text=\u65b0\u898f\u30eb\u30fc\u30eb +ExternalViewerGlobalSettingsPanel.browseHxDDirectory.text=\u53c2\u7167 +ExternalViewerGlobalSettingsPanel.jLabel2.text=\u8d77\u52d5\u53ef\u80fd\u306a\u30b3\u30f3\u30c6\u30f3\u30c4\u30d3\u30e5\u30fc\u30ef\u30fc\u306e\u62e1\u5f35\u5b50\u3092\u8a2d\u5b9a +ExternalViewerGlobalSettingsPanel.jLabel1.text=HxD\u30a8\u30c7\u30a3\u30bf\u30fc\u30d1\u30b9: +ExternalViewerGlobalSettingsPanel.editRuleButton.text=\u30eb\u30fc\u30eb\u3092\u7de8\u96c6 +ExternalViewerGlobalSettingsPanel.deleteRuleButton.text=\u30eb\u30fc\u30eb\u3092\u524a\u9664 +ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text=\u7279\u5b9a\u306eMIME\u30bf\u30a4\u30d7/\u62e1\u5f35\u5b50\u3092\u3082\u3064\u30d5\u30a1\u30a4\u30eb\u306b\u4f7f\u7528\u3059\u308b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d3\u30e5\u30fc\u30ef\u30fc\u3092\u8a2d\u5b9a: +ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title1=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 +ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title0=MIME\u30bf\u30a4\u30d7/\u62e1\u5f35\u5b50 +AddExternalViewerRulePanel.nameLabel.text=MIME\u30bf\u30a4\u30d7\u307e\u305f\u306f\u62e1\u5f35\u5b50 +AddExternalViewerRulePanel.browseButton.text=\u53c2\u7167 +AddExternalViewerRulePanel.exePathTextField.text= +AddExternalViewerRulePanel.exePathLabel.text=\u3053\u306e\u30bf\u30a4\u30d7\u307e\u305f\u306f\u62e1\u5f35\u5b50\u306e\u30d5\u30a1\u30a4\u30eb\u306b\u4f7f\u7528\u3059\u308b\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u30d1\u30b9 +AddExternalViewerRulePanel.extRadioButton.text=\u62e1\u5f35\u5b50 +AddExternalViewerRulePanel.mimeRadioButton.text=MIME\u30bf\u30a4\u30d7 +AddExternalViewerRulePanel.nameTextField.text= +ExternalViewerGlobalSettingsPanel.jButton2.text_1=jButton2 +ExternalViewerGlobalSettingsPanel.newRuleButton1.text_1=\u65b0\u898f\u30eb\u30fc\u30eb +ExternalViewerGlobalSettingsPanel.newRuleButton.text_1=\u65b0\u898f\u30eb\u30fc\u30eb +ExternalViewerGlobalSettingsPanel.browseHxDDirectory.text_1=\u53c2\u7167 +ExternalViewerGlobalSettingsPanel.jLabel2.text_1=\u8d77\u52d5\u53ef\u80fd\u306a\u30b3\u30f3\u30c6\u30f3\u30c4\u30d3\u30e5\u30fc\u30ef\u30fc\u306e\u62e1\u5f35\u5b50\u3092\u8a2d\u5b9a +ExternalViewerGlobalSettingsPanel.jLabel1.text_1=HxD\u30a8\u30c7\u30a3\u30bf\u30fc\u30d1\u30b9: +ExternalViewerGlobalSettingsPanel.HxDPath.text=C:/Program Files/HxD/HxD.exe +ExternalViewerGlobalSettingsPanel.editRuleButton.text_1=\u30eb\u30fc\u30eb\u3092\u7de8\u96c6 +ExternalViewerGlobalSettingsPanel.deleteRuleButton.text_1=\u30eb\u30fc\u30eb\u3092\u524a\u9664 +ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text_1=\u7279\u5b9a\u306eMIME\u30bf\u30a4\u30d7/\u62e1\u5f35\u5b50\u3092\u3082\u3064\u30d5\u30a1\u30a4\u30eb\u306b\u4f7f\u7528\u3059\u308b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d3\u30e5\u30fc\u30ef\u30fc\u3092\u8a2d\u5b9a: +ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title1_1=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 +ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title0_1=MIME\u30bf\u30a4\u30d7/\u62e1\u5f35\u5b50 +AutopsyOptionsPanel.maxSolrMemoryLabel.text=\u6700\u5927SolrJVM\u30e1\u30e2\u30ea\u30fc: +AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB +AutopsyOptionsPanel.solrJVMHeapWarning.text=\u6ce8: \u3053\u308c\u3092\u3042\u307e\u308a\u306b\u3082\u5927\u304d\u304f\u8a2d\u5b9a\u3059\u308b\u3068\u3001\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u5168\u4f53\u306b\u5f71\u97ff\u3059\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 +DataResultViewerTable.gotoPageTextField.text= +DataResultViewerTable.gotoPageLabel.AccessibleContext.accessibleName= +DataResultViewerTable.gotoPageLabel.text=\u30da\u30fc\u30b8\u306b\u79fb\u52d5: +DataResultViewerTable.pageNextButton.text= +DataResultViewerTable.pagePrevButton.text= +DataResultViewerTable.pagesLabel.text=\u30da\u30fc\u30b8: +DataResultViewerTable.pageNumLabel.text= +DataResultViewerTable.pageLabel.text=\u30da\u30fc\u30b8: +ViewPreferencesPanel.maxResultsLabel.text=\u30c6\u30fc\u30d6\u30eb\u3067\u8868\u793a\u3059\u308b\u6700\u5927\u7d50\u679c\u6570: +ViewPreferencesPanel.maxResultsLabel.toolTipText=\n\u3053\u306e\u5024\u30920 \u306b\u8a2d\u5b9a\u3059\u308b\u3068\u3001\u3059\u3079\u3066\u306e\u7d50\u679c\u304c\u7d50\u679c\u30c6\u30fc\u30d6\u30eb\u306b\u8868\u793a\u3055\u308c\u307e\u3059\u3002\n
\u3053\u306e\u5024\u30920 \u306b\u8a2d\u5b9a\u3059\u308b\u3068\u3001\u7d50\u679c\u6570\u304c\u591a\u3044\u5834\u5408UI\u306e\u5fdc\u7b54\u6027\u304c\u60aa\u304f\u306a\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059 \u3002\n +DataResultViewerTable.exportCSVButton.text=\u30c6\u30fc\u30d6\u30eb\u3092CSV\u3068\u3057\u3066\u4fdd\u5b58 +ViewPreferencesPanel.scoColumnsCheckbox.text=S(\u30b9\u30b3\u30a2)\u3001C(\u30b3\u30e1\u30f3\u30c8)\u3001O(\u767a\u751f) +ViewPreferencesPanel.scoColumnsWrapAroundText.text=\u975e\u8868\u793a\u306b\u3059\u308b\u3068\u8aad\u8fbc\u307f\u304c\u65e9\u304f\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +ViewPreferencesPanel.scoColumnsLabel.text=\u6b21\u306e\u305f\u3081\u306e\u5217\u3092\u8ffd\u52a0\u3057\u306a\u3044: diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index 39d3203b13..9d464c2585 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -56,6 +56,11 @@ import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; import org.netbeans.swing.etable.ETable; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonArray; +import java.util.Map; /** * Instances of this class display the BlackboardArtifacts associated with the @@ -552,6 +557,14 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat value = dateFormatter.format(new java.util.Date(epoch * 1000)); } break; + case JSON: + // Get the attribute's JSON value and convert to indented multiline display string + String jsonVal = attr.getValueString(); + JsonParser parser = new JsonParser(); + JsonObject json = parser.parse(jsonVal).getAsJsonObject(); + + value = toJsonDisplayString(json, ""); + break; } /* * Attribute sources column. @@ -588,6 +601,63 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat String getArtifactDisplayName() { return artifactDisplayName; } + + private static final String INDENT_RIGHT = " "; + private static final String NEW_LINE = "\n"; + + /** + * Recursively converts a JSON element into an indented multi-line + * display string. + * + * @param element JSON element to convert + * @param indentStr Starting indentation for the element. + * + * @return A multi-line display string. + */ + private String toJsonDisplayString(JsonElement element, String startIndent) { + + StringBuilder sb = new StringBuilder(""); + JsonObject obj = element.getAsJsonObject(); + + for (Map.Entry entry : obj.entrySet()) { + appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb ); + } + + String returnString = sb.toString(); + if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) { + returnString = returnString.substring(NEW_LINE.length()); + } + return returnString; + } + + + /** + * Converts the given JSON element into string and appends to the given string builder. + * + * @param entry JSON entry to parse + * @param startIndent Starting indentation for the element. + * @param sb String builder to append to. + */ + private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) { + if (jsonElement.isJsonArray()) { + JsonArray jsonArray = jsonElement.getAsJsonArray(); + if (jsonArray.size() > 0) { + int count = 1; + sb.append(NEW_LINE).append(String.format("%s%s", startIndent, jsonKey)); + for (JsonElement arrayMember : jsonArray) { + sb.append(NEW_LINE).append(String.format("%s%d", startIndent.concat(INDENT_RIGHT), count)); + sb.append(toJsonDisplayString(arrayMember, startIndent.concat(INDENT_RIGHT).concat(INDENT_RIGHT))); + count++; + } + } + } else if (jsonElement.isJsonObject()) { + sb.append(NEW_LINE).append(String.format("%s%s %s", startIndent, jsonKey, toJsonDisplayString(jsonElement.getAsJsonObject(), startIndent + INDENT_RIGHT))); + } else if (jsonElement.isJsonPrimitive()) { + sb.append(NEW_LINE).append(String.format("%s%s = %s", startIndent, jsonKey, jsonElement.getAsString())); + } else if (jsonElement.isJsonNull()) { + sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey)); + } + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerUtility.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerUtility.java index ea1e7b58a0..d460a16f03 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerUtility.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerUtility.java @@ -23,32 +23,42 @@ import org.openide.nodes.Node; import org.sleuthkit.datamodel.BlackboardArtifact; /** - * Utility classes for content viewers. In theory, this would live in the - * contentviewer package, but the initial method was needed only be viewers in - * corecomponents and therefore can stay out of public API. + * Utility methods for content viewers. */ public class DataContentViewerUtility { /** - * Returns the first non-Blackboard Artifact from a Node. Needed for (at - * least) Hex and Strings that want to view all types of content (not just - * AbstractFile), but don't want to display an artifact unless that's the - * only thing there. Scenario is hash hit or interesting item hit. + * Gets a Content object from the Lookup of a display Node object, + * preferring to return any Content object other than a BlackboardArtifact + * object. * - * @param node Node passed into content viewer + * This method was written with the needs of the hex and strings content + * viewers in mind - the algorithm is exactly what those viewers require. * - * @return highest priority content or null if there is no content + * @param node A display Node object. + * + * @return If there are multiple Content objects associated with the Node, + * the first Content object that is not a BlackboardArtifact object + * is returned. If no Content objects other than artifacts are found, + * the first BlackboardArtifact object found is returned. If no + * Content objects are found, null is returned. */ public static Content getDefaultContent(Node node) { - Content bbContentSeen = null; - for (Content content : (node).getLookup().lookupAll(Content.class)) { - if (content instanceof BlackboardArtifact) { - bbContentSeen = content; + Content artifact = null; + for (Content content : node.getLookup().lookupAll(Content.class)) { + if (content instanceof BlackboardArtifact && artifact == null) { + artifact = content; } else { return content; } } - return bbContentSeen; + return artifact; + } + + /* + * Private constructor to prevent instantiation of utility class. + */ + private DataContentViewerUtility() { } } diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED index a0d535f8e6..18e279dd2c 100755 --- a/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED @@ -30,9 +30,7 @@ PlatformUtil.getProcVmUsed.sigarNotInit.msg=Cannot get virt mem used, sigar not PlatformUtil.getProcVmUsed.gen.msg=Cannot get virt mem used, {0} PlatformUtil.getJvmMemInfo.usageText=JVM heap usage: {0}, JVM non-heap usage: {1} PlatformUtil.getPhysicalMemInfo.usageText=Physical memory usage (max, total, free): {0}, {1}, {2} -PlatformUtil.getAllMemUsageInfo.usageText={0}\n\ -{1}\n\ -Process Virtual Memory: {2} +PlatformUtil.getAllMemUsageInfo.usageText={0}\n{1}\nProcess Virtual Memory: {2} # {0} - file name ReadImageTask.mesageText=Reading image: {0} StringExtract.illegalStateException.cannotInit.msg=Unicode table not properly initialized, cannot instantiate StringExtract diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/coreutils/Bundle_ja.properties index 861bb54da9..5f460a7c53 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/coreutils/Bundle_ja.properties @@ -1,27 +1,35 @@ -OpenIDE-Module-Name=\u4E3B\u8981\u30E6\u30FC\u30C6\u30A3\u30EA\u30C6\u30A3 -JLNK.noPrefPath.text=\u512A\u5148\u7684\u306B\u4F7F\u7528\u3059\u308B\u30D1\u30B9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 -PlatformUtil.nameUnknown=\u4E0D\u660E -PlatformUtil.verUnknown=\u4E0D\u660E -PlatformUtil.archUnknown=\u4E0D\u660E -PlatformUtil.jrePath.jreDir.msg=\u57CB\u3081\u8FBC\u307E\u308C\u305FJRE\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u767A\u898B\u3055\u308C\u307E\u3057\u305F\uFF1A{0} -PlatformUtil.jrePath.usingJavaPath.msg=JAVA\u30D0\u30A4\u30CA\u30EA\u30D1\u30B9\u3092\u4F7F\u7528\uFF1A{0} -PlatformUtil.getPID.sigarNotInit.msg=PID\u3092\u5165\u624B\u3067\u304D\u307E\u305B\u3093\u3001Sigar\u304C\u521D\u671F\u5316\u3055\u308C\u3066\u3044\u307E\u305B\u3093 -PlatformUtil.getPID.gen.msg=PID\u3092\u5165\u624B\u3067\u304D\u307E\u305B\u3093\u3001{0} -PlatformUtil.getJavaPID.sigarNotInit.msg=JAVA\u30D7\u30ED\u30BB\u30B9\u306EPID\u304C\u5165\u624B\u3067\u304D\u307E\u305B\u3093\u3001Sigar\u304C\u521D\u671F\u5316\u3055\u308C\u3066\u3044\u307E\u305B\u3093 -PlatformUtil.getJavaPID.gen.msg=\u30AF\u30A8\u30EA\u30FC\u306EPID\u304C\u5165\u624B\u3067\u304D\u307E\u305B\u3093\uFF1A{0}, {1} -PlatformUtil.getJavaPIDs.sigarNotInit=JAVA\u30D7\u30ED\u30BB\u30B9\u306EPID\u304C\u5165\u624B\u3067\u304D\u307E\u305B\u3093\u3001Sigar\u304C\u521D\u671F\u5316\u3055\u308C\u3066\u3044\u307E\u305B\u3093 -PlatformUtil.getJavaPIDs.gen.msg=\u30AF\u30A8\u30EA\u30FC\u306EPID\u304C\u5165\u624B\u3067\u304D\u307E\u305B\u3093\uFF1A{0}, {1} -PlatformUtil.killProcess.sigarNotInit.msg=PID\u3092\u4F7F\u7528\u3057\u3066\u30D7\u30ED\u30BB\u30B9\u3092\u5F37\u5236\u7D42\u4E86\u3067\u304D\u307E\u305B\u3093\u3001Sigar\u304C\u521D\u671F\u5316\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002 -PlatformUtil.killProcess.gen.msg=\u30D7\u30ED\u30BB\u30B9\u3092\u5F37\u5236\u7D42\u4E86\u3067\u304D\u307E\u305B\u3093\uFF1A {0}, {1} -PlatformUtil.getProcVmUsed.sigarNotInit.msg=\u4F7F\u7528\u4E2D\u306E\u4EEE\u60F3\u30E1\u30E2\u30EA\u91CF\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3001Sigar\u304C\u521D\u671F\u5316\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002 -PlatformUtil.getProcVmUsed.gen.msg=\u4F7F\u7528\u4E2D\u306E\u4EEE\u60F3\u30E1\u30E2\u30EA\u91CF\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3001{0} -PlatformUtil.getJvmMemInfo.usageText=JVM\u30D2\u30FC\u30D7\u4F7F\u7528\u72B6\u6CC1\uFF1A{0}\u3001JVM\u975E\u30D2\u30FC\u30D7\u4F7F\u7528\u72B6\u6CC1\uFF1A{1} -PlatformUtil.getPhysicalMemInfo.usageText=\u7269\u7406\u30E1\u30E2\u30EA\u4F7F\u7528\u72B6\u6CC1\uFF08\u6700\u5927\u3001\u5408\u8A08\u3001\u5229\u7528\u53EF\u80FD\uFF09\uFF1A {0}, {1}, {2} +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +GetOrGenerateThumbnailTask.generatingPreviewFor={0} \u306e\u30d7\u30ec\u30d3\u30e5\u30fc\u3092\u751f\u6210\u4e2d\u3067\u3059 +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +GetOrGenerateThumbnailTask.loadingThumbnailFor={0} \u306e\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u3067\u3059 +ImageUtils.ffmpegLoadedError.msg=OpenCV FFMpeg\u30e9\u30a4\u30d6\u30e9\u30ea\u30fc\u304c\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u8a73\u7d30\u306f\u30ed\u30b0\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044 +ImageUtils.ffmpegLoadedError.title=OpenCV FFMpeg +OpenIDE-Module-Name=CoreUtils +JLNK.noPrefPath.text=\u63a8\u5968\u30d1\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f +PlatformUtil.nameUnknown=\u4e0d\u660e +PlatformUtil.verUnknown=\u4e0d\u660e +PlatformUtil.archUnknown=\u4e0d\u660e +PlatformUtil.jrePath.jreDir.msg=\u57cb\u3081\u8fbc\u307fjre\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u304c\u691c\u51fa\u3055\u308c\u305f\u5834\u6240: {0} +PlatformUtil.jrePath.usingJavaPath.msg=\u6b21\u306ejava\u30d0\u30a4\u30ca\u30ea\u30fc\u30d1\u30b9\u3092\u4f7f\u7528\u4e2d\u3067\u3059: {0} +PlatformUtil.getPID.sigarNotInit.msg=PID\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002Sigar\u304c\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +PlatformUtil.getPID.gen.msg=PID,{0} \u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093 +PlatformUtil.getJavaPID.sigarNotInit.msg=java\u30d7\u30ed\u30bb\u30b9\u306ePID\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002Sigar\u304c\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +PlatformUtil.getJavaPID.gen.msg=\u6b21\u306e\u30af\u30a8\u30ea\u306ePID\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093: {0}\u3001{1} +PlatformUtil.getJavaPIDs.sigarNotInit=java\u30d7\u30ed\u30bb\u30b9\u306ePID\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002Sigar\u304c\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +PlatformUtil.getJavaPIDs.gen.msg=\u6b21\u306e\u30af\u30a8\u30ea\u306ePID\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093: {0}\u3001{1} +PlatformUtil.killProcess.sigarNotInit.msg=PID\u306e\u30d7\u30ed\u30bb\u30b9\u3092\u5f37\u5236\u7d42\u4e86\u3067\u304d\u307e\u305b\u3093\u3002Sigar\u304c\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +PlatformUtil.killProcess.gen.msg=\u6b21\u306e\u30d7\u30ed\u30bb\u30b9\u3092\u5f37\u5236\u7d42\u4e86\u3067\u304d\u307e\u305b\u3093: {0}\u3001{1} +PlatformUtil.getProcVmUsed.sigarNotInit.msg=\u4f7f\u7528\u3057\u3066\u3044\u308b\u4eee\u60f3\u30e1\u30e2\u30ea\u30fc\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002Sigar\u304c\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +PlatformUtil.getProcVmUsed.gen.msg=\u4f7f\u7528\u3057\u3066\u3044\u308b\u4eee\u60f3\u30e1\u30e2\u30ea\u30fc\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3001{0} +PlatformUtil.getJvmMemInfo.usageText=JVM \u30d2\u30fc\u30d7\u4f7f\u7528\u7387: {0}\u3001\u4f7f\u7528\u3055\u308c\u3066\u3044\u306a\u3044JVM\u30d2\u30fc\u30d7\u9818\u57df: {1} +PlatformUtil.getPhysicalMemInfo.usageText=\u7269\u7406\u30e1\u30e2\u30ea\u30fc\u4f7f\u7528\u7387\uff08\u6700\u5927\u3001\u5408\u8a08\u3001\u7a7a\u304d\uff09: {0}\u3001{1}\u3001{2} PlatformUtil.getAllMemUsageInfo.usageText={0}\n\ {1}\n\ -\u30D7\u30ED\u30BB\u30B9\u4EEE\u60F3\u30E1\u30E2\u30EA\uFF1A{2} -StringExtract.illegalStateException.cannotInit.msg=\u30E6\u30CB\u30B3\u30FC\u30C9\u30C6\u30FC\u30D6\u30EB\u304C\u6B63\u3057\u304F\u521D\u671F\u5316\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3001StringExtract\u3092\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u5316\u3067\u304D\u307E\u305B\u3093 -ImageUtils.GetOrGenerateThumbnailTask.loadingThumbnailFor={0}\u306E\u30B5\u30E0\u30CD\u30FC\u30EB\u3092\u8AAD\u307F\u8FBC\u307F\u4E2D -ImageUtils.GetOrGenerateThumbnailTask.generatingPreviewFor={0}\u306E\u30D7\u30EC\u30D3\u30E5\u30FC\u3092\u4F5C\u6210\u4E2D -ImageUtils.ReadImageTask.mesage.text={0}\u3092\u8AAD\u307F\u53D6\u308A\u4E2D -VideoUtils.genVideoThumb.progress.text=\u4E00\u6642\u30D5\u30A1\u30A4\u30EB{0}\u3092\u62BD\u51FA\u4E2D \ No newline at end of file +\u6b21\u306e\u4eee\u60f3\u30e1\u30e2\u30ea\u30fc\u3092\u51e6\u7406: {2} +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +ReadImageTask.mesageText=\u6b21\u306e\u30a4\u30e1\u30fc\u30b8\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u3067\u3059: {0} +StringExtract.illegalStateException.cannotInit.msg=Unicode\u30c6\u30fc\u30d6\u30eb\u304c\u6b63\u3057\u304f\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002StringExtract\u3092\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5316\u3067\u304d\u307e\u305b\u3093 +TextConverter.convert.exception.txt=\u30c6\u30ad\u30b9\u30c8 {0} \u309216\u9032\u6570\u30c6\u30ad\u30b9\u30c8\u306b\u5909\u63db\u3067\u304d\u307e\u305b\u3093 +TextConverter.convertFromHex.exception.txt=16\u9032\u6570\u30c6\u30ad\u30b9\u30c8\u3092\u30c6\u30ad\u30b9\u30c8\u306b\u5909\u63db\u3067\u304d\u307e\u305b\u3093 +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +VideoUtils.genVideoThumb.progress.text=\u4e00\u6642\u30d5\u30a1\u30a4\u30eb {0} \u3092\u62bd\u51fa\u4e2d\u3067\u3059 diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/JLNK.java b/Core/src/org/sleuthkit/autopsy/coreutils/JLNK.java index 073b9c3107..5e8c680b9f 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/JLNK.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/JLNK.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012 Basis Technology Corp. + * Copyright 2012-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.coreutils; import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.openide.util.NbBundle; @@ -35,43 +36,43 @@ import org.sleuthkit.autopsy.coreutils.LnkEnums.NetworkProviderType; */ public class JLNK { - private int header; - private byte[] linkClassIdentifier; - private List linkFlags; - private List fileAttributesFlags; - private long crtime; - private long atime; - private long mtime; - private int fileSize; - private int iconIndex; - private int showCommand; - private short hotKey; + private final int header; + private final byte[] linkClassIdentifier; + private final List linkFlags; + private final List fileAttributesFlags; + private final long crtime; + private final long atime; + private final long mtime; + private final int fileSize; + private final int iconIndex; + private final int showCommand; + private final short hotKey; - private List linkTargetIdList; + private final List linkTargetIdList; - private boolean hasUnicodeLocalBaseAndCommonSuffixOffset; - private String localBasePath; - private String commonPathSuffix; - private String localBasePathUnicode; - private String commonPathSuffixUnicode; + private final boolean hasUnicodeLocalBaseAndCommonSuffixOffset; + private final String localBasePath; + private final String commonPathSuffix; + private final String localBasePathUnicode; + private final String commonPathSuffixUnicode; - private String name; - private String relativePath; - private String workingDir; - private String arguments; - private String iconLocation; + private final String name; + private final String relativePath; + private final String workingDir; + private final String arguments; + private final String iconLocation; - private int driveSerialNumber; - private DriveType driveType; - private String volumeLabel; + private final int driveSerialNumber; + private final DriveType driveType; + private final String volumeLabel; - private List commonNetworkRelativeListFlags; - private NetworkProviderType networkProviderType; - private boolean unicodeNetAndDeviceName; - private String netName; - private String netNameUnicode; - private String deviceName; - private String deviceNameUnicode; + private final List commonNetworkRelativeListFlags; + private final NetworkProviderType networkProviderType; + private final boolean unicodeNetAndDeviceName; + private final String netName; + private final String netNameUnicode; + private final String deviceName; + private final String deviceNameUnicode; public JLNK(int header, byte[] linkClassIdentifier, int linkFlags, int fileAttributesFlags, long crtime, long atime, @@ -87,14 +88,14 @@ public class JLNK { String netName, String netNameUnicode, String deviceName, String deviceNameUnicode) { this.header = header; - this.linkClassIdentifier = linkClassIdentifier; - this.linkFlags = new ArrayList(); + this.linkClassIdentifier = linkClassIdentifier.clone(); + this.linkFlags = new ArrayList<>(); for (LnkEnums.LinkFlags enumVal : LnkEnums.LinkFlags.values()) { if ((linkFlags & enumVal.getFlag()) == enumVal.getFlag()) { this.linkFlags.add(enumVal); } } - this.fileAttributesFlags = new ArrayList(); + this.fileAttributesFlags = new ArrayList<>(); for (LnkEnums.FileAttributesFlags enumVal : LnkEnums.FileAttributesFlags.values()) { if ((fileAttributesFlags & enumVal.getFlag()) == enumVal.getFlag()) { this.fileAttributesFlags.add(enumVal); @@ -121,7 +122,7 @@ public class JLNK { this.driveSerialNumber = driveSerialNumber; this.driveType = driveType; this.volumeLabel = volumeLabel; - this.commonNetworkRelativeListFlags = new ArrayList(); + this.commonNetworkRelativeListFlags = new ArrayList<>(); for (LnkEnums.CommonNetworkRelativeLinkFlags enumVal : LnkEnums.CommonNetworkRelativeLinkFlags.values()) { if ((commonNetworkRelativeListFlags & enumVal.getFlag()) == enumVal.getFlag()) { this.commonNetworkRelativeListFlags.add(enumVal); @@ -140,7 +141,7 @@ public class JLNK { } public List getCommonNetworkRelativeListFlags() { - return commonNetworkRelativeListFlags; + return Collections.unmodifiableList(commonNetworkRelativeListFlags); } public String getCommonPathSuffix() { @@ -176,7 +177,7 @@ public class JLNK { } public List getFileAttributesFlags() { - return fileAttributesFlags; + return Collections.unmodifiableList(fileAttributesFlags); } public int getFileSize() { @@ -196,7 +197,7 @@ public class JLNK { } public List getLinkTargetIdList() { - return linkTargetIdList; + return Collections.unmodifiableList(linkTargetIdList); } public int getIconIndex() { @@ -208,11 +209,11 @@ public class JLNK { } public byte[] getLinkClassIdentifier() { - return linkClassIdentifier; + return linkClassIdentifier.clone(); } public List getLinkFlags() { - return linkFlags; + return Collections.unmodifiableList(linkFlags); } public String getLocalBasePath() { diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/LnkEnums.java b/Core/src/org/sleuthkit/autopsy/coreutils/LnkEnums.java index dcad3b318c..c154f5ac8a 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/LnkEnums.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/LnkEnums.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012 Basis Technology Corp. + * Copyright 2012-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,6 +31,10 @@ class LnkEnums { private static final byte[] IEFRAME = new byte[]{(byte) 0x80, 0x53, 0x1c, (byte) 0x87, (byte) 0xa0, 0x42, 0x69, 0x10, (byte) 0xa2, (byte) 0xea, 0x08, 0x00, 0x2b, 0x30, 0x30, (byte) 0x9d}; + private LnkEnums() { + //private constructor for utility class + } + public enum CommonCLSIDS { CDrivesFolder(CDRIVES), @@ -38,20 +42,24 @@ class LnkEnums { IEFrameDLL(IEFRAME), Unknown(new byte[16]); - private byte[] flag; + private final byte[] flag; private CommonCLSIDS(byte[] flag) { - this.flag = flag; + this.flag = flag.clone(); } static CommonCLSIDS valueOf(byte[] type) { for (CommonCLSIDS value : CommonCLSIDS.values()) { - if (java.util.Arrays.equals(value.flag, type)) { + if (java.util.Arrays.equals(value.getFlag(), type)) { return value; } } return Unknown; } + + byte[] getFlag() { + return flag.clone(); + } } public enum LinkFlags { @@ -84,7 +92,7 @@ class LnkEnums { PreferEnvironmentPath(0x02000000), KeepLocalIDListForUNCTarget(0x04000000); - private int flag; + private final int flag; private LinkFlags(int flag) { this.flag = flag; @@ -105,7 +113,7 @@ class LnkEnums { DRIVE_CDROM(0x00000005), DRIVE_RAMDISK(0x00000006); - private int flag; + private final int flag; private DriveType(int flag) { this.flag = flag; @@ -117,7 +125,7 @@ class LnkEnums { static DriveType valueOf(int type) { for (DriveType value : DriveType.values()) { - if (value.flag == type) { + if (value.getFlag() == type) { return value; } } @@ -143,7 +151,7 @@ class LnkEnums { NOT_CONTENT_INDEXED(0x00002000), ENCRYPTED(0x00004000); - private int flag; + private final int flag; private FileAttributesFlags(int flag) { this.flag = flag; @@ -159,7 +167,7 @@ class LnkEnums { VolumeIDAndLocalBasePath(0x00000001), CommonNetworkRelativeLinkAndPathSuffix(0x00000002); - private int flag; + private final int flag; private LinkInfoFlags(int flag) { this.flag = flag; @@ -175,7 +183,7 @@ class LnkEnums { ValidDevice(0x00000001), ValidNetType(0x00000002); - private int flag; + private final int flag; private CommonNetworkRelativeLinkFlags(int flag) { this.flag = flag; @@ -231,7 +239,7 @@ class LnkEnums { WNNC_NET_GOOGLE(0x00430000), WNNC_NET_UNKNOWN(0x00000000); - private int flag; + private final int flag; private NetworkProviderType(int flag) { this.flag = flag; @@ -239,7 +247,7 @@ class LnkEnums { static NetworkProviderType valueOf(int type) { for (NetworkProviderType value : NetworkProviderType.values()) { - if (value.flag == type) { + if (value.getFlag() == type) { return value; } } @@ -250,4 +258,5 @@ class LnkEnums { return flag; } } + } diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/PhoneNumUtil.java b/Core/src/org/sleuthkit/autopsy/coreutils/PhoneNumUtil.java new file mode 100644 index 0000000000..1fa56d1bfd --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/PhoneNumUtil.java @@ -0,0 +1,109 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.coreutils; + +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber; +import java.util.logging.Level; + +/** + * + * Class to format and get information from a phone number + */ +public final class PhoneNumUtil { + + private static final Logger logger = Logger.getLogger(PhoneNumUtil.class.getName()); + + private PhoneNumUtil() { + } + + /** + * Get the country code from a phone number + * + * @param phoneNumber + * @return country code if can determine otherwise it will return "" + */ + public static String getCountryCode(String phoneNumber) { + String regionCode = null; + try { + PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + Phonenumber.PhoneNumber phoneNum = phoneNumberUtil.parse(phoneNumber, ""); + regionCode = phoneNumberUtil.getRegionCodeForNumber(phoneNum); + + if (regionCode == null) { + return ""; + } else { + return regionCode; + } + } catch (NumberParseException ex) { + logger.log(Level.WARNING, "Error getting country code, for phone number: {0}", phoneNumber); + return ""; + } + } + + /** + * Convert a phone number to the E164 format + * + * @param phoneNumber + * + * @return formated phone number if successful or original phone number if + * unsuccessful + */ + public static String convertToE164(String phoneNumber) { + PhoneNumberUtil phone_util = PhoneNumberUtil.getInstance(); + try { + Phonenumber.PhoneNumber phoneProto = phone_util.parse(phoneNumber, getCountryCode(phoneNumber)); + if (phone_util.isValidNumber(phoneProto)) { + return phone_util.format(phoneProto, PhoneNumberUtil.PhoneNumberFormat.E164); + } else { + logger.log(Level.WARNING, "Invalid phone number: {0}", phoneNumber); + return phoneNumber; + } + } catch (NumberParseException e) { + logger.log(Level.WARNING, "Error parsing phone number: {0}", phoneNumber); + return phoneNumber; + } + } + + /** + * Convert a phone number to the International format + * + * @param phoneNumber + * + * @return formated phone number if successful or original phone number if + * unsuccessful + */ + public static String convertToInternational(String phoneNumber) { + PhoneNumberUtil phone_util = PhoneNumberUtil.getInstance(); + try { + Phonenumber.PhoneNumber phoneProto = phone_util.parse(phoneNumber, getCountryCode(phoneNumber)); + if (phone_util.isValidNumber(phoneProto)) { + return phone_util.format(phoneProto, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL); + } else { + logger.log(Level.WARNING, "Invalid phone number: {0}", phoneNumber); + return phoneNumber; + } + } catch (NumberParseException e) { + logger.log(Level.WARNING, "Error parsing phone number: {0}", phoneNumber); + return phoneNumber; + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ThreadUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ThreadUtils.java index b9873469e7..0671290407 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ThreadUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ThreadUtils.java @@ -44,7 +44,7 @@ final public class ThreadUtils { } catch (InterruptedException ignored) { /* * Ignore interrupts. The policy implemented by this method is - * an unconditional wait.: + * an unconditional wait. */ } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java index 774d1f2f9e..e2950e5b12 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java @@ -122,6 +122,7 @@ public class ArtifactStringContent implements StringContent { case LONG: case DOUBLE: case BYTE: + case JSON: default: value = attr.getDisplayString(); break; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java new file mode 100644 index 0000000000..c095f48fee --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java @@ -0,0 +1,207 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017-2019 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.datamodel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.logging.Level; +import javax.swing.Action; +import org.apache.commons.lang3.StringUtils; +import org.openide.nodes.Children; +import org.openide.nodes.Sheet; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.Utilities; +import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.contentviewers.MessageContentViewer; +import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; +import org.sleuthkit.autopsy.coreutils.Logger; +import static org.sleuthkit.autopsy.datamodel.FileNode.getIconForFileType; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; +import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; +import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction; +import org.sleuthkit.autopsy.directorytree.ExtractAction; +import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; +import org.sleuthkit.autopsy.directorytree.ViewContextAction; +import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.datamodel.blackboardutils.Attachment; +import org.sleuthkit.datamodel.blackboardutils.FileAttachment; +import org.sleuthkit.datamodel.blackboardutils.URLAttachment; + +/** + * Node for a message attachment. + * + */ +public final class AttachmentNode extends DisplayableItemNode { + + private static final Logger LOGGER = Logger.getLogger(MessageContentViewer.class.getName()); + + private final Attachment attachment; + private final AbstractFile attachmentFile; + + public AttachmentNode(Attachment attachment) { + + super(Children.LEAF, createLookup(attachment)); + + super.setName(attachment.getLocation()); + super.setDisplayName(attachment.getLocation()); // SET NODE DISPLAY NAME, I.E., TEXT IN FIRST TABLE CELL + + this.attachment = attachment; + Long attachmentObjId = attachment.getObjId(); + AbstractFile attchmentAbstractFile = null; + + if (attachmentObjId != null && attachmentObjId > 0) { + try { + attchmentAbstractFile = Case.getCurrentCaseThrows().getSleuthkitCase().getAbstractFileById(attachmentObjId); + } catch (TskException | NoCurrentCaseException ex) { + LOGGER.log(Level.WARNING, "Error loading attachment file with object id " + attachmentObjId, ex); //NON-NLS + } + } + attachmentFile = attchmentAbstractFile; + + // set the icon for node + setIcon(); + } + + @Override + @NbBundle.Messages({ + "AttachmentNode.getActions.viewFileInDir.text=View File in Directory", + "AttachmentNode.getActions.viewInNewWin.text=View in New Window", + "AttachmentNode.getActions.openInExtViewer.text=Open in External Viewer Ctrl+E", + "AttachmentNode.getActions.searchFilesSameMD5.text=Search for files with the same MD5 hash"}) + public Action[] getActions(boolean context) { + + List actionsList = new ArrayList<>(); + actionsList.addAll(Arrays.asList(super.getActions(true))); + + // If there is an attachment file + if (this.attachmentFile != null) { + actionsList.add(new ViewContextAction(Bundle.AttachmentNode_getActions_viewFileInDir_text(), this.attachmentFile)); + actionsList.add(null); // Creates an item separator + + actionsList.add(new NewWindowViewAction(Bundle.AttachmentNode_getActions_viewInNewWin_text(), this)); + final Collection selectedFilesList + = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if (selectedFilesList.size() == 1) { + actionsList.add(new ExternalViewerAction( + Bundle.AttachmentNode_getActions_openInExtViewer_text(), this)); + } else { + actionsList.add(ExternalViewerShortcutAction.getInstance()); + } + actionsList.add(ViewFileInTimelineAction.createViewFileAction(this.attachmentFile)); + actionsList.add(null); // Creates an item separator + + actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); + actionsList.add(null); // Creates an item separator + + actionsList.add(AddContentTagAction.getInstance()); + if (1 == selectedFilesList.size()) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + + } + return actionsList.toArray(new Action[0]); + } + + @Override + protected Sheet createSheet() { + + // Create a new property sheet. + Sheet sheet = new Sheet(); + Sheet.Set sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + + sheetSet.put(new NodeProperty<>("Location", "Location", "", this.attachment.getLocation())); + + if (attachmentFile != null) { + long size = attachmentFile.getSize(); + String mimeType = attachmentFile.getMIMEType(); + + // @TODO Vik-5762: get SCO Columns + + sheetSet.put(new NodeProperty<>("Size", "Size", "", size)); + if (StringUtils.isNotEmpty(mimeType)) { + sheetSet.put(new NodeProperty<>("Mime type", "Mime type", "", mimeType)); + } + sheetSet.put(new NodeProperty<>("Known", "Known", "", attachmentFile.getKnown().getName())); + } + + return sheet; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + + @Override + public String getItemType() { + return getClass().getName(); + } + + private static Lookup createLookup(Attachment attachment) { + Long attachmentObjId = attachment.getObjId(); + if (attachmentObjId != null && attachmentObjId > 0) { + AbstractFile attachmentFile = null; + try { + attachmentFile = Case.getCurrentCaseThrows().getSleuthkitCase().getAbstractFileById(attachmentObjId); + if (attachmentFile != null) { + return Lookups.fixed(attachment, attachmentFile); + } else { + return Lookups.fixed(attachment); + } + } catch (TskException | NoCurrentCaseException ex) { + return Lookups.fixed(attachment); + } + } + return Lookups.fixed(attachment); + } + + /** + * Set the icon based on attachment type + */ + private void setIcon() { + if (attachmentFile != null) { + this.setIconBaseWithExtension(getIconForFileType(attachmentFile)); + } else if (attachment instanceof FileAttachment) { + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/document-question-16.png"); + } else if (attachment instanceof URLAttachment) { + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/url-16.png"); + } else { + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon-deleted.png"); + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index 7f0a4d2419..ae4211741a 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -43,6 +43,10 @@ ArtifactStringContent.attrsTableHeader.type=Type ArtifactStringContent.attrsTableHeader.value=Value ArtifactStringContent.failedToGetAttributes.message=Failed to get some or all attributes from case database ArtifactStringContent.failedToGetSourcePath.message=Failed to get source file path from case database +AttachmentNode.getActions.openInExtViewer.text=Open in External Viewer Ctrl+E +AttachmentNode.getActions.searchFilesSameMD5.text=Search for files with the same MD5 hash +AttachmentNode.getActions.viewFileInDir.text=View File in Directory +AttachmentNode.getActions.viewInNewWin.text=View in New Window # {0} - node name BaseChildFactory.NoSuchEventBusException.message=No event bus for node: {0} BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details @@ -297,6 +301,15 @@ OpenReportAction.actionPerformed.NoAssociatedEditorMessage=There is no associate OpenReportAction.actionPerformed.NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way. OpenReportAction.actionPerformed.MissingReportFileMessage=The report file no longer exists. OpenReportAction.actionPerformed.ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied. +PoolNode.createSheet.name.desc=no description +PoolNode.createSheet.name.displayName=Name +PoolNode.createSheet.name.name=Name +PoolNode.createSheet.offset.desc=no description +PoolNode.createSheet.offset.displayName=Starting offset +PoolNode.createSheet.offset.name=Starting offset +PoolNode.createSheet.type.desc=no description +PoolNode.createSheet.type.displayName=Type +PoolNode.createSheet.type.name=Type RecentFiles.aut0DayFilter.displayName.text=Final Day RecentFiles.aut1dayFilter.displayName.text=Final Day - 1 RecentFiles.aut2dayFilter.displayName.text=Final Day - 2 diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties index 2f91dfb27a..ee9b6fb4b6 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties @@ -1,26 +1,179 @@ -OpenIDE-Module-Name=\u30c7\u30fc\u30bf\u30e2\u30c7\u30eb -AbstractAbstractFileNode.nameColLbl=\u540d\u524d -AbstractAbstractFileNode.locationColLbl=\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3 -AbstractAbstractFileNode.modifiedTimeColLbl=\u4fee\u6b63\u65e5\u6642 -AbstractAbstractFileNode.changeTimeColLbl=\u5909\u66f4\u65e5\u6642 -AbstractAbstractFileNode.accessTimeColLbl=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 -AbstractAbstractFileNode.createdTimeColLbl=\u4f5c\u6210\u65e5\u6642 -AbstractAbstractFileNode.sizeColLbl=\u30b5\u30a4\u30ba -AbstractAbstractFileNode.modeColLbl=\u30e2\u30fc\u30c9 -AbstractAbstractFileNode.useridColLbl=\u30e6\u30fc\u30b6ID +AbstractAbstractFileNode.accessTimeColLbl=\u30a2\u30af\u30bb\u30b9\u6642\u523b +AbstractAbstractFileNode.attrAddrColLbl=\u5c5e\u6027\u30a2\u30c9\u30ec\u30b9 +AbstractAbstractFileNode.changeTimeColLbl=\u6642\u523b\u5909\u66f4 +AbstractAbstractFileNode.createdTimeColLbl=\u4f5c\u6210\u3057\u305f\u6642\u523b +AbstractAbstractFileNode.createSheet.comment.displayName=C +AbstractAbstractFileNode.createSheet.comment.name=C +# {0} - occurrenceCount +AbstractAbstractFileNode.createSheet.count.description=MD5\u76f8\u95a2\u5024\u304c\u767a\u751f\u3057\u305f {0} \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f +AbstractAbstractFileNode.createSheet.count.displayName=O +AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=\u5217\u304c\u5165\u529b\u3055\u308c\u305f\u3068\u304d\u306b\u3001\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u4e0a\u3067\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u304c\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f +AbstractAbstractFileNode.createSheet.count.name=O +AbstractAbstractFileNode.createSheet.interestingResult.description=\u30d5\u30a1\u30a4\u30eb\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u8208\u5473\u6df1\u3044\u7d50\u679c\u304c\u3042\u308a\u307e\u3059\u3002 +AbstractAbstractFileNode.createSheet.noScore.description=\u30b9\u30b3\u30a2\u306a\u3057 +AbstractAbstractFileNode.createSheet.notableFile.description=\u9855\u8457\u3068\u8a8d\u8b58\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3067\u3059\u3002 +AbstractAbstractFileNode.createSheet.notableTaggedFile.description=\u9855\u8457\u30bf\u30b0\u3067\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3067\u3059\u3002 +AbstractAbstractFileNode.createSheet.score.displayName=S +AbstractAbstractFileNode.createSheet.score.name=S +AbstractAbstractFileNode.createSheet.taggedFile.description=\u30d5\u30a1\u30a4\u30eb\u304c\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u307e\u3057\u305f\u3002 +AbstractAbstractFileNode.extensionColLbl=\u62e1\u5f35\u5b50 +AbstractAbstractFileNode.flagsDirColLbl=\u30d5\u30e9\u30b0(\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc) +AbstractAbstractFileNode.flagsMetaColLbl=\u30d5\u30e9\u30b0(\u30e1\u30bf) AbstractAbstractFileNode.groupidColLbl=\u30b0\u30eb\u30fc\u30d7ID AbstractAbstractFileNode.knownColLbl=\u65e2\u77e5 +AbstractAbstractFileNode.locationColLbl=\u5834\u6240 AbstractAbstractFileNode.md5HashColLbl=MD5\u30cf\u30c3\u30b7\u30e5 -AbstractContentChildren.CreateTSKNodeVisitor.exception.noNodeMsg=\u6307\u5b9a\u3055\u308c\u305fSleuthkitItem\u306e\u30ce\u30fc\u30c9\u304c\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u307e\u305b\u3093 -AbstractContentChildren.createAutopsyNodeVisitor.exception.noNodeMsg=\u6307\u5b9a\u3055\u308c\u305f\u8868\u793a\u53ef\u80fd\u306a\u30a2\u30a4\u30c6\u30e0(DisplayableItem)\u306e\u30ce\u30fc\u30c9\u304c\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +AbstractAbstractFileNode.metaAddrColLbl=\u30e1\u30bf\u30a2\u30c9\u30ec\u30b9 +AbstractAbstractFileNode.mimeType=MIME\u30bf\u30a4\u30d7 +AbstractAbstractFileNode.modeColLbl=\u30e2\u30fc\u30c9 +AbstractAbstractFileNode.modifiedTimeColLbl=MFT\u5909\u66f4\u6642\u523b +AbstractAbstractFileNode.nameColLbl=\u540d\u524d +AbstractAbstractFileNode.objectId=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8ID +AbstractAbstractFileNode.originalName=\u30aa\u30ea\u30b8\u30ca\u30eb\u540d +AbstractAbstractFileNode.sizeColLbl=\u30b5\u30a4\u30ba +AbstractAbstractFileNode.tagsProperty.displayName=\u30bf\u30b0 +AbstractAbstractFileNode.typeDirColLbl=\u30bf\u30a4\u30d7(\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc) +AbstractAbstractFileNode.typeMetaColLbl=\u30bf\u30a4\u30d7(\u30e1\u30bf) +AbstractAbstractFileNode.useridColLbl=\u30e6\u30fc\u30b6\u30fcID +AbstractContentNode.nodescription=\u8aac\u660e\u306a\u3057 +AbstractContentNode.valueLoading=\u5024\u306e\u8aad\u307f\u8fbc\u307f\u4e2d +AbstractFsContentNode.noDesc.text=\u8aac\u660e\u306a\u3057 +ArtifactStringContent.attrsTableHeader.sources=\u30bd\u30fc\u30b9 +ArtifactStringContent.attrsTableHeader.type=\u30bf\u30a4\u30d7 +ArtifactStringContent.attrsTableHeader.value=\u5024 +ArtifactStringContent.failedToGetAttributes.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u4e00\u90e8\u307e\u305f\u306f\u3059\u3079\u3066\u306e\u5c5e\u6027\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +ArtifactStringContent.failedToGetSourcePath.messagee=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +# {0} - \u30e2\u30fc\u30c9\u540d +BaseChildFactory.NoSuchEventBusException.message=\u6b21\u306e\u30ce\u30fc\u30c9\u306e\u30a4\u30d9\u30f3\u30c8\u30d0\u30b9\u306f\u3042\u308a\u307e\u305b\u3093: {0} +BlackboardArtifactNode.createSheet.artifactDetails.displayName=\u7d50\u679c\u8a73\u7d30 +BlackboardArtifactNode.createSheet.artifactDetails.name=\u7d50\u679c\u8a73\u7d30 +BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5\u30cf\u30c3\u30b7\u30e5 +BlackboardArtifactNode.createSheet.artifactMD5.name=MD5\u30cf\u30c3\u30b7\u30e5 +BlackboardArtifactNode.createSheet.artifactType.displayName=\u7d50\u679c\u30bf\u30a4\u30d7 +BlackboardArtifactNode.createSheet.artifactType.name=\u7d50\u679c\u30bf\u30a4\u30d7 +BlackboardArtifactNode.createSheet.comment.displayName=C +BlackboardArtifactNode.createSheet.comment.name=C +# {0} - occurrenceCount +# {1} - attributeType +BlackboardArtifactNode.createSheet.count.description=\u30bf\u30a4\u30d7 {1} \u306e\u76f8\u95a2\u5024\u304c\u767a\u751f\u3057\u305f {0} \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f +BlackboardArtifactNode.createSheet.count.displayName=O +BlackboardArtifactNode.createSheet.count.name=O +BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=\u76f8\u95a2\u30d7\u30ed\u30d1\u30c6\u30a3\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f +BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=\u5229\u7528\u53ef\u80fd\u306a\u76f8\u95a2\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u5024\u304c\u5b58\u5728\u3057\u306a\u3044\u305f\u3081\u3001\u305d\u306e\u4ed6\u306e\u767a\u751f\u3092\u691c\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +BlackboardArtifactNode.createSheet.fileSize.displayName=\u30b5\u30a4\u30ba +BlackboardArtifactNode.createSheet.fileSize.name=\u30b5\u30a4\u30ba +BlackboardArtifactNode.createSheet.interestingResult.description=\u7d50\u679c\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u8208\u5473\u6df1\u3044\u7d50\u679c\u304c\u3042\u308a\u307e\u3059\u3002 +BlackboardArtifactNode.createSheet.noScore.description=\u30b9\u30b3\u30a2\u306a\u3057 +BlackboardArtifactNode.createSheet.notableTaggedItem.description=\u9855\u8457\u3068\u8a8d\u8b58\u3055\u308c\u305f\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30d5\u30a1\u30a4\u30eb\u3067\u3059\u3002 +BlackboardArtifactNode.createSheet.notableTaggedItem.description=\u7d50\u679c\u307e\u305f\u306f\u9855\u8457\u3068\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30d5\u30a1\u30a4\u30eb\u3067\u3059\u3002 +BlackboardArtifactNode.createSheet.path.displayName=\u30d1\u30b9 +BlackboardArtifactNode.createSheet.path.name=\u30d1\u30b9 +BlackboardArtifactNode.createSheet.score.displayName=S +BlackboardArtifactNode.createSheet.score.name=S +BlackboardArtifactNode.createSheet.taggedItem.description=\u7d50\u679c\u307e\u305f\u306f\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30d5\u30a1\u30a4\u30eb\u304c\u30bf\u30b0\u4ed8\u3051\u307e\u3057\u305f\u3002 +BlackboardArtifactNode.createSheet.tags.displayName=\u30bf\u30b0 +# {0} - artifactDisplayName +BlackboardArtifactNode.displayName.artifact={0} \u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8 +BlackboardArtifactNode.getAction.errorTitle=\u30a2\u30af\u30b7\u30e7\u30f3\u306e\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +BlackboardArtifactNode.getAction.linkedFileMessage=\u9078\u629e\u3057\u305f\u7d50\u679c\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u53d6\u5f97\u4e2d\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3057\u305f\u3002 [\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a] \u30a2\u30af\u30b7\u30e7\u30f3\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +BlackboardArtifactNode.getAction.resultErrorMessage=\u9078\u629e\u3057\u305f\u7d50\u679c\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u53d6\u5f97\u4e2d\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3057\u305f\u3002 [\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306b\u7d50\u679c\u3092\u8868\u793a] \u30a2\u30af\u30b7\u30e7\u30f3\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +BlackboardArtifactTagNode.createSheet.userName.text=\u30e6\u30fc\u30b6\u30fc\u540d +BlackboardArtifactTagNode.viewSourceArtifact.text=\u30bd\u30fc\u30b9\u7d50\u679c\u3092\u8868\u793a +Category.five=CAT-5: \u95a2\u9023\u6027\u306a\u3057 +Category.four=CAT-4: \u4e8b\u4f8b/\u6bd4\u8f03(\u793e\u5185\u4f7f\u7528\u9650\u5b9a) +Category.one=CAT-1: \u5150\u7ae5\u52b4\u50cd(\u9055\u6cd5) +Category.three=CAT-3: CGI/\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3(\u5150\u7ae5\u52b4\u50cd) +Category.two=CAT-2: \u5150\u7ae5\u52b4\u50cd(\u9055\u6cd5\u3067\u306f\u306a\u3044/\u96e3\u3057\u3044\u5e74\u9f62) +Category.zero=CAT-0: \u672a\u5206\u985e +ContentTagNode.createSheet.artifactMD5.displayName=MD5\u30cf\u30c3\u30b7\u30e5 +ContentTagNode.createSheet.artifactMD5.name=MD5\u30cf\u30c3\u30b7\u30e5 +ContentTagNode.createSheet.userName.text=\u30e6\u30fc\u30b6\u30fc\u540d +DeletedContent.allDelFilter.text=\u3059\u3079\u3066 +DeletedContent.createSheet.filterType.desc=\u8aac\u660e\u306a\u3057 +DeletedContent.createSheet.filterType.displayName=\u30bf\u30a4\u30d7 +DeletedContent.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +DeletedContent.createSheet.name.displayName=\u540d\u524d +DeletedContent.deletedContentsNode.name=\u524a\u9664\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb +DeletedContent.fsDelFilter.text=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0 +DeleteReportAction.showConfirmDialog.errorMsg=\u30ec\u30dd\u30fc\u30c8\u306e\u524a\u9664\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +DeleteReportAction.showConfirmDialog.multiple.explanation=\u30c7\u30a3\u30b9\u30af\u4e0a\u306b\u30ec\u30dd\u30fc\u30c8\u304c\u6b8b\u308a\u307e\u3059\u3002 +DeleteReportAction.showConfirmDialog.single.explanation=\u30c7\u30a3\u30b9\u30af\u4e0a\u306b\u30ec\u30dd\u30fc\u30c8\u304c\u6b8b\u308a\u307e\u3059\u3002 +FileNode.getActions.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f Ctrl+E +FileNode.getActions.searchFilesSameMD5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 +FileNode.getActions.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a +FileNode.getActions.viewInNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a +FileTypeExtensionFilters.tskDatabaseFilter.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9 +FileTypes.bgCounting.placeholder=\ (\u30ab\u30a6\u30f3\u30c8\u4e2d...) +FileTypes.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +FileTypes.createSheet.name.displayName=\u540d\u524d +FileTypes.createSheet.name.name=\u540d\u524d +FileTypes.name.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 +FileTypesByMimeType.name.text=MIME\u30bf\u30a4\u30d7\u5225 +FileTypesByMimeTypeNode.createSheet.mediaSubtype.desc=\u8aac\u660e\u306a\u3057 +FileTypesByMimeTypeNode.createSheet.mediaSubtype.displayName=\u30b5\u30d6\u30bf\u30a4\u30d7 +FileTypesByMimeTypeNode.createSheet.mediaSubtype.name=\u30b5\u30d6\u30bf\u30a4\u30d7 +FileTypesByMimeTypeNode.createSheet.mediaType.desc=\u8aac\u660e\u306a\u3057 +FileTypesByMimeTypeNode.createSheet.mediaType.displayName=\u30bf\u30a4\u30d7 +FileTypesByMimeTypeNode.createSheet.mediaType.name=\u30bf\u30a4\u30d7 +GetSCOTask.occurrences.defaultDescription=\u76f8\u95a2\u30d7\u30ed\u30d1\u30c6\u30a3\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f +GetSCOTask.occurrences.multipleProperties=\u3053\u306e\u7d50\u679c\u306b\u8907\u6570\u306e\u7570\u306a\u308b\u76f8\u95a2\u30d7\u30ed\u30d1\u30c6\u30a3\u304c\u5b58\u5728\u3057\u307e\u3059 +ImageNode.action.runIngestMods.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c +ImageNode.createSheet.deviceId.desc=\u30a4\u30e1\u30fc\u30b8\u306e\u30c7\u30d0\u30a4\u30b9ID +ImageNode.createSheet.deviceId.displayName=\u30c7\u30d0\u30a4\u30b9ID +ImageNode.createSheet.deviceId.name=\u30c7\u30d0\u30a4\u30b9ID +ImageNode.createSheet.sectorSize.desc=\u30a4\u30e1\u30fc\u30b8\u306e\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30b9(\u30d0\u30a4\u30c8)\u3067\u3059\u3002 +ImageNode.createSheet.sectorSize.displayName=\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30b9(\u30d0\u30a4\u30c8) +ImageNode.createSheet.sectorSize.name=\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30b9(\u30d0\u30a4\u30c8) +ImageNode.createSheet.size.desc=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u30b5\u30a4\u30ba(\u30d0\u30a4\u30c8)\u3067\u3059\u3002 +ImageNode.createSheet.size.displayName=\u30b5\u30a4\u30ba(\u30d0\u30a4\u30c8) +ImageNode.createSheet.size.name=\u30b5\u30a4\u30ba(\u30d0\u30a4\u30c8) +ImageNode.createSheet.timezone.desc=\u30a4\u30e1\u30fc\u30b8\u306e\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 +ImageNode.createSheet.timezone.displayName=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 +ImageNode.createSheet.timezone.name=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 +ImageNode.createSheet.type.desc=\u30a4\u30e1\u30fc\u30b8\u306e\u30bf\u30a4\u30d7\u3067\u3059\u3002 +ImageNode.createSheet.type.displayName=\u30bf\u30a4\u30d7 +ImageNode.createSheet.type.name=\u30bf\u30a4\u30d7 +ImageNode.createSheet.type.text=\u30a4\u30e1\u30fc\u30b8 +ImageNode.getActions.openFileSearchByAttr.text=\u5c5e\u6027\u5225\u306b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u3092\u958b\u304f +KeyValueNode.menuItemText.viewFileInDir=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a +KeywordHits.createNodeForKey.accessTime.desc=\u30a2\u30af\u30bb\u30b9\u6642\u523b +KeywordHits.createNodeForKey.accessTime.displayName=\u30a2\u30af\u30bb\u30b9\u6642\u523b +KeywordHits.createNodeForKey.accessTime.name=AccessTime +KeywordHits.createNodeForKey.chgTime.desc=\u6642\u523b\u5909\u66f4 +KeywordHits.createNodeForKey.chgTime.displayName=\u5909\u66f4\u6642\u523b +KeywordHits.createNodeForKey.chgTime.name=ChangeTime +KeywordHits.createNodeForKey.modTime.desc=MFT\u5909\u66f4\u6642\u523b +KeywordHits.createNodeForKey.modTime.displayName=MFT\u5909\u66f4\u6642\u523b +KeywordHits.createNodeForKey.modTime.name=ModifiedTime +KeywordHits.createSheet.filesWithHits.desc=\u8aac\u660e\u306a\u3057 +KeywordHits.createSheet.filesWithHits.displayName=\u30d2\u30c3\u30c8\u306e\u3042\u308b\u30d5\u30a1\u30a4\u30eb +KeywordHits.createSheet.filesWithHits.name=\u30d2\u30c3\u30c8\u306e\u3042\u308b\u30d5\u30a1\u30a4\u30eb +KeywordHits.createSheet.listName.desc=\u8aac\u660e\u306a\u3057 +KeywordHits.createSheet.listName.displayName=\u30ea\u30b9\u30c8\u540d +KeywordHits.createSheet.listName.name=\u30ea\u30b9\u30c8\u540d +KeywordHits.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +KeywordHits.createSheet.name.displayName=\u540d\u524d +KeywordHits.createSheet.name.name=\u540d\u524d +KeywordHits.createSheet.numChildren.desc=\u8aac\u660e\u306a\u3057 +KeywordHits.createSheet.numChildren.displayName=\u5b50\u306e\u6570 +KeywordHits.createSheet.numChildren.name=\u5b50\u306e\u6570 +KeywordHits.kwHits.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d2\u30c3\u30c8 +KeywordHits.simpleLiteralSearch.text=\u5358\u72ec\u30ea\u30c6\u30e9\u30eb\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 +KeywordHits.singleRegexSearch.text=\u5358\u72ec\u6b63\u898f\u8868\u73fe\u691c\u7d22 +LayoutFileNode.getActions.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a +OpenIDE-Module-Name=DataModel +AbstractContentChildren.CreateTSKNodeVisitor.exception.noNodeMsg=\u6307\u5b9a\u3057\u305fSleuthkitItem\u306b\u5b9a\u7fa9\u3055\u308c\u305f\u30ce\u30fc\u30c9\u306f\u3042\u308a\u307e\u305b\u3093 +AbstractContentChildren.createAutopsyNodeVisitor.exception.noNodeMsg=\u6307\u5b9a\u3057\u305fDisplayableItem\u306b\u5b9a\u7fa9\u3055\u308c\u305f\u30ce\u30fc\u30c9\u306f\u3042\u308a\u307e\u305b\u3093 AbstractContentNode.exception.cannotChangeSysName.msg=\u30b7\u30b9\u30c6\u30e0\u540d\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093\u3002 -AbstractFsContentNode.noDesc.text=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 ArtifactStringContent.getStr.srcFilePath.text=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 -ArtifactStringContent.getStr.err=\u30b3\u30f3\u30c6\u30f3\u30c4\u53d6\u5f97\u30a8\u30e9\u30fc -ArtifactTypeNode.createSheet.artType.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -ArtifactTypeNode.createSheet.childCnt.name=\u30c1\u30e3\u30a4\u30eb\u30c9\u6570 -ArtifactTypeNode.createSheet.childCnt.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -BlackboardArtifactNode.noDesc.text=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +ArtifactStringContent.getStr.err=\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ArtifactTypeNode.createSheet.artType.name=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30bf\u30a4\u30d7 +ArtifactTypeNode.createSheet.artType.displayNamee=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30bf\u30a4\u30d7 +ArtifactTypeNode.createSheet.artType.desc=\u8aac\u660e\u306a\u3057 +ArtifactTypeNode.createSheet.childCnt.name=\u5b50\u306e\u30ab\u30a6\u30f3\u30c8 +ArtifactTypeNode.createSheet.childCnt.displayName=\u5b50\u306e\u30ab\u30a6\u30f3\u30c8 +ArtifactTypeNode.createSheet.childCnt.desc=\u8aac\u660e\u306a\u3057 +BlackboardArtifactNode.noDesc.text=\u8aac\u660e\u306a\u3057 BlackboardArtifactNode.createSheet.srcFile.name=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb BlackboardArtifactNode.createSheet.srcFile.displayName=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb BlackboardArtifactNode.createSheet.ext.name=\u62e1\u5f35\u5b50 @@ -32,7 +185,7 @@ BlackboardArtifactNode.createSheet.filePath.displayName=\u30d5\u30a1\u30a4\u30eb BlackboardArtifactNode.createSheet.dataSrc.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 BlackboardArtifactNode.createSheet.dataSrc.displayName=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 BlackboardArtifactTagNode.createSheet.srcFile.text=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb -BlackboardArtifactTagNode.createSheet.unavail.text=\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093 +BlackboardArtifactTagNode.createSheet.unavail.text=\u5229\u7528\u3067\u304d\u307e\u305b\u3093 BlackboardArtifactTagNode.createSheet.srcFilePath.text=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 BlackboardArtifactTagNode.createSheet.resultType.text=\u7d50\u679c\u30bf\u30a4\u30d7 BlackboardArtifactTagNode.createSheet.comment.text=\u30b3\u30e1\u30f3\u30c8 @@ -41,154 +194,145 @@ BlackboardArtifactTagTypeNode.createSheet.name.name=\u540d\u524d BlackboardArtifactTagTypeNode.createSheet.name.displayName=\u540d\u524d ContentTagNode.createSheet.file.name=\u30d5\u30a1\u30a4\u30eb ContentTagNode.createSheet.file.displayName=\u30d5\u30a1\u30a4\u30eb -ContentTagNode.createSheet.unavail.path=\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093 +ContentTagNode.createSheet.unavail.path=\u5229\u7528\u3067\u304d\u307e\u305b\u3093 ContentTagNode.createSheet.filePath.name=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 ContentTagNode.createSheet.filePath.displayName=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 ContentTagNode.createSheet.comment.name=\u30b3\u30e1\u30f3\u30c8 ContentTagNode.createSheet.comment.displayName=\u30b3\u30e1\u30f3\u30c8 +ContentTagNode.createSheet.fileModifiedTime.nam=MFT\u5909\u66f4\u6642\u523b +ContentTagNode.createSheet.fileModifiedTime.displayName=MFT\u5909\u66f4\u6642\u523b +ContentTagNode.createSheet.fileChangedTime.name=\u5909\u66f4\u3055\u308c\u305f\u523b\u523b +ContentTagNode.createSheet.fileChangedTime.displayName=\u5909\u66f4\u3055\u308c\u305f\u523b\u523b +ContentTagNode.createSheet.fileAccessedTime.name=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u6642\u523b +ContentTagNode.createSheet.fileAccessedTime.displayName=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u6642\u523b +ContentTagNode.createSheet.fileCreatedTime.name=\u4f5c\u6210\u3057\u305f\u6642\u523b +ContentTagNode.createSheet.fileCreatedTime.displayName=\u4f5c\u6210\u3057\u305f\u6642\u523b +ContentTagNode.createSheet.fileSize.name=\u30b5\u30a4\u30ba +ContentTagNode.createSheet.fileSize.displayName=\u30b5\u30a4\u30ba ContentTagTypeNode.displayName.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30b0 ContentTagTypeNode.createSheet.name.name=\u540d\u524d ContentTagTypeNode.createSheet.name.displayName=\u540d\u524d -ContentUtils.exception.msg={0}\u3092\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093 -DataModelActionsFactory.srcFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a -DataModelActionsFactory.fileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a -DataModelActionsFactory.viewNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a -DataModelActionsFactory.openExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u306b\u8868\u793a +ContentUtils.exception.msg={0} \u3092\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093 +DataModelActionsFactory.srcFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a +DataModelActionsFactory.fileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a +DataModelActionsFactory.viewNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a +DataModelActionsFactory.openExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f Ctrl+E DataSourcesNode.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 DataSourcesNode.group_by_datasource.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb DataSourcesNode.createSheet.name.name=\u540d\u524d DataSourcesNode.createSheet.name.displayName=\u540d\u524d -DataSourcesNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -DeletedContent.fsDelFilter.text=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0 -DeletedContent.allDelFilter.text=\u3059\u3079\u3066 -DeletedContent.deletedContentsNode.name=\u524a\u9664\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb -DeletedContent.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -DeletedContent.createSheet.filterType.name=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7 -DeletedContent.createSheet.filterType.displayName=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7 -DeletedContent.createSheet.filterType.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -DeletedContent.createKeys.maxObjects.msg=\u8868\u793a\u53ef\u80fd\u306a\u6570\u3088\u308a\u3082\u591a\u304f\u306e\u524a\u9664\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u307e\u3059\u3002\u6700\u521d\u306e{0}\u306e\u524a\u9664\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 -DeletedContent.createNodeForKey.typeNotSupported.msg=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u8868\u793a\u53ef\u80fd\u306a\u30a2\u30a4\u30c6\u30e0\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\uff1a{0} -DirectoryNode.parFolder.text=[\u30da\u30a2\u30ec\u30f3\u30c8\u30d5\u30a9\u30eb\u30c0] -DirectoryNode.curFolder.text=[\u73fe\u5728\u306e\u30d5\u30a9\u30eb\u30c0] -DirectoryNode.getActions.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a -DirectoryNode.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a +DataSourcesNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +DirectoryNode.parFolder.text=[\u89aa\u30d5\u30a9\u30eb\u30c0\u30fc] +DirectoryNode.curFolder.text=[\u73fe\u5728\u306e\u30d5\u30a9\u30eb\u30c0\u30fc] +DirectoryNode.getActions.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a +DirectoryNode.viewInNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a EmailExtracted.mailAccount.text=\u30a2\u30ab\u30a6\u30f3\u30c8 -EmailExtracted.mailFolder.text=\u30d5\u30a9\u30eb\u30c0 +EmailExtracted.mailFolder.text=\u30d5\u30a9\u30eb\u30c0\u30fc EmailExtracted.defaultAcct.text=\u30c7\u30d5\u30a9\u30eb\u30c8 EmailExtracted.defaultFolder.text=\u30c7\u30d5\u30a9\u30eb\u30c8 EmailExtracted.createSheet.name.name=\u540d\u524d EmailExtracted.createSheet.name.displayName=\u540d\u524d -EmailExtracted.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +EmailExtracted.createSheet.name.desc=\u8aac\u660e\u306a\u3057 ExtractedContentNode.name.text=\u62bd\u51fa\u3055\u308c\u305f\u30b3\u30f3\u30c6\u30f3\u30c4 ExtractedContentNode.createSheet.name.name=\u540d\u524d ExtractedContentNode.createSheet.name.displayName=\u540d\u524d -ExtractedContentNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -FileNode.getActions.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a -FileNode.getActions.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a -FileNode.getActions.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u3067\u958b\u304f -FileNode.getActions.searchFilesSameMD5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 +ExtractedContentNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +LocalFileNode.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a FileSize.fileSizeRootNode.name=\u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba -FileSize.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -FileSize.createSheet.filterType.name=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7 -FileSize.createSheet.filterType.displayName=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7 -FileSize.createSheet.filterType.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -FileSize.exception.notSupported.msg=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u8868\u793a\u53ef\u80fd\u306a\u30a2\u30a4\u30c6\u30e0\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\uff1a{0} -FileTypeChildren.exception.notSupported.msg=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u8868\u793a\u53ef\u80fd\u306a\u30a2\u30a4\u30c6\u30e0\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\uff1a{0} -FileTypeExtensionFilters.tskImgFilter.text=\u30a4\u30e1\u30fc\u30b8 -FileTypeExtensionFilters.tskVideoFilter.text=\u30d3\u30c7\u30aa -FileTypeExtensionFilters.tskAudioFilter.text=\u30aa\u30fc\u30c7\u30a3\u30aa -FileTypeExtensionFilters.tskArchiveFilter.text=\u30a2\u30fc\u30ab\u30a4\u30d6 -FileTypeExtensionFilters.tskDocumentFilter.text=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8 -FileTypeExtensionFilters.tskExecFilter.text=\u5b9f\u884c\u30d5\u30a1\u30a4\u30eb -FileTypeExtensionFilters.autDocHtmlFilter.text=HTML -FileTypeExtensionFilters.autDocOfficeFilter.text=\u30aa\u30d5\u30a3\u30b9 -FileTypeExtensionFilters.autoDocPdfFilter.text=PDF -FileTypeExtensionFilters.autDocTxtFilter.text=\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8 -FileTypeExtensionFilters.autDocRtfFilter.text=\u30ea\u30c3\u30c1\u30c6\u30ad\u30b9\u30c8 -FileTypesByExtNode.createSheet.filterType.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +FileSize.createSheet.name.name=\u540d\u524d +FileSize.createSheet.name.displayName=\u540d\u524d +FileSize.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +FileSize.createSheet.filterType.name=\u30b5\u30a4\u30ba\u7bc4\u56f2 +FileSize.createSheet.filterType.displayName=\u30b5\u30a4\u30ba\u7bc4\u56f2 +FileSize.createSheet.filterType.desc=\u8aac\u660e\u306a\u3057 +FileSize.exception.notSupported.msg=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u8868\u793a\u53ef\u80fd\u306a\u9805\u76ee\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093: {0} +FileTypeChildren.exception.notSupported.msg=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u8868\u793a\u53ef\u80fd\u306a\u9805\u76ee\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093: {0} +FileTypesByExtNode.createSheet.filterType.name=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 +FileTypesByExtNode.createSheet.filterType.displayName=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 +FileTypesByExtNode.createSheet.filterType.desc=\u8aac\u660e\u306a\u3057 FileTypesByExtNode.createSheet.fileExt.name=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50 FileTypesByExtNode.createSheet.fileExt.displayName=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50 -FileTypesByExtNode.createSheet.fileExt.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +FileTypesByExtNode.createSheet.fileExt.desc=\u8aac\u660e\u306a\u3057 +FileTypesByExtNode.fname.text=\u62e1\u5f35\u5b50\u5225 FileTypesByExtNode.createSheet.name.name=\u540d\u524d FileTypesByExtNode.createSheet.name.displayName=\u540d\u524d -FileTypesByExtNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +FileTypesByExtNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 HashsetHits.createSheet.name.name=\u540d\u524d HashsetHits.createSheet.name.displayName=\u540d\u524d -HashsetHits.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -ImageNode.getActions.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a -ImageNode.getActions.openFileSearchByAttr.text=\u5c5e\u6027\u306b\u3088\u308b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u3092\u958b\u304f +HashsetHits.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +ImageNode.getActions.viewInNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a ImageNode.createSheet.name.name=\u540d\u524d ImageNode.createSheet.name.displayName=\u540d\u524d -ImageNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -Installer.exception.tskVerStringNull.msg=Sleuth Kit JNI\u30c6\u30b9\u30c8\u30b3\u30fc\u30eb\u3067\u306f\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u305b\u3093\u3067\u3057\u305f\u304c\u3001\u30d0\u30fc\u30b8\u30e7\u30f3\u30b9\u30c8\u30ea\u30f3\u30b0\u306f\u30cc\u30eb\u3067\u3057\u305f\uff01 -Installer.tskLibErr.msg=Sleuth Kit JNI\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30c6\u30b9\u30c8\u30b3\u30fc\u30eb\u304c\u5931\u6557\u3057\u307e\u3057\u305f\uff01\n\n\u8a73\u7d30\uff1a {0} -Installer.tskLibErr.err=\u81f4\u547d\u7684\u30a8\u30e9\u30fc\uff01 -InterestingHits.interestingItems.text=\u7591\u308f\u3057\u3044\u30a2\u30a4\u30c6\u30e0 -InterestingHits.displayName.text=\u7591\u308f\u3057\u3044\u30a2\u30a4\u30c6\u30e0 +ImageNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +Installer.exception.tskVerStringNull.msg=Sleuth Kit\u306eJNI\u30c6\u30b9\u30c8\u30b3\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u306a\u3057\u3067\u8fd4\u3055\u308c\u307e\u3057\u305f\u304c\u3001\u30d0\u30fc\u30b8\u30e7\u30f3\u6587\u5b57\u5217\u304cnull\u3067\u3057\u305f\! +Installer.exception.taskVerStringBang.msg=Sleuth Kit\u306eJNI\u30c6\u30b9\u30c8\u30b3\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u306a\u3057\u3067\u8fd4\u3055\u308c\u307e\u3057\u305f\u304c\u3001\u30d0\u30fc\u30b8\u30e7\u30f3\u6587\u5b57\u5217\u304c""\u3067\u3057\u305f\! +Installer.tskLibErr.msg=Problem with Sleuth Kit\u306eJNI\u3067\u3059\u3002 Test call failed\!\n\nDetails: {0} +Installer.tskLibErr.err=\u81f4\u547d\u7684\u306a\u30a8\u30e9\u30fc\u3067\u3059\! +InterestingHits.interestingItems.text=\u8208\u5473\u6df1\u3044\u9805\u76ee +InterestingHits.displayName.text=\u8208\u5473\u6df1\u3044\u9805\u76ee InterestingHits.createSheet.name.name=\u540d\u524d InterestingHits.createSheet.name.displayName=\u540d\u524d -InterestingHits.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +InterestingHits.createSheet.name.desc=\u8aac\u660e\u306a\u3057 KeyValueNode.createSheet.name.name=\u540d\u524d KeyValueNode.createSheet.name.displayName=\u540d\u524d -KeyValueNode.createSheet.name.desc=\u8a72\u5f53\u306a\u3057 -KeyValueNode.createSheet.map.desc=\u8a72\u5f53\u306a\u3057 -KeywordHits.kwHits.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d2\u30c3\u30c8 -KeywordHits.createSheet.name.name=\u540d\u524d -KeywordHits.createSheet.name.displayName=\u540d\u524d -KeywordHits.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -KeywordHits.createSheet.listName.name=\u30ea\u30b9\u30c8\u540d -KeywordHits.createSheet.listName.displayName=\u30ea\u30b9\u30c8\u540d -KeywordHits.createSheet.listName.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -KeywordHits.createSheet.numChildren.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -KeywordHits.createSheet.filesWithHits.name=\u30d2\u30c3\u30c8\u3057\u305f\u30d5\u30a1\u30a4\u30eb -KeywordHits.createSheet.filesWithHits.displayName=\u30d2\u30c3\u30c8\u3057\u305f\u30d5\u30a1\u30a4\u30eb -KeywordHits.createSheet.filesWithHits.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -KeywordHits.createNodeForKey.modTime.displayName=\u4fee\u6b63\u65e5\u6642 -KeywordHits.createNodeForKey.modTime.desc=\u4fee\u6b63\u65e5\u6642 -KeywordHits.createNodeForKey.accessTime.displayName=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 -KeywordHits.createNodeForKey.accessTime.desc=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 -KeywordHits.createNodeForKey.chgTime.displayName=\u5909\u66f4\u65e5\u6642 -KeywordHits.createNodeForKey.chgTime.desc=\u5909\u66f4\u65e5\u6642 -KeywordHits.createNodeForKey.chgTime.name=\u5909\u66f4\u65e5\u6642 -KeywordHits.createNodeForKey.accessTime.name=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 -KeywordHits.createNodeForKey.modTime.name=\u4fee\u6b63\u65e5\u6642 -KnownFileFilterNode.selectionContext.dataSources=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 -KnownFileFilterNode.selectionContext.views=\u30d3\u30e5\u30fc +KeyValueNode.createSheet.name.desc=n/a +KeyValueNode.createSheet.map.desc=n/a LayoutFileNode.propertyType.parts=\u30d1\u30fc\u30c4 LayoutFileNode.createSheet.name.name=\u540d\u524d LayoutFileNode.createSheet.name.displayName=\u540d\u524d -LayoutFileNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -LayoutFileNode.createSheet.noDescr.text=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -LayoutFileNode.getActions.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a -LayoutFileNode.getActions.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u3067\u958b\u304f +LayoutFileNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +LayoutFileNode.createSheet.noDescr.text=\u8aac\u660e\u306a\u3057 +LayoutFileNode.getActions.viewInNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a +LayoutFileNode.getActions.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f Ctrl+E LocalFileNode.createSheet.name.name=\u540d\u524d LocalFileNode.createSheet.name.displayName=\u540d\u524d -LocalFileNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -LocalFileNode.createSheet.noDescr.text=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -LocalFileNode.getActions.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a -LocalFileNode.getActions.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u3067\u958b\u304f -LocalFileNode.getActions.searchFilesSameMd5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 +LocalFileNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +LocalFileNode.createSheet.noDescr.text=\u8aac\u660e\u306a\u3057 +LocalFileNode.getActions.viewInNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a +LocalFileNode.getActions.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f Ctrl+E +LocalFileNode.getActions.searchFilesSameMd5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 +OpenReportAction.actionDisplayName=\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u304f +OpenReportAction.actionPerformed.MessageBoxTitle=\u30ec\u30dd\u30fc\u30c8\u30aa\u30fc\u30d7\u30f3\u5931\u6557 +OpenReportAction.actionPerformed.NoAssociatedEditorMessage=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u30ec\u30dd\u30fc\u30c8\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30a8\u30c7\u30a3\u30bf\u30fc\u304c\u306a\u3044\u304b\u3001\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +OpenReportAction.actionPerformed.NoOpenInEditorSupportMessage=\u3053\u306e\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0(\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0)\u306f\u3053\u306e\u65b9\u6cd5\u3067\u30a8\u30c7\u30a3\u30bf\u30fc\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u304f\u3053\u3068\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u305b\u3093\u3002 +OpenReportAction.actionPerformed.MissingReportFileMessage=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +OpenReportAction.actionPerformed.ReportFileOpenPermissionDeniedMessage=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u304f\u8a31\u53ef\u304c\u62d2\u5426\u3055\u308c\u307e\u3057\u305f\u3002 RecentFiles.aut0DayFilter.displayName.text=\u6700\u7d42\u65e5 RecentFiles.aut1dayFilter.displayName.text=\u6700\u7d42\u65e5 - 1 -RecentFiles.aut2dayFilter.displayName.text=\u6700\u7d42\u65e5 - \uff12 -RecentFiles.aut3dayFilter.displayName.text=\u6700\u7d42\u65e5 - \uff13 -RecentFiles.aut4dayFilter.displayName.text=\u6700\u7d42\u65e5 - \uff14 -RecentFiles.aut5dayFilter.displayName.text=\u6700\u7d42\u65e5 - \uff15 -RecentFiles.aut6dayFilter.displayName.text=\u6700\u7d42\u65e5 - \uff16 -RecentFilesFilterChildren.exception.defaultVisit.msg=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u8868\u793a\u3067\u304d\u308b\u30a2\u30a4\u30c6\u30e0\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\uff1a{0} -Installer.exception.taskVerStringBang.msg=Sleuth Kit JNI\u30c6\u30b9\u30c8\u30b3\u30fc\u30eb\u3067\u306f\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u305b\u3093\u3067\u3057\u305f\u304c\u3001\u30d0\u30fc\u30b8\u30e7\u30f3\u30b9\u30c8\u30ea\u30f3\u30b0\u306f""\u3067\u3057\u305f\uff01 +RecentFiles.aut2dayFilter.displayName.text=\u6700\u7d42\u65e5 - 2 +RecentFiles.aut3dayFilter.displayName.text=\u6700\u7d42\u65e5 - 3 +RecentFiles.aut4dayFilter.displayName.text=\u6700\u7d42\u65e5 - 4 +RecentFiles.aut5dayFilter.displayName.text=\u6700\u7d42\u65e5 - 5 +RecentFiles.aut6dayFilter.displayName.text=\u6700\u7d42\u65e5 - 6 +RecentFilesFilterChildren.exception.defaultVisit.msg=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u8868\u793a\u53ef\u80fd\u306a\u9805\u76ee\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093: {0} RecentFilesFilterNode.createSheet.filterType.name=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7 RecentFilesFilterNode.createSheet.filterType.displayName=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7 -RecentFilesFilterNode.createSheet.filterType.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +RecentFilesFilterNode.createSheet.filterType.desc=\u8aac\u660e\u306a\u3057 RecentFilesNode.createSheet.name.name=\u540d\u524d RecentFilesNode.createSheet.name.displayName=\u540d\u524d -RecentFilesNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -RecentFilesNode.name.text=\u6700\u8fd1\u4f7f\u7528\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb -ResultsNode.name.text=\u7d50\u679c -ResultsNode.createSheet.name.name=\u540d\u524d +RecentFilesNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +RecentFilesNode.name.text=\u6700\u8fd1\u306e\u30d5\u30a1\u30a4\u30eb +ReportNode.sourceModuleNameProperty.name=\u30bd\u30fc\u30b9\u30e2\u30b8\u30e5\u30fc\u30eb\u540d +ReportNode.sourceModuleNameProperty.displayName=\u30bd\u30fc\u30b9\u30e2\u30b8\u30e5\u30fc\u30eb\u540d +ReportNode.sourceModuleNameProperty.desc=\u30ec\u30dd\u30fc\u30c8\u3092\u4f5c\u6210\u3057\u305f\u30e2\u30b8\u30e5\u30fc\u30eb\u540d +ReportNode.createdTimeProperty.name=\u4f5c\u6210\u3057\u305f\u6642\u523b +ReportNode.createdTimeProperty.displayName=\u4f5c\u6210\u3057\u305f\u6642\u523b +ReportNode.createdTimeProperty.desc=\u30ec\u30dd\u30fc\u30c8\u304c\u4f5c\u6210\u3055\u308c\u305f\u6642\u523b +ReportNode.pathProperty.name= \u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 +ReportNode.pathProperty.displayName= \u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 +ReportNode.pathProperty.desc=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u306e\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9 +ReportNode.reportNameProperty.name=\u30ec\u30dd\u30fc\u30c8\u540d +ReportNode.reportNameProperty.displayName=\u30ec\u30dd\u30fc\u30c8\u540d +ReportNode.reportNameProperty.desc=\u30ec\u30dd\u30fc\u30c8\u540d +ReportsListNode.displayName=\u30ec\u30dd\u30fc\u30c8 +ResultsNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 ResultsNode.createSheet.name.displayName=\u540d\u524d -ResultsNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -TagNameNode.namePlusTags.text={0}\u30bf\u30b0 +ResultsNode.createSheet.name.name=\u540d\u524d +ResultsNode.name.text=\u7d50\u679c +SlackFileNode.getActions.viewInNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a +SlackFileNode.getActions.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a +SpecialDirectoryNode.getActions.viewInNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a +TagNameNode.namePlusTags.text={0} \u30bf\u30b0 TagNameNode.contentTagTypeNodeKey.text=\u30b3\u30f3\u30c6\u30f3\u30c4\u30bf\u30b0 TagNameNode.bbArtTagTypeNodeKey.text=\u7d50\u679c\u30bf\u30b0 TagNameNode.bookmark.text=\u30d6\u30c3\u30af\u30de\u30fc\u30af @@ -196,81 +340,62 @@ TagNameNode.createSheet.name.name=\u540d\u524d TagNameNode.createSheet.name.displayName=\u540d\u524d TagsNode.displayName.text=\u30bf\u30b0 TagsNode.createSheet.name.name=\u540d\u524d -AbstractAbstractFileNode.flagsDirColLbl=\u30d5\u30e9\u30b0\uff08\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff09 -AbstractAbstractFileNode.flagsMetaColLbl=\u30d5\u30e9\u30b0\uff08\u30e1\u30bf\u30c7\u30fc\u30bf\uff09 -AbstractAbstractFileNode.metaAddrColLbl=\u30e1\u30bf\u30c7\u30fc\u30bf\u30a2\u30c9\u30ec\u30b9 -AbstractAbstractFileNode.attrAddrColLbl=\u5c5e\u6027\u30a2\u30c9\u30ec\u30b9 -AbstractAbstractFileNode.typeDirColLbl=\u30bf\u30a4\u30d7\uff08\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff09 -AbstractAbstractFileNode.typeMetaColLbl=\u30bf\u30a4\u30d7\uff08\u30e1\u30bf\u30c7\u30fc\u30bf\uff09 -ArtifactTypeNode.createSheet.childCnt.displayName=\u30c1\u30e3\u30a4\u30eb\u30c9\u6570 TagsNode.createSheet.name.displayName=\u540d\u524d ViewsNode.name.text=\u30d3\u30e5\u30fc ViewsNode.createSheet.name.name=\u540d\u524d ViewsNode.createSheet.name.displayName=\u540d\u524d -ViewsNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -VirtualDirectoryNode.getActions.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a +ViewsNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +ImageNode.createSheet.deviceId.desc=\u30a4\u30e1\u30fc\u30b8\u306e\u30c7\u30d0\u30a4\u30b9ID +VirtualDirectoryNode.createSheet.deviceId.displayName=\u30c7\u30d0\u30a4\u30b9ID +VirtualDirectoryNode.createSheet.deviceId.name=\u30c7\u30d0\u30a4\u30b9ID +VirtualDirectoryNode.createSheet.size.desc=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u30b5\u30a4\u30ba(\u30d0\u30a4\u30c8)\u3067\u3059\u3002 +VirtualDirectoryNode.createSheet.size.displayName=\u30b5\u30a4\u30ba(\u30d0\u30a4\u30c8) +VirtualDirectoryNode.createSheet.size.name=\u30b5\u30a4\u30ba(\u30d0\u30a4\u30c8) +VirtualDirectoryNode.createSheet.timezone.desc=\u30a4\u30e1\u30fc\u30b8\u306e\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 +VirtualDirectoryNode.createSheet.timezone.displayName=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 +VirtualDirectoryNode.createSheet.timezone.name=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 +ImageNode.createSheet.type.desc=\u30a4\u30e1\u30fc\u30b8\u306e\u30bf\u30a4\u30d7\u3067\u3059\u3002 +VirtualDirectoryNode.createSheet.type.displayName=\u30bf\u30a4\u30d7 +VirtualDirectoryNode.createSheet.type.name=\u30bf\u30a4\u30d7 +VirtualDirectoryNode.createSheet.type.text=\u8ad6\u7406\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8 +VirtualDirectoryNode.getActions.viewInNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a VirtualDirectoryNode.createSheet.name.name=\u540d\u524d VirtualDirectoryNode.createSheet.name.displayName=\u540d\u524d -VirtualDirectoryNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -VirtualDirectoryNode.createSheet.noDesc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -VolumeNode.getActions.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a +VirtualDirectoryNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 +VirtualDirectoryNode.createSheet.noDesc=\u8aac\u660e\u306a\u3057 +VolumeNode.getActions.viewInNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a VolumeNode.createSheet.name.name=\u540d\u524d VolumeNode.createSheet.name.displayName=\u540d\u524d -VolumeNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +VolumeNode.createSheet.name.desc=\u8aac\u660e\u306a\u3057 VolumeNode.createSheet.id.name=ID VolumeNode.createSheet.id.displayName=ID -VolumeNode.createSheet.id.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -VolumeNode.createSheet.startSector.name=\u6700\u521d\u306e\u30bb\u30af\u30bf\u30fc -VolumeNode.createSheet.startSector.displayName=\u6700\u521d\u306e\u30bb\u30af\u30bf\u30fc -VolumeNode.createSheet.startSector.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -VolumeNode.createSheet.lenSectors.name=\u30bb\u30af\u30bf\u30fc\u306e\u9577\u3055 -VolumeNode.createSheet.lenSectors.displayName=\u30bb\u30af\u30bf\u30fc\u306e\u9577\u3055 -VolumeNode.createSheet.lenSectors.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +VolumeNode.createSheet.id.desc=\u8aac\u660e\u306a\u3057 +VolumeNode.createSheet.startSector.name=\u30bb\u30af\u30bf\u30fc\u3092\u958b\u59cb\u4e2d\u3067\u3059 +VolumeNode.createSheet.startSector.displayName=\u30bb\u30af\u30bf\u30fc\u3092\u958b\u59cb\u4e2d\u3067\u3059 +VolumeNode.createSheet.startSector.desc=\u8aac\u660e\u306a\u3057 +VolumeNode.createSheet.lenSectors.name=\u30bb\u30af\u30bf\u30fc\u306e\u9577\u3055: +VolumeNode.createSheet.lenSectors.displayName=\u30bb\u30af\u30bf\u30fc\u306e\u9577\u3055: +VolumeNode.createSheet.lenSectors.desc=\u8aac\u660e\u306a\u3057 VolumeNode.createSheet.description.name=\u8aac\u660e VolumeNode.createSheet.description.displayName=\u8aac\u660e -VolumeNode.createSheet.description.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +VolumeNode.createSheet.description.desc=\u8aac\u660e\u306a\u3057 VolumeNode.createSheet.flags.name=\u30d5\u30e9\u30b0 VolumeNode.createSheet.flags.displayName=\u30d5\u30e9\u30b0 -VolumeNode.createSheet.flags.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -ArtifactTypeNode.createSheet.artType.name=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30bf\u30a4\u30d7 -ArtifactTypeNode.createSheet.artType.displayName=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30bf\u30a4\u30d7 -KeywordHits.createSheet.numChildren.name=\u30c1\u30e3\u30a4\u30eb\u30c9\u6570 -KeywordHits.createSheet.numChildren.displayName=\u30c1\u30e3\u30a4\u30eb\u30c9\u6570 -KeywordHits.simpleLiteralSearch.text=\u30b7\u30f3\u30b0\u30eb\u30ea\u30c6\u30e9\u30eb\u691c\u7d22 -KeywordHits.singleRegexSearch.text=\u30b7\u30f3\u30b0\u30eb\u6b63\u898f\u8868\u73fe\u691c\u7d22 -AbstractAbstractFileNode.objectId=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8ID +VolumeNode.createSheet.flags.desc=\u8aac\u660e\u306a\u3057 ArtifactStringContent.getStr.artifactId.text=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8ID -OpenReportAction.actionDisplayName=\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u304f -OpenReportAction.actionPerformed.MessageBoxTitle=\u5931\u6557\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u304f -OpenReportAction.actionPerformed.MissingReportFileMessage=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u306f\u306a\u304f\u306a\u308a\u307e\u3057\u305f\u3002 -OpenReportAction.actionPerformed.NoAssociatedEditorMessage=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u30ec\u30dd\u30fc\u30c8\u306b\u95a2\u9023\u3059\u308b\u30a8\u30c7\u30a3\u30bf\u30fc\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u307e\u305f\u306f\u3001\u95a2\u9023\u3059\u308b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u958b\u59cb\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 -OpenReportAction.actionPerformed.NoOpenInEditorSupportMessage=\u3053\u306e\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\uff08\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0\uff09\u306f\u3053\u306e\u65b9\u6cd5\u3067\u3001\u30d5\u30a1\u30a4\u30eb\u3092\u30a8\u30c7\u30a3\u30bf\u30fc\u306b\u3066\u958b\u304f\u306e\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u305b\u3093\u3002 -OpenReportAction.actionPerformed.ReportFileOpenPermissionDeniedMessage=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u304f\u30d1\u30fc\u30df\u30c3\u30b7\u30e7\u30f3\u304c\u62d2\u5426\u3055\u308c\u307e\u3057\u305f\u3002 -ReportNode.createdTimeProperty.desc=\u30bf\u30a4\u30e0\u30ec\u30dd\u30fc\u30c8\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f\u3002 -ReportNode.createdTimeProperty.displayName=\u4f5c\u6210\u65e5\u6642 -ReportNode.createdTimeProperty.name=\u4f5c\u6210\u65e5\u6642 -ReportNode.pathProperty.desc=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u306e\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9 -ReportNode.pathProperty.displayName=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u3092\u30ec\u30dd\u30fc\u30c8 -ReportNode.pathProperty.name=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u3092\u30ec\u30dd\u30fc\u30c8 -ReportNode.sourceModuleNameProperty.desc=\u30ec\u30dd\u30fc\u30c8\u3092\u4f5c\u6210\u3057\u305f\u30e2\u30b8\u30e5\u30fc\u30eb\u540d -ReportNode.sourceModuleNameProperty.displayName=\u30bd\u30fc\u30b9\u30e2\u30b8\u30e5\u30fc\u30eb\u540d -ReportNode.sourceModuleNameProperty.name=\u30bd\u30fc\u30b9\u30e2\u30b8\u30e5\u30fc\u30eb\u540d -ReportsListNode.displayName=\u30ec\u30dd\u30fc\u30c8 -AbstractAbstractFileNode.mimeType=MIME\u30bf\u30a4\u30d7 -ContentTagNode.createSheet.fileModifiedTime.name=\u4fee\u6b63\u65e5\u6642 -ContentTagNode.createSheet.fileModifiedTime.displayName=\u4fee\u6b63\u65e5\u6642 -ContentTagNode.createSheet.fileChangedTime.name=\u5909\u66f4\u65e5\u6642 -ContentTagNode.createSheet.fileChangedTime.displayName=\u5909\u66f4\u65e5\u6642 -ContentTagNode.createSheet.fileAccessedTime.name=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 -ContentTagNode.createSheet.fileAccessedTime.displayName=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 -ContentTagNode.createSheet.fileCreatedTime.name=\u4f5c\u6210\u65e5\u6642 -ContentTagNode.createSheet.fileCreatedTime.displayName=\u4f5c\u6210\u65e5\u6642 -ContentTagNode.createSheet.fileSize.name=\u30b5\u30a4\u30ba -ContentTagNode.createSheet.fileSize.displayName=\u30b5\u30a4\u30ba -LocalFileNode.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a -ReportNode.reportNameProperty.name=\u30ec\u30dd\u30fc\u30c8\u540d -ReportNode.reportNameProperty.displayName=\u30ec\u30dd\u30fc\u30c8\u540d -ReportNode.reportNameProperty.desc=\u30ec\u30dd\u30fc\u30c8\u306e\u540d\u524d DeleteReportAction.actionDisplayName.singleReport=\u30ec\u30dd\u30fc\u30c8\u3092\u524a\u9664 DeleteReportAction.actionDisplayName.multipleReports=\u30ec\u30dd\u30fc\u30c8\u3092\u524a\u9664 -DeleteReportAction.actionPerformed.showConfirmDialog.title=\u524a\u9664\u3092\u78ba\u5b9a\u3059\u308b \ No newline at end of file +DeleteReportAction.actionPerformed.showConfirmDialog.title=\u524a\u9664\u3092\u78ba\u5b9a +DeleteReportAction.actionPerformed.showConfirmDialog.single.msg=\u30b1\u30fc\u30b9\u304b\u3089\u30ec\u30dd\u30fc\u30c8\u30921\u4ef6\u524a\u9664\u3057\u307e\u3059\u304b? +DeleteReportAction.actionPerformed.showConfirmDialog.multiple.msg=\u30b1\u30fc\u30b9\u304b\u3089\u30ec\u30dd\u30fc\u30c8\u3092 {0} \u4ef6\u524a\u9664\u3057\u307e\u3059\u304b? +FileTypeExtensionFilters.tskImgFilter.text=\u30a4\u30e1\u30fc\u30b8 +FileTypeExtensionFilters.tskVideoFilter.text=\u52d5\u753b +FileTypeExtensionFilters.tskAudioFilter.text=\u30aa\u30fc\u30c7\u30a3\u30aa +FileTypeExtensionFilters.tskArchiveFilter.text=\u30a2\u30fc\u30ab\u30a4\u30d6 +FileTypeExtensionFilters.tskDocumentFilter.text=\u6587\u66f8 +FileTypeExtensionFilters.tskExecFilter.text=\u5b9f\u884c\u30d5\u30a1\u30a4\u30eb +FileTypeExtensionFilters.autDocHtmlFilter.text=HTML +FileTypeExtensionFilters.autDocOfficeFilter.text=Office +FileTypeExtensionFilters.autoDocPdfFilter.text=PDF +FileTypeExtensionFilters.autDocTxtFilter.text=\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8 +FileTypeExtensionFilters.autDocRtfFilter.text=\u30ea\u30c3\u30c1\u30c6\u30ad\u30b9\u30c8 diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ContentNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/ContentNodeVisitor.java index 2277cb4b56..af7eb3a249 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ContentNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ContentNodeVisitor.java @@ -35,6 +35,8 @@ interface ContentNodeVisitor { T visit(LocalDirectoryNode ldn); T visit(VolumeNode vn); + + T visit(PoolNode pn); T visit(DirectoryNode dn); @@ -85,6 +87,11 @@ interface ContentNodeVisitor { public T visit(VolumeNode vn) { return defaultVisit(vn); } + + @Override + public T visit(PoolNode pn) { + return defaultVisit(pn); + } @Override public T visit(LayoutFileNode lcn) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/CreateSleuthkitNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/CreateSleuthkitNodeVisitor.java index d9ff5fc33b..b5d808153d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/CreateSleuthkitNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/CreateSleuthkitNodeVisitor.java @@ -28,6 +28,7 @@ import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LocalDirectory; import org.sleuthkit.datamodel.LocalFile; +import org.sleuthkit.datamodel.Pool; import org.sleuthkit.datamodel.SlackFile; import org.sleuthkit.datamodel.SleuthkitItemVisitor; import org.sleuthkit.datamodel.SleuthkitVisitableItem; @@ -58,6 +59,11 @@ public class CreateSleuthkitNodeVisitor extends SleuthkitItemVisitor.Default visit(Volume volume) { return new VolumeNode(volume); } + + @Override + public AbstractContentNode visit(Pool pool) { + return new PoolNode(pool); + } @Override public AbstractContentNode visit(LayoutFile lf) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index bb324bc7bb..24f437251f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -59,6 +59,8 @@ public interface DisplayableItemNodeVisitor { T visit(ImageNode in); T visit(VolumeNode vn); + + T visit(PoolNode pn); T visit(SlackFileNode sfn); @@ -186,6 +188,11 @@ public interface DisplayableItemNodeVisitor { T visit(InterestingHits.InterestingItemTypeNode aThis); + /* + * Attachments + */ + T visit(AttachmentNode node); + /** * Visitor with an implementable default behavior for all types. Override * specific visit types to not use the default behavior. @@ -257,6 +264,11 @@ public interface DisplayableItemNodeVisitor { public T visit(ImageNode in) { return defaultVisit(in); } + + @Override + public T visit(PoolNode pn) { + return defaultVisit(pn); + } @Override public T visit(VolumeNode vn) { @@ -522,5 +534,11 @@ public interface DisplayableItemNodeVisitor { public T visit(Accounts.DefaultAccountTypeNode node) { return defaultVisit(node); } + + @Override + public T visit(AttachmentNode node) { + return defaultVisit(node); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index cefd59a041..d97e980a8b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -42,6 +42,8 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO; @@ -228,7 +230,7 @@ public class ExtractedContent implements AutopsyVisitableItem { TypeFactory() { super(); - // these are shown in other parts of the UI tree + // these are shown in other parts of the UI doNotShow.add(new BlackboardArtifact.Type(TSK_GEN_INFO)); doNotShow.add(new BlackboardArtifact.Type(TSK_EMAIL_MSG)); doNotShow.add(new BlackboardArtifact.Type(TSK_HASHSET_HIT)); @@ -238,6 +240,10 @@ public class ExtractedContent implements AutopsyVisitableItem { doNotShow.add(new BlackboardArtifact.Type(TSK_ACCOUNT)); doNotShow.add(new BlackboardArtifact.Type(TSK_DATA_SOURCE_USAGE)); doNotShow.add(new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE)); + doNotShow.add(new BlackboardArtifact.Type(TSK_TL_EVENT)); + + //This is not meant to be shown in the UI at all. It is more of a meta artifact. + doNotShow.add(new BlackboardArtifact.Type(TSK_ASSOCIATED_OBJECT)); } private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index b81fd6760a..f668893eb1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2019 Basis Technology Corp. + * Copyright 2012-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,6 +34,7 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; @@ -105,7 +106,7 @@ public class ImageNode extends AbstractContentNode { */ @Override @Messages({"ImageNode.action.runIngestMods.text=Run Ingest Modules", - "ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes",}) + "ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes"}) public Action[] getActions(boolean context) { List actionsList = new ArrayList<>(); @@ -113,13 +114,12 @@ public class ImageNode extends AbstractContentNode { actionsList.add(a); } actionsList.addAll(ExplorerNodeActionVisitor.getActions(content)); - actionsList.add(new FileSearchAction( - Bundle.ImageNode_getActions_openFileSearchByAttr_text())); + actionsList.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text())); actionsList.add(new ViewSummaryInformationAction(content.getId())); actionsList.add(new RunIngestModulesAction(Collections.singletonList(content))); - actionsList.add(new NewWindowViewAction( - NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this)); - return actionsList.toArray(new Action[actionsList.size()]); + actionsList.add(new NewWindowViewAction(NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this)); + actionsList.add(new DeleteDataSourceAction(content.getId())); + return actionsList.toArray(new Action[0]); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/PoolNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/PoolNode.java new file mode 100644 index 0000000000..9bc7736952 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/PoolNode.java @@ -0,0 +1,204 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datamodel; + +import java.util.ArrayList; +import java.util.List; +import javax.swing.Action; +import org.apache.commons.lang3.tuple.Pair; +import org.openide.nodes.Sheet; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; +import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR; +import org.sleuthkit.datamodel.Pool; +import org.sleuthkit.datamodel.Tag; + +/** + * This class is used to represent the "Node" for the pool. + */ +public class PoolNode extends AbstractContentNode { + + /** + * Helper so that the display name and the name used in building the path + * are determined the same way. + * + * @param pool Pool to get the name of + * + * @return short name for the pool + */ + static String nameForPool(Pool pool) { + return pool.getType().getName(); + } + + /** + * + * @param pool underlying Content instance + */ + public PoolNode(Pool pool) { + super(pool); + + // set name, display name, and icon + String poolName = nameForPool(pool); + this.setDisplayName(poolName); + + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/pool-icon.png"); //NON-NLS + } + + /** + * Right click action for volume node + * + * @param popup + * + * @return + */ + @Override + public Action[] getActions(boolean popup) { + List actionsList = new ArrayList<>(); + + for (Action a : super.getActions(true)) { + actionsList.add(a); + } + + return actionsList.toArray(new Action[actionsList.size()]); + + } + + @NbBundle.Messages({ + "PoolNode.createSheet.name.name=Name", + "PoolNode.createSheet.name.displayName=Name", + "PoolNode.createSheet.name.desc=no description", + "PoolNode.createSheet.type.name=Type", + "PoolNode.createSheet.type.displayName=Type", + "PoolNode.createSheet.type.desc=no description", + }) + @Override + protected Sheet createSheet() { + Sheet sheet = super.createSheet(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + if (sheetSet == null) { + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + + Pool pool = this.getContent(); + sheetSet.put(new NodeProperty<>(Bundle.PoolNode_createSheet_name_name(), + Bundle.PoolNode_createSheet_name_displayName(), + Bundle.PoolNode_createSheet_name_desc(), + this.getDisplayName())); + sheetSet.put(new NodeProperty<>(Bundle.PoolNode_createSheet_type_name(), + Bundle.PoolNode_createSheet_type_displayName(), + Bundle.PoolNode_createSheet_type_desc(), + pool.getType().getName())); + + return sheet; + } + + @Override + public T accept(ContentNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public String getItemType() { + return getClass().getName(); + } + + /** + * Reads and returns a list of all tags associated with this content node. + * + * Null implementation of an abstract method. + * + * @return list of tags associated with the node. + */ + @Override + protected List getAllTagsFromDatabase() { + return new ArrayList<>(); + } + + /** + * Returns correlation attribute instance for the underlying content of the + * node. + * + * Null implementation of an abstract method. + * + * @return correlation attribute instance for the underlying content of the + * node. + */ + @Override + protected CorrelationAttributeInstance getCorrelationAttributeInstance() { + return null; + } + + /** + * Returns Score property for the node. + * + * Null implementation of an abstract method. + * + * @param tags list of tags. + * + * @return Score property for the underlying content of the node. + */ + @Override + protected Pair getScorePropertyAndDescription(List tags) { + return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); + } + + /** + * Returns comment property for the node. + * + * Null implementation of an abstract method. + * + * @param tags list of tags + * @param attribute correlation attribute instance + * + * @return Comment property for the underlying content of the node. + */ + @Override + protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { + return DataResultViewerTable.HasCommentStatus.NO_COMMENT; + } + + /** + * Returns occurrences/count property for the node. + * + * Null implementation of an abstract method. + * + * @param attributeType the type of the attribute to count + * @param attributeValue the value of the attribute to coun + * @param defaultDescription a description to use when none is determined by + * the getCountPropertyAndDescription method + * + * @return count property for the underlying content of the node. + */ + @Override + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) { + return Pair.of(-1L, NO_DESCR); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle_ja.properties new file mode 100644 index 0000000000..1e28a40973 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle_ja.properties @@ -0,0 +1,28 @@ +Accounts.BINNode.accountsProperty.displayName=\u30a2\u30ab\u30a6\u30f3\u30c8 +Accounts.BINNode.bankCityProperty.displayName=\u9280\u884c\u6240\u5728\u5730\u5e02\u753a\u6751\u533a +Accounts.BINNode.bankCountryProperty.displayName=\u9280\u884c\u6240\u5728\u56fd +Accounts.BINNode.bankPhoneProperty.displayName=\u9280\u884c\u96fb\u8a71\u756a\u53f7# +Accounts.BINNode.bankProperty.displayName=\u9280\u884c +Accounts.BINNode.bankURLProperty.displayName=\u9280\u884cURL +Accounts.BINNode.binProperty.displayName=\u9280\u884c\u8b58\u5225\u756a\u53f7 +Accounts.BINNode.brandProperty.displayName=\u30d6\u30e9\u30f3\u30c9 +Accounts.BINNode.cardTypeProperty.displayName=\u652f\u6255\u3044\u30ab\u30fc\u30c9\u306e\u7a2e\u985e +Accounts.BINNode.noDescription=\u8aac\u660e\u306a\u3057 +Accounts.BINNode.schemeProperty.displayName=\u30af\u30ec\u30b8\u30c3\u30c8\u30ab\u30fc\u30c9\u30b9\u30ad\u30fc\u30e0 +# {0} - \u5b50\u306e\u6570 +Accounts.ByBINNode.displayName=BIN\u5225 ({0}) +Accounts.ByBINNode.name=BIN\u5225 +# {0} - \u5b50\u306e\u6570 +Accounts.ByFileNode.displayName=\u30d5\u30a1\u30a4\u30eb\u5225 ({0}) +Accounts.FileWithCCNNode.accountsProperty.displayName=\u30a2\u30ab\u30a6\u30f3\u30c8 +Accounts.FileWithCCNNode.nameProperty.displayName=\u30d5\u30a1\u30a4\u30eb +Accounts.FileWithCCNNode.noDescription=\u8aac\u660e\u306a\u3057 +Accounts.FileWithCCNNode.statusProperty.displayName=\u30b9\u30c6\u30fc\u30bf\u30b9 +# {0} - \u30ed\u30fc\u30d5\u30a1\u30a4\u30eb\u540d +# {1} - Solr\u30c1\u30e3\u30f3\u30afID +Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_\u30c1\u30e3\u30f3\u30af_{1} +Accounts.RootNode.displayName=\u30a2\u30ab\u30a6\u30f3\u30c8 +AccountsRootNode.name=\u30a2\u30ab\u30a6\u30f3\u30c8 +ApproveAccountsAction.name=\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u627f\u8a8d +RejectAccountsAction.name=\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u62d2\u5426 +ToggleShowRejected.name=\u62d2\u5426\u3055\u308c\u305f\u7d50\u679c\u3092\u8868\u793a diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/utils/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datamodel/utils/Bundle_ja.properties new file mode 100644 index 0000000000..b3e4e61e7d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/utils/Bundle_ja.properties @@ -0,0 +1,7 @@ +FileTypeCategory.Audio.displayName=\u30aa\u30fc\u30c7\u30a3\u30aa +FileTypeCategory.Documents.displayName=\u6587\u66f8 +FileTypeCategory.Executables.displayName=\u5b9f\u884c\u30d5\u30a1\u30a4\u30eb +FileTypeCategory.Image.displayName=\u30a4\u30e1\u30fc\u30b8 +FileTypeCategory.Media.displayName=\u30e1\u30c7\u30a3\u30a2 +FileTypeCategory.Video.displayName=\u52d5\u753b +FileTypeCategory.Visual.displayName=\u30d3\u30b8\u30e5\u30a2\u30eb diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle_ja.properties new file mode 100644 index 0000000000..7086b898db --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle_ja.properties @@ -0,0 +1,21 @@ +# \u3053\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u30d8\u30c3\u30c0\u30fc\u3092\u5909\u66f4\u3059\u308b\u306b\u306f\u3001[\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u30d7\u30ed\u30d1\u30c6\u30a3] \u3067 [\u30e9\u30a4\u30bb\u30f3\u30b9\u30d8\u30c3\u30c0\u30fc] \u3092\u9078\u629e\u3057\u307e\u3059\u3002 +# \u3053\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u5909\u66f4\u3059\u308b\u306b\u306f\u3001[\u30c4\u30fc\u30eb | \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8] \u3092\u9078\u629e\u3057\u3001 +# \u30a8\u30c7\u30a3\u30bf\u30fc\u3067\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u958b\u304d\u307e\u3059\u3002 + +AddRawImageTask.for.device=\u30c7\u30d0\u30a4\u30b9\u7528 +AddRawImageTask.image.critical.error.adding=\u8ffd\u52a0\u4e2d\u306e\u91cd\u5927\u306a\u30a8\u30e9\u30fc +AddRawImageTask.image.critical.error.adding=\u8ffd\u52a0\u4e2d\u306e\u91cd\u5927\u3067\u306f\u306a\u3044\u30a8\u30e9\u30fc +AddRawImageTask.image.notExisting=\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +AddRawImageTask.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AddRawImageTask.progress.add.text=\u6b21\u306e\u30ed\u30fc\u30a4\u30e1\u30fc\u30b8\u3092\u8ffd\u52a0\u4e2d\u3067\u3059: +RawDSInputPanel.error.text=\u30de\u30eb\u30c1\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u30d1\u30b9\u306f "C:" \u30c9\u30e9\u30a4\u30d6\u306b\u3042\u308a\u307e\u3059 +RawDSInputPanel.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306e\u53d6\u5f97\u4e2d\u306b\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +RawDSInputPanel.pathLabel.text=\u672a\u4f7f\u7528\u9818\u57df\u306e\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u3092\u53c2\u7167: +RawDSInputPanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb +RawDSInputPanel.browseButton.text=\u53c2\u7167 +RawDSInputPanel.pathTextField.text= +RawDSInputPanel.jBreakFileUpLabel.text=\u6b21\u307e\u3067\u30a4\u30e1\u30fc\u30b8\u3092\u7d30\u5206\u5316: +RawDSInputPanel.jNoBreakupRadioButton.text=\u7d30\u5206\u5316\u3057\u306a\u3044 +RawDSInputPanel.j2GBBreakupRadioButton.text=2GB\u30c1\u30e3\u30f3\u30af +RawDSInputPanel.timeZoneLabel.text=\u5165\u529b\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044: +RawDSProcessor.dataSourceType=\u672a\u4f7f\u7528\u9818\u57df\u306e\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/AbstractSingleEntityParser.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/AbstractSingleEntityParser.java new file mode 100755 index 0000000000..cdd84fa06e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/AbstractSingleEntityParser.java @@ -0,0 +1,130 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Template parse method for reports that make artifacts from a single XRY + * Entity. + */ +abstract class AbstractSingleEntityParser implements XRYFileParser { + + private static final Logger logger = Logger.getLogger(AbstractSingleEntityParser.class.getName()); + + protected static final String PARSER_NAME = "XRY DSP"; + + @Override + public void parse(XRYFileReader reader, Content parent) throws IOException, TskCoreException { + Path reportPath = reader.getReportPath(); + logger.log(Level.INFO, String.format("[XRY DSP] Processing report at [ %s ]", reportPath.toString())); + + while (reader.hasNextEntity()) { + String xryEntity = reader.nextEntity(); + String[] xryLines = xryEntity.split("\n"); + + List keyValuePairs = new ArrayList<>(); + + //First line of the entity is the title, the entity will always be non-empty. + logger.log(Level.INFO, String.format("[XRY DSP] Processing [ %s ]", xryLines[0])); + + String namespace = ""; + //Process each line, searching for a key value pair or a namespace. + for (int i = 1; i < xryLines.length; i++) { + String xryLine = xryLines[i]; + + String candidateNamespace = xryLine.trim(); + //Check if the line is a namespace, which gives context to the keys + //that follow. + if (isNamespace(candidateNamespace)) { + namespace = candidateNamespace; + continue; + } + + //Check if this line resembles a Key Value pair. + if(!XRYKeyValuePair.isPair(xryLine)) { + logger.log(Level.WARNING, String.format("[XRY DSP] Expected a key value " + + "pair on this line (in brackets) [ %s ], but one was not detected.", + xryLine)); + continue; + } + + XRYKeyValuePair pair = XRYKeyValuePair.from(xryLine, namespace); + + //Verify the implementation recognizes the key. + if (!canProcess(pair)) { + logger.log(Level.WARNING, String.format("[XRY DSP] The following key, " + + "value pair (in brackets) [ %s ] was not recognized. Discarding...", + pair)); + continue; + } + + //Empty values are meaningless for blackboard attributes. + if (pair.getValue().isEmpty()) { + logger.log(Level.WARNING, String.format("[XRY DSP] The following key value pair" + + "(in brackets) [ %s ] was recognized, but the value was empty. Discarding...", + pair)); + continue; + } + + keyValuePairs.add(pair); + } + + if(!keyValuePairs.isEmpty()) { + makeArtifact(keyValuePairs, parent); + } + } + } + + /** + * Determines if the XRY key value pair can be processed by the + * implementation. + */ + abstract boolean canProcess(XRYKeyValuePair pair); + + /** + * Determines if the namespace candidate is a known namespace. A namespace + * candidate is a string literal that makes up an entire line. + * + * Ex: + * + * To + * Tel : +1245325 + * + * "To" would be the candidate namespace that was extracted. + * + * @param nameSpace Namespace to test. Namespaces are trimmed of whitespace + * only. + * @return Indication if this namespace can be processed. + */ + abstract boolean isNamespace(String nameSpace); + + /** + * Makes an artifact from the parsed key value pairs. + */ + abstract void makeArtifact(List keyValuePairs, Content parent) throws TskCoreException; + +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/Bundle.properties new file mode 100755 index 0000000000..54024e1025 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/Bundle.properties @@ -0,0 +1,4 @@ +XRYDataSourceProcessorConfigPanel.fileBrowserButton.text=Browse +XRYDataSourceProcessorConfigPanel.filePathTextField.text= +XRYDataSourceProcessorConfigPanel.xrySelectFolderLabel.text=Select a folder containing exported XRY text files +XRYDataSourceProcessorConfigPanel.errorLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/Bundle.properties-MERGED new file mode 100755 index 0000000000..c3b75fd3c4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/Bundle.properties-MERGED @@ -0,0 +1,16 @@ +XRYDataSourceProcessor.childNotReadable=Top level path [ %s ] is not readable +XRYDataSourceProcessor.dataSourceType=XRY Text Export +XRYDataSourceProcessor.fileAdded=Added %s to the case database +XRYDataSourceProcessor.ioError=I/O error occured trying to test the selected folder +XRYDataSourceProcessor.noCurrentCase=No case is open. +XRYDataSourceProcessor.noPathSelected=Please select a folder containing exported XRY text files +XRYDataSourceProcessor.notAFolder=The selected path is not a folder +XRYDataSourceProcessor.notReadable=Selected path is not readable +XRYDataSourceProcessor.notXRYFolder=Selected folder did not contain any XRY text files +XRYDataSourceProcessor.preppingFiles=Preparing to add files to the case database +XRYDataSourceProcessor.processingFiles=Processing all XRY files... +XRYDataSourceProcessor.unexpectedError=Internal error occurred while processing XRY report +XRYDataSourceProcessorConfigPanel.fileBrowserButton.text=Browse +XRYDataSourceProcessorConfigPanel.filePathTextField.text= +XRYDataSourceProcessorConfigPanel.xrySelectFolderLabel.text=Select a folder containing exported XRY text files +XRYDataSourceProcessorConfigPanel.errorLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYCallsFileParser.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYCallsFileParser.java new file mode 100755 index 0000000000..ef31f5f417 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYCallsFileParser.java @@ -0,0 +1,333 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Parses XRY Calls files and creates artifacts. + */ +final class XRYCallsFileParser extends AbstractSingleEntityParser { + + private static final Logger logger = Logger.getLogger(XRYCallsFileParser.class.getName()); + + //Pattern is in reverse due to a Java 8 bug, see calculateSecondsSinceEpoch() + //function for more details. + private static final DateTimeFormatter DATE_TIME_PARSER + = DateTimeFormatter.ofPattern("[(XXX) ][O ][(O) ]a h:m:s M/d/y"); + + private static final String DEVICE_LOCALE = "(device)"; + private static final String NETWORK_LOCALE = "(network)"; + + /** + * All of the known XRY keys for call reports and their corresponding + * blackboard attribute types, if any. + */ + private enum XryKey { + NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME), + TIME("time", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME), + DIRECTION("direction", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION), + CALL_TYPE("call type", null), + NUMBER("number", null), + TEL("tel", null), + TO("to", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO), + FROM("from", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM), + DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED), + DURATION("duration", null), + STORAGE("storage", null), + INDEX("index", null), + TYPE("type", null), + NAME("name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); + + private final String name; + private final BlackboardAttribute.ATTRIBUTE_TYPE type; + + XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type) { + this.name = name; + this.type = type; + } + + public BlackboardAttribute.ATTRIBUTE_TYPE getType() { + return type; + } + + /** + * Indicates if the display name of the XRY key is a recognized type. + */ + public static boolean contains(String key) { + try { + XryKey.fromDisplayName(key); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + /** + * Matches the display name of the xry key to the appropriate enum type. + * + * It is assumed that XRY key string is recognized. Otherwise, an + * IllegalArgumentException is thrown. Test all membership with + * contains() before hand. + */ + public static XryKey fromDisplayName(String key) { + String normalizedKey = key.trim().toLowerCase(); + for (XryKey keyChoice : XryKey.values()) { + if (normalizedKey.equals(keyChoice.name)) { + return keyChoice; + } + } + + throw new IllegalArgumentException(String.format("Key [%s] was not found." + + " All keys should be tested with contains.", key)); + } + } + + /** + * All known XRY namespaces for call reports. + */ + private enum XryNamespace { + TO("to"), + FROM("from"), + NONE(null); + + private final String name; + + XryNamespace(String name) { + this.name = name; + } + + /** + * Indicates if the display name of the XRY namespace is a recognized + * type. + */ + public static boolean contains(String xryNamespace) { + try { + XryNamespace.fromDisplayName(xryNamespace); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + /** + * Matches the display name of the xry namespace to the appropriate enum + * type. + * + * It is assumed that XRY namespace string is recognized. Otherwise, an + * IllegalArgumentException is thrown. Test all membership with + * contains() before hand. + */ + public static XryNamespace fromDisplayName(String xryNamespace) { + String normalizedNamespace = xryNamespace.trim().toLowerCase(); + for (XryNamespace keyChoice : XryNamespace.values()) { + if (normalizedNamespace.equals(keyChoice.name)) { + return keyChoice; + } + } + + throw new IllegalArgumentException(String.format("Key [%s] was not found." + + " All keys should be tested with contains.", xryNamespace)); + } + } + + @Override + boolean canProcess(XRYKeyValuePair pair) { + return XryKey.contains(pair.getKey()); + } + + @Override + boolean isNamespace(String nameSpace) { + return XryNamespace.contains(nameSpace); + } + + @Override + void makeArtifact(List keyValuePairs, Content parent) throws TskCoreException { + List attributes = new ArrayList<>(); + for(XRYKeyValuePair pair : keyValuePairs) { + Optional attribute = getBlackboardAttribute(pair); + if(attribute.isPresent()) { + attributes.add(attribute.get()); + } + } + if(!attributes.isEmpty()) { + BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG); + artifact.addAttributes(attributes); + } + } + + /** + * Creates the appropriate blackboard attribute given a single XRY Key Value + * pair, if any. Most XRY keys are mapped to an attribute type in the enum above. + */ + private Optional getBlackboardAttribute(XRYKeyValuePair pair) { + XryKey xryKey = XryKey.fromDisplayName(pair.getKey()); + XryNamespace xryNamespace = XryNamespace.NONE; + if (XryNamespace.contains(pair.getNamespace())) { + xryNamespace = XryNamespace.fromDisplayName(pair.getNamespace()); + } + + switch (xryKey) { + case TEL: + case NUMBER: + //Apply the namespace + switch (xryNamespace) { + case FROM: + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, + PARSER_NAME, pair.getValue())); + case TO: + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, + PARSER_NAME, pair.getValue())); + default: + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, + PARSER_NAME, pair.getValue())); + } + case TIME: + try { + //Tranform value to seconds since epoch + long dateTimeSinceEpoch = calculateSecondsSinceEpoch(pair.getValue()); + return Optional.of(new BlackboardAttribute(xryKey.getType(), + PARSER_NAME, dateTimeSinceEpoch)); + } catch (DateTimeParseException ex) { + logger.log(Level.WARNING, String.format("[XRY DSP] Assumption" + + " about the date time formatting of call logs is " + + "not right. Here is the value [ %s ]", pair.getValue()), ex); + return Optional.empty(); + } + default: + //Otherwise, the XryKey enum contains the correct BlackboardAttribute + //type. + if (xryKey.getType() != null) { + return Optional.of(new BlackboardAttribute(xryKey.getType(), + PARSER_NAME, pair.getValue())); + } + + logger.log(Level.INFO, String.format("[XRY DSP] Key value pair " + + "(in brackets) [ %s ] was recognized but " + + "more data or time is needed to finish implementation. Discarding... ", + pair)); + return Optional.empty(); + } + } + + /** + * Removes the locale from the date time value. + * + * Locale in this case being (Device) or (Network). + * + * @param dateTime XRY datetime value to be sanitized. + * @return A purer date time value. + */ + private String removeDateTimeLocale(String dateTime) { + String result = dateTime; + int deviceIndex = result.toLowerCase().indexOf(DEVICE_LOCALE); + if (deviceIndex != -1) { + result = result.substring(0, deviceIndex); + } + int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE); + if (networkIndex != -1) { + result = result.substring(0, networkIndex); + } + return result; + } + + /** + * Parses the date time value and calculates seconds since epoch. + * + * @param dateTime + * @return + */ + private long calculateSecondsSinceEpoch(String dateTime) { + String dateTimeWithoutLocale = removeDateTimeLocale(dateTime).trim(); + /** + * The format of time in XRY Messages reports is of the form: + * + * 1/3/1990 1:23:54 AM UTC+4 + * + * In our current version of Java (openjdk-1.8.0.222), there is a bug + * with having the timezone offset (UTC+4 or GMT-7) at the end of the + * date time input. This is fixed in later versions of the JDK (9 and + * beyond). https://bugs.openjdk.java.net/browse/JDK-8154050 Rather than + * update the JDK to accommodate this, the components of the date time + * string are reversed: + * + * UTC+4 AM 1:23:54 1/3/1990 + * + * The java time package will correctly parse this date time format. + */ + String reversedDateTime = reverseOrderOfDateTimeComponents(dateTimeWithoutLocale); + /** + * Furthermore, the DateTimeFormatter's timezone offset letter ('O') + * does not recognize UTC but recognizes GMT. According to + * https://en.wikipedia.org/wiki/Coordinated_Universal_Time, GMT only + * differs from UTC by at most 1 second and so substitution will only + * introduce a trivial amount of error. + */ + String reversedDateTimeWithGMT = reversedDateTime.replace("UTC", "GMT"); + TemporalAccessor result = DATE_TIME_PARSER.parseBest(reversedDateTimeWithGMT, + ZonedDateTime::from, + LocalDateTime::from, + OffsetDateTime::from); + //Query for the ZoneID + if (result.query(TemporalQueries.zoneId()) == null) { + //If none, assumed GMT+0. + return ZonedDateTime.of(LocalDateTime.from(result), + ZoneId.of("GMT")).toEpochSecond(); + } else { + return Instant.from(result).getEpochSecond(); + } + } + + /** + * Reverses the order of the date time components. + * + * Example: 1/3/1990 1:23:54 AM UTC+4 becomes UTC+4 AM 1:23:54 1/3/1990 + * + * @param dateTime + * @return + */ + private String reverseOrderOfDateTimeComponents(String dateTime) { + StringBuilder reversedDateTime = new StringBuilder(dateTime.length()); + String[] dateTimeComponents = dateTime.split(" "); + for (String component : dateTimeComponents) { + reversedDateTime.insert(0, " ").insert(0, component); + } + return reversedDateTime.toString().trim(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYContactsFileParser.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYContactsFileParser.java new file mode 100755 index 0000000000..b2dae939e3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYContactsFileParser.java @@ -0,0 +1,105 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Parses XRY Contacts-Contacts files and creates artifacts. + */ +final class XRYContactsFileParser extends AbstractSingleEntityParser { + + private static final Logger logger = Logger.getLogger(XRYContactsFileParser.class.getName()); + + //All of the known XRY keys for contacts. + private static final Map XRY_KEYS = + new HashMap() {{ + put("name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); + put("tel", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER); + put("mobile", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE); + put("home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME); + put("related application", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME); + put("address home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION); + put("email home", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_HOME); + put("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED); + + //Ignoring or need more information to decide. + put("storage", null); + put("other", null); + put("picture", null); + put("index", null); + put("account name", null); + + }}; + + @Override + boolean canProcess(XRYKeyValuePair pair) { + String normalizedKey = pair.getKey().toLowerCase(); + return XRY_KEYS.containsKey(normalizedKey); + } + + @Override + boolean isNamespace(String nameSpace) { + //No namespaces are currently known for this report type. + return false; + } + + /** + * Creates the appropriate blackboard attribute given a single XRY Key Value + * pair. + */ + private Optional getBlackboardAttribute(XRYKeyValuePair pair) { + String normalizedKey = pair.getKey().toLowerCase(); + BlackboardAttribute.ATTRIBUTE_TYPE attrType = XRY_KEYS.get(normalizedKey); + if(attrType != null) { + return Optional.of(new BlackboardAttribute(attrType, PARSER_NAME, pair.getValue())); + } + + logger.log(Level.INFO, String.format("[XRY DSP] Key value pair " + + "(in brackets) [ %s ] was recognized but we need " + + "more data or time to finish implementation. Discarding... ", + pair)); + return Optional.empty(); + } + + @Override + void makeArtifact(List keyValuePairs, Content parent) throws TskCoreException { + List attributes = new ArrayList<>(); + for(XRYKeyValuePair pair : keyValuePairs) { + Optional attribute = getBlackboardAttribute(pair); + if(attribute.isPresent()) { + attributes.add(attribute.get()); + } + } + if(!attributes.isEmpty()) { + BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); + artifact.addAttributes(attributes); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessor.java new file mode 100755 index 0000000000..83ddbc1f5b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessor.java @@ -0,0 +1,363 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import com.google.common.collect.Lists; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.swing.JPanel; +import javax.swing.SwingWorker; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; +import org.openide.util.lookup.ServiceProviders; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; +import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.LocalFilesDataSource; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; + +/** + * An XRY Report data source processor. + */ +@ServiceProviders(value = { + @ServiceProvider(service = DataSourceProcessor.class), + @ServiceProvider(service = AutoIngestDataSourceProcessor.class) +}) +public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDataSourceProcessor { + + private final XRYDataSourceProcessorConfigPanel configPanel; + + private static final int XRY_FILES_DEPTH = 1; + + //Background processor to relieve the EDT from adding files to the case + //database and parsing the report files. + private XRYReportProcessorSwingWorker swingWorker; + + private static final Logger logger = Logger.getLogger(XRYDataSourceProcessor.class.getName()); + + public XRYDataSourceProcessor() { + configPanel = XRYDataSourceProcessorConfigPanel.getInstance(); + } + + @Override + @NbBundle.Messages({ + "XRYDataSourceProcessor.dataSourceType=XRY Text Export" + }) + public String getDataSourceType() { + return Bundle.XRYDataSourceProcessor_dataSourceType(); + } + + @Override + public JPanel getPanel() { + return configPanel; + } + + /** + * Tests the selected path. + * + * This functions checks permissions to the path directly and then to each + * of its top most children, if it is a folder. + */ + @Override + @NbBundle.Messages({ + "XRYDataSourceProcessor.noPathSelected=Please select a folder containing exported XRY text files", + "XRYDataSourceProcessor.notReadable=Selected path is not readable", + "XRYDataSourceProcessor.notXRYFolder=Selected folder did not contain any XRY text files", + "XRYDataSourceProcessor.ioError=I/O error occured trying to test the selected folder", + "XRYDataSourceProcessor.childNotReadable=Top level path [ %s ] is not readable", + "XRYDataSourceProcessor.notAFolder=The selected path is not a folder" + }) + public boolean isPanelValid() { + configPanel.clearErrorText(); + String selectedFilePath = configPanel.getSelectedFilePath(); + if (selectedFilePath.isEmpty()) { + configPanel.setErrorText(Bundle.XRYDataSourceProcessor_noPathSelected()); + return false; + } + + File selectedFile = new File(selectedFilePath); + Path selectedPath = selectedFile.toPath(); + + //Test permissions + if (!Files.isReadable(selectedPath)) { + configPanel.setErrorText(Bundle.XRYDataSourceProcessor_notReadable()); + return false; + } + + try { + BasicFileAttributes attr = Files.readAttributes(selectedPath, + BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS); + + if (!attr.isDirectory()) { + configPanel.setErrorText(Bundle.XRYDataSourceProcessor_notAFolder()); + return false; + } + + //Ensure all of the XRY_FILES_DEPTH paths are readable. + try (Stream allFiles = Files.walk(selectedPath, XRY_FILES_DEPTH)) { + Iterator allFilesIterator = allFiles.iterator(); + while (allFilesIterator.hasNext()) { + Path currentPath = allFilesIterator.next(); + if (!Files.isReadable(currentPath)) { + Path fileName = currentPath.subpath(currentPath.getNameCount() - 2, + currentPath.getNameCount()); + configPanel.setErrorText(String.format( + Bundle.XRYDataSourceProcessor_childNotReadable(), + fileName.toString())); + return false; + } + } + } + + //Validate the folder. + if (!XRYFolder.isXRYFolder(selectedPath)) { + configPanel.setErrorText(Bundle.XRYDataSourceProcessor_notXRYFolder()); + return false; + } + } catch (IOException | UncheckedIOException ex) { + configPanel.setErrorText(Bundle.XRYDataSourceProcessor_ioError()); + logger.log(Level.WARNING, "[XRY DSP] I/O exception encountered trying to test the XRY folder.", ex); + return false; + } + + return true; + } + + /** + * Tests if the given path is an XRY Folder. + * + * This function assumes the calling thread has sufficient privileges to + * read the folder and its child content. + * + * @param dataSourcePath Path to test + * @return 100 if the folder passes the XRY Folder check, 0 otherwise. + * @throws AutoIngestDataSourceProcessorException if an I/O error occurs + * during disk reads. + */ + @Override + public int canProcess(Path dataSourcePath) throws AutoIngestDataSourceProcessorException { + try { + if (XRYFolder.isXRYFolder(dataSourcePath)) { + return 100; + } + } catch (IOException ex) { + throw new AutoIngestDataSourceProcessorException("[XRY DSP] encountered I/O error " + ex.getMessage(), ex); + } + return 0; + } + + /** + * Processes the XRY folder that the examiner selected. The heavy lifting is + * done off of the EDT, so this function will return while the + * path is still being processed. + * + * This function assumes the calling thread has sufficient privileges to + * read the folder and its child content, which should have been validated + * in isPanelValid(). + */ + @Override + @NbBundle.Messages({ + "XRYDataSourceProcessor.noCurrentCase=No case is open." + }) + public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + progressMonitor.setIndeterminate(true); + + String selectedFilePath = configPanel.getSelectedFilePath(); + File selectedFile = new File(selectedFilePath); + Path selectedPath = selectedFile.toPath(); + + try { + XRYFolder xryFolder = new XRYFolder(selectedPath); + FileManager fileManager = Case.getCurrentCaseThrows() + .getServices().getFileManager(); + String uniqueUUID = UUID.randomUUID().toString(); + //Move heavy lifting to a background task. + swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor, + callback, fileManager, uniqueUUID); + swingWorker.execute(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, + Lists.newArrayList(Bundle.XRYDataSourceProcessor_noCurrentCase(), + ex.getMessage()), Lists.newArrayList()); + } + } + + /** + * Processes the XRY Folder encountered in an auto-ingest context. The heavy + * lifting is done off of the EDT, so this function will return while the + * path is still being processed. + * + * This function assumes the calling thread has sufficient privileges to + * read the folder and its child content. + * + * @param deviceId + * @param dataSourcePath + * @param progressMonitor + * @param callBack + */ + @Override + public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) { + progressMonitor.setIndeterminate(true); + + try { + XRYFolder xryFolder = new XRYFolder(dataSourcePath); + FileManager fileManager = Case.getCurrentCaseThrows() + .getServices().getFileManager(); + //Move heavy lifting to a background task. + swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor, + callBack, fileManager, deviceId); + swingWorker.execute(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex); + callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, + Lists.newArrayList(Bundle.XRYDataSourceProcessor_noCurrentCase(), + ex.getMessage()), Lists.newArrayList()); + } + } + + @Override + public void cancel() { + if (swingWorker != null) { + swingWorker.cancel(true); + } + } + + @Override + public void reset() { + //Clear the current selected file path. + configPanel.clearSelectedFilePath(); + } + + /** + * Relieves the EDT from having to process the XRY report and write to the + * case database. + */ + private class XRYReportProcessorSwingWorker extends SwingWorker { + + private final DataSourceProcessorProgressMonitor progressMonitor; + private final DataSourceProcessorCallback callback; + private final FileManager fileManager; + private final XRYFolder xryFolder; + private final String uniqueUUID; + + public XRYReportProcessorSwingWorker(XRYFolder folder, + DataSourceProcessorProgressMonitor progressMonitor, + DataSourceProcessorCallback callback, + FileManager fileManager, + String uniqueUUID) { + + this.xryFolder = folder; + this.progressMonitor = progressMonitor; + this.callback = callback; + this.fileManager = fileManager; + this.uniqueUUID = uniqueUUID; + } + + @Override + @NbBundle.Messages({ + "XRYDataSourceProcessor.preppingFiles=Preparing to add files to the case database", + "XRYDataSourceProcessor.processingFiles=Processing all XRY files..." + }) + protected LocalFilesDataSource doInBackground() throws TskCoreException, + TskDataException, IOException { + progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_preppingFiles()); + + List nonXRYFiles = xryFolder.getNonXRYFiles(); + List filePaths = nonXRYFiles.stream() + //Map paths to string representations. + .map(Path::toString) + .collect(Collectors.toList()); + LocalFilesDataSource dataSource = fileManager.addLocalFilesDataSource( + uniqueUUID, + "XRY Text Export", //Name + "", //Timezone + filePaths, + new ProgressMonitorAdapter(progressMonitor)); + + //Process the report files. + progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_processingFiles()); + XRYReportProcessor.process(xryFolder, dataSource); + return dataSource; + } + + @Override + @NbBundle.Messages({ + "XRYDataSourceProcessor.unexpectedError=Internal error occurred while processing XRY report" + }) + public void done() { + try { + LocalFilesDataSource newDataSource = get(); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS, + Lists.newArrayList(), Lists.newArrayList(newDataSource)); + } catch (InterruptedException ex) { + logger.log(Level.WARNING, "[XRY DSP] Thread was interrupted while processing the XRY report." + + " The case may or may not have the complete XRY report.", ex); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS, + Lists.newArrayList(), Lists.newArrayList()); + } catch (ExecutionException ex) { + logger.log(Level.SEVERE, "[XRY DSP] Unexpected internal error while processing XRY report.", ex); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, + Lists.newArrayList(Bundle.XRYDataSourceProcessor_unexpectedError(), + ex.toString()), Lists.newArrayList()); + } + } + + /** + * Makes the DSP progress monitor compatible with the File Manager + * progress updater. + */ + private class ProgressMonitorAdapter implements FileManager.FileAddProgressUpdater { + + private final DataSourceProcessorProgressMonitor progressMonitor; + + ProgressMonitorAdapter(DataSourceProcessorProgressMonitor progressMonitor) { + this.progressMonitor = progressMonitor; + } + + @Override + @NbBundle.Messages({ + "XRYDataSourceProcessor.fileAdded=Added %s to the case database" + }) + public void fileAdded(AbstractFile newFile) { + progressMonitor.setProgressText(String.format(Bundle.XRYDataSourceProcessor_fileAdded(), newFile.getName())); + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessorConfigPanel.form b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessorConfigPanel.form new file mode 100755 index 0000000000..2fd4848e91 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessorConfigPanel.form @@ -0,0 +1,88 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessorConfigPanel.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessorConfigPanel.java new file mode 100755 index 0000000000..a641ea1f73 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessorConfigPanel.java @@ -0,0 +1,219 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; +import javax.swing.JFileChooser; +import javax.swing.JPanel; +import org.apache.commons.lang3.StringUtils; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; + +/** + * Allows an examiner to configure the XRY Data source processor. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives +final class XRYDataSourceProcessorConfigPanel extends JPanel { + + private static final long serialVersionUID = 1L; + + private static final String PROP_LAST_USED_PATH = "LAST_USED_PATH"; + private static final String SETTINGS_CONTEXT = "XRYDataSourceProcessorConfigPanel_Settings"; + + private static final XRYDataSourceProcessorConfigPanel INSTANCE = + new XRYDataSourceProcessorConfigPanel(); + + //Used to communicate with the DSP infrastructure. This config + //panel will indicate when it is ready for an update. + private final PropertyChangeSupport pcs; + + /** + * Creates new form XRYDataSourceConfigPanel. + * Prevent direct instantiation. + */ + private XRYDataSourceProcessorConfigPanel() { + initComponents(); + pcs = new PropertyChangeSupport(this); + } + + /** + * Persists the last used path between application runs. + */ + private void setLastUsedPath(Path selection) { + Path parent = selection.getParent(); + ModuleSettings.setConfigSetting(SETTINGS_CONTEXT, + PROP_LAST_USED_PATH, parent.toString()); + } + + /** + * Retrieves the last used path, if any. This path will be saved across + * application runs. + */ + private Optional getLastUsedPath() { + String lastFolderPath = ModuleSettings.getConfigSetting( + SETTINGS_CONTEXT, PROP_LAST_USED_PATH); + if (StringUtils.isNotBlank(lastFolderPath)) { + Path lastPath = Paths.get(lastFolderPath); + if (Files.exists(lastPath)) { + return Optional.of(lastPath); + } + } + return Optional.empty(); + } + + /** + * Gets the singleton XRYDataSourceProcessorConfigPanel. + */ + static XRYDataSourceProcessorConfigPanel getInstance() { + return INSTANCE; + } + + /** + * Clears the error label. + */ + void clearErrorText() { + errorLabel.setText(null); + } + + /** + * Sets the error label to show the supplied text. + */ + void setErrorText(String text) { + errorLabel.setText(text); + } + + /** + * Clears the selected file path. + */ + void clearSelectedFilePath() { + filePathTextField.setText(null); + } + + /** + * Gets the file path selected by the examiner. + */ + String getSelectedFilePath() { + return filePathTextField.getText(); + } + + /** + * Adds a property change listener to this config panel. + */ + @Override + public synchronized void addPropertyChangeListener(PropertyChangeListener pcl) { + super.addPropertyChangeListener(pcl); + pcs.addPropertyChangeListener(pcl); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + filePathTextField = new javax.swing.JTextField(); + fileBrowserButton = new javax.swing.JButton(); + xrySelectFolderLabel = new javax.swing.JLabel(); + errorLabel = new javax.swing.JLabel(); + + filePathTextField.setEditable(false); + filePathTextField.setText(org.openide.util.NbBundle.getMessage(XRYDataSourceProcessorConfigPanel.class, "XRYDataSourceProcessorConfigPanel.filePathTextField.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(fileBrowserButton, org.openide.util.NbBundle.getMessage(XRYDataSourceProcessorConfigPanel.class, "XRYDataSourceProcessorConfigPanel.fileBrowserButton.text")); // NOI18N + fileBrowserButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + fileBrowserButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(xrySelectFolderLabel, org.openide.util.NbBundle.getMessage(XRYDataSourceProcessorConfigPanel.class, "XRYDataSourceProcessorConfigPanel.xrySelectFolderLabel.text")); // NOI18N + + errorLabel.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(XRYDataSourceProcessorConfigPanel.class, "XRYDataSourceProcessorConfigPanel.errorLabel.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(xrySelectFolderLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(filePathTextField) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(fileBrowserButton))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(xrySelectFolderLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(filePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(fileBrowserButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(errorLabel) + .addContainerGap(235, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + /** + * Opens a JFileChooser instance so that the examiner can select a XRY + * report folder. + */ + private void fileBrowserButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileBrowserButtonActionPerformed + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + Optional lastUsedPath = getLastUsedPath(); + if(lastUsedPath.isPresent()) { + fileChooser.setCurrentDirectory(lastUsedPath.get().toFile()); + } + int returnVal = fileChooser.showOpenDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File selection = fileChooser.getSelectedFile(); + setLastUsedPath(selection.toPath()); + filePathTextField.setText(selection.getAbsolutePath()); + + //This will notify the wizard to revalidate the data source processor. + pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } + }//GEN-LAST:event_fileBrowserButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel errorLabel; + private javax.swing.JButton fileBrowserButton; + private javax.swing.JTextField filePathTextField; + private javax.swing.JLabel xrySelectFolderLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDeviceGenInfoFileParser.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDeviceGenInfoFileParser.java new file mode 100755 index 0000000000..14d8d54660 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDeviceGenInfoFileParser.java @@ -0,0 +1,171 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Parses XRY Device-General Information files and creates artifacts. + */ +final class XRYDeviceGenInfoFileParser extends AbstractSingleEntityParser { + + private static final Logger logger = Logger.getLogger(XRYDeviceGenInfoFileParser.class.getName()); + + //All known XRY keys for Device Gen Info reports. + private static final String ATTRIBUTE_KEY = "attribute"; + private static final String DATA_KEY = "data"; + + //All of the known XRY Attribute values for device gen info. The value of the + //attribute keys are actionable for this parser. + //Ex: + // Data: Nokia + // Attribute: Device Type + private static final Map XRY_ATTRIBUTE_VALUES + = new HashMap() { + { + put("device name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_NAME); + put("device type", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE); + put("mobile id (imei)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMEI); + put("security code", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PASSWORD); + put("unlock code", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PASSWORD); + put("imei/meid", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMEI); + put("model", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MODEL); + put("wifi address", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS); + put("subscriber id (imsi)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI); + + //There could be two of these on an artifact, not aware of a way + //to distinguish between two DATE_TIMEs such as the ones below. + put("device clock", null); + put("pc clock", null); + + //Ignore these for now, need more data or time to finish implementation. + put("device family", null); + put("advertising id", null); + put("device status", null); + put("baseband version", null); + put("sim status", null); + put("manufacturer", null); + put("revision", null); + } + }; + + + @Override + boolean canProcess(XRYKeyValuePair pair) { + String key = pair.getKey().trim().toLowerCase(); + return key.equals(DATA_KEY) || key.equals(ATTRIBUTE_KEY); + } + + @Override + boolean isNamespace(String nameSpace) { + //No known namespaces + return false; + } + + @Override + void makeArtifact(List keyValuePairs, Content parent) throws TskCoreException { + List attributes = new ArrayList<>(); + for(int i = 0; i < keyValuePairs.size(); i+=2) { + Optional attribute; + if(i + 1 == keyValuePairs.size()) { + attribute = getBlackboardAttribute(keyValuePairs.get(i)); + } else { + attribute = getBlackboardAttribute(keyValuePairs.get(i), keyValuePairs.get(i+1)); + } + if(attribute.isPresent()) { + attributes.add(attribute.get()); + } + } + if(!attributes.isEmpty()) { + BlackboardArtifact artifact = parent.newArtifact( + BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_INFO); + artifact.addAttributes(attributes); + } + } + + /** + * Creates the appropriate blackboard attribute given a single XRY Key Value + * pair. It is assumed that only 'Data' keys can appear by themselves. + * If a Data key is by itself, its value most closely resembles a TSK_PATH attribute. + */ + private Optional getBlackboardAttribute(XRYKeyValuePair pair) { + if (pair.hasKey(DATA_KEY)) { + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, + PARSER_NAME, pair.getValue())); + } + + logger.log(Level.WARNING, "Expected a 'Data' key value pair, but [ %s ] " + + "was found.", pair); + + return Optional.empty(); + } + + /** + * Creates the appropriate blackboard attribute given two XRY Key Value + * pairs. The expectation is that one pair is the 'Data' key and the other is + * an 'Attribute' key. If the attribute value is recognized but has no corresponding + * Blackboard attribute type, the Optional will be empty. + */ + private Optional getBlackboardAttribute(XRYKeyValuePair firstPair, XRYKeyValuePair secondPair) { + String attributeValue; + String dataValue; + if (firstPair.hasKey(DATA_KEY) && secondPair.hasKey(ATTRIBUTE_KEY)) { + dataValue = firstPair.getValue(); + attributeValue = secondPair.getValue(); + } else if (firstPair.hasKey(ATTRIBUTE_KEY) && secondPair.hasKey(DATA_KEY)) { + dataValue = secondPair.getValue(); + attributeValue = firstPair.getValue(); + } else { + logger.log(Level.WARNING, String.format("[XRY DSP] Expected these key value" + + " pairs (in brackets) [ %s ], [ %s ] to be an 'Attribute' and 'Data' " + + "pair.", firstPair, secondPair)); + return Optional.empty(); + } + + String normalizedAttributeValue = attributeValue.toLowerCase(); + if (!XRY_ATTRIBUTE_VALUES.containsKey(normalizedAttributeValue)) { + logger.log(Level.WARNING, String.format("[XRY DSP] Key value pair " + + "(in brackets) [ %s : %s ] was not recognized. Discarding... ", + attributeValue, dataValue)); + return Optional.empty(); + } + + BlackboardAttribute.ATTRIBUTE_TYPE attrType = XRY_ATTRIBUTE_VALUES.get(normalizedAttributeValue); + if (attrType == null) { + logger.log(Level.WARNING, String.format("[XRY DSP] Key value pair " + + "(in brackets) [ %s : %s ] was recognized but we need " + + "more data or time to finish implementation. Discarding... ", + attributeValue, dataValue)); + return Optional.empty(); + } + + return Optional.of(new BlackboardAttribute(attrType, PARSER_NAME, dataValue)); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileParser.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileParser.java new file mode 100755 index 0000000000..1787641e78 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileParser.java @@ -0,0 +1,46 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.io.IOException; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Interface for XRY file parsing. + */ +interface XRYFileParser { + + /** + * Parses XRY entities and creates artifacts from the interpreted content. + * + * See XRYFileReader for more information on XRY entities. It is expected + * that implementations will create artifacts on the supplied Content + * object. + * + * @param reader Produces XRY entities from a given XRY file. + * @param parent Content object that will act as the source of the + * artifacts. + * @throws IOException If an I/O error occurs during reading. + * @throws TskCoreException If an error occurs during artifact creation. + */ + void parse(XRYFileReader reader, Content parent) throws IOException, TskCoreException; + +} + diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileParserFactory.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileParserFactory.java new file mode 100755 index 0000000000..f7a28d6bff --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileParserFactory.java @@ -0,0 +1,81 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +/** + * Instantiates XRYFileParsers by report type. + */ +final class XRYFileParserFactory { + + /** + * Creates the correct implementation of a XRYFileParser for the specified + * report type. + * + * It is assumed that the report type is supported, which means the client + * needs to have tested with supports beforehand. Otherwise, an + * IllegalArgumentException is thrown. + * + * @param reportType A supported XRY report type. + * @return A XRYFileParser with defined behavior for the report type. + * @throws IllegalArgumentException if the report type is not supported or + * is null. This is a misuse of the API. It is assumed that the report type + * has been tested with the supports method. + */ + static XRYFileParser get(String reportType) { + if (reportType == null) { + throw new IllegalArgumentException("Report type cannot be null"); + } + + switch (reportType.trim().toLowerCase()) { + case "calls": + return new XRYCallsFileParser(); + case "contacts/contacts": + case "contacts": + return new XRYContactsFileParser(); + case "device/general information": + return new XRYDeviceGenInfoFileParser(); + case "messages/sms": + return new XRYMessagesFileParser(); + case "web/bookmarks": + return new XRYWebBookmarksFileParser(); + default: + throw new IllegalArgumentException(reportType + " not recognized."); + } + } + + /** + * Tests if a XRYFileParser implementation exists for the report type. + * + * @param reportType Report type to test. + * @return Indication if the report type can be parsed. + */ + static boolean supports(String reportType) { + try { + //Attempt a get. + get(reportType); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + //Prevent direct instantiation + private XRYFileParserFactory() { + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileReader.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileReader.java new file mode 100755 index 0000000000..726c86f170 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileReader.java @@ -0,0 +1,371 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.MalformedInputException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Optional; +import java.util.logging.Level; +import java.util.NoSuchElementException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.apache.commons.io.FilenameUtils; + +/** + * Extracts XRY entities and determines the report type. An example of an XRY + * entity would be: + * + * Calls # 1 + * Call Type: Missed + * Time: 1/2/2019 1:23:45 PM (Device) + * From + * Tel: 12345678 + */ +final class XRYFileReader implements AutoCloseable { + + private static final Logger logger = Logger.getLogger(XRYFileReader.class.getName()); + + //Assume UTF_16LE + private static final Charset CHARSET = StandardCharsets.UTF_16LE; + + //Assume the header begins with 'xry export'. + private static final String START_OF_HEADER = "xry export"; + + //Assume all XRY reports have the type on the 3rd line + //relative to the start of the header. + private static final int LINE_WITH_REPORT_TYPE = 3; + + //Assume all headers are 5 lines in length. + private static final int HEADER_LENGTH_IN_LINES = 5; + + //Assume TXT extension + private static final String EXTENSION = "txt"; + + //Assume 0xFFFE is the BOM + private static final int[] BOM = {0xFF, 0xFE}; + + //Entity to be consumed during file iteration. + private final StringBuilder xryEntity; + + //Underlying reader for the xry file. + private final BufferedReader reader; + + //Reference to the original xry file. + private final Path xryFilePath; + + /** + * Creates an XRYFileReader. As part of construction, the XRY file is opened + * and the reader is advanced past the header. This leaves the reader + * positioned at the start of the first XRY entity. + * + * The file is assumed to be encoded in UTF-16LE and is NOT verified to be + * an XRY file before reading. It is expected that the isXRYFile function + * has been called on the path beforehand. Otherwise, the behavior is + * undefined. + * + * @param xryFile XRY file to read. It is assumed that the caller has read + * access to the path. + * @throws IOException if an I/O error occurs. + */ + public XRYFileReader(Path xryFile) throws IOException { + reader = Files.newBufferedReader(xryFile, CHARSET); + xryFilePath = xryFile; + + //Advance the reader to the start of the header. + advanceToHeader(reader); + + //Advance the reader past the header to the start + //of the first XRY entity. + for (int i = 1; i < HEADER_LENGTH_IN_LINES; i++) { + reader.readLine(); + } + + xryEntity = new StringBuilder(); + } + + /** + * Extracts the report type from the XRY file. + * + * @return The XRY report type + * @throws IOException if an I/O error occurs. + * @throws IllegalArgumentExcepton If the XRY file does not have a report + * type. This is a misuse of the API. The validity of the Path should have + * been checked with isXRYFile before creating an XRYFileReader. + */ + public String getReportType() throws IOException { + Optional reportType = getType(xryFilePath); + if (reportType.isPresent()) { + return reportType.get(); + } + + throw new IllegalArgumentException(xryFilePath.toString() + " does not " + + "have a report type."); + } + + /** + * Returns the raw path of the XRY report file. + * + * @return + * @throws IOException + */ + public Path getReportPath() throws IOException { + return xryFilePath; + } + + /** + * Advances the reader until a valid XRY entity is detected or EOF is + * reached. + * + * @return Indication that there is another XRY entity to consume or that + * the file has been exhausted. + * @throws IOException if an I/O error occurs. + */ + public boolean hasNextEntity() throws IOException { + //Entity has yet to be consumed. + if (xryEntity.length() > 0) { + return true; + } + + String line; + while ((line = reader.readLine()) != null) { + if (marksEndOfEntity(line)) { + if (xryEntity.length() > 0) { + //Found a non empty XRY entity. + return true; + } + } else { + xryEntity.append(line).append('\n'); + } + } + + //Check if EOF was hit before an entity delimiter was found. + return xryEntity.length() > 0; + } + + /** + * Returns an XRY entity if there is one, otherwise an exception is thrown. + * Clients should test for another entity by calling hasNextEntity(). + * + * @return A non-empty XRY entity. + * @throws IOException if an I/O error occurs. + * @throws NoSuchElementException if there are no more XRY entities to + * consume. + */ + public String nextEntity() throws IOException { + if (hasNextEntity()) { + String returnVal = xryEntity.toString(); + xryEntity.setLength(0); + return returnVal; + } else { + throw new NoSuchElementException(); + } + } + + /** + * Peek at the next XRY entity without consuming it. If there are not more + * XRY entities left, an exception is thrown. Clients should test for + * another entity by calling hasNextEntity(). + * + * @return A non-empty XRY entity. + * @throws IOException if an I/O error occurs. + * @throws NoSuchElementException if there are no more XRY entities to peek. + */ + public String peek() throws IOException { + if (hasNextEntity()) { + return xryEntity.toString(); + } else { + throw new NoSuchElementException(); + } + } + + /** + * Closes any file handles this reader may have open. + * + * @throws IOException + */ + @Override + public void close() throws IOException { + reader.close(); + } + + /** + * Determines if the line encountered during file reading signifies the end + * of an XRY entity. + * + * @param line + * @return + */ + private boolean marksEndOfEntity(String line) { + return line.isEmpty(); + } + + /** + * Checks if the Path is an XRY file. In order to be an XRY file, it must + * have a txt extension, a 0xFFFE BOM (for UTF-16LE), and a non-empty report + * type. The encoding is not verified any further than checking the BOM. To + * get the report type, the file is read with a UTF-16LE decoder. If a + * failure directly related to the decoding is encountered, it is logged and + * the file is assumed not to be an XRY file. A direct consequence is that + * there may be false positives. + * + * All other I/O exceptions are propagated up. If the Path represents a + * symbolic link, this function will not follow it. + * + * @param file Path to test. It is assumed that the caller has read access + * to the file. + * @return Indicates whether the Path is a XRY file. + * + * @throws IOException if an I/O error occurs + */ + public static boolean isXRYFile(Path file) throws IOException { + String parsedExtension = FilenameUtils.getExtension(file.toString()); + + //A XRY file should have a txt extension. + if (!EXTENSION.equals(parsedExtension)) { + return false; + } + + BasicFileAttributes attr = Files.readAttributes(file, + BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS); + + //Do not follow symbolic links. XRY files cannot be a directory. + if (attr.isSymbolicLink() || attr.isDirectory()) { + return false; + } + + //Check 0xFFFE BOM + if (!isXRYBOM(file)) { + return false; + } + + try { + Optional reportType = getType(file); + //All valid XRY reports should have a type. + return reportType.isPresent(); + } catch (MalformedInputException ex) { + logger.log(Level.WARNING, String.format("File at path [%s] had " + + "0xFFFE BOM but was not encoded in UTF-16LE.", file.toString()), ex); + return false; + } + } + + /** + * Checks the leading bytes of the Path to verify they match the expected + * 0xFFFE BOM. + * + * @param file Path to check. It is assumed that the caller has read access + * to the file. + * + * @return Indication if the leading bytes match. + * @throws IOException if an I/O error occurs. + */ + private static boolean isXRYBOM(Path file) throws IOException { + try (InputStream in = Files.newInputStream(file, StandardOpenOption.READ)) { + for (int bomByte : BOM) { + if (in.read() != bomByte) { + return false; + } + } + } + + return true; + } + + /** + * Reads the report type from the Path. It is assumed that the Path will + * have a UTF-16LE encoding. A MalformedInputException will be thrown if + * there is a decoding error. + * + * @param file + * @return + * @throws IOException + */ + private static Optional getType(Path file) throws IOException { + try (BufferedReader reader = Files.newBufferedReader(file, CHARSET)) { + //Header may not start at the beginning of the file. + advanceToHeader(reader); + + //Advance the reader to the line before the report type. + for (int i = 1; i < LINE_WITH_REPORT_TYPE - 1; i++) { + reader.readLine(); + } + + String reportTypeLine = reader.readLine(); + if (reportTypeLine != null && !reportTypeLine.isEmpty()) { + return Optional.of(reportTypeLine); + } + return Optional.empty(); + } + } + + /** + * Advances the reader to the start of the header. The XRY Export header may + * not be the first n lines of the file. It may be preceded by new lines or + * white space. + * + * This function will consume the first line of the header, which will be + * 'XRY Export'. + * + * @param reader BufferedReader pointing to the xry file + * @throws IOException if an I/O error occurs + */ + private static void advanceToHeader(BufferedReader reader) throws IOException { + String line; + if((line = reader.readLine()) == null) { + return; + } + + String normalizedLine = line.trim().toLowerCase(); + if (normalizedLine.equals(START_OF_HEADER)) { + return; + } + + /** + * The first line may have 0xFFFE BOM prepended to it, which will cause + * the equality check to fail. This bit a logic will try to remove those + * bytes and attempt another check. + */ + byte[] normalizedBytes = normalizedLine.getBytes(CHARSET); + if (normalizedBytes.length > 2) { + normalizedLine = new String(normalizedBytes, 2, + normalizedBytes.length - 2, CHARSET); + if (normalizedLine.equals(START_OF_HEADER)) { + return; + } + } + + /** + * All other lines will need to match completely. + */ + while ((line = reader.readLine()) != null) { + normalizedLine = line.trim().toLowerCase(); + if (normalizedLine.equals(START_OF_HEADER)) { + return; + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFolder.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFolder.java new file mode 100755 index 0000000000..f78154d796 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFolder.java @@ -0,0 +1,135 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +/** + * Extracts XRY files and (optionally) non-XRY files from a XRY (Report) folder. + */ +final class XRYFolder { + + //Depth that will contain XRY files. All XRY files will be immediate + //children of their parent folder. + private static final int XRY_FILES_DEPTH = 1; + + //Raw path to the XRY folder. + private final Path xryFolderPath; + + public XRYFolder(Path folder) { + xryFolderPath = folder; + } + + /** + * Finds all paths in the XRY report folder which are not XRY files. Only + * the first directory level is searched. As a result, some paths may point + * to directories. + * + * @return A non-null collection of paths + * @throws IOException If an I/O error occurs. + */ + public List getNonXRYFiles() throws IOException { + try (Stream allFiles = Files.walk(xryFolderPath, XRY_FILES_DEPTH)) { + List otherFiles = new ArrayList<>(); + Iterator allFilesIterator = allFiles.iterator(); + while (allFilesIterator.hasNext()) { + Path currentPath = allFilesIterator.next(); + if (!currentPath.equals(xryFolderPath) + && !XRYFileReader.isXRYFile(currentPath)) { + otherFiles.add(currentPath); + } + } + return otherFiles; + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + /** + * Creates XRYFileReader instances for all XRY files found in the top level + * of the folder. + * + * @return A non-null collection of file readers. + * @throws IOException If an I/O error occurs. + */ + public List getXRYFileReaders() throws IOException { + try (Stream allFiles = Files.walk(xryFolderPath, XRY_FILES_DEPTH)) { + List fileReaders = new ArrayList<>(); + + Iterator allFilesIterator = allFiles.iterator(); + while (allFilesIterator.hasNext()) { + Path currentFile = allFilesIterator.next(); + if (XRYFileReader.isXRYFile(currentFile)) { + fileReaders.add(new XRYFileReader(currentFile)); + } + } + return fileReaders; + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + /** + * Searches for XRY files at the top level of a given folder. If at least + * one file matches, the entire directory is assumed to be an XRY report. + * + * This function will not follow any symbolic links, the directory is tested + * as is. + * + * @param folder Path to test. Assumes that caller has read access to the + * folder and all of the top level files. + * @return Indicates whether the Path is an XRY report. + * + * @throws IOException Error occurred during File I/O. + * @throws SecurityException If the security manager denies access to any of + * the files. + */ + public static boolean isXRYFolder(Path folder) throws IOException { + BasicFileAttributes attr = Files.readAttributes(folder, + BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS); + + if (!attr.isDirectory()) { + return false; + } + + //Files.walk by default will not follow symbolic links. + try (Stream allFiles = Files.walk(folder, XRY_FILES_DEPTH)) { + Iterator allFilesIterator = allFiles.iterator(); + while (allFilesIterator.hasNext()) { + Path currentFile = allFilesIterator.next(); + if (XRYFileReader.isXRYFile(currentFile)) { + return true; + } + } + + return false; + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYKeyValuePair.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYKeyValuePair.java new file mode 100755 index 0000000000..a14f571060 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYKeyValuePair.java @@ -0,0 +1,132 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +/** + * A XRY Key Value pair. XRY Key Value pairs make up the body of XRY entities. + * + * Example: + * + * Attribute: Device Name + * Time: 3/20/2012 12:06:30 AM + * Status: Read + */ +class XRYKeyValuePair { + + private static final char KEY_VALUE_DELIMITER = ':'; + + private final String key; + private final String value; + private final String namespace; + + XRYKeyValuePair(String key, String value, String namespace) { + this.key = key.trim(); + this.value = value.trim(); + this.namespace = namespace.trim(); + } + + /** + * Tests if the key of this pair matches some expected value. + * + * The expected value and the key of this pair will both be + * normalized (trimmed and lowercased). + * + * @param targetKey Key name to test. + */ + boolean hasKey(String targetKey) { + String normalizedKey = key.toLowerCase(); + String normalizedTargetKey = targetKey.trim().toLowerCase(); + return normalizedKey.equals(normalizedTargetKey); + } + + /** + * Retrieves the value contained within this pair. + */ + String getValue() { + return value; + } + + /** + * Retrieves the key contained within this pair. + */ + String getKey() { + return key; + } + + /** + * Retrieves the namespace contained within this pair. + */ + String getNamespace() { + return namespace; + } + + /** + * Tests if the input has the structure of a key value pair. + * + * @param xryLine XRY entity line to test. + */ + static boolean isPair(String input) { + int dataKeyIndex = input.indexOf(KEY_VALUE_DELIMITER); + //No key structure found. + return dataKeyIndex != -1; + } + + /** + * Extracts a key value pair from the input. + * + * This function assumes that there is a key value structure on the line. It + * can be verified using hasKeyValueForm(). + * + * @param input Input key value string to parse. + * @param namespace Namespace to assign to this pair. + * @return + */ + static XRYKeyValuePair from(String input, String namespace) { + if(!isPair(input)) { + throw new IllegalArgumentException("Input does not have the structure" + + " of a key value pair"); + } + + int keyIndex = input.indexOf(KEY_VALUE_DELIMITER); + String parsedKey = input.substring(0, keyIndex); + String parsedValue = input.substring(keyIndex + 1); + return new XRYKeyValuePair(parsedKey, parsedValue, namespace); + } + + /** + * Extracts a key value pair from the input. + * + * This function assumes that there is a key value structure on the line. It + * can be verified using hasKeyValueForm(). + * + * @param input Input key value string to parse. + * @return + */ + static XRYKeyValuePair from(String input) { + return from(input, ""); + } + + @Override + public String toString() { + if(namespace.isEmpty()) { + return key + " : " + value; + } + return "(Namespace: " + namespace +") " + key + " : " + value; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java new file mode 100755 index 0000000000..74242ddca5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java @@ -0,0 +1,721 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.io.IOException; +import java.nio.file.Path; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Parses Messages-SMS files and creates artifacts. + */ +final class XRYMessagesFileParser implements XRYFileParser { + + private static final Logger logger = Logger.getLogger( + XRYMessagesFileParser.class.getName()); + + private static final String PARSER_NAME = "XRY DSP"; + + //Pattern is in reverse due to a Java 8 bug, see calculateSecondsSinceEpoch() + //function for more details. + private static final DateTimeFormatter DATE_TIME_PARSER + = DateTimeFormatter.ofPattern("[(XXX) ][O ][(O) ]a h:m:s M/d/y"); + + private static final String DEVICE_LOCALE = "(device)"; + private static final String NETWORK_LOCALE = "(network)"; + + private static final int READ = 1; + private static final int UNREAD = 0; + + /** + * All of the known XRY keys for message reports and their corresponding + * blackboard attribute types, if any. + */ + private enum XryKey { + DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED), + DIRECTION("direction", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION), + MESSAGE("message", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT), + NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON), + TEXT("text", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT), + TIME("time", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME), + SERVICE_CENTER("service center", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER), + FROM("from", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM), + TO("to", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO), + //The following keys either need special processing or more time and data to find a type. + STORAGE("storage", null), + NUMBER("number", null), + TYPE("type", null), + TEL("tel", null), + FOLDER("folder", null), + NAME("name", null), + INDEX("index", null), + STATUS("status", null); + + private final String name; + private final BlackboardAttribute.ATTRIBUTE_TYPE type; + + XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type) { + this.name = name; + this.type = type; + } + + public BlackboardAttribute.ATTRIBUTE_TYPE getType() { + return type; + } + + public String getDisplayName() { + return name; + } + + /** + * Indicates if the display name of the XRY key is a recognized type. + * + * @param xryKey + * @return + */ + public static boolean contains(String name) { + try { + XryKey.fromDisplayName(name); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + /** + * Matches the display name of the xry key to the appropriate enum type. + * + * It is assumed that XRY key string is recognized. Otherwise, an + * IllegalArgumentException is thrown. Test all membership with + * contains() before hand. + * + * @param xryKey + * @return + */ + public static XryKey fromDisplayName(String name) { + String normalizedName = name.trim().toLowerCase(); + for (XryKey keyChoice : XryKey.values()) { + if (normalizedName.equals(keyChoice.name)) { + return keyChoice; + } + } + + throw new IllegalArgumentException(String.format("Key [ %s ] was not found." + + " All keys should be tested with contains.", name)); + } + } + + /** + * All of the known XRY namespaces for message reports. + */ + private enum XryNamespace { + FROM("from"), + PARTICIPANT("participant"), + TO("to"), + NONE(null); + + private final String name; + + XryNamespace(String name) { + this.name = name; + } + + /** + * Indicates if the display name of the XRY namespace is a recognized + * type. + * + * @param xryNamespace + * @return + */ + public static boolean contains(String xryNamespace) { + try { + XryNamespace.fromDisplayName(xryNamespace); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + /** + * Matches the display name of the xry namespace to the appropriate enum + * type. + * + * It is assumed that XRY namespace string is recognized. Otherwise, an + * IllegalArgumentException is thrown. Test all membership with + * contains() before hand. + * + * @param xryNamespace + * @return + */ + public static XryNamespace fromDisplayName(String xryNamespace) { + String normalizedNamespace = xryNamespace.trim().toLowerCase(); + for (XryNamespace keyChoice : XryNamespace.values()) { + if (normalizedNamespace.equals(keyChoice.name)) { + return keyChoice; + } + } + + throw new IllegalArgumentException(String.format("Namespace [%s] was not found." + + " All namespaces should be tested with contains.", xryNamespace)); + } + } + + /** + * All known XRY meta keys for message reports. + */ + private enum XryMetaKey { + REFERENCE_NUMBER("reference number"), + SEGMENT_COUNT("segments"), + SEGMENT_NUMBER("segment number"); + + private final String name; + + XryMetaKey(String name) { + this.name = name; + } + + public String getDisplayName() { + return name; + } + + /** + * Indicates if the display name of the XRY key is a recognized type. + * + * @param xryKey + * @return + */ + public static boolean contains(String name) { + try { + XryMetaKey.fromDisplayName(name); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + /** + * Matches the display name of the xry key to the appropriate enum type. + * + * It is assumed that XRY key string is recognized. Otherwise, an + * IllegalArgumentException is thrown. Test all membership with + * contains() before hand. + * + * @param xryKey + * @return + */ + public static XryMetaKey fromDisplayName(String name) { + String normalizedName = name.trim().toLowerCase(); + for (XryMetaKey keyChoice : XryMetaKey.values()) { + if (normalizedName.equals(keyChoice.name)) { + return keyChoice; + } + } + + throw new IllegalArgumentException(String.format("Key [ %s ] was not found." + + " All keys should be tested with contains.", name)); + } + } + + /** + * Message-SMS report artifacts can span multiple XRY entities and their + * attributes can span multiple lines. The "Text" and "Message" keys are the + * only known key value pair that can span multiple lines. Messages can be + * segmented, meaning that their "Text" and "Message" content can appear in + * multiple XRY entities. Our goal for a segmented message is to aggregate + * all of the text pieces and create 1 artifact. + * + * This parse implementation assumes that segments are contiguous and that + * they ascend incrementally. There are checks in place to verify this + * assumption is correct, otherwise an error will appear in the logs. + * + * @param reader The XRYFileReader that reads XRY entities from the + * Message-SMS report. + * @param parent The parent Content to create artifacts from. + * @throws IOException If an I/O error is encountered during report reading + * @throws TskCoreException If an error during artifact creation is + * encountered. + */ + @Override + public void parse(XRYFileReader reader, Content parent) throws IOException, TskCoreException { + Path reportPath = reader.getReportPath(); + logger.log(Level.INFO, String.format("[XRY DSP] Processing report at" + + " [ %s ]", reportPath.toString())); + + //Keep track of the reference numbers that have been parsed. + Set referenceNumbersSeen = new HashSet<>(); + + while (reader.hasNextEntity()) { + String xryEntity = reader.nextEntity(); + List attributes = getBlackboardAttributes(xryEntity, reader, referenceNumbersSeen); + //Only create artifacts with non-empty attributes. + if (!attributes.isEmpty()) { + BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); + artifact.addAttributes(attributes); + } + } + } + + /** + * Extracts all blackboard attributes from the XRY Entity. This function will + * unify any segmented text, if need be. + */ + private List getBlackboardAttributes(String xryEntity, + XRYFileReader reader, Set referenceValues) throws IOException { + String[] xryLines = xryEntity.split("\n"); + //First line of the entity is the title, each XRY entity is non-empty. + logger.log(Level.INFO, String.format("[XRY DSP] Processing [ %s ]", xryLines[0])); + + List attributes = new ArrayList<>(); + + //Count the key value pairs in the XRY entity. + int keyCount = getCountOfKeyValuePairs(xryLines); + for (int i = 1; i <= keyCount; i++) { + //Get the ith key value pair in the entity. Always expect to have + //a valid value. + XRYKeyValuePair pair = getKeyValuePairByIndex(xryLines, i).get(); + if (XryMetaKey.contains(pair.getKey())) { + //Skip meta keys, they are being handled seperately. + continue; + } + + if (!XryKey.contains(pair.getKey())) { + logger.log(Level.WARNING, String.format("[XRY DSP] The following key, " + + "value pair (in brackets) [ %s ], " + + "was not recognized. Discarding...", pair)); + continue; + } + + if (pair.getValue().isEmpty()) { + logger.log(Level.WARNING, String.format("[XRY DSP] The following key " + + "(in brackets) [ %s ] was recognized, but the value " + + "was empty. Discarding...", pair.getKey())); + continue; + } + + //Assume text and message are the only fields that can be segmented + //among multiple XRY entities. + if (pair.hasKey(XryKey.TEXT.getDisplayName()) + || pair.hasKey(XryKey.MESSAGE.getDisplayName())) { + String segmentedText = getSegmentedText(xryLines, reader, referenceValues); + pair = new XRYKeyValuePair(pair.getKey(), + //Assume text is segmented by word. + pair.getValue() + " " + segmentedText, + pair.getNamespace()); + } + + //Get the corresponding blackboard attribute, if any. + Optional attribute = getBlackboardAttribute(pair); + if (attribute.isPresent()) { + attributes.add(attribute.get()); + } + } + + return attributes; + } + + /** + * Counts the key value pairs in an XRY entity. Skips counting the first + * line as it is assumed to be the title. + */ + private Integer getCountOfKeyValuePairs(String[] xryEntity) { + int count = 0; + for (int i = 1; i < xryEntity.length; i++) { + if (XRYKeyValuePair.isPair(xryEntity[i])) { + count++; + } + } + return count; + } + + /** + * Builds up segmented message entities so that the text is unified for a + * single artifact. + * + * @param reader File reader that is producing XRY entities. + * @param referenceNumbersSeen All known references numbers up until this point. + * @param xryEntity The source XRY entity. + * @return + * @throws IOException + */ + private String getSegmentedText(String[] xryEntity, XRYFileReader reader, + Set referenceNumbersSeen) throws IOException { + Optional referenceNumber = getMetaKeyValue(xryEntity, XryMetaKey.REFERENCE_NUMBER); + //Check if there is any segmented text. + if (!referenceNumber.isPresent()) { + return ""; + } + + logger.log(Level.INFO, String.format("[XRY DSP] Message entity " + + "appears to be segmented with reference number [ %d ]", referenceNumber.get())); + + if (referenceNumbersSeen.contains(referenceNumber.get())) { + logger.log(Level.SEVERE, String.format("[XRY DSP] This reference [ %d ] has already " + + "been seen. This means that the segments are not " + + "contiguous. Any segments contiguous with this " + + "one will be aggregated and another " + + "(otherwise duplicate) artifact will be created.", referenceNumber.get())); + } + + referenceNumbersSeen.add(referenceNumber.get()); + + Optional segmentNumber = getMetaKeyValue(xryEntity, XryMetaKey.SEGMENT_NUMBER); + if (!segmentNumber.isPresent()) { + logger.log(Level.SEVERE, String.format("No segment " + + "number was found on the message entity" + + "with reference number [%d]", referenceNumber.get())); + return ""; + } + + StringBuilder segmentedText = new StringBuilder(); + + int currentSegmentNumber = segmentNumber.get(); + while (reader.hasNextEntity()) { + //Peek at the next to see if it has the same reference number. + String nextEntity = reader.peek(); + String[] nextEntityLines = nextEntity.split("\n"); + Optional nextReferenceNumber = getMetaKeyValue(nextEntityLines, XryMetaKey.REFERENCE_NUMBER); + + if (!nextReferenceNumber.isPresent() + || !Objects.equals(nextReferenceNumber, referenceNumber)) { + //Don't consume the next entity. It is not related + //to the current message thread. + break; + } + + //Consume the entity, it is a part of the message thread. + reader.nextEntity(); + + Optional nextSegmentNumber = getMetaKeyValue(nextEntityLines, XryMetaKey.SEGMENT_NUMBER); + + logger.log(Level.INFO, String.format("[XRY DSP] Processing [ %s ] " + + "segment with reference number [ %d ]", nextEntityLines[0], referenceNumber.get())); + + if (!nextSegmentNumber.isPresent()) { + logger.log(Level.SEVERE, String.format("[XRY DSP] Segment with reference" + + " number [ %d ] did not have a segment number associated with it." + + " It cannot be determined if the reconstructed text will be in order.", referenceNumber.get())); + } else if (nextSegmentNumber.get() != currentSegmentNumber + 1) { + logger.log(Level.SEVERE, String.format("[XRY DSP] Contiguous " + + "segments are not ascending incrementally. Encountered " + + "segment [ %d ] after segment [ %d ]. This means the reconstructed " + + "text will be out of order.", nextSegmentNumber.get(), currentSegmentNumber)); + } + + int keyCount = getCountOfKeyValuePairs(nextEntityLines); + for (int i = 1; i <= keyCount; i++) { + XRYKeyValuePair pair = getKeyValuePairByIndex(nextEntityLines, i).get(); + if (pair.hasKey(XryKey.TEXT.getDisplayName()) + || pair.hasKey(XryKey.MESSAGE.getDisplayName())) { + segmentedText.append(pair.getValue()).append(' '); + } + } + + if (nextSegmentNumber.isPresent()) { + currentSegmentNumber = nextSegmentNumber.get(); + } + } + + //Remove the trailing space. + if (segmentedText.length() > 0) { + segmentedText.setLength(segmentedText.length() - 1); + } + + return segmentedText.toString(); + } + + /** + * Extracts the value of the XRY meta key, if any. + * + * @param xryLines XRY entity to extract from. + * @param metaKey The key type to extract. + * @return + */ + private Optional getMetaKeyValue(String[] xryLines, XryMetaKey metaKey) { + for (String xryLine : xryLines) { + if (!XRYKeyValuePair.isPair(xryLine)) { + continue; + } + + XRYKeyValuePair pair = XRYKeyValuePair.from(xryLine); + if (pair.hasKey(metaKey.getDisplayName())) { + try { + return Optional.of(Integer.parseInt(pair.getValue())); + } catch (NumberFormatException ex) { + logger.log(Level.SEVERE, String.format("[XRY DSP] Value [ %s ] for " + + "meta key [ %s ] was not an integer.", pair.getValue(), metaKey), ex); + } + } + } + return Optional.empty(); + } + + /** + * Extracts the ith XRY Key Value pair in the XRY Entity. + * + * The total number of pairs can be determined via getCountOfKeyValuePairs(). + * + * @param xryLines XRY entity. + * @param index The requested Key Value pair. + * @return + */ + private Optional getKeyValuePairByIndex(String[] xryLines, int index) { + int pairsParsed = 0; + String namespace = ""; + for (int i = 1; i < xryLines.length; i++) { + String xryLine = xryLines[i]; + if (XryNamespace.contains(xryLine)) { + namespace = xryLine.trim(); + continue; + } + + if (!XRYKeyValuePair.isPair(xryLine)) { + logger.log(Level.SEVERE, String.format("[XRY DSP] Expected a key value " + + "pair on this line (in brackets) [ %s ], but one was not detected." + + " Discarding...", xryLine)); + continue; + } + + XRYKeyValuePair pair = XRYKeyValuePair.from(xryLine); + String value = pair.getValue(); + //Build up multiple lines. + for (; (i + 1) < xryLines.length + && !XRYKeyValuePair.isPair(xryLines[i + 1]) + && !XryNamespace.contains(xryLines[i + 1]); i++) { + String continuedValue = xryLines[i + 1].trim(); + //Assume multi lined values are split by word. + value = value + " " + continuedValue; + } + + pair = new XRYKeyValuePair(pair.getKey(), value, namespace); + pairsParsed++; + if (pairsParsed == index) { + return Optional.of(pair); + } + } + + return Optional.empty(); + } + + /** + * Creates an attribute from the extracted key value pair. + * + * @param nameSpace The namespace of this key value pair. It will have been + * verified beforehand, otherwise it will be NONE. + * @param key The recognized XRY key. + * @param value The value associated with that key. + * @return Corresponding blackboard attribute, if any. + */ + private Optional getBlackboardAttribute(XRYKeyValuePair pair) { + XryNamespace namespace = XryNamespace.NONE; + if (XryNamespace.contains(pair.getNamespace())) { + namespace = XryNamespace.fromDisplayName(pair.getNamespace()); + } + XryKey key = XryKey.fromDisplayName(pair.getKey()); + String normalizedValue = pair.getValue().toLowerCase().trim(); + + switch (key) { + case TEL: + case NUMBER: + switch (namespace) { + case FROM: + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, + PARSER_NAME, pair.getValue())); + case TO: + case PARTICIPANT: + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, + PARSER_NAME, pair.getValue())); + default: + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, + PARSER_NAME, pair.getValue())); + } + case TIME: + try { + //Tranform value to seconds since epoch + long dateTimeSinceInEpoch = calculateSecondsSinceEpoch(pair.getValue()); + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START, + PARSER_NAME, dateTimeSinceInEpoch)); + } catch (DateTimeParseException ex) { + logger.log(Level.WARNING, String.format("[XRY DSP] Assumption" + + " about the date time formatting of messages is " + + "not right. Here is the pair [ %s ]", pair), ex); + return Optional.empty(); + } + case TYPE: + switch (normalizedValue) { + case "incoming": + case "outgoing": + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, + PARSER_NAME, pair.getValue())); + case "deliver": + case "submit": + case "status report": + //Ignore for now. + return Optional.empty(); + default: + logger.log(Level.WARNING, String.format("[XRY DSP] Unrecognized " + + " value for key pair [ %s ].", pair)); + return Optional.empty(); + } + case STATUS: + switch (normalizedValue) { + case "read": + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS, + PARSER_NAME, READ)); + case "unread": + return Optional.of(new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS, + PARSER_NAME, UNREAD)); + case "sending failed": + case "deleted": + case "unsent": + case "sent": + //Ignore for now. + return Optional.empty(); + default: + logger.log(Level.WARNING, String.format("[XRY DSP] Unrecognized " + + " value for key pair [ %s ].", pair)); + return Optional.empty(); + } + default: + //Otherwise, the XryKey enum contains the correct BlackboardAttribute + //type. + if (key.getType() != null) { + return Optional.of(new BlackboardAttribute(key.getType(), + PARSER_NAME, pair.getValue())); + } + + logger.log(Level.INFO, String.format("[XRY DSP] Key value pair " + + "(in brackets) [ %s ] was recognized but " + + "more data or time is needed to finish implementation. Discarding... ", pair)); + + return Optional.empty(); + } + } + + /** + * Removes the locale from the date time value. + * + * Locale in this case being (Device) or (Network). + * + * @param dateTime XRY datetime value to be sanitized. + * @return A purer date time value. + */ + private String removeDateTimeLocale(String dateTime) { + String result = dateTime; + int deviceIndex = result.toLowerCase().indexOf(DEVICE_LOCALE); + if (deviceIndex != -1) { + result = result.substring(0, deviceIndex); + } + int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE); + if (networkIndex != -1) { + result = result.substring(0, networkIndex); + } + return result; + } + + /** + * Parses the date time value and calculates seconds since epoch. + * + * @param dateTime + * @return + */ + private long calculateSecondsSinceEpoch(String dateTime) { + String dateTimeWithoutLocale = removeDateTimeLocale(dateTime).trim(); + /** + * The format of time in XRY Messages reports is of the form: + * + * 1/3/1990 1:23:54 AM UTC+4 + * + * In our current version of Java (openjdk-1.8.0.222), there is a bug + * with having the timezone offset (UTC+4 or GMT-7) at the end of the + * date time input. This is fixed in later versions of the JDK (9 and + * beyond). https://bugs.openjdk.java.net/browse/JDK-8154050 Rather than + * update the JDK to accommodate this, the components of the date time + * string are reversed: + * + * UTC+4 AM 1:23:54 1/3/1990 + * + * The java time package will correctly parse this date time format. + */ + String reversedDateTime = reverseOrderOfDateTimeComponents(dateTimeWithoutLocale); + /** + * Furthermore, the DateTimeFormatter's timezone offset letter ('O') + * does not recognize UTC but recognizes GMT. According to + * https://en.wikipedia.org/wiki/Coordinated_Universal_Time, GMT only + * differs from UTC by at most 1 second and so substitution will only + * introduce a trivial amount of error. + */ + String reversedDateTimeWithGMT = reversedDateTime.replace("UTC", "GMT"); + TemporalAccessor result = DATE_TIME_PARSER.parseBest(reversedDateTimeWithGMT, + ZonedDateTime::from, + LocalDateTime::from, + OffsetDateTime::from); + //Query for the ZoneID + if (result.query(TemporalQueries.zoneId()) == null) { + //If none, assumed GMT+0. + return ZonedDateTime.of(LocalDateTime.from(result), + ZoneId.of("GMT")).toEpochSecond(); + } else { + return Instant.from(result).getEpochSecond(); + } + } + + /** + * Reverses the order of the date time components. + * + * Example: 1/3/1990 1:23:54 AM UTC+4 becomes UTC+4 AM 1:23:54 1/3/1990 + * + * @param dateTime + * @return + */ + private String reverseOrderOfDateTimeComponents(String dateTime) { + StringBuilder reversedDateTime = new StringBuilder(dateTime.length()); + String[] dateTimeComponents = dateTime.split(" "); + for (String component : dateTimeComponents) { + reversedDateTime.insert(0, " ").insert(0, component); + } + return reversedDateTime.toString().trim(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYReportProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYReportProcessor.java new file mode 100755 index 0000000000..a67ce772f4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYReportProcessor.java @@ -0,0 +1,79 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.io.IOException; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Processes all XRY files in an XRY folder. + */ +final class XRYReportProcessor { + + private static final Logger logger = Logger.getLogger(XRYReportProcessor.class.getName()); + + /** + * Processes all XRY Files and creates artifacts on the given Content + * instance. + * + * All resources will be closed if an exception is encountered. + * + * @param folder XRY folder to process + * @param parent Content instance to hold newly created artifacts. + * @throws IOException If an I/O exception occurs. + * @throws TskCoreException If an error occurs adding artifacts. + */ + static void process(XRYFolder folder, Content parent) throws IOException, TskCoreException { + //Get all XRY file readers from this folder. + List xryFileReaders = folder.getXRYFileReaders(); + + try { + for (XRYFileReader xryFileReader : xryFileReaders) { + String reportType = xryFileReader.getReportType(); + if (XRYFileParserFactory.supports(reportType)) { + XRYFileParser parser = XRYFileParserFactory.get(reportType); + parser.parse(xryFileReader, parent); + } else { + logger.log(Level.WARNING, String.format("[XRY DSP] XRY File (in brackets) " + + "[ %s ] was found, but no parser to support its report type exists. " + + "Report type is [ %s ]", xryFileReader.getReportPath().toString(), reportType)); + } + } + } finally { + try { + //Try to close all resources + for (XRYFileReader xryFileReader : xryFileReaders) { + xryFileReader.close(); + } + } catch (IOException ex) { + logger.log(Level.WARNING, "[XRY DSP] Encountered I/O exception trying " + + "to close all xry file readers.", ex); + } + } + } + + //Prevent direct instantiation. + private XRYReportProcessor() { + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYWebBookmarksFileParser.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYWebBookmarksFileParser.java new file mode 100755 index 0000000000..9678db0274 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYWebBookmarksFileParser.java @@ -0,0 +1,83 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourceprocessors.xry; + +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Parses XRY Web-Bookmark files and creates artifacts. + */ +final class XRYWebBookmarksFileParser extends AbstractSingleEntityParser { + + //All known XRY keys for web bookmarks. + private static final Map XRY_KEYS + = new HashMap() { + { + put("web address", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL); + put("domain", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN); + put("application", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME); + } + }; + + @Override + boolean canProcess(XRYKeyValuePair pair) { + String normalizedKey = pair.getKey().toLowerCase(); + return XRY_KEYS.containsKey(normalizedKey); + } + + @Override + boolean isNamespace(String nameSpace) { + //No known namespaces for web reports. + return false; + } + + /** + * Creates the appropriate blackboard attribute given a single XRY Key Value + * pair. + */ + private Optional getBlackboardAttribute(XRYKeyValuePair pair) { + String normalizedKey = pair.getKey().toLowerCase(); + return Optional.of(new BlackboardAttribute( + XRY_KEYS.get(normalizedKey), + PARSER_NAME, pair.getValue())); + } + + @Override + void makeArtifact(List keyValuePairs, Content parent) throws TskCoreException { + List attributes = new ArrayList<>(); + for(XRYKeyValuePair pair : keyValuePairs) { + Optional attribute = getBlackboardAttribute(pair); + if(attribute.isPresent()) { + attributes.add(attribute.get()); + } + } + if(!attributes.isEmpty()) { + BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK); + artifact.addAttributes(attributes); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/diagnostics/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/diagnostics/Bundle_ja.properties index 860af87697..c21e75e73c 100644 --- a/Core/src/org/sleuthkit/autopsy/diagnostics/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/diagnostics/Bundle_ja.properties @@ -1,22 +1,29 @@ -CTL_PerformancePanelAction=\u30D1\u30D5\u30A9\u30FC\u30DE\u30F3\u30B9\u8A3A\u65AD -PerformancePanel.cpuTest.basemsg=CPU\u30C6\u30B9\u30C8\u5B9F\u884C\u4E2D -PerformancePanel.cpuTest.cpuLabel.MBHashedPerSec.text=\uFF11\u79D2\u3054\u3068\u306B{0}MB\u30CF\u30C3\u30B7\u30E5\u5B8C\u4E86 -PerformancePanel.cpuTest.cpuLabel.md5AlgNotFound.text=MD5\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F -PerformancePanel.dbTest.dbLabel.errPerformQuery.text=\u30AF\u30A8\u30EA\u3092\u5B9F\u884C\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -PerformancePanel.dbTest.dbLabel.recordsPerSec.text=\uFF11\u79D2\u3054\u3068\u306B{0}\u30EC\u30B3\u30FC\u30C9 -PerformancePanel.dbTest.status.running=DB\u30C6\u30B9\u30C8\u3092\u5B9F\u884C\u4E2D -PerformancePanel.done.statusMsg.err.text=\u30A8\u30E9\u30FC\uFF1A{0} -PerformancePanel.fileReadLabel.errMakeFileReader.text=\u30D5\u30A1\u30A4\u30EB\u30EA\u30FC\u30C0\u30FC\u3092\u4F5C\u6210\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -PerformancePanel.fileReadLabel.imgPathNotExist.text=\u30A4\u30E1\u30FC\u30B8\u30D1\u30B9\u304C\u5B58\u5728\u3057\u307E\u305B\u3093 -PerformancePanel.FileReadTest.fileReadLabel.skipped.text=\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u307E\u3057\u305F -PerformancePanel.FileReadTest.statusMsg.runningFileReadTest.text=\u30D5\u30A1\u30A4\u30EB\u30EA\u30FC\u30C7\u30A3\u30F3\u30B0\u30C6\u30B9\u30C8\u3092\u5B9F\u884C\u4E2D -PerformancePanel.imgTest.statusMsg.runningImgReadTest.text=\u30A4\u30E1\u30FC\u30B8\u30EA\u30FC\u30C7\u30A3\u30F3\u30B0\u30C6\u30B9\u30C8\u5B9F\u884C\u4E2D -PerformancePanel.jLabel1.text=\u30A4\u30E1\u30FC\u30B8\u30EA\u30FC\u30C7\u30A3\u30F3\u30B0\uFF1A -PerformancePanel.jLabel2.text=\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u30EA\u30FC\u30C7\u30A3\u30F3\u30B0\uFF1A -PerformancePanel.jLabel3.text=CPU\uFF1A -PerformancePanel.jLabel4.text=\u30D5\u30A1\u30A4\u30EB\u30EA\u30FC\u30C7\u30A3\u30F3\u30B0\uFF1A -PerformancePanel.jLabel5.text=\u30B7\u30B9\u30C6\u30E0\u306E\u30DC\u30C8\u30EB\u30CD\u30C3\u30AF\u3092\u7279\u5B9A\u3059\u308B\u4E00\u9023\u306E\u30C6\u30B9\u30C8\u3092\u884C\u3046\u30D1\u30CD\u30EB\u3067\u3059\u3002 -PerformancePanel.label.caseNotOpen.text=\u30B1\u30FC\u30B9\u304C\u958B\u3044\u3066\u3044\u307E\u305B\u3093 -PerformancePanel.label.noImgInCase.text=\u30B1\u30FC\u30B9\u306B\u30A4\u30E1\u30FC\u30B8\u304C\u3042\u308A\u307E\u305B\u3093 -PerformancePanel.startButton.text=\u958B\u59CB -PerformancePanel.title=\u30D1\u30D5\u30A9\u30FC\u30DE\u30F3\u30B9\u8A3A\u65AD \ No newline at end of file +CTL_PerformancePanelAction=\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u8a3a\u65ad +PerformancePanel.jLabel1.text=\u30a4\u30e1\u30fc\u30b8\u306e\u8aad\u307f\u8fbc\u307f: +PerformancePanel.jLabel2.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u8aad\u307f\u8fbc\u307f: +PerformancePanel.jLabel3.text=CPU: +PerformancePanel.imgReadLabel.text=\ +PerformancePanel.dbReadLabel.text=\ +PerformancePanel.cpuTimeLabel.text=\ +PerformancePanel.startButton.text=\u958b\u59cb +PerformancePanel.statusLabel.text=\ +PerformancePanel.jLabel4.text=\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u8fbc\u307f: +PerformancePanel.fileReadLabel.text=\ +PerformancePanel.jLabel5.text=\u3053\u306e\u30d1\u30cd\u30eb\u306f\u4e00\u9023\u306e\u30c6\u30b9\u30c8\u3092\u5b9f\u65bd\u3059\u308b\u3053\u3068\u3067\u3001\u30b7\u30b9\u30c6\u30e0\u306e\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u306e\u7279\u5b9a\u3092\u652f\u63f4\u3057\u307e\u3059\u3002 +PerformancePanel.title=\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u8a3a\u65ad +PerformancePanel.cpuTest.basemsg=CPU\u30c6\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d\u3067\u3059 +PerformancePanel.cpuTest.cpuLabel.md5AlgNotFound.text=MD5\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +PerformancePanel.cpuTest.cpuLabel.MBHashedPerSec.text=\u30cf\u30c3\u30b7\u30e5\u6e08\u307f {0} MB / \u79d2 +PerformancePanel.imgTest.statusMsg.runningImgReadTest.text=\u30a4\u30e1\u30fc\u30b8\u8aad\u307f\u8fbc\u307f\u30c6\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d\u3067\u3059 +PerformancePanel.label.caseNotOpen.text=\u30b1\u30fc\u30b9\u304c\u958b\u3044\u3066\u3044\u307e\u305b\u3093 +PerformancePanel.label.noImgInCase.text=\u30b1\u30fc\u30b9\u306b\u30a4\u30e1\u30fc\u30b8\u304c\u3042\u308a\u307e\u305b\u3093 +PerformancePanel.ImgTest.imgLabel.MBReadPerSec.text=\u8aad\u307f\u53d6\u308a\u6e08\u307f {0} MB / \u79d2({1}) +PerformancePanel.FileReadTest.fileReadLabel.skipped.text=\u30b9\u30ad\u30c3\u30d7\u3055\u308c\u307e\u3057\u305f +PerformancePanel.FileReadTest.statusMsg.runningFileReadTest.text=\u30d5\u30a1\u30a4\u30eb\u8aad\u307f\u8fbc\u307f\u30c6\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d\u3067\u3059 +PerformancePanel.fileReadLabel.imgPathNotExist.text=\u30a4\u30e1\u30fc\u30b8\u30d1\u30b9\u304c\u5b58\u5728\u3057\u307e\u305b\u3093 +PerformancePanel.fileReadLabel.errMakeFileReader.text=\u30d5\u30a1\u30a4\u30eb\u30ea\u30fc\u30c0\u30fc\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PerformancePanel.ImgTest.fileReadLabel.MBReadPerSec.text=\u8aad\u307f\u53d6\u308a\u6e08\u307f {0} MB / \u79d2({1}) +PerformancePanel.dbTest.status.running=DB\u30c6\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d\u3067\u3059 +PerformancePanel.dbTest.dbLabel.recordsPerSec.text={0} \u30ec\u30b3\u30fc\u30c9 / \u79d2 +PerformancePanel.dbTest.dbLabel.errPerformQuery.text=\u30af\u30a8\u30ea\u3092\u5b9f\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PerformancePanel.done.statusMsg.err.text=\u30a8\u30e9\u30fc: {0} diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties index 4badcfe9d8..100b2fd67b 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties @@ -65,14 +65,6 @@ ExplorerNodeActionVisitor.volDetail.noVolMatchErr=Error: No Volume Matches. ExplorerNodeActionVisitor.imgDetail.noVolMatchesErr=Error: No Volume Matches. ExplorerNodeActionVisitor.exception.probGetParent.text=Problem getting parent from {0}: {1} ExtractAction.title.extractFiles.text=Extract File(s) -ExtractAction.extractFiles.cantCreateFolderErr.msg=Could not create selected folder. -ExtractAction.confDlg.destFileExist.msg=Destination file {0} already exists, overwrite? -ExtractAction.confDlg.destFileExist.title=File Exists -ExtractAction.msgDlg.cantOverwriteFile.msg=Could not overwrite existing file {0} -ExtractAction.notifyDlg.noFileToExtr.msg=No file(s) to extract. -ExtractAction.progress.extracting=Extracting -ExtractAction.progress.cancellingExtraction={0} (Cancelling...) -ExtractAction.done.notifyMsg.fileExtr.text=File(s) extracted. ExtractUnallocAction.notifyMsg.unallocAlreadyBeingExtr.msg=Unallocated Space is already being extracted on this Image. Please select a different Image. ExtractUnallocAction.msgDlg.folderDoesntExist.msg=Folder does not exist. Please choose a valid folder before continuing ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg=Select directory to save to diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED index f70a374bc5..63b416b176 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED @@ -25,7 +25,6 @@ ExternalViewerAction.actionPerformed.failure.support.message=This platform (oper ExternalViewerAction.actionPerformed.failure.title=Open File Failure {0} ExternalViewerAction.actionPerformed.urlFailure.title=Open URL Failure ExternalViewerShortcutAction.title.text=Open in External Viewer Ctrl+E -ExtractAction.noOpenCase.errMsg=No open case available. ExtractUnallocAction.imageError=Error extracting unallocated space from image ExtractUnallocAction.noFiles=No unallocated files found on volume ExtractUnallocAction.noOpenCase.errMsg=No open case available. @@ -107,14 +106,6 @@ ExplorerNodeActionVisitor.volDetail.noVolMatchErr=Error: No Volume Matches. ExplorerNodeActionVisitor.imgDetail.noVolMatchesErr=Error: No Volume Matches. ExplorerNodeActionVisitor.exception.probGetParent.text=Problem getting parent from {0}: {1} ExtractAction.title.extractFiles.text=Extract File(s) -ExtractAction.extractFiles.cantCreateFolderErr.msg=Could not create selected folder. -ExtractAction.confDlg.destFileExist.msg=Destination file {0} already exists, overwrite? -ExtractAction.confDlg.destFileExist.title=File Exists -ExtractAction.msgDlg.cantOverwriteFile.msg=Could not overwrite existing file {0} -ExtractAction.notifyDlg.noFileToExtr.msg=No file(s) to extract. -ExtractAction.progress.extracting=Extracting -ExtractAction.progress.cancellingExtraction={0} (Cancelling...) -ExtractAction.done.notifyMsg.fileExtr.text=File(s) extracted. ExtractUnallocAction.notifyMsg.unallocAlreadyBeingExtr.msg=Unallocated Space is already being extracted on this Image. Please select a different Image. ExtractUnallocAction.msgDlg.folderDoesntExist.msg=Folder does not exist. Please choose a valid folder before continuing ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg=Select directory to save to diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle_ja.properties index 567e7b1478..1d92def2e4 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle_ja.properties @@ -1,8 +1,44 @@ -CTL_DirectoryTreeTopComponent=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c4\u30ea\u30fc -HINT_DirectoryTreeTopComponent=\u3053\u308c\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c4\u30ea\u30fc\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u3059 -OpenIDE-Module-Name=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c4\u30ea\u30fc -FileSystemDetailsPanel.imgOffsetLabel.text=\u30a4\u30e1\u30fc\u30b8\u30aa\u30d5\u30bb\u30c3\u30c8\uff1a -FileSystemDetailsPanel.fsTypeLabel.text=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u30bf\u30a4\u30d7\uff1a +CSVWriter.done.notifyMsg.error=CSV \u30d5\u30a1\u30a4\u30eb\u3078\u306e\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +# {0} - \u51fa\u529b\u30d5\u30a1\u30a4\u30eb +CSVWriter.done.notifyMsg.success={0} \u306b\u66f8\u304d\u8fbc\u307f\u307e\u3057\u305f +CSVWriter.progress.cancelling=\u53d6\u308a\u6d88\u3057\u4e2d\u3067\u3059 +CSVWriter.progress.extracting=CSV \u30d5\u30a1\u30a4\u30eb\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u4e2d\u3067\u3059 +CTL_DirectoryTreeTopComponent=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u30d5\u30a1\u30a4\u30eb +DataResultFilterNode.viewSourceArtifact.text=\u30bd\u30fc\u30b9\u7d50\u679c\u3092\u8868\u793a +# {0} - dataSourceCount +DirectoryTreeTopComponent.componentOpened.groupDataSources.text=\u3053\u306e\u30b1\u30fc\u30b9\u306b\u306f {0} \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5225\u306b\u30b0\u30eb\u30fc\u30d7\u5316\u3057\u3066\u8aad\u307f\u8fbc\u307f\u3092\u65e9\u304f\u3057\u307e\u3059\u304b? +DirectoryTreeTopComponent.componentOpened.groupDataSources.title=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5225\u306b\u30b0\u30eb\u30fc\u30d7\u5316\u3057\u307e\u3059\u304b? +DirectoryTreeTopComponent.emptyMimeNode.text=\u30c7\u30fc\u30bf\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u8b58\u5225\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +DirectoryTreeTopComponent.resultsView.title=\u30ea\u30c3\u30b9\u30f3\u4e2d\u3067\u3059 +ExportCSV.saveNodesToCSV.empty=\u30c7\u30fc\u30bf\u306f\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f +# {0} - \u51fa\u529b\u30d5\u30a1\u30a4\u30eb +ExportCSV.saveNodesToCSV.fileExists=\u30d5\u30a1\u30a4\u30eb {0} \u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059 +ExportCSV.saveNodesToCSV.noCurrentCase=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093 +ExportCSV.title.text=\u9078\u629e\u3057\u305f\u884c\u3092CSV\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +ExternalViewerAction.actionPerformed.failure.exe.message=\u30d5\u30a1\u30a4\u30eb\u306f\u5b9f\u884c\u53ef\u80fd\u30d5\u30a1\u30a4\u30eb\u3067\u3042\u308b\u305f\u3081\u958b\u304d\u307e\u305b\u3093\u3002 +ExternalViewerAction.actionPerformed.failure.IO.message=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u30d5\u30a1\u30a4\u30eb\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30a8\u30c7\u30a3\u30bf\u30fc\u304c\u306a\u3044\u304b\u3001\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ExternalViewerAction.actionPerformed.failure.missingFile.message=\u30d5\u30a1\u30a4\u30eb\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +ExternalViewerAction.actionPerformed.failure.open.url=URL\u3092\u958b\u3051\u307e\u305b\u3093 +ExternalViewerAction.actionPerformed.failure.permission.message=\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u304f\u8a31\u53ef\u304c\u62d2\u5426\u3055\u308c\u307e\u3057\u305f\u3002 +ExternalViewerAction.actionPerformed.failure.support.message=\u3053\u306e\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0(\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0)\u306f\u3053\u306e\u65b9\u6cd5\u3067\u30a8\u30c7\u30a3\u30bf\u30fc\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u304f\u3053\u3068\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u305b\u3093\u3002 +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +ExternalViewerAction.actionPerformed.failure.title=\u30d5\u30a1\u30a4\u30eb\u30aa\u30fc\u30d7\u30f3\u5931\u6557 {0} +ExternalViewerAction.actionPerformed.urlFailure.title=URL\u30aa\u30fc\u30d7\u30f3\u5931\u6557 +ExternalViewerShortcutAction.title.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f Ctrl+E +ExtractAction.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +ExtractUnallocAction.imageError=\u30a4\u30e1\u30fc\u30b8\u304b\u3089\u672a\u4f7f\u7528\u9818\u57df\u306e\u62bd\u51fa\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ExtractUnallocAction.noFiles=\u30dc\u30ea\u30e5\u30fc\u30e0\u306b\u672a\u4f7f\u7528\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +ExtractUnallocAction.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +ExtractUnallocAction.volumeError=\u30dc\u30ea\u30e5\u30fc\u30e0\u304b\u3089\u672a\u4f7f\u7528\u9818\u57df\u306e\u62bd\u51fa\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +# {0} - fileName +ExtractUnallocAction.volumeInProgress=\u672a\u4f7f\u7528\u9818\u57df\u3092\u3059\u3067\u306b {0} \u306b\u62bd\u51fa\u4e2d\u3067\u3059 - \u3053\u306e\u30dc\u30ea\u30e5\u30fc\u30e0\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059 +FileSystemDetailsAction.title.text=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u8a73\u7d30 +# {0} - dataSourceCount +GroupDataSourcesDialog.groupDataSources.text=\u3053\u306e\u30b1\u30fc\u30b9\u306b\u306f {0} \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002 +HINT_DirectoryTreeTopComponent=\u3053\u308c\u306fDirectoryTree\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u3059 +OpenIDE-Module-Name=DirectoryTree +FileSystemDetailsPanel.imgOffsetLabel.text=\u30a4\u30e1\u30fc\u30b8\u30aa\u30d5\u30bb\u30c3\u30c8: +FileSystemDetailsPanel.fsTypeLabel.text=FileSystem\u30bf\u30a4\u30d7: FileSystemDetailsPanel.jLabel2.text=\u30d0\u30a4\u30c8 FileSystemDetailsPanel.jLabel3.text=\u30d0\u30a4\u30c8 FileSystemDetailsPanel.fsTypeValue.text=... @@ -13,73 +49,123 @@ FileSystemDetailsPanel.blockCountValue.text=... FileSystemDetailsPanel.rootInumValue.text=... FileSystemDetailsPanel.firstInumValue.text=... FileSystemDetailsPanel.lastInumValue.text=... -FileSystemDetailsPanel.volumeIDLabel.text=\u30dc\u30ea\u30e5\u30fc\u30e0ID\uff1a -FileSystemDetailsPanel.blockSizeLabel.text=\u30d6\u30ed\u30c3\u30af\u30b5\u30a4\u30ba\uff1a -FileSystemDetailsPanel.blockCountLabel.text=\u30d6\u30ed\u30c3\u30af\u6570\uff1a -FileSystemDetailsPanel.rootInumLabel.text=\u30eb\u30fc\u30c8\u30e1\u30bf\u30c7\u30fc\u30bf\u30a8\u30f3\u30c8\u30ea\u30fc\uff1a -FileSystemDetailsPanel.firstInumLabel.text=\u6700\u521d\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u30a8\u30f3\u30c8\u30ea\u30fc\uff1a -FileSystemDetailsPanel.lastInumLabel.text=\u6700\u5f8c\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u30a8\u30f3\u30c8\u30ea\u30fc\uff1a +FileSystemDetailsPanel.volumeIDLabel.text=\u30dc\u30ea\u30e5\u30fc\u30e0ID: +FileSystemDetailsPanel.blockSizeLabel.text=\u30d6\u30ed\u30c3\u30af\u30b5\u30a4\u30ba: +FileSystemDetailsPanel.blockCountLabel.text=\u30d6\u30ed\u30c3\u30af\u30ab\u30a6\u30f3\u30c8: +FileSystemDetailsPanel.rootInumLabel.text=\u30eb\u30fc\u30c8\u30e1\u30bf\u30c7\u30fc\u30bf\u5165\u529b: +FileSystemDetailsPanel.firstInumLabel.text=\u6700\u521d\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u5165\u529b: +FileSystemDetailsPanel.lastInumLabel.text=\u6700\u5f8c\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u5165\u529b: FileSystemDetailsPanel.OKButton.text=OK -VolumeDetailsPanel.volumeIDLabel.text=\u30dc\u30ea\u30e5\u30fc\u30e0ID\uff1a +SelectionContext.dataSourceFiles=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb +SelectionContext.dataSources=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 +SelectionContext.views=\u30d3\u30e5\u30fc +ViewContextAction.errorMessage.cannotFindDirectory=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u898b\u3064\u3051\u3089\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ViewContextAction.errorMessage.cannotFindNode=\u30c4\u30ea\u30fc\u5185\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30ce\u30fc\u30c9\u3092\u898b\u3064\u3051\u3089\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ViewContextAction.errorMessage.cannotSelectDirectory=\u30c4\u30ea\u30fc\u5185\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u9078\u629e\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +VolumeDetailsPanel.volumeIDLabel.text=\u30dc\u30ea\u30e5\u30fc\u30e0ID: VolumeDetailsPanel.volumeIDValue.text=... VolumeDetailsPanel.startValue.text=... VolumeDetailsPanel.lengthValue.text=... VolumeDetailsPanel.descValue.text=... VolumeDetailsPanel.flagsValue.text=... -VolumeDetailsPanel.startLabel.text=\u6700\u521d\u306e\u30bb\u30af\u30bf\u30fc\uff1a -VolumeDetailsPanel.lengthLabel.text=\u30bb\u30af\u30bf\u30fc\u306e\u9577\u3055\uff1a -VolumeDetailsPanel.descLabel.text=\u8aac\u660e\uff1a -VolumeDetailsPanel.flagsLabel.text=\u30d5\u30e9\u30b0\uff1a -VolumeDetailsPanel.jLabel1.text=\u30dc\u30ea\u30e5\u30fc\u30e0\u4e00\u822c\u60c5\u5831 +VolumeDetailsPanel.startLabel.text=\u30bb\u30af\u30bf\u30fc\u3092\u958b\u59cb\u4e2d\u3067\u3059: +VolumeDetailsPanel.lengthLabel.text=\u30bb\u30af\u30bf\u30fc\u306e\u9577\u3055: +VolumeDetailsPanel.descLabel.text=\u8a18\u8ff0: +VolumeDetailsPanel.flagsLabel.text=\u30d5\u30e9\u30b0: +VolumeDetailsPanel.jLabel1.text=\u30dc\u30ea\u30e5\u30fc\u30e0\u60c5\u5831\u5168\u822c VolumeDetailsPanel.OKButton.text=OK ImageDetailsPanel.imageInfoLabel.text=\u30a4\u30e1\u30fc\u30b8\u60c5\u5831 -ImageDetailsPanel.imgNameLabel.text=\u540d\u524d\uff1a +ImageDetailsPanel.imgNameLabel.text=\u540d\u524d: ImageDetailsPanel.imgNameValue.text=... -ImageDetailsPanel.imgTypeLabel.text=\u30bf\u30a4\u30d7\uff1a +ImageDetailsPanel.imgTypeLabel.text=\u30bf\u30a4\u30d7: ImageDetailsPanel.imgTypeValue.text=... ImageDetailsPanel.OKButton.text=OK -ImageDetailsPanel.imgSectorSizeLabel.text=\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30ba\uff1a +ImageDetailsPanel.imgSectorSizeLabel.text=\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30ba: ImageDetailsPanel.imgSectorSizeValue.text=... +DirectoryTreeTopComponent.backButton.text= +DirectoryTreeTopComponent.forwardButton.text= ImageDetailsPanel.imgTotalSizeValue.text=... -ImageDetailsPanel.imgTotalSizeLabel.text=\u5408\u8a08\u30b5\u30a4\u30ba\uff1a +ImageDetailsPanel.imgTotalSizeLabel.text=\u5408\u8a08\u30b5\u30a4\u30ba: ImageDetailsPanel.imgHashValue.text=... -ImageDetailsPanel.imgHashLabel.text=\u30cf\u30c3\u30b7\u30e5\u5024\uff1a -DataResultFilterNode.action.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a -DataResultFilterNode.action.viewSrcFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a -DataResultFilterNode.action.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a -DataResultFilterNode.action.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u3067\u958b\u304f -DataResultFilterNode.action.searchFilesSameMd5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 -DataResultFilterNode.action.viewInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u306b\u8868\u793a -DirectoryTreeFilterNode.action.openFileSrcByAttr.text=\u5c5e\u6027\u306b\u3088\u308b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u3092\u958b\u304f +ImageDetailsPanel.imgHashLabel.text=\u30cf\u30c3\u30b7\u30e5\u5024: +DataResultFilterNode.action.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a +DataResultFilterNode.action.viewSrcFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a +DataResultFilterNode.action.viewInNewWin.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a +DataResultFilterNode.action.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f Ctrl+E +DataResultFilterNode.action.searchFilesSameMd5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 +DataResultFilterNode.action.viewInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3067\u8868\u793a +DirectoryTreeFilterNode.action.collapseAll.text=\u3059\u3079\u3066\u3092\u6298\u308a\u305f\u305f\u3080 +DirectoryTreeFilterNode.action.openFileSrcByAttr.text=\u5c5e\u6027\u5225\u306b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u3092\u958b\u304f DirectoryTreeFilterNode.action.runIngestMods.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c -DirectoryTreeTopComponent.action.viewArtContent.text=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u8868\u793a +DirectoryTreeTopComponent.action.viewArtContent.text=\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c\u306e\u5185\u5bb9\u3092\u8868\u793a ExplorerNodeActionVisitor.action.imgDetails.title=\u30a4\u30e1\u30fc\u30b8\u8a73\u7d30 -ExplorerNodeActionVisitor.action.extUnallocToSingleFiles=\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u5185\u306e\u30c7\u30fc\u30bf\u3092\u30b7\u30f3\u30b0\u30eb\u30d5\u30a1\u30a4\u30eb\u306b\u62bd\u51fa +ExplorerNodeActionVisitor.action.extUnallocToSingleFiles=\u672a\u4f7f\u7528\u9818\u57df\u3092\u5358\u4e00\u30d5\u30a1\u30a4\u30eb\u306b\u62bd\u51fa ExplorerNodeActionVisitor.action.fileSystemDetails.title=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u8a73\u7d30 ExplorerNodeActionVisitor.action.volumeDetails.title=\u30dc\u30ea\u30e5\u30fc\u30e0\u8a73\u7d30 -ExplorerNodeActionVisitor.action.extUnallocToSingleFile=\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u5185\u306e\u30c7\u30fc\u30bf\u3092\u30b7\u30f3\u30b0\u30eb\u30d5\u30a1\u30a4\u30eb\u306b\u62bd\u51fa -ExplorerNodeActionVisitor.volDetail.noVolMatchErr=\u30a8\u30e9\u30fc\uff1a\u4e00\u81f4\u3059\u308b\u30dc\u30ea\u30e5\u30fc\u30e0\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -ExplorerNodeActionVisitor.imgDetail.noVolMatchesErr=\u30a8\u30e9\u30fc\uff1a\u4e00\u81f4\u3059\u308b\u30dc\u30ea\u30e5\u30fc\u30e0\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -ExplorerNodeActionVisitor.exception.probGetParent.text={0}\: {1}\u304b\u3089\u30da\u30a2\u30ec\u30f3\u30c8\u3092\u53d6\u5f97\u3059\u308b\u969b\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ExplorerNodeActionVisitor.action.extUnallocToSingleFile=\u672a\u4f7f\u7528\u9818\u57df\u3092\u5358\u4e00\u30d5\u30a1\u30a4\u30eb\u306b\u62bd\u51fa +ExplorerNodeActionVisitor.volDetail.noVolMatchErr=\u30a8\u30e9\u30fc: \u4e00\u81f4\u3059\u308b\u30dc\u30ea\u30e5\u30fc\u30e0\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +ExplorerNodeActionVisitor.imgDetail.noVolMatchesErr=\u30a8\u30e9\u30fc: \u4e00\u81f4\u3059\u308b\u30dc\u30ea\u30e5\u30fc\u30e0\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +ExplorerNodeActionVisitor.exception.probGetParent.text={0} \u304b\u3089\u6b21\u306e\u89aa\u3092\u53d6\u5f97\u4e2d\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {1} ExtractAction.title.extractFiles.text=\u30d5\u30a1\u30a4\u30eb\u3092\u62bd\u51fa -ExtractAction.extractFiles.cantCreateFolderErr.msg=\u6307\u5b9a\u3055\u308c\u305f\u30d5\u30a9\u30eb\u30c0\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -ExtractAction.confDlg.destFileExist.msg=\u4fdd\u5b58\u5148\u306e\u30d5\u30a1\u30a4\u30eb{0}\u306f\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3001\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b\uff1f +ExtractAction.extractFiles.cantCreateFolderErr.msg=\u9078\u629e\u3057\u305f\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ExtractAction.confDlg.destFileExist.msg=\u5b9b\u5148\u30d5\u30a1\u30a4\u30eb {0} \u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b? ExtractAction.confDlg.destFileExist.title=\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u307e\u3059 -ExtractAction.msgDlg.cantOverwriteFile.msg=\u65e2\u5b58\u30d5\u30a1\u30a4\u30eb{0}\u3092\u4e0a\u66f8\u304d\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -ExtractAction.notifyDlg.noFileToExtr.msg=\u62bd\u51fa\u3067\u304d\u308b\u30d5\u30a1\u30a4\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -ExtractAction.progress.extracting=\u62bd\u51fa\u4e2d -ExtractAction.progress.cancellingExtraction={0}\uff08\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026\uff09 +ExtractAction.msgDlg.cantOverwriteFile.msg=\u65e2\u5b58\u306e\u30d5\u30a1\u30a4\u30eb {0} \u3092\u4e0a\u66f8\u304d\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +ExtractAction.notifyDlg.noFileToExtr.msg=\u62bd\u51fa\u3059\u308b\u30d5\u30a1\u30a4\u30eb\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +ExtractAction.progress.extracting=\u62bd\u51fa\u4e2d\u3067\u3059 +ExtractAction.progress.cancellingExtraction={0} (\u53d6\u308a\u6d88\u3057\u4e2d\u3067\u3059...) ExtractAction.done.notifyMsg.fileExtr.text=\u30d5\u30a1\u30a4\u30eb\u304c\u62bd\u51fa\u3055\u308c\u307e\u3057\u305f\u3002 -ExtractUnallocAction.notifyMsg.unallocAlreadyBeingExtr.msg=\u3053\u306e\u30a4\u30e1\u30fc\u30b8\u306e\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u306e\u30c7\u30fc\u30bf\u306f\u65e2\u306b\u62bd\u51fa\u4e2d\u3067\u3059\u3002\u5225\u306e\u30a4\u30e1\u30fc\u30b8\u3092\u9078\u629e\u3057\u3066\u4e0b\u3055\u3044\u3002 -ExtractUnallocAction.msgDlg.folderDoesntExist.msg=\u30d5\u30a9\u30eb\u30c0\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002\u7d9a\u884c\u3059\u308b\u524d\u306b\u6709\u52b9\u306a\u30d5\u30a9\u30eb\u30c0\u3092\u9078\u629e\u3057\u3066\u4e0b\u3055\u3044\u3002 -ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg=\u4fdd\u5b58\u5148\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u9078\u629e\u3057\u3066\u4e0b\u3055\u3044 -ExtractUnallocAction.confDlg.unallocFileAlreadyExist.msg=\u3053\u306e\u30dc\u30ea\u30e5\u30fc\u30e0\u306e\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u306e\u30d5\u30a1\u30a4\u30eb{0}\u306f\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u65e2\u5b58\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b\uff1f -ExtractUnallocAction.progress.extractUnalloc.title=\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u306e\u30c7\u30fc\u30bf\u3092\u62bd\u51fa\u4e2d -ExtractUnallocAction.progress.displayName.cancelling.text=\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u306e\u30c7\u30fc\u30bf\u3092\u62bd\u51fa\u4e2d\uff08\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026\uff09 -ExtractUnallocAction.processing.counter.msg={0}\uff0f{1} MBs\u3092\u51e6\u7406\u4e2d -ExtractUnallocAction.done.notifyMsg.completedExtract.title=\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u306e\u62bd\u51fa\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f\u3002 -ExtractUnallocAction.done.notifyMsg.completedExtract.msg=\u30d5\u30a1\u30a4\u30eb\u306f{0}\u3078\u62bd\u51fa\u3055\u308c\u307e\u3057\u305f -ExtractUnallocAction.done.errMsg.title=\u62bd\u51fa\u30a8\u30e9\u30fc -ExtractUnallocAction.done.errMsg.msg=\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u3092\u62bd\u51fa\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a{0} -DirectoryTreeFilterNode.action.collapseAll.text=\u3059\u3079\u3066\u30b3\u30e9\u30d7\u30b9 -ExtractAction.done.notifyMsg.extractErr=\u4e0b\u8a18\u306e\u30d5\u30a1\u30a4\u30eb\u306e\u62bd\u51fa\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a {0} +ExtractUnallocAction.notifyMsg.unallocAlreadyBeingExtr.msg=\u672a\u4f7f\u7528\u9818\u57df\u306f\u3053\u306e\u30dc\u30ea\u30e5\u30fc\u30e0\u4e0a\u3067\u3059\u3067\u306b\u62bd\u51fa\u4e2d\u3067\u3059\u3002\u5225\u306e\u30a4\u30e1\u30fc\u30b8\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +ExtractUnallocAction.msgDlg.folderDoesntExist.msg=\u30d5\u30a9\u30eb\u30c0\u30fc\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002\u6709\u52b9\u306a\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u9078\u629e\u3057\u3066\u7d9a\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg=\u4fdd\u5b58\u5148\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u9078\u629e +ExtractUnallocAction.confDlg.unallocFileAlreadyExist.msg=\u3053\u306e\u30dc\u30ea\u30e5\u30fc\u30e0\u306eUnalloc\u30d5\u30a1\u30a4\u30eb {0} \u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b? +ExtractUnallocAction.progress.extractUnalloc.title=\u672a\u4f7f\u7528\u9818\u57df\u3092\u62bd\u51fa\u4e2d\u3067\u3059 +ExtractUnallocAction.progress.displayName.cancelling.text=\u672a\u4f7f\u7528\u9818\u57df\u3092\u62bd\u51fa\u4e2d\u3067\u3059(\u53d6\u308a\u6d88\u3057\u4e2d\u3067\u3059...) +ExtractUnallocAction.processing.counter.msg=\u30e1\u30e2\u30ea\u30fc {1} MB\u306e {0} \u3092\u51e6\u7406\u4e2d\u3067\u3059 +ExtractUnallocAction.done.notifyMsg.completedExtract.title=\u672a\u4f7f\u7528\u9818\u57df\u306e\u62bd\u51fa\u3092\u5b8c\u4e86\u3057\u307e\u3057\u305f\u3002 +ExtractUnallocAction.done.notifyMsg.completedExtract.msg=\u30d5\u30a1\u30a4\u30eb\u3092 {0} \u306b\u62bd\u51fa\u3057\u307e\u3057\u305f +ExtractUnallocAction.done.errMsg.title=\u62bd\u51fa\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ExtractUnallocAction.done.errMsg.msg=\u6b21\u306e\u672a\u4f7f\u7528\u9818\u57df\u306e\u62bd\u51fa\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +ExtractAction.done.notifyMsg.extractErr=\u6b21\u306e\u30d5\u30a1\u30a4\u30eb\u306e\u62bd\u51fa\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +OptionsCategory_Name_ExternalViewer=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc +OptionsCategory_Keywords_ExternalViewer=ExternalViewer +ExternalViewerGlobalSettingsPanel.exePathLabel.MIME.text=\u3053\u306eMIME\u30bf\u30a4\u30d7\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30d7\u30ed\u30b0\u30e9\u30e0 +ExternalViewerGlobalSettingsPanel.exePathLabel.EXT.text=\u3053\u306e\u62e1\u5f35\u5b50\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30d7\u30ed\u30b0\u30e9\u30e0 +ExternalViewerGlobalSettingsPanel.exePathLabel.empty.text=MIME\u30bf\u30a4\u30d7\u307e\u305f\u306f\u62e1\u5f35\u5b50\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +AddExternalViewerRuleDialog.saveButton.title=\u4fdd\u5b58 +AddExternalViewerRuleDialog.cancelButton.title=\u53d6\u308a\u6d88\u3057 +AddExternalViewerRuleDialog.title=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u30eb\u30fc\u30eb +ExternalViewerGlobalSettingsPanel.JOptionPane.invalidMime.message=MIME \u30bf\u30a4\u30d7\u304c\u7121\u52b9\u3067\u3059\u3002[\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3] \u30d1\u30cd\u30eb\u306b\u30ab\u30b9\u30bf\u30e0\u30bf\u30a4\u30d7\u3092\u8ffd\u52a0\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +ExternalViewerGlobalSettingsPanel.JOptionPane.invalidMime.title=\u7121\u52b9\u306aMIME\u30bf\u30a4\u30d7 +ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExt.message=\u62e1\u5f35\u5b50\u304c\u7121\u52b9\u3067\u3059\u3002 +ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExt.title=\u7121\u52b9\u306a\u62e1\u5f35\u5b50 +ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExePath.message=\u5b9f\u884c\u53ef\u80fd\u306a\u30d7\u30ed\u30b0\u30e9\u30e0\u3078\u306e\u30d1\u30b9\u304c\u7121\u52b9\u3067\u3059 +ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExePath.title=\u7121\u52b9\u306a\u30d1\u30b9 +ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.message=\u3053\u306eMIME\u30bf\u30a4\u30d7\u307e\u305f\u306f\u62e1\u5f35\u5b50\u306e\u30eb\u30fc\u30eb\u304c\u3059\u3067\u306b\u5b58\u5728\u3057\u3066\u3044\u307e\u3059\u3002\u305d\u306e\u30eb\u30fc\u30eb\u3092\u7de8\u96c6\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.title=\u30eb\u30fc\u30eb\u306f\u8ffd\u52a0\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +AddExternalViewerRulePanel.mimeRadioButton.text=MIME\u30bf\u30a4\u30d7 +AddExternalViewerRulePanel.nameTextField.text= +AddExternalViewerRulePanel.nameLabel.text=MIME\u30bf\u30a4\u30d7\u307e\u305f\u306f\u62e1\u5f35\u5b50 +AddExternalViewerRulePanel.browseButton.text=\u53c2\u7167 +AddExternalViewerRulePanel.exePathTextField.text= +AddExternalViewerRulePanel.exePathLabel.text=\u3053\u306e\u30bf\u30a4\u30d7\u307e\u305f\u306f\u62e1\u5f35\u5b50\u306e\u30d5\u30a1\u30a4\u30eb\u306b\u4f7f\u7528\u3059\u308b\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u30d1\u30b9 +AddExternalViewerRulePanel.extRadioButton.text=\u62e1\u5f35\u5b50 +GroupDataSourcesDialog.dataSourceCountLabel.text=jLabel1 +GroupDataSourcesDialog.queryLabel.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5225\u306b\u30b0\u30eb\u30fc\u30d7\u5316\u3057\u3066\u8aad\u307f\u8fbc\u307f\u3092\u65e9\u304f\u3057\u307e\u3059\u304b? +GroupDataSourcesDialog.yesButton.text=\u306f\u3044 +GroupDataSourcesDialog.noButton.text=\u3044\u3044\u3048 +GroupDataSourcesDialog.title=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5225\u306b\u30b0\u30eb\u30fc\u30d7\u5316\u3057\u307e\u3059\u304b? +DirectoryTreeTopComponent.openViewPreferencesButton.text= +ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title3=\u30bf\u30a4\u30c8\u30eb4 +ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title2=\u30bf\u30a4\u30c8\u30eb3 +ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title3_1=\u30bf\u30a4\u30c8\u30eb4 +ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title2_1=\u30bf\u30a4\u30c8\u30eb3 +ExternalViewerGlobalSettingsPanel.newRuleButton1.text=\u65b0\u898f\u30eb\u30fc\u30eb +ExternalViewerGlobalSettingsPanel.editRuleButton.text=\u30eb\u30fc\u30eb\u3092\u7de8\u96c6 +ExternalViewerGlobalSettingsPanel.deleteRuleButton.text=\u30eb\u30fc\u30eb\u3092\u524a\u9664 +ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u306e\u30ab\u30b9\u30bf\u30e0\u30eb\u30fc\u30eb\u3092\u8ffd\u52a0: +ExternalViewerGlobalSettingsPanel.newRuleButton.text=\u65b0\u898f\u30eb\u30fc\u30eb +ExternalViewerGlobalSettingsPanel.jButton2.text=jButton2 +ExternalViewerGlobalSettingsPanel.browseHxDDirectory.text=\u53c2\u7167 +ExternalViewerGlobalSettingsPanel.HxDLabel.text=HxD\u30a8\u30c7\u30a3\u30bf\u30fc\u30d1\u30b9: +ExternalViewerGlobalSettingsPanel.ContentViewerExtensionLabel.text=\u30b3\u30f3\u30c6\u30f3\u30c4\u30d3\u30e5\u30fc\u30ef\u30fc\u306e\u62e1\u5f35\u5b50\u3092\u8ffd\u52a0: diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 11d61460a6..bb43f64c84 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -136,11 +136,12 @@ public class DataResultFilterNode extends FilterNode { public Action[] getActions(boolean popup) { List actions = new ArrayList<>(); - - final DisplayableItemNode originalNode = (DisplayableItemNode) this.getOriginal(); - List accept = originalNode.accept(getActionsDIV); - if (accept != null) { - actions.addAll(accept); + if (this.getOriginal() instanceof DisplayableItemNode) { + final DisplayableItemNode originalNode = (DisplayableItemNode) this.getOriginal(); + List accept = originalNode.accept(getActionsDIV); + if (accept != null) { + actions.addAll(accept); + } } //actions.add(new IndexContentFilesAction(nodeContent, "Index")); @@ -291,6 +292,9 @@ public class DataResultFilterNode extends FilterNode { for (Action a : ban.getActions(true)) { actionsList.add(a); } + + //Add seperator between the decorated actions and the actions from the node itself. + actionsList.add(null); BlackboardArtifact ba = ban.getLookup().lookup(BlackboardArtifact.class); final int artifactTypeID = ba.getArtifactTypeID(); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerAction.java index cc50b2cd2a..020a2499fa 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerAction.java @@ -86,7 +86,7 @@ public class ExternalViewerAction extends AbstractAction { * @param title Name of the action * @param fileNode File to display */ - public ExternalViewerAction(String title, Node fileNode) { + public ExternalViewerAction (String title, Node fileNode) { this(title, fileNode.getLookup().lookup(org.sleuthkit.datamodel.AbstractFile.class), fileNode instanceof SlackFileNode); } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java index 46c2161838..0efe3bdcfb 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2018 Basis Technology Corp. + * Copyright 2013-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,32 +18,13 @@ */ package org.sleuthkit.autopsy.directorytree; -import java.awt.Component; +import org.sleuthkit.autopsy.directorytree.actionhelpers.ExtractActionHelper; import java.awt.event.ActionEvent; -import java.io.File; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; import javax.swing.AbstractAction; -import javax.swing.JFileChooser; -import javax.swing.JOptionPane; -import javax.swing.SwingWorker; -import org.netbeans.api.progress.ProgressHandle; -import org.openide.util.Cancellable; import org.openide.util.NbBundle; import org.openide.util.Utilities; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.coreutils.FileUtil; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor; +import org.openide.util.Lookup; import org.sleuthkit.datamodel.AbstractFile; /** @@ -51,10 +32,6 @@ import org.sleuthkit.datamodel.AbstractFile; */ public final class ExtractAction extends AbstractAction { - private Logger logger = Logger.getLogger(ExtractAction.class.getName()); - - private String userDefinedExportPath; - // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every // node in the array returns a reference to the same action object from Node.getActions(boolean). @@ -82,300 +59,10 @@ public final class ExtractAction extends AbstractAction { */ @Override public void actionPerformed(ActionEvent e) { - Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); - if (selectedFiles.size() > 1) { - extractFiles(e, selectedFiles); - } else if (selectedFiles.size() == 1) { - AbstractFile source = selectedFiles.iterator().next(); - if (source.isDir()) { - extractFiles(e, selectedFiles); - } else { - extractFile(e, selectedFiles.iterator().next()); - } - } - } + Lookup lookup = Utilities.actionsGlobalContext(); + Collection selectedFiles =lookup.lookupAll(AbstractFile.class); + ExtractActionHelper extractor = new ExtractActionHelper(); + extractor.extract(e, selectedFiles); - /** - * Called when user has selected a single file to extract - * - * @param event - * @param selectedFile Selected file - */ - @NbBundle.Messages({"ExtractAction.noOpenCase.errMsg=No open case available."}) - private void extractFile(ActionEvent event, AbstractFile selectedFile) { - Case openCase; - try { - openCase = Case.getCurrentCaseThrows(); - } catch (NoCurrentCaseException ex) { - JOptionPane.showMessageDialog((Component) event.getSource(), Bundle.ExtractAction_noOpenCase_errMsg()); - logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS - return; - } - JFileChooser fileChooser = new JFileChooser(); - fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase))); - // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden - fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName()))); - if (fileChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) { - updateExportDirectory(fileChooser.getSelectedFile().getParent(), openCase); - - ArrayList fileExtractionTasks = new ArrayList<>(); - fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile())); - runExtractionTasks(event, fileExtractionTasks); - } - } - - /** - * Called when a user has selected multiple files to extract - * - * @param event - * @param selectedFiles Selected files - */ - private void extractFiles(ActionEvent event, Collection selectedFiles) { - Case openCase; - try { - openCase = Case.getCurrentCaseThrows(); - } catch (NoCurrentCaseException ex) { - JOptionPane.showMessageDialog((Component) event.getSource(), Bundle.ExtractAction_noOpenCase_errMsg()); - logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS - return; - } - JFileChooser folderChooser = new JFileChooser(); - folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - folderChooser.setCurrentDirectory(new File(getExportDirectory(openCase))); - if (folderChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) { - File destinationFolder = folderChooser.getSelectedFile(); - if (!destinationFolder.exists()) { - try { - destinationFolder.mkdirs(); - } catch (Exception ex) { - JOptionPane.showMessageDialog((Component) event.getSource(), NbBundle.getMessage(this.getClass(), - "ExtractAction.extractFiles.cantCreateFolderErr.msg")); - logger.log(Level.INFO, "Unable to create folder(s) for user " + destinationFolder.getAbsolutePath(), ex); //NON-NLS - return; - } - } - updateExportDirectory(destinationFolder.getPath(), openCase); - - /* - * get the unique set of files from the list. A user once reported - * extraction taking days because it was extracting the same PST - * file 20k times. They selected 20k email messages in the tree and - * chose to extract them. - */ - Set uniqueFiles = new HashSet<>(selectedFiles); - - // make a task for each file - ArrayList fileExtractionTasks = new ArrayList<>(); - for (AbstractFile source : uniqueFiles) { - // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden - fileExtractionTasks.add(new FileExtractionTask(source, new File(destinationFolder, source.getId() + "-" + FileUtil.escapeFileName(source.getName())))); - } - runExtractionTasks(event, fileExtractionTasks); - } - } - - /** - * Get the export directory path. - * - * @param openCase The current case. - * - * @return The export directory path. - */ - private String getExportDirectory(Case openCase) { - String caseExportPath = openCase.getExportDirectory(); - - if (userDefinedExportPath == null) { - return caseExportPath; - } - - File file = new File(userDefinedExportPath); - if (file.exists() == false || file.isDirectory() == false) { - return caseExportPath; - } - - return userDefinedExportPath; - } - - /** - * Update the default export directory. If the directory path matches the - * case export directory, then the directory used will always match the - * export directory of any given case. Otherwise, the path last used will be - * saved. - * - * @param exportPath The export path. - * @param openCase The current case. - */ - private void updateExportDirectory(String exportPath, Case openCase) { - if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) { - userDefinedExportPath = null; - } else { - userDefinedExportPath = exportPath; - } - } - - /** - * Execute a series of file extraction tasks. - * - * @param event ActionEvent whose source will be used for - * centering popup dialogs. - * @param fileExtractionTasks List of file extraction tasks. - */ - private void runExtractionTasks(ActionEvent event, List fileExtractionTasks) { - - // verify all of the sources and destinations are OK - for (Iterator it = fileExtractionTasks.iterator(); it.hasNext();) { - FileExtractionTask task = it.next(); - - if (ContentUtils.isDotDirectory(task.source)) { - //JOptionPane.showMessageDialog((Component) e.getSource(), "Cannot extract virtual " + task.source.getName() + " directory.", "File is Virtual Directory", JOptionPane.WARNING_MESSAGE); - it.remove(); - continue; - } - - /* - * This code assumes that each destination is unique. We previously - * satisfied that by adding the unique ID. - */ - if (task.destination.exists()) { - if (JOptionPane.showConfirmDialog((Component) event.getSource(), - NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.msg", task.destination.getAbsolutePath()), - NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.title"), - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (!FileUtil.deleteFileDir(task.destination)) { - JOptionPane.showMessageDialog((Component) event.getSource(), - NbBundle.getMessage(this.getClass(), "ExtractAction.msgDlg.cantOverwriteFile.msg", task.destination.getAbsolutePath())); - it.remove(); - } - } else { - it.remove(); - } - } - } - - // launch a thread to do the work - if (!fileExtractionTasks.isEmpty()) { - try { - FileExtracter extracter = new FileExtracter(fileExtractionTasks); - extracter.execute(); - } catch (Exception ex) { - logger.log(Level.WARNING, "Unable to start background file extraction thread", ex); //NON-NLS - } - } else { - MessageNotifyUtil.Message.info( - NbBundle.getMessage(this.getClass(), "ExtractAction.notifyDlg.noFileToExtr.msg")); - } - } - - /** - * Stores source and destination for file extraction. - */ - private class FileExtractionTask { - - AbstractFile source; - File destination; - - /** - * Create an instance of the FileExtractionTask. - * - * @param source The file to be extracted. - * @param destination The destination for the extraction. - */ - FileExtractionTask(AbstractFile source, File destination) { - this.source = source; - this.destination = destination; - } - } - - /** - * Thread that does the actual extraction work - */ - private class FileExtracter extends SwingWorker { - - private final Logger logger = Logger.getLogger(FileExtracter.class.getName()); - private ProgressHandle progress; - private final List extractionTasks; - - /** - * Create an instance of the FileExtracter. - * - * @param extractionTasks List of file extraction tasks. - */ - FileExtracter(List extractionTasks) { - this.extractionTasks = extractionTasks; - } - - @Override - protected Object doInBackground() throws Exception { - if (extractionTasks.isEmpty()) { - return null; - } - - // Setup progress bar. - final String displayName = NbBundle.getMessage(this.getClass(), "ExtractAction.progress.extracting"); - progress = ProgressHandle.createHandle(displayName, new Cancellable() { - @Override - public boolean cancel() { - if (progress != null) { - progress.setDisplayName( - NbBundle.getMessage(this.getClass(), "ExtractAction.progress.cancellingExtraction", displayName)); - } - return ExtractAction.FileExtracter.this.cancel(true); - } - }); - progress.start(); - progress.switchToIndeterminate(); - - /* - * @@@ Add back in -> Causes exceptions int workUnits = 0; for - * (FileExtractionTask task : extractionTasks) { workUnits += - * calculateProgressBarWorkUnits(task.source); } - * progress.switchToDeterminate(workUnits); - */ - // Do the extraction tasks. - for (FileExtractionTask task : this.extractionTasks) { - // @@@ Note, we are no longer passing in progress - ExtractFscContentVisitor.extract(task.source, task.destination, null, this); - } - - return null; - } - - @Override - protected void done() { - boolean msgDisplayed = false; - try { - super.get(); - } catch (InterruptedException | ExecutionException ex) { - logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS - MessageNotifyUtil.Message.info( - NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.extractErr", ex.getMessage())); - msgDisplayed = true; - } finally { - progress.finish(); - if (!this.isCancelled() && !msgDisplayed) { - MessageNotifyUtil.Message.info( - NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.fileExtr.text")); - } - } - } - - /** - * Calculate the number of work units for the progress bar. - * - * @param file File whose children will be reviewed to get the number of - * work units. - * - * @return The number of work units. - */ - /* - * private int calculateProgressBarWorkUnits(AbstractFile file) { int - * workUnits = 0; if (file.isFile()) { workUnits += file.getSize(); } - * else { try { for (Content child : file.getChildren()) { if (child - * instanceof AbstractFile) { workUnits += - * calculateProgressBarWorkUnits((AbstractFile) child); } } } catch - * (TskCoreException ex) { logger.log(Level.SEVERE, "Could not get - * children of content", ex); //NON-NLS } } return workUnits; - } - */ } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java index b2a3a93abf..58afb28fbe 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java @@ -138,7 +138,7 @@ final class ExtractUnallocAction extends AbstractAction { try { openCase = Case.getCurrentCaseThrows(); } catch (NoCurrentCaseException ex) { - MessageNotifyUtil.Message.info(Bundle.ExtractAction_noOpenCase_errMsg()); + MessageNotifyUtil.Message.info(Bundle.ExtractUnallocAction_noOpenCase_errMsg()); return; } List copyList = new ArrayList() { diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/actionhelpers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/directorytree/actionhelpers/Bundle.properties new file mode 100644 index 0000000000..310cf0a475 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/directorytree/actionhelpers/Bundle.properties @@ -0,0 +1,8 @@ +ExtractActionHelper.extractFiles.cantCreateFolderErr.msg=Could not create selected folder. +ExtractActionHelper.confDlg.destFileExist.msg=Destination file {0} already exists, overwrite? +ExtractActionHelper.confDlg.destFileExist.title=File Exists +ExtractActionHelper.msgDlg.cantOverwriteFile.msg=Could not overwrite existing file {0} +ExtractActionHelper.notifyDlg.noFileToExtr.msg=No file(s) to extract. +ExtractActionHelper.progress.extracting=Extracting +ExtractActionHelper.progress.cancellingExtraction={0} (Cancelling...) +ExtractActionHelper.done.notifyMsg.fileExtr.text=File(s) extracted. diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/actionhelpers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/directorytree/actionhelpers/Bundle.properties-MERGED new file mode 100644 index 0000000000..f86fa5612d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/directorytree/actionhelpers/Bundle.properties-MERGED @@ -0,0 +1,9 @@ +ExtractActionHelper.extractFiles.cantCreateFolderErr.msg=Could not create selected folder. +ExtractActionHelper.confDlg.destFileExist.msg=Destination file {0} already exists, overwrite? +ExtractActionHelper.confDlg.destFileExist.title=File Exists +ExtractActionHelper.msgDlg.cantOverwriteFile.msg=Could not overwrite existing file {0} +ExtractActionHelper.noOpenCase.errMsg=No open case available. +ExtractActionHelper.notifyDlg.noFileToExtr.msg=No file(s) to extract. +ExtractActionHelper.progress.extracting=Extracting +ExtractActionHelper.progress.cancellingExtraction={0} (Cancelling...) +ExtractActionHelper.done.notifyMsg.fileExtr.text=File(s) extracted. diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/actionhelpers/ExtractActionHelper.java b/Core/src/org/sleuthkit/autopsy/directorytree/actionhelpers/ExtractActionHelper.java new file mode 100644 index 0000000000..339a58142d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/directorytree/actionhelpers/ExtractActionHelper.java @@ -0,0 +1,356 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this content 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.directorytree.actionhelpers; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.SwingWorker; +import org.netbeans.api.progress.ProgressHandle; +import org.openide.util.Cancellable; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.FileUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * Helper class for methods needed by actions which extract files. + */ +public class ExtractActionHelper { + + private final Logger logger = Logger.getLogger(ExtractActionHelper.class.getName()); + private String userDefinedExportPath; + + /** + * Extract the specified collection of files with an event specified for + * context. + * + * @param event The event that caused the extract method to be + * called. + * @param selectedFiles The files to be extracted from the current case. + */ + public void extract(ActionEvent event, Collection selectedFiles) { + if (selectedFiles.size() > 1) { + extractFiles(event, selectedFiles); + } else if (selectedFiles.size() == 1) { + AbstractFile source = selectedFiles.iterator().next(); + if (source.isDir()) { + extractFiles(event, selectedFiles); + } else { + extractFile(event, selectedFiles.iterator().next()); + } + } + } + + /** + * Called when user has selected a single file to extract + * + * @param event + * @param selectedFile Selected file + */ + @NbBundle.Messages({"ExtractActionHelper.noOpenCase.errMsg=No open case available."}) + private void extractFile(ActionEvent event, AbstractFile selectedFile) { + Case openCase; + try { + openCase = Case.getCurrentCaseThrows(); + } catch (NoCurrentCaseException ex) { + JOptionPane.showMessageDialog((Component) event.getSource(), Bundle.ExtractActionHelper_noOpenCase_errMsg()); + logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS + return; + } + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase))); + // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden + fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName()))); + if (fileChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) { + updateExportDirectory(fileChooser.getSelectedFile().getParent(), openCase); + + ArrayList fileExtractionTasks = new ArrayList<>(); + fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile())); + runExtractionTasks(event, fileExtractionTasks); + } + } + + /** + * Called when a user has selected multiple files to extract + * + * @param event + * @param selectedFiles Selected files + */ + private void extractFiles(ActionEvent event, Collection selectedFiles) { + Case openCase; + try { + openCase = Case.getCurrentCaseThrows(); + } catch (NoCurrentCaseException ex) { + JOptionPane.showMessageDialog((Component) event.getSource(), Bundle.ExtractActionHelper_noOpenCase_errMsg()); + logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS + return; + } + JFileChooser folderChooser = new JFileChooser(); + folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + folderChooser.setCurrentDirectory(new File(getExportDirectory(openCase))); + if (folderChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) { + File destinationFolder = folderChooser.getSelectedFile(); + if (!destinationFolder.exists()) { + try { + destinationFolder.mkdirs(); + } catch (Exception ex) { + JOptionPane.showMessageDialog((Component) event.getSource(), NbBundle.getMessage(this.getClass(), + "ExtractAction.extractFiles.cantCreateFolderErr.msg")); + logger.log(Level.INFO, "Unable to create folder(s) for user " + destinationFolder.getAbsolutePath(), ex); //NON-NLS + return; + } + } + updateExportDirectory(destinationFolder.getPath(), openCase); + + /* + * get the unique set of files from the list. A user once reported + * extraction taking days because it was extracting the same PST + * file 20k times. They selected 20k email messages in the tree and + * chose to extract them. + */ + Set uniqueFiles = new HashSet<>(selectedFiles); + + // make a task for each file + ArrayList fileExtractionTasks = new ArrayList<>(); + for (AbstractFile source : uniqueFiles) { + // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden + fileExtractionTasks.add(new FileExtractionTask(source, new File(destinationFolder, source.getId() + "-" + FileUtil.escapeFileName(source.getName())))); + } + runExtractionTasks(event, fileExtractionTasks); + } + } + + /** + * Get the export directory path. + * + * @param openCase The current case. + * + * @return The export directory path. + */ + private String getExportDirectory(Case openCase) { + String caseExportPath = openCase.getExportDirectory(); + + if (userDefinedExportPath == null) { + return caseExportPath; + } + + File file = new File(userDefinedExportPath); + if (file.exists() == false || file.isDirectory() == false) { + return caseExportPath; + } + + return userDefinedExportPath; + } + + /** + * Update the default export directory. If the directory path matches the + * case export directory, then the directory used will always match the + * export directory of any given case. Otherwise, the path last used will be + * saved. + * + * @param exportPath The export path. + * @param openCase The current case. + */ + private void updateExportDirectory(String exportPath, Case openCase) { + if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) { + userDefinedExportPath = null; + } else { + userDefinedExportPath = exportPath; + } + } + + /** + * Execute a series of file extraction tasks. + * + * @param event ActionEvent whose source will be used for + * centering popup dialogs. + * @param fileExtractionTasks List of file extraction tasks. + */ + private void runExtractionTasks(ActionEvent event, List fileExtractionTasks) { + + // verify all of the sources and destinations are OK + for (Iterator it = fileExtractionTasks.iterator(); it.hasNext();) { + FileExtractionTask task = it.next(); + + if (ContentUtils.isDotDirectory(task.source)) { + it.remove(); + continue; + } + + /* + * This code assumes that each destination is unique. We previously + * satisfied that by adding the unique ID. + */ + if (task.destination.exists()) { + if (JOptionPane.showConfirmDialog((Component) event.getSource(), + NbBundle.getMessage(this.getClass(), "ExtractActionHelper.confDlg.destFileExist.msg", task.destination.getAbsolutePath()), + NbBundle.getMessage(this.getClass(), "ExtractActionHelper.confDlg.destFileExist.title"), + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + if (!FileUtil.deleteFileDir(task.destination)) { + JOptionPane.showMessageDialog((Component) event.getSource(), + NbBundle.getMessage(this.getClass(), "ExtractActionHelper.msgDlg.cantOverwriteFile.msg", task.destination.getAbsolutePath())); + it.remove(); + } + } else { + it.remove(); + } + } + } + + // launch a thread to do the work + if (!fileExtractionTasks.isEmpty()) { + try { + FileExtracter extracter = new FileExtracter(fileExtractionTasks); + extracter.execute(); + } catch (Exception ex) { + logger.log(Level.WARNING, "Unable to start background file extraction thread", ex); //NON-NLS + } + } else { + MessageNotifyUtil.Message.info( + NbBundle.getMessage(this.getClass(), "ExtractActionHelper.notifyDlg.noFileToExtr.msg")); + } + } + + /** + * Stores source and destination for file extraction. + */ + private class FileExtractionTask { + + AbstractFile source; + File destination; + + /** + * Create an instance of the FileExtractionTask. + * + * @param source The file to be extracted. + * @param destination The destination for the extraction. + */ + FileExtractionTask(AbstractFile source, File destination) { + this.source = source; + this.destination = destination; + } + } + + /** + * Thread that does the actual extraction work + */ + private class FileExtracter extends SwingWorker { + + private final Logger logger = Logger.getLogger(FileExtracter.class.getName()); + private ProgressHandle progress; + private final List extractionTasks; + + /** + * Create an instance of the FileExtracter. + * + * @param extractionTasks List of file extraction tasks. + */ + FileExtracter(List extractionTasks) { + this.extractionTasks = extractionTasks; + } + + @Override + protected Object doInBackground() throws Exception { + if (extractionTasks.isEmpty()) { + return null; + } + + // Setup progress bar. + final String displayName = NbBundle.getMessage(this.getClass(), "ExtractActionHelper.progress.extracting"); + progress = ProgressHandle.createHandle(displayName, new Cancellable() { + @Override + public boolean cancel() { + if (progress != null) { + progress.setDisplayName( + NbBundle.getMessage(this.getClass(), "ExtractActionHelper.progress.cancellingExtraction", displayName)); + } + return ExtractActionHelper.FileExtracter.this.cancel(true); + } + }); + progress.start(); + progress.switchToIndeterminate(); + + /* + * @@@ Add back in -> Causes exceptions int workUnits = 0; for + * (FileExtractionTask task : extractionTasks) { workUnits += + * calculateProgressBarWorkUnits(task.source); } + * progress.switchToDeterminate(workUnits); + */ + // Do the extraction tasks. + for (FileExtractionTask task : this.extractionTasks) { + // @@@ Note, we are no longer passing in progress + ContentUtils.ExtractFscContentVisitor.extract(task.source, task.destination, null, this); + } + + return null; + } + + @Override + protected void done() { + boolean msgDisplayed = false; + try { + super.get(); + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS + MessageNotifyUtil.Message.info( + NbBundle.getMessage(this.getClass(), "ExtractActionHelper.done.notifyMsg.extractErr", ex.getMessage())); + msgDisplayed = true; + } finally { + progress.finish(); + if (!this.isCancelled() && !msgDisplayed) { + MessageNotifyUtil.Message.info( + NbBundle.getMessage(this.getClass(), "ExtractActionHelper.done.notifyMsg.fileExtr.text")); + } + } + } + + /** + * Calculate the number of work units for the progress bar. + * + * @param file File whose children will be reviewed to get the number of + * work units. + * + * @return The number of work units. + */ + /* + * private int calculateProgressBarWorkUnits(AbstractFile file) { int + * workUnits = 0; if (file.isFile()) { workUnits += file.getSize(); } + * else { try { for (Content child : file.getChildren()) { if (child + * instanceof AbstractFile) { workUnits += + * calculateProgressBarWorkUnits((AbstractFile) child); } } } catch + * (TskCoreException ex) { logger.log(Level.SEVERE, "Could not get + * children of content", ex); //NON-NLS } } return workUnits; } + */ + } +} diff --git a/Core/src/org/sleuthkit/autopsy/events/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/events/Bundle_ja.properties index ec61e857ea..e00b218ce5 100644 --- a/Core/src/org/sleuthkit/autopsy/events/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/events/Bundle_ja.properties @@ -1,10 +1,10 @@ -MessageServiceConnectionInfo.ConnectionCheck.Everything=\u30DB\u30B9\u30C8\u540D\u3001\u30DD\u30FC\u30C8\u756A\u53F7\u3001\u30E6\u30FC\u30B6\u30FC\u540D\u307E\u305F\u306F\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u7121\u52B9\u3067\u3059 -MessageServiceConnectionInfo.ConnectionCheck.Hostname=\u7121\u52B9\u306A\u30DB\u30B9\u30C8\u540D -MessageServiceConnectionInfo.ConnectionCheck.Port=\u7121\u52B9\u306A\u30DD\u30FC\u30C8\u756A\u53F7 -MessageServiceConnectionInfo.ConnectionCheck.Username=\u7121\u52B9\u306A\u30E6\u30FC\u30B6\u30FC\u540D -MessageServiceConnectionInfo.ConnectionCheck.Password=\u7121\u52B9\u306A\u30D1\u30B9\u30EF\u30FC\u30C9 -MessageServiceConnectionInfo.ConnectionCheck.UsernameAndPassword=\u7121\u52B9\u306A\u30E6\u30FC\u30B6\u30FC\u540D\u307E\u305F\u306F\u30D1\u30B9\u30EF\u30FC\u30C9 -MessageServiceConnectionInfo.ConnectionCheck.HostnameOrPort=\u7121\u52B9\u306A\u30DB\u30B9\u30C8\u540D\u307E\u305F\u306F\u30DD\u30FC\u30C8\u756A\u53F7 -MessageServiceConnectionInfo.MissingHostname=\u30DB\u30B9\u30C8\u540D\u304C\u5FC5\u8981\u3067\u3059\u3002 -MessageServiceConnectionInfo.MissingUsername=\u30E6\u30FC\u30B6\u30FC\u540D\u304C\u5FC5\u8981\u3067\u3059\u3002 -MessageServiceConnectionInfo.MissingPassword=\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u5FC5\u8981\u3067\u3059\u3002 \ No newline at end of file +MessageServiceConnectionInfo.ConnectionCheck.Everything=\u7121\u52b9\u306a\u30db\u30b9\u30c8\u540d\u3001\u30dd\u30fc\u30c8\u756a\u53f7\u3001\u30e6\u30fc\u30b6\u30fc\u540d\u3001\u304a\u3088\u3073/\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u3059\u3002 +MessageServiceConnectionInfo.ConnectionCheck.Hostname=\u7121\u52b9\u306a\u30db\u30b9\u30c8\u540d\u3067\u3059\u3002 +MessageServiceConnectionInfo.ConnectionCheck.Port=\u7121\u52b9\u306a\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u3059\u3002 +MessageServiceConnectionInfo.ConnectionCheck.Username=\u7121\u52b9\u306a\u30e6\u30fc\u30b6\u30fc\u540d\u3067\u3059\u3002 +MessageServiceConnectionInfo.ConnectionCheck.Password=\u7121\u52b9\u306a\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u3059\u3002 +MessageServiceConnectionInfo.ConnectionCheck.UsernameAndPassword=\u7121\u52b9\u306a\u30db\u30b9\u30c8\u540d\u304a\u3088\u3073/\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u3059\u3002 +MessageServiceConnectionInfo.ConnectionCheck.HostnameOrPort=\u7121\u52b9\u306a\u30db\u30b9\u30c8\u540d\u304a\u3088\u3073/\u307e\u305f\u306f\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u3059\u3002 +MessageServiceConnectionInfo.MissingHostname=\u30db\u30b9\u30c8\u540d\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002 +MessageServiceConnectionInfo.MissingUsername=\u30e6\u30fc\u30b6\u30fc\u540d\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002 +MessageServiceConnectionInfo.MissingPassword=\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/examples/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/examples/Bundle_ja.properties new file mode 100644 index 0000000000..8fc0db1332 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/examples/Bundle_ja.properties @@ -0,0 +1,6 @@ +SampleContentViewer.jLabel1.text=jLabel1 +SampleIngestModuleFactory.moduleName=\u30b5\u30f3\u30d7\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb +SampleIngestModuleFactory.moduleDescription=\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u958b\u767a\u8005\u306e\u30b5\u30f3\u30d7\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3068\u3057\u3066\u6a5f\u80fd\u3057\u307e\u3059\u3002 +SampleIngestModuleIngestJobSettingsPanel.skipKnownFilesCheckBox.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb(NSRL)\u3092\u30b9\u30ad\u30c3\u30d7 +SampleIngestModuleFactory.moduleName=\u30b5\u30f3\u30d7\u30eb\u5b9f\u884c\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb +SampleExecutableIngestModuleFactory.moduleDescription=\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u958b\u767a\u8005\u306e\u30b5\u30f3\u30d7\u30eb\u5b9f\u884c\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3068\u3057\u3066\u6a5f\u80fd\u3057\u307e\u3059\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/exceptions/AutopsyException.java b/Core/src/org/sleuthkit/autopsy/exceptions/AutopsyException.java new file mode 100644 index 0000000000..1c91c6d773 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/exceptions/AutopsyException.java @@ -0,0 +1,88 @@ +/* + * Autopsy + * + * Copyright 2019 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.exceptions; + +/** + * An exception to be thrown which can contain a user-friendly message. + */ +public abstract class AutopsyException extends Exception { + + private static final long serialVersionUID = 1L; + + private final String userMessage; + + /** + * Constructs an AutopsyException with identical exception and user-friendly + * messages. + * + * @param message Exception message. + */ + public AutopsyException(String message) { + super(message); + this.userMessage = message; + } + + /** + * Constructs an AutopsyException with an exception message and user-friendly message. + * + * @param message Exception message. + * @param userMessage The user-friendly message to include in this + * exception. + */ + public AutopsyException(String message, String userMessage) { + super(message); + this.userMessage = userMessage; + } + + /** + * Constructs an AutopsyException with identical exception and user-friendly + * messages. + * + * @param message Exception message. + * @param cause Exception cause. + */ + public AutopsyException(String message, Throwable cause) { + super(message, cause); + this.userMessage = message; + } + + /** + * Constructs an AutopsyException with an exception message, a user-friendly messages, and a cause. + * + * @param message Exception message. + * @param userMessage The user-friendly message to include in this + * exception. + * @param cause Exception cause. + */ + public AutopsyException(String message, String userMessage, Throwable cause) { + super(message, cause); + this.userMessage = userMessage; + } + + /** + * Get the user-friendly message if one exists. + * + * @return The user-friendly message if one was explicitly set, otherwise + * returns the exception message. + */ + public String getUserMessage() { + return userMessage; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java b/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java new file mode 100644 index 0000000000..87b4fed4f7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java @@ -0,0 +1,102 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.featureaccess; + +import java.io.File; +import java.nio.file.Paths; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; + +/** + * Check if access to various features is permitted for the current user and the + * current case, if any. + * + * IMPORTANT: These utilities are not concerned with transitory restrictions on + * access to a feature, e.g., whether or not ingest is running. + */ +final public class FeatureAccessUtils { + + private final static String MULTIUSER_CASE_RESTRICTED_FILE_NAME = "mualimit"; // NON-NLS + private final static String MULTIUSER_CASE_RESTRICTED_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), MULTIUSER_CASE_RESTRICTED_FILE_NAME).toString(); + private static final int DATA_SRC_DEL_MIN_DB_MAJOR_VER = 8; + private static final int DATA_SRC_DEL_MIN_DB_MINOR_VER = 4; + + /** + * Indicates whether or not a user can create multi-user cases. + * + * @return True or false. + */ + public static boolean canCreateMultiUserCases() { + return UserPreferences.getIsMultiUserModeEnabled() && multiUserCaseRestrictionsFileAbsent(); + } + + /** + * Indicates whether or not a user can add data sources to a case. + * + * @return True or false. + */ + public static boolean canAddDataSources() { + return currentCaseIsSingleUserCase() || multiUserCaseRestrictionsFileAbsent(); + } + + /** + * Indicates whether or not a user can delete data sources from a case. + * + * @return True or false. + */ + public static boolean canDeleteDataSources() { + boolean dataSourceDeletionAllowed = false; + if (Case.isCaseOpen()) { + CaseDbSchemaVersionNumber version = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); + dataSourceDeletionAllowed + = ((version.getMajor() > DATA_SRC_DEL_MIN_DB_MAJOR_VER) || (version.getMajor() == DATA_SRC_DEL_MIN_DB_MAJOR_VER && version.getMinor() >= DATA_SRC_DEL_MIN_DB_MINOR_VER)) + && (currentCaseIsSingleUserCase() || multiUserCaseRestrictionsFileAbsent()); + } + return dataSourceDeletionAllowed; + } + + /** + * Indicates whether or not the current case is a single-user case. + * + * @return True or false. + */ + private static boolean currentCaseIsSingleUserCase() { + return Case.isCaseOpen() && Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE; + } + + /** + * Indicates whether or not the current user is allowed to create or modify + * (add or delete data sources) multi-user cases. + * + * @return True or false. + */ + public static boolean multiUserCaseRestrictionsFileAbsent() { + File accessLimitingFile = new File(MULTIUSER_CASE_RESTRICTED_FILE_PATH); + return !accessLimitingFile.exists(); + } + + /** + * Private constructor to prevent instantiation of this utility class. + */ + private FeatureAccessUtils() { + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties new file mode 100644 index 0000000000..42c8538d93 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties @@ -0,0 +1,66 @@ +FileSearchDialog.jLabel1.text=File Type +FileSearchDialog.dsCheckBox.text=Data source +FileSearchDialog.cancelButton.text=Cancel +FileSearchDialog.freqCheckBox.text=CR Frequency +FileSearchDialog.sizeCheckBox.text=Size +FileSearchDialog.kwCheckBox.text=Keyword +FileSearchDialog.addParentButton.text=Add +FileSearchDialog.deleteParentButton.text=Delete +FileSearchDialog.parentFullRadioButton.text=Full +FileSearchDialog.parentSubstringRadioButton.text=Substring +FileSearchDialog.jLabel2.text=(All will be used) +FileSearchDialog.jLabel3.text=Group by attribute: +FileSearchDialog.jLabel4.text=Order groups by: +FileSearchDialog.orderAttrRadioButton.text=Attribute +FileSearchDialog.orderSizeRadioButton.text=Group Size +FileSearchDialog.jLabel5.text=Order files by: +FileSearchDialog.parentCheckBox.text=Parent +FileSearchPanel.sortingPanel.border.title=Grouping +FileSearchPanel.searchButton.text=Show +FileSearchPanel.addButton.text=Add +FileSearchPanel.substringRadioButton.text=Substring +FileSearchPanel.fullRadioButton.text=Full +FileSearchPanel.parentCheckbox.text=Parent Folder: +FileSearchPanel.keywordCheckbox.text=Keyword: +FileSearchPanel.crFrequencyCheckbox.text=Past Occurrences: +FileSearchPanel.dataSourceCheckbox.text=Data Source: +FileSearchPanel.sizeCheckbox.text=File Size: +FileSearchPanel.orderGroupsByLabel.text=Order Groups By: +FileSearchPanel.filtersScrollPane.border.title=Filters +FileSearchPanel.parentLabel.text=(All will be used) +FileSearchPanel.deleteButton.text=Delete +FileSearchPanel.orderByLabel.text=Order Within Groups By: +FileSearchPanel.groupByLabel.text=Group By: +FileSearchDialog.searchButton.text=Search +FileSearchDialog.hashCheckBox.text=Hash Set +FileSearchDialog.intCheckBox.text=Interesting Items +FileSearchDialog.tagsCheckBox.text=Tags +FileSearchDialog.objCheckBox.text=Objects +FileSearchDialog.exifCheckBox.text=Must contain EXIF data +FileSearchDialog.notableCheckBox.text=Must have been tagged as notable +FileSearchDialog.scoreCheckBox.text=Has score +FileSearchPanel.cancelButton.text=Cancel +FileSearchPanel.hashSetCheckbox.text=Hash Set: +FileSearchPanel.tagsCheckbox.text=Tag: +FileSearchPanel.interestingItemsCheckbox.text=Interesting Item: +FileSearchPanel.scoreCheckbox.text=Has Score: +FileSearchPanel.exifCheckbox.text=Must contain EXIF data +FileSearchPanel.notableCheckbox.text=Must have been tagged as notable +FileSearchPanel.objectsCheckbox.text=Object Detected: +ResultsPanel.currentPageLabel.text=Page: - +ResultsPanel.pageControlsLabel.text=Pages: +ResultsPanel.gotoPageLabel.text=Go to Page: +ResultsPanel.pageSizeLabel.text=Page Size: +ResultsPanel.instancesList.border.title=Instances +DiscoveryExtractAction.title.extractFiles.text=Extract File +FileSearchPanel.includeRadioButton.text=Include +FileSearchPanel.excludeRadioButton.text=Exclude +FileSearchPanel.knownFilesCheckbox.toolTipText= +FileSearchPanel.knownFilesCheckbox.text=Hide known files +GroupListPanel.groupKeyList.border.title=Groups +DiscoveryTopComponent.imagesButton.text=Images +DiscoveryTopComponent.videosButton.text=Videos +ResultsPanel.resultsSplitPane.toolTipText= +FileSearchPanel.stepTwoLabel.text=Step 2: Filter which images to show +FileSearchPanel.stepThreeLabel.text=Step 3: Choose display settings +DiscoveryTopComponent.stepOneLabel.text=Step 1: Pick File Type diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED new file mode 100644 index 0000000000..6626742f46 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -0,0 +1,217 @@ +CTL_OpenFileDiscoveryAction=File Discovery +# {0} - dataSourceName +DataSourceModuleWrapper.exifModule.text=Exif Parser module was not run on data source: {0}\n +# {0} - dataSourceName +DataSourceModuleWrapper.fileTypeModule.text=File Type Identification module was not run on data source: {0}\n +# {0} - dataSourceName +DataSourceModuleWrapper.hashModule.text=Hash Lookup module was not run on data source: {0}\n +DiscoveryTopComponent.name=\ File Discovery +FileGroup.groupSortingAlgorithm.groupName.text=Group Name +FileGroup.groupSortingAlgorithm.groupSize.text=Group Size +# {0} - Data source name +# {1} - Data source ID +FileSearch.DataSourceGroupKey.datasourceAndID={0}(ID: {1}) +# {0} - Data source ID +FileSearch.DataSourceGroupKey.idOnly=Data source (ID: {0}) +FileSearch.FileTagGroupKey.noSets=None +# {0} - file name +FileSearch.genVideoThumb.progress.text=extracting temporary file {0} +FileSearch.GroupingAttributeType.datasource.displayName=Data Source +FileSearch.GroupingAttributeType.fileType.displayName=File Type +FileSearch.GroupingAttributeType.frequency.displayName=Past Occurrences +FileSearch.GroupingAttributeType.hash.displayName=Hash Set +FileSearch.GroupingAttributeType.interestingItem.displayName=Interesting Item +FileSearch.GroupingAttributeType.keywordList.displayName=Keyword +FileSearch.GroupingAttributeType.none.displayName=None +FileSearch.GroupingAttributeType.object.displayName=Object Detected +FileSearch.GroupingAttributeType.parent.displayName=Parent Folder +FileSearch.GroupingAttributeType.size.displayName=File Size +FileSearch.GroupingAttributeType.tag.displayName=Tag +FileSearch.HashHitsGroupKey.noHashHits=None +FileSearch.InterestingItemGroupKey.noSets=None +FileSearch.KeywordListGroupKey.noKeywords=None +FileSearch.NoGroupingGroupKey.allFiles=All Files +FileSearch.ObjectDetectedGroupKey.noSets=None +FileSearchData.FileSize.LARGE_IMAGE.displayName=Large: 1-50MB +FileSearchData.FileSize.LARGE_VIDEO.displayName=Large: 1-5GB +FileSearchData.FileSize.MEDIUM_IMAGE.displayName=Medium: 100KB-1MB +FileSearchData.FileSize.MEDIUM_VIDEO.displayName=Medium: 100MB-1GB +FileSearchData.FileSize.SMALL_IMAGE.displayName=Small: 16-100KB +FileSearchData.FileSize.SMALL_VIDEO.displayName=Small: 500KB-100MB +FileSearchData.FileSize.XLARGE_IMAGE.displayName=XLarge: 50-200MB +FileSearchData.FileSize.XLARGE_VIDEO.displayName=XLarge: 5-10GB +FileSearchData.FileSize.XSMALL_IMAGE.displayName=XSmall: 0-16KB +FileSearchData.FileSize.XSMALL_VIDEO.displayName=XSmall: 0-500KB +FileSearchData.FileSize.XXLARGE_IMAGE.displayName=XXLarge: 200MB+ +FileSearchData.FileSize.XXLARGE_VIDEO.displayName=XXLarge: 10GB+ +FileSearchData.FileType.Audio.displayName=Audio +FileSearchData.FileType.Documents.displayName=Documents +FileSearchData.FileType.Executables.displayName=Executables +FileSearchData.FileType.Image.displayName=Image +FileSearchData.FileType.Other.displayName=Other/Unknown +FileSearchData.FileType.Video.displayName=Video +FileSearchData.Frequency.common.displayName=Common (11 - 100) +FileSearchData.Frequency.known.displayName=Known (NSRL) +FileSearchData.Frequency.rare.displayName=Rare (2-10) +FileSearchData.Frequency.unique.displayName=Unique (1) +FileSearchData.Frequency.unknown.displayName=Unknown +FileSearchData.Frequency.verycommon.displayName=Very Common (100+) +FileSearchData.Score.interesting.displayName=Interesting +FileSearchData.Score.notable.displayName=Notable +FileSearchData.Score.unknown.displayName=Unknown +FileSearchDialog.jLabel1.text=File Type +FileSearchDialog.dsCheckBox.text=Data source +FileSearchDialog.cancelButton.text=Cancel +FileSearchDialog.freqCheckBox.text=CR Frequency +FileSearchDialog.sizeCheckBox.text=Size +FileSearchDialog.kwCheckBox.text=Keyword +FileSearchDialog.addParentButton.text=Add +FileSearchDialog.deleteParentButton.text=Delete +FileSearchDialog.parentFullRadioButton.text=Full +FileSearchDialog.parentSubstringRadioButton.text=Substring +FileSearchDialog.jLabel2.text=(All will be used) +FileSearchDialog.jLabel3.text=Group by attribute: +FileSearchDialog.jLabel4.text=Order groups by: +FileSearchDialog.orderAttrRadioButton.text=Attribute +FileSearchDialog.orderSizeRadioButton.text=Group Size +FileSearchDialog.jLabel5.text=Order files by: +FileSearchDialog.parentCheckBox.text=Parent +FileSearchFiltering.concatenateSetNamesForDisplay.comma=, +# {0} - Data source name +# {1} - Data source ID +FileSearchFiltering.DataSourceFilter.datasource={0}({1}) +# {0} - filters +FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0} +FileSearchFiltering.DataSourceFilter.or=\ or +FileSearchFiltering.ExifFilter.desc=Files that contain EXIF data +# {0} - filters +FileSearchFiltering.FileTypeFilter.desc=Files with type: {0} +FileSearchFiltering.FileTypeFilter.or=\ or +# {0} - filters +FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0} +FileSearchFiltering.FrequencyFilter.or=\ or +# {0} - filters +FileSearchFiltering.HashSetFilter.desc=Files with hash set hits in set(s): {0} +# {0} - filters +FileSearchFiltering.InterestingItemSetFilter.desc=Files with interesting item hits in set(s): {0} +# {0} - filters +FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0} +FileSearchFiltering.KnownFilter.desc=Files which are not known +# {0} - filters +FileSearchFiltering.ObjectDetectionFilter.desc=Files with objects detected in set(s): {0} +# {0} - filters +FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0} +FileSearchFiltering.ParentFilter.exact=(exact match) +FileSearchFiltering.ParentFilter.or=\ or +FileSearchFiltering.ParentFilter.substring=(substring) +FileSearchFiltering.ParentSearchTerm.excludeString=\ (exclude) +FileSearchFiltering.ParentSearchTerm.fullString=\ (exact) +FileSearchFiltering.ParentSearchTerm.includeString=\ (include) +FileSearchFiltering.ParentSearchTerm.subString=\ (substring) +FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable +# {0} - filters +FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0} +# {0} - filters +FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0} +FileSearchFiltering.SizeFilter.or=\ or +# {0} - Minimum bytes +# {1} - Maximum bytes +FileSearchFiltering.SizeFilter.range=({0} to {1}) +# {0} - tag names +FileSearchFiltering.TagsFilter.desc=Files that have been tagged {0} +FileSearchFiltering.TagsFilter.or=\ or +FileSearchPanel.dialogTitle.text=Test file search +FileSearchPanel.sortingPanel.border.title=Grouping +FileSearchPanel.searchButton.text=Show +FileSearchPanel.addButton.text=Add +FileSearchPanel.substringRadioButton.text=Substring +FileSearchPanel.fullRadioButton.text=Full +FileSearchPanel.parentCheckbox.text=Parent Folder: +FileSearchPanel.keywordCheckbox.text=Keyword: +FileSearchPanel.crFrequencyCheckbox.text=Past Occurrences: +FileSearchPanel.dataSourceCheckbox.text=Data Source: +FileSearchPanel.sizeCheckbox.text=File Size: +FileSearchPanel.orderGroupsByLabel.text=Order Groups By: +FileSearchPanel.filtersScrollPane.border.title=Filters +FileSearchPanel.parentLabel.text=(All will be used) +FileSearchPanel.deleteButton.text=Delete +FileSearchPanel.orderByLabel.text=Order Within Groups By: +FileSearchPanel.groupByLabel.text=Group By: +FileSearchDialog.searchButton.text=Search +FileSearchDialog.hashCheckBox.text=Hash Set +FileSearchDialog.intCheckBox.text=Interesting Items +FileSearchDialog.tagsCheckBox.text=Tags +FileSearchDialog.objCheckBox.text=Objects +FileSearchDialog.exifCheckBox.text=Must contain EXIF data +FileSearchDialog.notableCheckBox.text=Must have been tagged as notable +FileSearchDialog.scoreCheckBox.text=Has score +FileSearchPanel.cancelButton.text=Cancel +FileSearchPanel.hashSetCheckbox.text=Hash Set: +FileSearchPanel.tagsCheckbox.text=Tag: +FileSearchPanel.interestingItemsCheckbox.text=Interesting Item: +FileSearchPanel.scoreCheckbox.text=Has Score: +FileSearchPanel.exifCheckbox.text=Must contain EXIF data +FileSearchPanel.notableCheckbox.text=Must have been tagged as notable +FileSearchPanel.objectsCheckbox.text=Object Detected: +FileSorter.SortingMethod.datasource.displayName=Data Source +FileSorter.SortingMethod.filename.displayName=File Name +FileSorter.SortingMethod.filesize.displayName=File Size +FileSorter.SortingMethod.filetype.displayName=File Type +FileSorter.SortingMethod.frequency.displayName=Central Repo Frequency +FileSorter.SortingMethod.fullPath.displayName=Full Path +FileSorter.SortingMethod.keywordlist.displayName=Keyword List Names +GroupsListPanel.noResults.message.text=No results were found for the selected filters. +GroupsListPanel.noResults.title.text=No results found +ImageThumbnailPanel.bytes.text=bytes +# {0} - numberOfInstances +ImageThumbnailPanel.countLabel.text=Number of Instances: {0} +ImageThumbnailPanel.gigaBytes.text=GB +ImageThumbnailPanel.isDeleted.text=All instances of file are deleted. +ImageThumbnailPanel.kiloBytes.text=KB +ImageThumbnailPanel.megaBytes.text=MB +# {0} - fileSize +# {1} - units +ImageThumbnailPanel.sizeLabel.text=Size: {0} {1} +ImageThumbnailPanel.terraBytes.text=TB +OpenFileDiscoveryAction.resultsIncomplete.text=Results may be incomplete +ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it. +ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable. +ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag. +ResultFile.score.taggedFile.description=At least one instance of the file has been tagged. +# {0} - currentPage +# {1} - totalPages +ResultsPanel.currentPage.displayValue=Page: {0} of {1} +ResultsPanel.currentPageLabel.text=Page: - +# {0} - selectedPage +# {1} - maxPage +ResultsPanel.invalidPageNumber.message=The selected page number {0} does not exist. Please select a value from 1 to {1}. +ResultsPanel.invalidPageNumber.title=Invalid Page Number +ResultsPanel.openInExternalViewer.name=Open in External Viewer +ResultsPanel.pageControlsLabel.text=Pages: +ResultsPanel.gotoPageLabel.text=Go to Page: +ResultsPanel.pageSizeLabel.text=Page Size: +ResultsPanel.instancesList.border.title=Instances +DiscoveryExtractAction.title.extractFiles.text=Extract File +FileSearchPanel.includeRadioButton.text=Include +FileSearchPanel.excludeRadioButton.text=Exclude +FileSearchPanel.knownFilesCheckbox.toolTipText= +FileSearchPanel.knownFilesCheckbox.text=Hide known files +GroupListPanel.groupKeyList.border.title=Groups +DiscoveryTopComponent.imagesButton.text=Images +DiscoveryTopComponent.videosButton.text=Videos +ResultsPanel.resultsSplitPane.toolTipText= +FileSearchPanel.stepTwoLabel.text=Step 2: Filter which images to show +FileSearchPanel.stepThreeLabel.text=Step 3: Choose display settings +DiscoveryTopComponent.stepOneLabel.text=Step 1: Pick File Type +ResultsPanel.viewFileInDir.name=View File in Directory +VideoThumbnailPanel.bytes.text=bytes +# {0} - numberOfInstances +VideoThumbnailPanel.countLabel.text=Number of Instances: {0} +VideoThumbnailPanel.deleted.text=All instances of file are deleted. +VideoThumbnailPanel.gigaBytes.text=GB +VideoThumbnailPanel.kiloBytes.text=KB +VideoThumbnailPanel.megaBytes.text=MB +# {0} - fileSize +# {1} - units +VideoThumbnailPanel.sizeLabel.text=Size: {0} {1} +VideoThumbnailPanel.terraBytes.text=TB diff --git a/Core/src/org/sleuthkit/autopsy/filequery/DataSourceModulesWrapper.java b/Core/src/org/sleuthkit/autopsy/filequery/DataSourceModulesWrapper.java new file mode 100644 index 0000000000..53cabedfb3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/DataSourceModulesWrapper.java @@ -0,0 +1,125 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.modules.exif.ExifParserModuleFactory; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory; +import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; +import org.sleuthkit.datamodel.IngestJobInfo; +import org.sleuthkit.datamodel.IngestModuleInfo; + +/** + * Wrapper class to keep track of which modules were run on each data source. + */ +class DataSourceModulesWrapper { + + private boolean hashModuleRun = false; + private boolean fileTypeModuleRun = false; + private boolean exifModuleRun = false; + private final String dataSourceName; + + /** + * Create a new DataSourceModulesWrapper object + * + * @param dsName The name of the data source being kept track of. + */ + DataSourceModulesWrapper(String dsName) { + dataSourceName = dsName; + } + + @Messages({"# {0} - dataSourceName", + "DataSourceModuleWrapper.hashModule.text=Hash Lookup module was not run on data source: {0}\n", + "# {0} - dataSourceName", + "DataSourceModuleWrapper.fileTypeModule.text=File Type Identification module was not run on data source: {0}\n", + "# {0} - dataSourceName", + "DataSourceModuleWrapper.exifModule.text=Exif Parser module was not run on data source: {0}\n" + }) + /** + * Get the message which indicates which modules were not run on this data + * source. + */ + String getMessage() { + String message = ""; + if (!hashModuleRun) { + message += Bundle.DataSourceModuleWrapper_hashModule_text(dataSourceName); + } + if (!fileTypeModuleRun) { + message += Bundle.DataSourceModuleWrapper_fileTypeModule_text(dataSourceName); + } + if (!exifModuleRun) { + message += Bundle.DataSourceModuleWrapper_exifModule_text(dataSourceName); + } + return message; + } + + /** + * Update which modules were run for this data source based on the specified + * ingest job. + * + * @param jobInfo The IngestJobInfo for the job which was run on this data + * source. + */ + void updateModulesRun(IngestJobInfo jobInfo) { + for (IngestModuleInfo moduleInfo : jobInfo.getIngestModuleInfo()) { + if (hashModuleRun && fileTypeModuleRun && exifModuleRun) { + return; + } + updateHashModuleStatus(moduleInfo); + updateFileTypeStatus(moduleInfo); + updateExifStatus(moduleInfo); + } + } + + /** + * Update whether the Hash Lookup module was run for this data source. + * + * @param moduleInfo Information regarding a module which was run on this + * data source. + */ + private void updateHashModuleStatus(IngestModuleInfo moduleInfo) { + if (!hashModuleRun && moduleInfo.getDisplayName().equals(HashLookupModuleFactory.getModuleName())) { + hashModuleRun = true; + } + } + + /** + * Update whether the File Type ID module was run for this data source. + * + * @param moduleInfo Information regarding a module which was run on this + * data source. + */ + private void updateFileTypeStatus(IngestModuleInfo moduleInfo) { + if (!fileTypeModuleRun && moduleInfo.getDisplayName().equals(FileTypeIdModuleFactory.getModuleName())) { + fileTypeModuleRun = true; + } + } + + /** + * Update whether the Exif module was run for this data source. + * + * @param moduleInfo Information regarding a module which was run on this + * data source. + */ + private void updateExifStatus(IngestModuleInfo moduleInfo) { + if (!exifModuleRun && moduleInfo.getDisplayName().equals(ExifParserModuleFactory.getModuleName())) { + exifModuleRun = true; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryEventUtils.java b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryEventUtils.java new file mode 100644 index 0000000000..f73cf4171e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryEventUtils.java @@ -0,0 +1,335 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import com.google.common.eventbus.EventBus; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.sleuthkit.autopsy.filequery.FileSearch.GroupKey; +import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; + +/** + * Class to handle event bus and events for file discovery tool. + */ +final class DiscoveryEventUtils { + + private final static EventBus discoveryEventBus = new EventBus(); + + /** + * Get the file discovery event bus. + * + * @return The file discovery event bus. + */ + static EventBus getDiscoveryEventBus() { + return discoveryEventBus; + } + + private DiscoveryEventUtils() { + } + + /** + * Event to signal the start of a search being performed. + */ + static final class SearchStartedEvent { + + private final FileType fileType; + + /** + * Construct a new SearchStartedEvent + * + * @param type The type of file the search event is for. + */ + SearchStartedEvent(FileType type) { + this.fileType = type; + } + + /** + * Get the type of file the search is being performed for. + * + * @return The type of files being searched for. + */ + FileType getType() { + return fileType; + } + } + + /** + * Event to signal the completion of a search being performed. + */ + static final class SearchCompleteEvent { + + private final Map groupMap; + private final List searchFilters; + private final FileSearch.AttributeType groupingAttribute; + private final FileGroup.GroupSortingAlgorithm groupSort; + private final FileSorter.SortingMethod fileSortMethod; + + /** + * Construct a new SearchCompleteEvent, + * + * @param groupMap The map of groups which were found by the + * search. + * @param searchFilters The search filters which were used by the + * search. + * @param groupingAttribute The grouping attribute used by the search. + * @param groupSort The sorting algorithm used for groups. + * @param fileSortMethod The sorting method used for files. + */ + SearchCompleteEvent(Map groupMap, List searchfilters, + FileSearch.AttributeType groupingAttribute, FileGroup.GroupSortingAlgorithm groupSort, + FileSorter.SortingMethod fileSortMethod) { + this.groupMap = groupMap; + this.searchFilters = searchfilters; + this.groupingAttribute = groupingAttribute; + this.groupSort = groupSort; + this.fileSortMethod = fileSortMethod; + } + + /** + * Get the map of groups found by the search. + * + * @return The map of groups which were found by the search. + */ + Map getGroupMap() { + return Collections.unmodifiableMap(groupMap); + } + + /** + * Get the file filters used by the search. + * + * @return The search filters which were used by the search. + */ + List getFilters() { + return Collections.unmodifiableList(searchFilters); + } + + /** + * Get the grouping attribute used by the search. + * + * @return The grouping attribute used by the search. + */ + FileSearch.AttributeType getGroupingAttr() { + return groupingAttribute; + } + + /** + * Get the sorting algorithm used for groups. + * + * @return The sorting algorithm used for groups. + */ + FileGroup.GroupSortingAlgorithm getGroupSort() { + return groupSort; + } + + /** + * Get the sorting method used for files. + * + * @return The sorting method used for files. + */ + FileSorter.SortingMethod getFileSort() { + return fileSortMethod; + } + + } + + /** + * Event to signal the completion of page retrieval and include the page + * contents. + */ + static final class PageRetrievedEvent { + + private final List results; + private final int page; + private final FileType resultType; + + /** + * Construct a new PageRetrievedEvent. + * + * @param resultType The type of files which exist in the page. + * @param page The number of the page which was retrieved. + * @param results The list of files in the page retrieved. + */ + PageRetrievedEvent(FileType resultType, int page, List results) { + this.results = results; + this.page = page; + this.resultType = resultType; + } + + /** + * Get the list of files in the page retrieved. + * + * @return The list of files in the page retrieved. + */ + List getSearchResults() { + return Collections.unmodifiableList(results); + } + + /** + * Get the page number which was retrieved. + * + * @return The number of the page which was retrieved. + */ + int getPageNumber() { + return page; + } + + /** + * Get the type of files which exist in the page. + * + * @return The type of files which exist in the page. + */ + FileType getType() { + return resultType; + } + } + + /** + * Event to signal that there were no results for the search. + */ + static final class NoResultsEvent { + + /** + * Construct a new NoResultsEvent. + */ + NoResultsEvent() { + //no arg constructor + } + } + + /** + * Event to signal that a search has been cancelled + */ + static final class SearchCancelledEvent { + + /** + * Construct a new SearchCancelledEvent. + */ + SearchCancelledEvent() { + //no arg constructor + } + + } + + /** + * Event to signal that a group has been selected. + */ + static final class GroupSelectedEvent { + + private final FileType resultType; + private final GroupKey groupKey; + private final int groupSize; + private final List searchfilters; + private final FileSearch.AttributeType groupingAttribute; + private final FileGroup.GroupSortingAlgorithm groupSort; + private final FileSorter.SortingMethod fileSortMethod; + + /** + * Construct a new GroupSelectedEvent. + * + * @param searchfilters The search filters which were used by the + * search. + * @param groupingAttribute The grouping attribute used by the search. + * @param groupSort The sorting algorithm used for groups. + * @param fileSortMethod The sorting method used for files. + * @param groupKey The key associated with the group which was + * selected. + * @param groupSize The number of files in the group which was + * selected. + * @param resultType The type of files which exist in the group. + */ + GroupSelectedEvent(List searchfilters, + FileSearch.AttributeType groupingAttribute, FileGroup.GroupSortingAlgorithm groupSort, + FileSorter.SortingMethod fileSortMethod, GroupKey groupKey, int groupSize, FileType resultType) { + this.searchfilters = searchfilters; + this.groupingAttribute = groupingAttribute; + this.groupSort = groupSort; + this.fileSortMethod = fileSortMethod; + this.groupKey = groupKey; + this.groupSize = groupSize; + this.resultType = resultType; + } + + /** + * Get the type of files which exist in the group. + * + * @return The type of files which exist in the group. + */ + FileType getResultType() { + return resultType; + } + + /** + * Get the group key which is used to uniquely identify the group + * selected. + * + * @return The group key which is used to uniquely identify the group + * selected. + */ + GroupKey getGroupKey() { + return groupKey; + } + + /** + * Get the number of files in the group which was selected. + * + * @return The number of files in the group which was selected. + */ + int getGroupSize() { + return groupSize; + } + + /** + * Get the sorting algorithm used in the group which was selected. + * + * @return The sorting algorithm used for groups. + */ + FileGroup.GroupSortingAlgorithm getGroupSort() { + return groupSort; + } + + /** + * Get the sorting method used for files in the group. + * + * @return The sorting method used for files. + */ + FileSorter.SortingMethod getFileSort() { + return fileSortMethod; + } + + /** + * Get the file filters which were used by the search + * + * @return The search filters which were used by the search. + */ + List getFilters() { + return Collections.unmodifiableList(searchfilters); + } + + /** + * Get the grouping attribute used to create the groups. + * + * @return The grouping attribute used by the search. + */ + FileSearch.AttributeType getGroupingAttr() { + return groupingAttribute; + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryExtractAction.java b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryExtractAction.java new file mode 100644 index 0000000000..27d1d8e401 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryExtractAction.java @@ -0,0 +1,60 @@ +/* + * Autopsy + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this content 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.filequery; + +import org.sleuthkit.autopsy.directorytree.actionhelpers.ExtractActionHelper; +import java.awt.event.ActionEvent; +import java.util.Collection; +import java.util.HashSet; +import javax.swing.AbstractAction; +import org.openide.util.NbBundle; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * Extracts AbstractFiles to a location selected by the user. + */ +public final class DiscoveryExtractAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + private final Collection files = new HashSet<>(); + + /** + * Construct a new DiscoveryExtractAction for the extraction of the + * specified files. + * + * @param selectedFiles The files to extract from the current case. + */ + public DiscoveryExtractAction(Collection selectedFiles) { + super(NbBundle.getMessage(DiscoveryExtractAction.class, "DiscoveryExtractAction.title.extractFiles.text")); + files.addAll(selectedFiles); + } + + /** + * Asks user to choose destination, then extracts content to destination + * (recursing on directories). + * + * @param e The action event. + */ + @Override + public void actionPerformed(ActionEvent e) { + ExtractActionHelper extractor = new ExtractActionHelper(); + extractor.extract(e, files); + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentsChildren.java b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryThumbnailChildren.java old mode 100755 new mode 100644 similarity index 67% rename from Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentsChildren.java rename to Core/src/org/sleuthkit/autopsy/filequery/DiscoveryThumbnailChildren.java index 7dbe2133e0..35ee60ac5c --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentsChildren.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryThumbnailChildren.java @@ -1,5 +1,5 @@ /* - * Autopsy Forensic Browser + * Autopsy * * Copyright 2019 Basis Technology Corp. * Contact: carrier sleuthkit org @@ -16,52 +16,42 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.communications.relationships; +package org.sleuthkit.autopsy.filequery; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.TreeSet; -import java.util.logging.Level; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; /** - * Factory for creating thumbnail children nodes. + * Create a node containing the children to display in the + * DataResultViewerThumbnail */ -final class AttachmentsChildren extends Children.Keys { +class DiscoveryThumbnailChildren extends Children.Keys { - private static final Logger logger = Logger.getLogger(AttachmentsChildren.class.getName()); - - private final Set artifacts; + private final List files; /* - * Creates the list of thumbnails from the given list of - * BlackboardArtifacts. - * - * The thumbnails will be initialls sorted by size, then name so that they - * appear sorted by size by default. + * Creates the list of thumbnails from the given list of AbstractFiles. */ - AttachmentsChildren(Set artifacts) { + DiscoveryThumbnailChildren(List files) { super(false); - this.artifacts = artifacts; - + this.files = files; } @Override protected Node[] createNodes(AbstractFile t) { - return new Node[]{new AttachementNode(t)}; + return new Node[]{new ThumbnailNode(t)}; } @Override @@ -76,28 +66,16 @@ final class AttachmentsChildren extends Children.Keys { return result; }); - - artifacts.forEach((bba) -> { - try { - for (Content childContent : bba.getChildren()) { - if (childContent instanceof AbstractFile) { - thumbnails.add((AbstractFile) childContent); - } - } - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Unable to get children from artifact.", ex); //NON-NLS - } - }); - + thumbnails.addAll(files); setKeys(thumbnails); } /** * A node for representing a thumbnail. */ - static class AttachementNode extends FileNode { + static class ThumbnailNode extends FileNode { - AttachementNode(AbstractFile file) { + ThumbnailNode(AbstractFile file) { super(file, false); } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryTopComponent.form b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryTopComponent.form new file mode 100644 index 0000000000..b8486b8ae6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryTopComponent.form @@ -0,0 +1,233 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryTopComponent.java b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryTopComponent.java new file mode 100644 index 0000000000..6e6fcf7cec --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryTopComponent.java @@ -0,0 +1,342 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import com.google.common.eventbus.Subscribe; +import java.awt.Color; +import java.util.List; +import java.util.stream.Collectors; +import javax.swing.SwingUtilities; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import org.openide.util.NbBundle; +import org.openide.windows.Mode; +import org.openide.windows.RetainLocation; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.corecomponents.DataContentPanel; +import org.sleuthkit.autopsy.corecomponents.TableFilterNode; +import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.datamodel.FileNode; +import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * Create a dialog for displaying the file discovery tool + */ +@TopComponent.Description(preferredID = "DiscoveryTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER) +@TopComponent.Registration(mode = "discovery", openAtStartup = false) +@RetainLocation("discovery") +@NbBundle.Messages("DiscoveryTopComponent.name= File Discovery") +public final class DiscoveryTopComponent extends TopComponent { + + private static final long serialVersionUID = 1L; + private static final String PREFERRED_ID = "DiscoveryTopComponent"; // NON-NLS + private static final Color SELECTED_COLOR = new Color(216, 230, 242); + private static final Color UNSELECTED_COLOR = new Color(240, 240, 240); + private final FileSearchPanel fileSearchPanel; + private final GroupListPanel groupListPanel; + private final DataContentPanel dataContentPanel; + private final ResultsPanel resultsPanel; + + /** + * Creates new form FileDiscoveryDialog + */ + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) + public DiscoveryTopComponent() { + initComponents(); + setName(Bundle.DiscoveryTopComponent_name()); + fileSearchPanel = new FileSearchPanel(); + dataContentPanel = DataContentPanel.createInstance(); + resultsPanel = new ResultsPanel(); + groupListPanel = new GroupListPanel(); + leftSplitPane.setLeftComponent(fileSearchPanel); + leftSplitPane.setRightComponent(groupListPanel); + rightSplitPane.setTopComponent(resultsPanel); + rightSplitPane.setBottomComponent(dataContentPanel); + //add list selection listener so the content viewer will be updated with the selected file + //when a file is selected in the results panel + resultsPanel.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + if (!e.getValueIsAdjusting()) { + SwingUtilities.invokeLater(() -> { + AbstractFile file = resultsPanel.getSelectedFile(); + if (file != null) { + dataContentPanel.setNode(new TableFilterNode(new FileNode(file), false)); + } else { + dataContentPanel.setNode(null); + } + }); + } + } + }); + + } + + /** + * Get the current DiscoveryTopComponent if it is open. + * + * @return The open DiscoveryTopComponent or null if it has not been opened. + */ + public static DiscoveryTopComponent getTopComponent() { + return (DiscoveryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + } + + /** + * Reset the top component so it isn't displaying any results. + */ + public void resetTopComponent() { + resultsPanel.resetResultViewer(); + groupListPanel.resetGroupList(); + } + + /** + * Update the search settings to a default state. + */ + void updateSearchSettings() { + resetTopComponent(); + fileSearchPanel.resetPanel(); + imagesButton.setSelected(true); + imagesButton.setEnabled(false); + imagesButton.setBackground(SELECTED_COLOR); + imagesButton.setForeground(Color.BLACK); + videosButton.setSelected(false); + videosButton.setEnabled(true); + videosButton.setBackground(UNSELECTED_COLOR); + fileSearchPanel.setSelectedType(FileSearchData.FileType.IMAGE); + } + + @Override + public void componentOpened() { + super.componentOpened(); + WindowManager.getDefault().setTopComponentFloating(this, true); + DiscoveryEventUtils.getDiscoveryEventBus().register(this); + DiscoveryEventUtils.getDiscoveryEventBus().register(resultsPanel); + DiscoveryEventUtils.getDiscoveryEventBus().register(groupListPanel); + DiscoveryEventUtils.getDiscoveryEventBus().register(fileSearchPanel); + } + + @Override + protected void componentClosed() { + fileSearchPanel.cancelSearch(); + DiscoveryEventUtils.getDiscoveryEventBus().unregister(this); + DiscoveryEventUtils.getDiscoveryEventBus().unregister(fileSearchPanel); + DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel); + DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel); + super.componentClosed(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JSplitPane mainSplitPane = new javax.swing.JSplitPane(); + leftSplitPane = new javax.swing.JSplitPane(); + rightSplitPane = new javax.swing.JSplitPane(); + javax.swing.JPanel toolBarPanel = new javax.swing.JPanel(); + javax.swing.JToolBar toolBar = new javax.swing.JToolBar(); + imagesButton = new javax.swing.JButton(); + javax.swing.JLabel stepOneLabel = new javax.swing.JLabel(); + videosButton = new javax.swing.JButton(); + javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(62, 0), new java.awt.Dimension(62, 0), new java.awt.Dimension(62, 32767)); + + setPreferredSize(new java.awt.Dimension(1400, 900)); + setLayout(new java.awt.BorderLayout()); + + mainSplitPane.setDividerLocation(450); + mainSplitPane.setPreferredSize(new java.awt.Dimension(1400, 828)); + + leftSplitPane.setDividerLocation(325); + leftSplitPane.setToolTipText(""); + leftSplitPane.setPreferredSize(new java.awt.Dimension(400, 828)); + mainSplitPane.setLeftComponent(leftSplitPane); + + rightSplitPane.setDividerLocation(475); + rightSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); + rightSplitPane.setResizeWeight(0.5); + rightSplitPane.setPreferredSize(new java.awt.Dimension(1000, 828)); + mainSplitPane.setRightComponent(rightSplitPane); + + add(mainSplitPane, java.awt.BorderLayout.CENTER); + + toolBar.setFloatable(false); + toolBar.setRollover(true); + + imagesButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/pictures-icon.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(imagesButton, org.openide.util.NbBundle.getMessage(DiscoveryTopComponent.class, "DiscoveryTopComponent.imagesButton.text")); // NOI18N + imagesButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/pictures-icon.png"))); // NOI18N + imagesButton.setFocusable(false); + imagesButton.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + imagesButton.setMaximumSize(new java.awt.Dimension(90, 43)); + imagesButton.setMinimumSize(new java.awt.Dimension(90, 43)); + imagesButton.setPreferredSize(new java.awt.Dimension(90, 43)); + imagesButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + imagesButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(stepOneLabel, org.openide.util.NbBundle.getMessage(DiscoveryTopComponent.class, "DiscoveryTopComponent.stepOneLabel.text")); // NOI18N + + videosButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/video-icon.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(videosButton, org.openide.util.NbBundle.getMessage(DiscoveryTopComponent.class, "DiscoveryTopComponent.videosButton.text")); // NOI18N + videosButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/video-icon.png"))); // NOI18N + videosButton.setDisabledSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/video-icon.png"))); // NOI18N + videosButton.setFocusable(false); + videosButton.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + videosButton.setMaximumSize(new java.awt.Dimension(90, 43)); + videosButton.setMinimumSize(new java.awt.Dimension(90, 43)); + videosButton.setPreferredSize(new java.awt.Dimension(90, 43)); + videosButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + videosButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout toolBarPanelLayout = new javax.swing.GroupLayout(toolBarPanel); + toolBarPanel.setLayout(toolBarPanelLayout); + toolBarPanelLayout.setHorizontalGroup( + toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(toolBarPanelLayout.createSequentialGroup() + .addContainerGap(486, Short.MAX_VALUE) + .addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, toolBarPanelLayout.createSequentialGroup() + .addComponent(imagesButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(videosButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, toolBarPanelLayout.createSequentialGroup() + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, 0) + .addComponent(stepOneLabel) + .addGap(62, 62, 62))) + .addComponent(toolBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(486, Short.MAX_VALUE)) + ); + toolBarPanelLayout.setVerticalGroup( + toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(toolBar, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(toolBarPanelLayout.createSequentialGroup() + .addGap(4, 4, 4) + .addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(stepOneLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(filler1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(videosButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(imagesButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(5, 5, 5)) + ); + + add(toolBarPanel, java.awt.BorderLayout.PAGE_START); + }// //GEN-END:initComponents + + private void imagesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_imagesButtonActionPerformed + resetTopComponent(); + imagesButton.setSelected(true); + imagesButton.setEnabled(false); + imagesButton.setBackground(SELECTED_COLOR); + imagesButton.setForeground(Color.BLACK); + videosButton.setSelected(false); + videosButton.setEnabled(true); + videosButton.setBackground(UNSELECTED_COLOR); + fileSearchPanel.setSelectedType(FileSearchData.FileType.IMAGE); + }//GEN-LAST:event_imagesButtonActionPerformed + + private void videosButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_videosButtonActionPerformed + resetTopComponent(); + imagesButton.setSelected(false); + imagesButton.setEnabled(true); + imagesButton.setBackground(UNSELECTED_COLOR); + videosButton.setSelected(true); + videosButton.setEnabled(false); + videosButton.setBackground(SELECTED_COLOR); + videosButton.setForeground(Color.BLACK); + fileSearchPanel.setSelectedType(FileSearchData.FileType.VIDEO); + }//GEN-LAST:event_videosButtonActionPerformed + + /** + * Update the user interface in response to a search being cancelled. + * + * @param searchCancelledEvent The SearchCancelledEvent received. + */ + @Subscribe + void handleSearchCancelledEvent(DiscoveryEventUtils.SearchCancelledEvent searchCancelledEvent) { + SwingUtilities.invokeLater(() -> { + if (fileSearchPanel.getSelectedType() == FileType.VIDEO) { + imagesButton.setEnabled(true); + } else if (fileSearchPanel.getSelectedType() == FileType.IMAGE) { + videosButton.setEnabled(true); + } + }); + } + + /** + * Update the user interface in response to a search being completed. + * + * @param searchCompletedEvent The SearchCompletedEvent received. + */ + @Subscribe + void handleSearchCompletedEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompletedEvent) { + SwingUtilities.invokeLater(() -> { + if (fileSearchPanel.getSelectedType() == FileType.VIDEO) { + imagesButton.setEnabled(true); + } else if (fileSearchPanel.getSelectedType() == FileType.IMAGE) { + videosButton.setEnabled(true); + } + }); + } + + @Override + public List availableModes(List modes) { + /* + * This looks like the right thing to do, but online discussions seems + * to indicate this method is effectively deprecated. A break point + * placed here was never hit. + */ + return modes.stream().filter(mode -> mode.getName().equals("discovery")) + .collect(Collectors.toList()); + } + + /** + * Update the user interface in response to a search being started. + * + * @param searchStartedEvent The SearchStartedEvent received. + */ + @Subscribe + void handleSearchStartedEvent(DiscoveryEventUtils.SearchStartedEvent searchStartedEvent) { + SwingUtilities.invokeLater(() -> { + imagesButton.setEnabled(false); + videosButton.setEnabled(false); + }); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton imagesButton; + private javax.swing.JSplitPane leftSplitPane; + private javax.swing.JSplitPane rightSplitPane; + private javax.swing.JButton videosButton; + // End of variables declaration//GEN-END:variables + +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java b/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java new file mode 100644 index 0000000000..3c69c4c8f3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java @@ -0,0 +1,176 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.filequery; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.filequery.FileSearch.GroupKey; + +/** + * Class for storing files that belong to a particular group. + */ +class FileGroup implements Comparable { + + private final FileGroup.GroupSortingAlgorithm groupSortingType; + private final GroupKey groupKey; + private final List files; + private final String displayName; + + /** + * Create a FileGroup object with its first file. + * + * @param groupSortingType The method for sorting the group + * @param groupKey The GroupKey for this group + */ + FileGroup(FileGroup.GroupSortingAlgorithm groupSortingType, GroupKey groupKey) { + this.groupSortingType = groupSortingType; + this.groupKey = groupKey; + files = new ArrayList<>(); + this.displayName = groupKey.getDisplayName(); + } + + /** + * Add a ResultFile to the group. Will not be sorted at this time. + * + * @param file The ResultFile to add to the FileGroup + */ + void addFile(ResultFile file) { + if (files.contains(file)) { + ResultFile existingCopy = files.get(files.indexOf(file)); //get the copy of this which exists in the list + existingCopy.addDuplicate(file.getFirstInstance()); + } else { + files.add(file); + } + } + + /** + * Get the display name for this group. + * + * @return The display name of the group. + */ + String getDisplayName() { + return displayName; // NON-NLS + } + + /** + * Get the key which uniquely identifies each group. + * + * @return The unique key for the group. + */ + GroupKey getGroupKey() { + return groupKey; + } + + /** + * Sort all the files in the group + */ + void sortFiles(FileSorter sorter) { + Collections.sort(files, sorter); + } + + /** + * Compare this group to another group for sorting. Uses the algorithm + * specified in groupSortingType. + * + * @param otherGroup the group to compare this one to + * + * @return -1 if this group should be displayed before the other group, 1 + * otherwise + */ + @Override + public int compareTo(FileGroup otherGroup) { + + switch (groupSortingType) { + case BY_GROUP_SIZE: + return compareGroupsBySize(this, otherGroup); + case BY_GROUP_NAME: + default: + return compareGroupsByGroupKey(this, otherGroup); + } + } + + /** + * Compare two groups based on the group key + * + * @param group1 + * @param group2 + * + * @return -1 if group1 should be displayed before group2, 1 otherwise + */ + private static int compareGroupsByGroupKey(FileGroup group1, FileGroup group2) { + return group1.getGroupKey().compareTo(group2.getGroupKey()); + } + + /** + * Compare two groups based on the group size. Falls back on the group key + * if the groups are the same size. + * + * @param group1 + * @param group2 + * + * @return -1 if group1 should be displayed before group2, 1 otherwise + */ + private static int compareGroupsBySize(FileGroup group1, FileGroup group2) { + if (group1.getFiles().size() != group2.getFiles().size()) { + return -1 * Long.compare(group1.getFiles().size(), group2.getFiles().size()); // High to low + } else { + // If the groups have the same size, fall through to the BY_GROUP_NAME sorting + return compareGroupsByGroupKey(group1, group2); + } + } + + /** + * Enum to specify how to sort the group. + */ + @Messages({"FileGroup.groupSortingAlgorithm.groupSize.text=Group Size", + "FileGroup.groupSortingAlgorithm.groupName.text=Group Name"}) + enum GroupSortingAlgorithm { + BY_GROUP_NAME(Bundle.FileGroup_groupSortingAlgorithm_groupName_text()), // Sort using the group key (for example, if grouping by size sort from largest to smallest value) + BY_GROUP_SIZE(Bundle.FileGroup_groupSortingAlgorithm_groupSize_text()); // Sort from largest to smallest group + + private final String displayName; + + /** + * Construct a GroupSortingAlgorithm enum value. + * + * @param name The name to display to the user for the enum value. + */ + GroupSortingAlgorithm(String name) { + displayName = name; + } + + @Override + public String toString() { + return displayName; + } + + } + + /** + * Get the list of ResultFile objects in the group + * + * @return List of ResultFile objects + */ + List getFiles() { + return Collections.unmodifiableList(files); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java new file mode 100644 index 0000000000..495b70f4e4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java @@ -0,0 +1,2075 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.filequery; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.io.Files; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; +import java.io.IOException; +import java.nio.file.Paths; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Level; +import javax.imageio.ImageIO; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.netbeans.api.progress.ProgressHandle; +import org.opencv.core.Mat; +import org.opencv.highgui.VideoCapture; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; +import org.sleuthkit.autopsy.centralrepository.datamodel.InstanceTableCallback; +import org.sleuthkit.autopsy.corelibs.ScalrWrapper; +import org.sleuthkit.autopsy.coreutils.ImageUtils; +import org.sleuthkit.autopsy.coreutils.Logger; +import static org.sleuthkit.autopsy.coreutils.VideoUtils.getVideoFileInTempDir; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.filequery.FileSearchData.FileSize; +import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; +import org.sleuthkit.autopsy.filequery.FileSearchData.Frequency; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.CaseDbAccessManager; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; + +/** + * Main class to perform the file search. + */ +class FileSearch { + + private final static Logger logger = Logger.getLogger(FileSearch.class.getName()); + private static final int MAXIMUM_CACHE_SIZE = 10; + private static final String THUMBNAIL_FORMAT = "png"; //NON-NLS + private static final String VIDEO_THUMBNAIL_DIR = "video-thumbnails"; //NON-NLS + private static final Cache>> searchCache = CacheBuilder.newBuilder() + .maximumSize(MAXIMUM_CACHE_SIZE) + .build(); + + /** + * Run the file search and returns the SearchResults object for debugging. + * Caching new results for access at later time. + * + * @param userName The name of the user performing the search. + * @param filters The filters to apply + * @param groupAttributeType The attribute to use for grouping + * @param groupSortingType The method to use to sort the groups + * @param fileSortingMethod The method to use to sort the files within the + * groups + * @param caseDb The case database + * @param centralRepoDb The central repository database. Can be null if + * not needed. + * + * @return The raw search results + * + * @throws FileSearchException + */ + static SearchResults runFileSearchDebug(String userName, + List filters, + AttributeType groupAttributeType, + FileGroup.GroupSortingAlgorithm groupSortingType, + FileSorter.SortingMethod fileSortingMethod, + SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { + // Make a list of attributes that we want to add values for. This ensures the + // ResultFile objects will have all needed fields set when it's time to group + // and sort them. For example, if we're grouping by central repo frequency, we need + // to make sure we've loaded those values before grouping. + List attributesNeededForGroupingOrSorting = new ArrayList<>(); + attributesNeededForGroupingOrSorting.add(groupAttributeType); + attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes()); + + // Run the queries for each filter + List resultFiles = FileSearchFiltering.runQueries(filters, caseDb, centralRepoDb); + + // Add the data to resultFiles for any attributes needed for sorting and grouping + addAttributes(attributesNeededForGroupingOrSorting, resultFiles, caseDb, centralRepoDb); + + // Collect everything in the search results + SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod); + searchResults.add(resultFiles); + + // Sort and group the results + searchResults.sortGroupsAndFiles(); + Map> resultHashMap = searchResults.toLinkedHashMap(); + SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod); + synchronized (searchCache) { + searchCache.put(searchKey, resultHashMap); + } + return searchResults; + } + + /** + * Run the file search to get the group keys and sizes. Clears cache of + * search results, caching new results for access at later time. + * + * @param userName The name of the user performing the search. + * @param filters The filters to apply + * @param groupAttributeType The attribute to use for grouping + * @param groupSortingType The method to use to sort the groups + * @param fileSortingMethod The method to use to sort the files within the + * groups + * @param caseDb The case database + * @param centralRepoDb The central repository database. Can be null if + * not needed. + * + * @return A LinkedHashMap grouped and sorted according to the parameters + * + * @throws FileSearchException + */ + static Map getGroupSizes(String userName, + List filters, + AttributeType groupAttributeType, + FileGroup.GroupSortingAlgorithm groupSortingType, + FileSorter.SortingMethod fileSortingMethod, + SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { + Map> searchResults = runFileSearch(userName, filters, + groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb); + LinkedHashMap groupSizes = new LinkedHashMap<>(); + for (GroupKey groupKey : searchResults.keySet()) { + groupSizes.put(groupKey, searchResults.get(groupKey).size()); + } + return groupSizes; + } + + /** + * Get the files from the specified group from the cache, if the the group + * was not cached perform a search caching the groups. + * + * @param userName The name of the user performing the search. + * @param filters The filters to apply + * @param groupAttributeType The attribute to use for grouping + * @param groupSortingType The method to use to sort the groups + * @param fileSortingMethod The method to use to sort the files within the + * groups + * @param groupKey The key which uniquely identifies the group to + * get entries from + * @param startingEntry The first entry to return + * @param numberOfEntries The number of entries to return + * @param caseDb The case database + * @param centralRepoDb The central repository database. Can be null if + * not needed. + * + * @return A LinkedHashMap grouped and sorted according to the parameters + * + * @throws FileSearchException + */ + static List getFilesInGroup(String userName, + List filters, + AttributeType groupAttributeType, + FileGroup.GroupSortingAlgorithm groupSortingType, + FileSorter.SortingMethod fileSortingMethod, + GroupKey groupKey, + int startingEntry, + int numberOfEntries, + SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { + //the group should be in the cache at this point + List filesInGroup = null; + SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod); + Map> resultsMap; + synchronized (searchCache) { + resultsMap = searchCache.getIfPresent(searchKey); + } + if (resultsMap != null) { + filesInGroup = resultsMap.get(groupKey); + } + List page = new ArrayList<>(); + if (filesInGroup == null) { + logger.log(Level.INFO, "Group {0} was not cached, performing search to cache all groups again", groupKey); + runFileSearch(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb); + synchronized (searchCache) { + resultsMap = searchCache.getIfPresent(searchKey.getKeyString()); + } + if (resultsMap != null) { + filesInGroup = resultsMap.get(groupKey); + } + if (filesInGroup == null) { + logger.log(Level.WARNING, "Group {0} did not exist in cache or new search results", groupKey); + return page; //group does not exist + } + } + // Check that there is data after the starting point + if (filesInGroup.size() < startingEntry) { + logger.log(Level.WARNING, "Group only contains {0} files, starting entry of {1} is too large.", new Object[]{filesInGroup.size(), startingEntry}); + return page; + } + // Add files to the page + for (int i = startingEntry; (i < startingEntry + numberOfEntries) + && (i < filesInGroup.size()); i++) { + page.add(filesInGroup.get(i)); + } + return page; + } + + /** + * Run the file search. Caching new results for access at later time. + * + * @param userName The name of the user performing the search. + * @param filters The filters to apply + * @param groupAttributeType The attribute to use for grouping + * @param groupSortingType The method to use to sort the groups + * @param fileSortingMethod The method to use to sort the files within the + * groups + * @param caseDb The case database + * @param centralRepoDb The central repository database. Can be null if + * not needed. + * + * @return A LinkedHashMap grouped and sorted according to the parameters + * + * @throws FileSearchException + */ + private static Map> runFileSearch(String userName, + List filters, + AttributeType groupAttributeType, + FileGroup.GroupSortingAlgorithm groupSortingType, + FileSorter.SortingMethod fileSortingMethod, + SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { + + // Make a list of attributes that we want to add values for. This ensures the + // ResultFile objects will have all needed fields set when it's time to group + // and sort them. For example, if we're grouping by central repo frequency, we need + // to make sure we've loaded those values before grouping. + List attributesNeededForGroupingOrSorting = new ArrayList<>(); + attributesNeededForGroupingOrSorting.add(groupAttributeType); + attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes()); + + // Run the queries for each filter + List resultFiles = FileSearchFiltering.runQueries(filters, caseDb, centralRepoDb); + + // Add the data to resultFiles for any attributes needed for sorting and grouping + addAttributes(attributesNeededForGroupingOrSorting, resultFiles, caseDb, centralRepoDb); + + // Collect everything in the search results + SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod); + searchResults.add(resultFiles); + Map> resultHashMap = searchResults.toLinkedHashMap(); + SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod); + synchronized (searchCache) { + searchCache.put(searchKey, resultHashMap); + } + // Return a version of the results in general Java objects + return resultHashMap; + } + + /** + * Add any attributes corresponding to the attribute list to the given + * result files. For example, specifying the KeywordListAttribute will + * populate the list of keyword set names in the ResultFile objects. + * + * @param attrs The attributes to add to the list of result files + * @param resultFiles The result files + * @param caseDb The case database + * @param centralRepoDb The central repository database. Can be null if not + * needed. + * + * @throws FileSearchException + */ + private static void addAttributes(List attrs, List resultFiles, SleuthkitCase caseDb, EamDb centralRepoDb) + throws FileSearchException { + for (AttributeType attr : attrs) { + attr.addAttributeToResultFiles(resultFiles, caseDb, centralRepoDb); + } + } + + /** + * Computes the CR frequency of all the given hashes and updates the list of + * files. + * + * @param hashesToLookUp Hashes to find the frequency of + * @param currentFiles List of files to update with frequencies + */ + private static void computeFrequency(Set hashesToLookUp, List currentFiles, EamDb centralRepoDb) { + + if (hashesToLookUp.isEmpty()) { + return; + } + + String hashes = String.join("','", hashesToLookUp); + hashes = "'" + hashes + "'"; + try { + CorrelationAttributeInstance.Type attributeType = centralRepoDb.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); + String tableName = EamDbUtil.correlationTypeToInstanceTableName(attributeType); + + String selectClause = " value, COUNT(value) FROM " + + "(SELECT DISTINCT case_id, value FROM " + tableName + + " WHERE value IN (" + + hashes + + ")) AS foo GROUP BY value"; + + FrequencyCallback callback = new FrequencyCallback(currentFiles); + centralRepoDb.processSelectClause(selectClause, callback); + + } catch (EamDbException ex) { + logger.log(Level.WARNING, "Error getting frequency counts from Central Repository", ex); // NON-NLS + } + + } + + private static String createSetNameClause(List files, + int artifactTypeID, int setNameAttrID) throws FileSearchException { + + // Concatenate the object IDs in the list of files + String objIdList = ""; // NON-NLS + for (ResultFile file : files) { + if (!objIdList.isEmpty()) { + objIdList += ","; // NON-NLS + } + objIdList += "\'" + file.getFirstInstance().getId() + "\'"; // NON-NLS + } + + // Get pairs of (object ID, set name) for all files in the list of files that have + // the given artifact type. + return "blackboard_artifacts.obj_id AS object_id, blackboard_attributes.value_text AS set_name " + + "FROM blackboard_artifacts " + + "INNER JOIN blackboard_attributes ON blackboard_artifacts.artifact_id=blackboard_attributes.artifact_id " + + "WHERE blackboard_attributes.artifact_type_id=\'" + artifactTypeID + "\' " + + "AND blackboard_attributes.attribute_type_id=\'" + setNameAttrID + "\' " + + "AND blackboard_artifacts.obj_id IN (" + objIdList + ") "; // NON-NLS + } + + /** + * Get the video thumbnails for a file which exists in a + * VideoThumbnailsWrapper and update the VideoThumbnailsWrapper to include + * them. + * + * @param thumbnailWrapper the object which contains the file to generate + * thumbnails for. + * + */ + @NbBundle.Messages({"# {0} - file name", + "FileSearch.genVideoThumb.progress.text=extracting temporary file {0}"}) + static void getVideoThumbnails(VideoThumbnailsWrapper thumbnailWrapper) { + AbstractFile file = thumbnailWrapper.getResultFile().getFirstInstance(); + String cacheDirectory; + try { + cacheDirectory = Case.getCurrentCaseThrows().getCacheDirectory(); + } catch (NoCurrentCaseException ex) { + cacheDirectory = null; + logger.log(Level.WARNING, "Unable to get cache directory, video thumbnails will not be saved", ex); + } + + if (cacheDirectory == null || file.getMd5Hash() == null || !Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile().exists()) { + java.io.File tempFile; + try { + tempFile = getVideoFileInTempDir(file); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Exception while getting open case.", ex); //NON-NLS + int[] framePositions = new int[]{ + 0, + 0, + 0, + 0}; + thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); + return; + } + if (tempFile.exists() == false || tempFile.length() < file.getSize()) { + ProgressHandle progress = ProgressHandle.createHandle(Bundle.FileSearch_genVideoThumb_progress_text(file.getName())); + progress.start(100); + try { + Files.createParentDirs(tempFile); + if (Thread.interrupted()) { + int[] framePositions = new int[]{ + 0, + 0, + 0, + 0}; + thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); + return; + } + ContentUtils.writeToFile(file, tempFile, progress, null, true); + } catch (IOException ex) { + logger.log(Level.WARNING, "Error extracting temporary file for " + file.getParentPath() + "/" + file.getName(), ex); //NON-NLS + } finally { + progress.finish(); + } + } + VideoCapture videoFile = new VideoCapture(); // will contain the video + BufferedImage bufferedImage = null; + + try { + if (!videoFile.open(tempFile.toString())) { + logger.log(Level.WARNING, "Error opening {0} for preview generation.", file.getParentPath() + "/" + file.getName()); //NON-NLS + int[] framePositions = new int[]{ + 0, + 0, + 0, + 0}; + thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); + return; + } + double fps = videoFile.get(5); // gets frame per second + double totalFrames = videoFile.get(7); // gets total frames + if (fps <= 0 || totalFrames <= 0) { + logger.log(Level.WARNING, "Error getting fps or total frames for {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS + int[] framePositions = new int[]{ + 0, + 0, + 0, + 0}; + thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); + return; + } + if (Thread.interrupted()) { + int[] framePositions = new int[]{ + 0, + 0, + 0, + 0}; + thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); + return; + } + + double duration = 1000 * (totalFrames / fps); //total milliseconds + + int[] framePositions = new int[]{ + (int) (duration * .01), + (int) (duration * .25), + (int) (duration * .5), + (int) (duration * .75),}; + + Mat imageMatrix = new Mat(); + List videoThumbnails = new ArrayList<>(); + if (cacheDirectory == null || file.getMd5Hash() == null) { + cacheDirectory = null; + } else { + try { + FileUtils.forceMkdir(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile()); + } catch (IOException ex) { + cacheDirectory = null; + logger.log(Level.WARNING, "Unable to make video thumbnails directory, thumbnails will not be saved", ex); + } + } + for (int i = 0; i < framePositions.length; i++) { + if (!videoFile.set(0, framePositions[i])) { + logger.log(Level.WARNING, "Error seeking to " + framePositions[i] + "ms in {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS + // If we can't set the time, continue to the next frame position and try again. + + videoThumbnails.add(ImageUtils.getDefaultThumbnail()); + if (cacheDirectory != null) { + try { + ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT, + Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS) + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex); + } + } + continue; + } + // Read the frame into the image/matrix. + if (!videoFile.read(imageMatrix)) { + logger.log(Level.WARNING, "Error reading frame at " + framePositions[i] + "ms from {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS + // If the image is bad for some reason, continue to the next frame position and try again. + videoThumbnails.add(ImageUtils.getDefaultThumbnail()); + if (cacheDirectory != null) { + try { + ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT, + Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS) + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex); + } + } + + continue; + } + // If the image is empty, return since no buffered image can be created. + if (imageMatrix.empty()) { + videoThumbnails.add(ImageUtils.getDefaultThumbnail()); + if (cacheDirectory != null) { + try { + ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT, + Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS) + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex); + } + } + continue; + } + + int matrixColumns = imageMatrix.cols(); + int matrixRows = imageMatrix.rows(); + + // Convert the matrix that contains the frame to a buffered image. + if (bufferedImage == null) { + bufferedImage = new BufferedImage(matrixColumns, matrixRows, BufferedImage.TYPE_3BYTE_BGR); + } + + byte[] data = new byte[matrixRows * matrixColumns * (int) (imageMatrix.elemSize())]; + imageMatrix.get(0, 0, data); //copy the image to data + + if (imageMatrix.channels() == 3) { + for (int k = 0; k < data.length; k += 3) { + byte temp = data[k]; + data[k] = data[k + 2]; + data[k + 2] = temp; + } + } + + bufferedImage.getRaster().setDataElements(0, 0, matrixColumns, matrixRows, data); + if (Thread.interrupted()) { + thumbnailWrapper.setThumbnails(videoThumbnails, framePositions); + try { + FileUtils.forceDelete(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile()); + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to delete directory for cancelled video thumbnail process", ex); + } + return; + } + BufferedImage thumbnail = ScalrWrapper.resizeFast(bufferedImage, ImageUtils.ICON_SIZE_LARGE); + videoThumbnails.add(thumbnail); + if (cacheDirectory != null) { + try { + ImageIO.write(thumbnail, THUMBNAIL_FORMAT, + Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS) + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to save video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex); + } + } + } + thumbnailWrapper.setThumbnails(videoThumbnails, framePositions); + } finally { + videoFile.release(); // close the file} + } + } else { + loadSavedThumbnails(cacheDirectory, thumbnailWrapper); + } + } + + /** + * Load the thumbnails that exist in the cache directory for the specified + * video file. + * + * @param cacheDirectory The directory which exists for the video + * thumbnails. + * @param thumbnailWrapper The VideoThumbnailWrapper object which contains + * information about the file and the thumbnails + * associated with it. + */ + private static void loadSavedThumbnails(String cacheDirectory, VideoThumbnailsWrapper thumbnailWrapper) { + int[] framePositions = new int[4]; + List videoThumbnails = new ArrayList<>(); + int thumbnailNumber = 0; + String md5 = thumbnailWrapper.getResultFile().getFirstInstance().getMd5Hash(); + for (String fileName : Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5).toFile().list()) { + try { + videoThumbnails.add(ImageIO.read(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5, fileName).toFile())); + } catch (IOException ex) { + videoThumbnails.add(ImageUtils.getDefaultThumbnail()); + logger.log(Level.WARNING, "Unable to read saved video thumbnail " + fileName + " for " + md5, ex); + } + int framePos = Integer.valueOf(FilenameUtils.getBaseName(fileName).substring(2)); + framePositions[thumbnailNumber] = framePos; + thumbnailNumber++; + + } + thumbnailWrapper.setThumbnails(videoThumbnails, framePositions); + } + + /** + * Private helper method for creating video thumbnails, for use when no + * thumbnails are created. + * + * @return List containing the default thumbnail. + */ + private static List createDefaultThumbnailList() { + List videoThumbnails = new ArrayList<>(); + videoThumbnails.add(ImageUtils.getDefaultThumbnail()); + videoThumbnails.add(ImageUtils.getDefaultThumbnail()); + videoThumbnails.add(ImageUtils.getDefaultThumbnail()); + videoThumbnails.add(ImageUtils.getDefaultThumbnail()); + return videoThumbnails; + } + + private FileSearch() { + // Class should not be instantiated + } + + /** + * Base class for the grouping attributes. + */ + abstract static class AttributeType { + + /** + * For a given file, return the key for the group it belongs to for this + * attribute type. + * + * @param file the result file to be grouped + * + * @return the key for the group this file goes in + */ + abstract GroupKey getGroupKey(ResultFile file); + + /** + * Add any extra data to the ResultFile object from this attribute. + * + * @param files The list of files to enhance + * @param caseDb The case database + * @param centralRepoDb The central repository database. Can be null if + * not needed. + * + * @throws FileSearchException + */ + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { + // Default is to do nothing + } + } + + /** + * The key used for grouping for each attribute type. + */ + abstract static class GroupKey implements Comparable { + + /** + * Get the string version of the group key for display. Each display + * name should correspond to a unique GroupKey object. + * + * @return The display name for this key + */ + abstract String getDisplayName(); + + /** + * Subclasses must implement equals(). + * + * @param otherKey + * + * @return true if the keys are equal, false otherwise + */ + @Override + abstract public boolean equals(Object otherKey); + + /** + * Subclasses must implement hashCode(). + * + * @return the hash code + */ + @Override + abstract public int hashCode(); + + /** + * It should not happen with the current setup, but we need to cover the + * case where two different GroupKey subclasses are compared against + * each other. Use a lexicographic comparison on the class names. + * + * @param otherGroupKey The other group key + * + * @return result of alphabetical comparison on the class name + */ + int compareClassNames(GroupKey otherGroupKey) { + return this.getClass().getName().compareTo(otherGroupKey.getClass().getName()); + } + + @Override + public String toString() { + return getDisplayName(); + } + } + + /** + * Attribute for grouping/sorting by file size + */ + static class FileSizeAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new FileSizeGroupKey(file); + } + } + + /** + * Key representing a file size group + */ + private static class FileSizeGroupKey extends GroupKey { + + private final FileSize fileSize; + + FileSizeGroupKey(ResultFile file) { + if (file.getFileType() == FileType.VIDEO) { + fileSize = FileSize.fromVideoSize(file.getFirstInstance().getSize()); + } else { + fileSize = FileSize.fromImageSize(file.getFirstInstance().getSize()); + } + } + + @Override + String getDisplayName() { + return getFileSize().toString(); + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof FileSizeGroupKey) { + FileSizeGroupKey otherFileSizeGroupKey = (FileSizeGroupKey) otherGroupKey; + return Integer.compare(getFileSize().getRanking(), otherFileSizeGroupKey.getFileSize().getRanking()); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof FileSizeGroupKey)) { + return false; + } + + FileSizeGroupKey otherFileSizeGroupKey = (FileSizeGroupKey) otherKey; + return getFileSize().equals(otherFileSizeGroupKey.getFileSize()); + } + + @Override + public int hashCode() { + return Objects.hash(getFileSize().getRanking()); + } + + /** + * @return the fileSize + */ + FileSize getFileSize() { + return fileSize; + } + } + + /** + * Attribute for grouping/sorting by parent path + */ + static class ParentPathAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new ParentPathGroupKey(file); + } + } + + /** + * Key representing a parent path group + */ + private static class ParentPathGroupKey extends GroupKey { + + private String parentPath; + private Long parentID; + + ParentPathGroupKey(ResultFile file) { + Content parent; + try { + parent = file.getFirstInstance().getParent(); + } catch (TskCoreException ignored) { + parent = null; + } + //Find the directory this file is in if it is an embedded file + while (parent != null && parent instanceof AbstractFile && ((AbstractFile) parent).isFile()) { + try { + parent = parent.getParent(); + } catch (TskCoreException ignored) { + parent = null; + } + } + setParentPathAndID(parent, file); + } + + /** + * Helper method to set the parent path and parent ID. + * + * @param parent The parent content object. + * @param file The ResultFile object. + */ + private void setParentPathAndID(Content parent, ResultFile file) { + if (parent != null) { + try { + parentPath = parent.getUniquePath(); + parentID = parent.getId(); + } catch (TskCoreException ignored) { + //catch block left blank purposefully next if statement will handle case when exception takes place as well as when parent is null + } + + } + if (parentPath == null) { + if (file.getFirstInstance().getParentPath() != null) { + parentPath = file.getFirstInstance().getParentPath(); + } else { + parentPath = ""; // NON-NLS + } + parentID = -1L; + } + } + + @Override + String getDisplayName() { + return getParentPath(); + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof ParentPathGroupKey) { + ParentPathGroupKey otherParentPathGroupKey = (ParentPathGroupKey) otherGroupKey; + int comparisonResult = getParentPath().compareTo(otherParentPathGroupKey.getParentPath()); + if (comparisonResult == 0) { + comparisonResult = getParentID().compareTo(otherParentPathGroupKey.getParentID()); + } + return comparisonResult; + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof ParentPathGroupKey)) { + return false; + } + + ParentPathGroupKey otherParentPathGroupKey = (ParentPathGroupKey) otherKey; + return getParentPath().equals(otherParentPathGroupKey.getParentPath()) && getParentID().equals(otherParentPathGroupKey.getParentID()); + } + + @Override + public int hashCode() { + int hashCode = 11; + hashCode = 61 * hashCode + Objects.hash(getParentPath()); + hashCode = 61 * hashCode + Objects.hash(getParentID()); + return hashCode; + } + + /** + * @return the parentPath + */ + String getParentPath() { + return parentPath; + } + + /** + * @return the parentID + */ + Long getParentID() { + return parentID; + } + } + + /** + * Attribute for grouping/sorting by data source + */ + static class DataSourceAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new DataSourceGroupKey(file); + } + } + + /** + * Key representing a data source group + */ + private static class DataSourceGroupKey extends GroupKey { + + private final long dataSourceID; + private String displayName; + + @NbBundle.Messages({ + "# {0} - Data source name", + "# {1} - Data source ID", + "FileSearch.DataSourceGroupKey.datasourceAndID={0}(ID: {1})", + "# {0} - Data source ID", + "FileSearch.DataSourceGroupKey.idOnly=Data source (ID: {0})"}) + DataSourceGroupKey(ResultFile file) { + dataSourceID = file.getFirstInstance().getDataSourceObjectId(); + + try { + // The data source should be cached so this won't actually be a database query. + Content ds = file.getFirstInstance().getDataSource(); + displayName = Bundle.FileSearch_DataSourceGroupKey_datasourceAndID(ds.getName(), ds.getId()); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error looking up data source with ID " + dataSourceID, ex); // NON-NLS + displayName = Bundle.FileSearch_DataSourceGroupKey_idOnly(dataSourceID); + } + } + + @Override + String getDisplayName() { + return displayName; + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof DataSourceGroupKey) { + DataSourceGroupKey otherDataSourceGroupKey = (DataSourceGroupKey) otherGroupKey; + return Long.compare(getDataSourceID(), otherDataSourceGroupKey.getDataSourceID()); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof DataSourceGroupKey)) { + return false; + } + + DataSourceGroupKey otherDataSourceGroupKey = (DataSourceGroupKey) otherKey; + return getDataSourceID() == otherDataSourceGroupKey.getDataSourceID(); + } + + @Override + public int hashCode() { + return Objects.hash(getDataSourceID()); + } + + /** + * @return the dataSourceID + */ + long getDataSourceID() { + return dataSourceID; + } + } + + /** + * Attribute for grouping/sorting by file type + */ + static class FileTypeAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new FileTypeGroupKey(file); + } + } + + /** + * Key representing a file type group + */ + private static class FileTypeGroupKey extends GroupKey { + + private final FileType fileType; + + FileTypeGroupKey(ResultFile file) { + fileType = file.getFileType(); + } + + @Override + String getDisplayName() { + return getFileType().toString(); + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof FileTypeGroupKey) { + FileTypeGroupKey otherFileTypeGroupKey = (FileTypeGroupKey) otherGroupKey; + return Integer.compare(getFileType().getRanking(), otherFileTypeGroupKey.getFileType().getRanking()); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof FileTypeGroupKey)) { + return false; + } + + FileTypeGroupKey otherFileTypeGroupKey = (FileTypeGroupKey) otherKey; + return getFileType().equals(otherFileTypeGroupKey.getFileType()); + } + + @Override + public int hashCode() { + return Objects.hash(getFileType().getRanking()); + } + + /** + * @return the fileType + */ + FileType getFileType() { + return fileType; + } + } + + /** + * Attribute for grouping/sorting by keyword lists + */ + static class KeywordListAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new KeywordListGroupKey(file); + } + + @Override + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + // Get pairs of (object ID, keyword list name) for all files in the list of files that have + // keyword list hits. + String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); + + SetKeywordListNamesCallback callback = new SetKeywordListNamesCallback(files); + try { + caseDb.getCaseDbAccessManager().select(selectQuery, callback); + } catch (TskCoreException ex) { + throw new FileSearchException("Error looking up keyword list attributes", ex); // NON-NLS + } + } + + /** + * Callback to process the results of the CaseDbAccessManager select + * query. Will add the keyword list names to the list of ResultFile + * objects. + */ + private static class SetKeywordListNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + List resultFiles; + + /** + * Create the callback. + * + * @param resultFiles List of files to add keyword list names to + */ + SetKeywordListNamesCallback(List resultFiles) { + this.resultFiles = resultFiles; + } + + @Override + public void process(ResultSet rs) { + try { + // Create a temporary map of object ID to ResultFile + Map tempMap = new HashMap<>(); + for (ResultFile file : resultFiles) { + tempMap.put(file.getFirstInstance().getId(), file); + } + + while (rs.next()) { + try { + Long objId = rs.getLong("object_id"); // NON-NLS + String keywordListName = rs.getString("set_name"); // NON-NLS + + tempMap.get(objId).addKeywordListName(keywordListName); + + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS + } + } + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Failed to get keyword list names", ex); // NON-NLS + } + } + } + } + + /** + * Key representing a keyword list group + */ + private static class KeywordListGroupKey extends GroupKey { + + private final List keywordListNames; + private final String keywordListNamesString; + + @NbBundle.Messages({ + "FileSearch.KeywordListGroupKey.noKeywords=None"}) + KeywordListGroupKey(ResultFile file) { + keywordListNames = file.getKeywordListNames(); + + if (keywordListNames.isEmpty()) { + keywordListNamesString = Bundle.FileSearch_KeywordListGroupKey_noKeywords(); + } else { + keywordListNamesString = String.join(",", keywordListNames); // NON-NLS + } + } + + @Override + String getDisplayName() { + return getKeywordListNamesString(); + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof KeywordListGroupKey) { + KeywordListGroupKey otherKeywordListNamesGroupKey = (KeywordListGroupKey) otherGroupKey; + + // Put the empty list at the end + if (getKeywordListNames().isEmpty()) { + if (otherKeywordListNamesGroupKey.getKeywordListNames().isEmpty()) { + return 0; + } else { + return 1; + } + } else if (otherKeywordListNamesGroupKey.getKeywordListNames().isEmpty()) { + return -1; + } + + return getKeywordListNamesString().compareTo(otherKeywordListNamesGroupKey.getKeywordListNamesString()); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof KeywordListGroupKey)) { + return false; + } + + KeywordListGroupKey otherKeywordListGroupKey = (KeywordListGroupKey) otherKey; + return getKeywordListNamesString().equals(otherKeywordListGroupKey.getKeywordListNamesString()); + } + + @Override + public int hashCode() { + return Objects.hash(getKeywordListNamesString()); + } + + /** + * @return the keywordListNames + */ + List getKeywordListNames() { + return Collections.unmodifiableList(keywordListNames); + } + + /** + * @return the keywordListNamesString + */ + String getKeywordListNamesString() { + return keywordListNamesString; + } + } + + /** + * Attribute for grouping/sorting by frequency in the central repository + */ + static class FrequencyAttribute extends AttributeType { + + static final int BATCH_SIZE = 50; // Number of hashes to look up at one time + + @Override + GroupKey getGroupKey(ResultFile file) { + return new FrequencyGroupKey(file); + } + + @Override + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + if (centralRepoDb == null) { + for (ResultFile file : files) { + if (file.getFrequency() == Frequency.UNKNOWN && file.getFirstInstance().getKnown() == TskData.FileKnown.KNOWN) { + file.setFrequency(Frequency.KNOWN); + } + } + } else { + processResultFilesForCR(files, centralRepoDb); + } + } + + /** + * Private helper method for adding Frequency attribute when CR is + * enabled. + * + * @param files The list of ResultFiles to caluclate frequency + * for. + * @param centralRepoDb The central repository currently in use. + */ + private void processResultFilesForCR(List files, + EamDb centralRepoDb) { + List currentFiles = new ArrayList<>(); + Set hashesToLookUp = new HashSet<>(); + for (ResultFile file : files) { + if (file.getFirstInstance().getKnown() == TskData.FileKnown.KNOWN) { + file.setFrequency(Frequency.KNOWN); + } + if (file.getFrequency() == Frequency.UNKNOWN + && file.getFirstInstance().getMd5Hash() != null + && !file.getFirstInstance().getMd5Hash().isEmpty()) { + hashesToLookUp.add(file.getFirstInstance().getMd5Hash()); + currentFiles.add(file); + } + if (hashesToLookUp.size() >= BATCH_SIZE) { + computeFrequency(hashesToLookUp, currentFiles, centralRepoDb); + + hashesToLookUp.clear(); + currentFiles.clear(); + } + } + computeFrequency(hashesToLookUp, currentFiles, centralRepoDb); + } + } + + /** + * Callback to use with findInterCaseValuesByCount which generates a list of + * values for common property search + */ + private static class FrequencyCallback implements InstanceTableCallback { + + private final List files; + + private FrequencyCallback(List files) { + this.files = new ArrayList<>(files); + } + + @Override + public void process(ResultSet resultSet) { + try { + + while (resultSet.next()) { + String hash = resultSet.getString(1); + int count = resultSet.getInt(2); + for (Iterator iterator = files.iterator(); iterator.hasNext();) { + ResultFile file = iterator.next(); + if (file.getFirstInstance().getMd5Hash().equalsIgnoreCase(hash)) { + file.setFrequency(Frequency.fromCount(count)); + iterator.remove(); + } + } + } + + // The files left had no matching entries in the CR, so mark them as unique + for (ResultFile file : files) { + file.setFrequency(Frequency.UNIQUE); + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "Error getting frequency counts from Central Repository", ex); // NON-NLS + } + } + } + + /** + * Key representing a central repository frequency group + */ + private static class FrequencyGroupKey extends GroupKey { + + private final Frequency frequency; + + FrequencyGroupKey(ResultFile file) { + frequency = file.getFrequency(); + } + + @Override + String getDisplayName() { + return getFrequency().toString(); + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof FrequencyGroupKey) { + FrequencyGroupKey otherFrequencyGroupKey = (FrequencyGroupKey) otherGroupKey; + return Integer.compare(getFrequency().getRanking(), otherFrequencyGroupKey.getFrequency().getRanking()); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof FrequencyGroupKey)) { + return false; + } + + FrequencyGroupKey otherFrequencyGroupKey = (FrequencyGroupKey) otherKey; + return getFrequency().equals(otherFrequencyGroupKey.getFrequency()); + } + + @Override + public int hashCode() { + return Objects.hash(getFrequency().getRanking()); + } + + /** + * @return the frequency + */ + Frequency getFrequency() { + return frequency; + } + } + + /** + * Attribute for grouping/sorting by hash set lists + */ + static class HashHitsAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new HashHitsGroupKey(file); + } + + @Override + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + // Get pairs of (object ID, hash set name) for all files in the list of files that have + // hash set hits. + String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); + + HashSetNamesCallback callback = new HashSetNamesCallback(files); + try { + caseDb.getCaseDbAccessManager().select(selectQuery, callback); + } catch (TskCoreException ex) { + throw new FileSearchException("Error looking up hash set attributes", ex); // NON-NLS + } + } + + /** + * Callback to process the results of the CaseDbAccessManager select + * query. Will add the hash set names to the list of ResultFile objects. + */ + private static class HashSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + List resultFiles; + + /** + * Create the callback. + * + * @param resultFiles List of files to add hash set names to + */ + HashSetNamesCallback(List resultFiles) { + this.resultFiles = resultFiles; + } + + @Override + public void process(ResultSet rs) { + try { + // Create a temporary map of object ID to ResultFile + Map tempMap = new HashMap<>(); + for (ResultFile file : resultFiles) { + tempMap.put(file.getFirstInstance().getId(), file); + } + + while (rs.next()) { + try { + Long objId = rs.getLong("object_id"); // NON-NLS + String hashSetName = rs.getString("set_name"); // NON-NLS + + tempMap.get(objId).addHashSetName(hashSetName); + + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS + } + } + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Failed to get hash set names", ex); // NON-NLS + } + } + } + } + + /** + * Key representing a hash hits group + */ + private static class HashHitsGroupKey extends GroupKey { + + private final List hashSetNames; + private final String hashSetNamesString; + + @NbBundle.Messages({ + "FileSearch.HashHitsGroupKey.noHashHits=None"}) + HashHitsGroupKey(ResultFile file) { + hashSetNames = file.getHashSetNames(); + + if (hashSetNames.isEmpty()) { + hashSetNamesString = Bundle.FileSearch_HashHitsGroupKey_noHashHits(); + } else { + hashSetNamesString = String.join(",", hashSetNames); // NON-NLS + } + } + + @Override + String getDisplayName() { + return getHashSetNamesString(); + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof HashHitsGroupKey) { + HashHitsGroupKey otherHashHitsGroupKey = (HashHitsGroupKey) otherGroupKey; + + // Put the empty list at the end + if (getHashSetNames().isEmpty()) { + if (otherHashHitsGroupKey.getHashSetNames().isEmpty()) { + return 0; + } else { + return 1; + } + } else if (otherHashHitsGroupKey.getHashSetNames().isEmpty()) { + return -1; + } + + return getHashSetNamesString().compareTo(otherHashHitsGroupKey.getHashSetNamesString()); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof HashHitsGroupKey)) { + return false; + } + + HashHitsGroupKey otherHashHitsGroupKey = (HashHitsGroupKey) otherKey; + return getHashSetNamesString().equals(otherHashHitsGroupKey.getHashSetNamesString()); + } + + @Override + public int hashCode() { + return Objects.hash(getHashSetNamesString()); + } + + /** + * @return the hashSetNames + */ + List getHashSetNames() { + return Collections.unmodifiableList(hashSetNames); + } + + /** + * @return the hashSetNamesString + */ + String getHashSetNamesString() { + return hashSetNamesString; + } + } + + /** + * Attribute for grouping/sorting by interesting item set lists + */ + static class InterestingItemAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new InterestingItemGroupKey(file); + } + + @Override + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + // Get pairs of (object ID, interesting item set name) for all files in the list of files that have + // interesting file set hits. + String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); + + InterestingFileSetNamesCallback callback = new InterestingFileSetNamesCallback(files); + try { + caseDb.getCaseDbAccessManager().select(selectQuery, callback); + } catch (TskCoreException ex) { + throw new FileSearchException("Error looking up interesting file set attributes", ex); // NON-NLS + } + } + + /** + * Callback to process the results of the CaseDbAccessManager select + * query. Will add the interesting file set names to the list of + * ResultFile objects. + */ + private static class InterestingFileSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + List resultFiles; + + /** + * Create the callback. + * + * @param resultFiles List of files to add interesting file set + * names to + */ + InterestingFileSetNamesCallback(List resultFiles) { + this.resultFiles = resultFiles; + } + + @Override + public void process(ResultSet rs) { + try { + // Create a temporary map of object ID to ResultFile + Map tempMap = new HashMap<>(); + for (ResultFile file : resultFiles) { + tempMap.put(file.getFirstInstance().getId(), file); + } + + while (rs.next()) { + try { + Long objId = rs.getLong("object_id"); // NON-NLS + String setName = rs.getString("set_name"); // NON-NLS + + tempMap.get(objId).addInterestingSetName(setName); + + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS + } + } + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Failed to get interesting file set names", ex); // NON-NLS + } + } + } + } + + /** + * Key representing a interesting item set group + */ + private static class InterestingItemGroupKey extends GroupKey { + + private final List interestingItemSetNames; + private final String interestingItemSetNamesString; + + @NbBundle.Messages({ + "FileSearch.InterestingItemGroupKey.noSets=None"}) + InterestingItemGroupKey(ResultFile file) { + interestingItemSetNames = file.getInterestingSetNames(); + + if (interestingItemSetNames.isEmpty()) { + interestingItemSetNamesString = Bundle.FileSearch_InterestingItemGroupKey_noSets(); + } else { + interestingItemSetNamesString = String.join(",", interestingItemSetNames); // NON-NLS + } + } + + @Override + String getDisplayName() { + return getInterestingItemSetNamesString(); + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof InterestingItemGroupKey) { + InterestingItemGroupKey otherInterestingItemGroupKey = (InterestingItemGroupKey) otherGroupKey; + + // Put the empty list at the end + if (this.getInterestingItemSetNames().isEmpty()) { + if (otherInterestingItemGroupKey.getInterestingItemSetNames().isEmpty()) { + return 0; + } else { + return 1; + } + } else if (otherInterestingItemGroupKey.getInterestingItemSetNames().isEmpty()) { + return -1; + } + + return getInterestingItemSetNamesString().compareTo(otherInterestingItemGroupKey.getInterestingItemSetNamesString()); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof InterestingItemGroupKey)) { + return false; + } + + InterestingItemGroupKey otherInterestingItemGroupKey = (InterestingItemGroupKey) otherKey; + return getInterestingItemSetNamesString().equals(otherInterestingItemGroupKey.getInterestingItemSetNamesString()); + } + + @Override + public int hashCode() { + return Objects.hash(getInterestingItemSetNamesString()); + } + + /** + * @return the interestingItemSetNames + */ + List getInterestingItemSetNames() { + return Collections.unmodifiableList(interestingItemSetNames); + } + + /** + * @return the interestingItemSetNamesString + */ + String getInterestingItemSetNamesString() { + return interestingItemSetNamesString; + } + } + + /** + * Attribute for grouping/sorting by objects detected + */ + static class ObjectDetectedAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new ObjectDetectedGroupKey(file); + } + + @Override + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + // Get pairs of (object ID, object type name) for all files in the list of files that have + // objects detected + String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID()); + + ObjectDetectedNamesCallback callback = new ObjectDetectedNamesCallback(files); + try { + caseDb.getCaseDbAccessManager().select(selectQuery, callback); + } catch (TskCoreException ex) { + throw new FileSearchException("Error looking up object detected attributes", ex); // NON-NLS + } + } + + /** + * Callback to process the results of the CaseDbAccessManager select + * query. Will add the object type names to the list of ResultFile + * objects. + */ + private static class ObjectDetectedNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + List resultFiles; + + /** + * Create the callback. + * + * @param resultFiles List of files to add object detected names to + */ + ObjectDetectedNamesCallback(List resultFiles) { + this.resultFiles = resultFiles; + } + + @Override + public void process(ResultSet rs) { + try { + // Create a temporary map of object ID to ResultFile + Map tempMap = new HashMap<>(); + for (ResultFile file : resultFiles) { + tempMap.put(file.getFirstInstance().getId(), file); + } + + while (rs.next()) { + try { + Long objId = rs.getLong("object_id"); // NON-NLS + String setName = rs.getString("set_name"); // NON-NLS + + tempMap.get(objId).addObjectDetectedName(setName); + + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS + } + } + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Failed to get object detected names", ex); // NON-NLS + } + } + } + } + + /** + * Key representing an object detected group + */ + private static class ObjectDetectedGroupKey extends GroupKey { + + private final List objectDetectedNames; + private final String objectDetectedNamesString; + + @NbBundle.Messages({ + "FileSearch.ObjectDetectedGroupKey.noSets=None"}) + ObjectDetectedGroupKey(ResultFile file) { + objectDetectedNames = file.getObjectDetectedNames(); + + if (objectDetectedNames.isEmpty()) { + objectDetectedNamesString = Bundle.FileSearch_ObjectDetectedGroupKey_noSets(); + } else { + objectDetectedNamesString = String.join(",", objectDetectedNames); // NON-NLS + } + } + + @Override + String getDisplayName() { + return getObjectDetectedNamesString(); + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof ObjectDetectedGroupKey) { + ObjectDetectedGroupKey otherObjectDetectedGroupKey = (ObjectDetectedGroupKey) otherGroupKey; + + // Put the empty list at the end + if (this.getObjectDetectedNames().isEmpty()) { + if (otherObjectDetectedGroupKey.getObjectDetectedNames().isEmpty()) { + return 0; + } else { + return 1; + } + } else if (otherObjectDetectedGroupKey.getObjectDetectedNames().isEmpty()) { + return -1; + } + + return getObjectDetectedNamesString().compareTo(otherObjectDetectedGroupKey.getObjectDetectedNamesString()); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof ObjectDetectedGroupKey)) { + return false; + } + + ObjectDetectedGroupKey otherObjectDetectedGroupKey = (ObjectDetectedGroupKey) otherKey; + return getObjectDetectedNamesString().equals(otherObjectDetectedGroupKey.getObjectDetectedNamesString()); + } + + @Override + public int hashCode() { + return Objects.hash(getObjectDetectedNamesString()); + } + + /** + * @return the objectDetectedNames + */ + List getObjectDetectedNames() { + return Collections.unmodifiableList(objectDetectedNames); + } + + /** + * @return the objectDetectedNamesString + */ + String getObjectDetectedNamesString() { + return objectDetectedNamesString; + } + } + + /** + * Attribute for grouping/sorting by tag name + */ + static class FileTagAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new FileTagGroupKey(file); + } + + @Override + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + try { + for (ResultFile resultFile : files) { + List contentTags = caseDb.getContentTagsByContent(resultFile.getFirstInstance()); + + for (ContentTag tag : contentTags) { + resultFile.addTagName(tag.getName().getDisplayName()); + } + } + } catch (TskCoreException ex) { + throw new FileSearchException("Error looking up file tag attributes", ex); // NON-NLS + } + } + } + + /** + * Represents a key for a specific search for a specific user. + */ + private static class SearchKey implements Comparable { + + private final String keyString; + + /** + * Construct a new SearchKey with all information that defines a search. + * + * @param userName The name of the user performing the search. + * @param filters The FileFilters being used for the search. + * @param groupAttributeType The AttributeType to group by. + * @param groupSortingType The algorithm to sort the groups by. + * @param fileSortingMethod The method to sort the files by. + */ + SearchKey(String userName, List filters, + AttributeType groupAttributeType, + FileGroup.GroupSortingAlgorithm groupSortingType, + FileSorter.SortingMethod fileSortingMethod) { + StringBuilder searchStringBuilder = new StringBuilder(); + searchStringBuilder.append(userName); + for (FileSearchFiltering.FileFilter filter : filters) { + searchStringBuilder.append(filter.toString()); + } + searchStringBuilder.append(groupAttributeType).append(groupSortingType).append(fileSortingMethod); + keyString = searchStringBuilder.toString(); + } + + @Override + public int compareTo(SearchKey otherSearchKey) { + return getKeyString().compareTo(otherSearchKey.getKeyString()); + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof SearchKey)) { + return false; + } + + SearchKey otherSearchKey = (SearchKey) otherKey; + return getKeyString().equals(otherSearchKey.getKeyString()); + } + + @Override + public int hashCode() { + int hash = 5; + hash = 79 * hash + Objects.hashCode(getKeyString()); + return hash; + } + + /** + * @return the keyString + */ + String getKeyString() { + return keyString; + } + } + + /** + * Key representing a file tag group + */ + private static class FileTagGroupKey extends GroupKey { + + private final List tagNames; + private final String tagNamesString; + + @NbBundle.Messages({ + "FileSearch.FileTagGroupKey.noSets=None"}) + FileTagGroupKey(ResultFile file) { + tagNames = file.getTagNames(); + + if (tagNames.isEmpty()) { + tagNamesString = Bundle.FileSearch_FileTagGroupKey_noSets(); + } else { + tagNamesString = String.join(",", tagNames); // NON-NLS + } + } + + @Override + String getDisplayName() { + return getTagNamesString(); + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof FileTagGroupKey) { + FileTagGroupKey otherFileTagGroupKey = (FileTagGroupKey) otherGroupKey; + + // Put the empty list at the end + if (getTagNames().isEmpty()) { + if (otherFileTagGroupKey.getTagNames().isEmpty()) { + return 0; + } else { + return 1; + } + } else if (otherFileTagGroupKey.getTagNames().isEmpty()) { + return -1; + } + + return getTagNamesString().compareTo(otherFileTagGroupKey.getTagNamesString()); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + + if (!(otherKey instanceof FileTagGroupKey)) { + return false; + } + + FileTagGroupKey otherFileTagGroupKey = (FileTagGroupKey) otherKey; + return getTagNamesString().equals(otherFileTagGroupKey.getTagNamesString()); + } + + @Override + public int hashCode() { + return Objects.hash(getTagNamesString()); + } + + /** + * @return the tagNames + */ + List getTagNames() { + return Collections.unmodifiableList(tagNames); + } + + /** + * @return the tagNamesString + */ + String getTagNamesString() { + return tagNamesString; + } + } + + /** + * Default attribute used to make one group + */ + static class NoGroupingAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new NoGroupingGroupKey(); + } + } + + /** + * Dummy key for when there is no grouping. All files will have the same + * key. + */ + private static class NoGroupingGroupKey extends GroupKey { + + NoGroupingGroupKey() { + // Nothing to save - all files will get the same GroupKey + } + + @NbBundle.Messages({ + "FileSearch.NoGroupingGroupKey.allFiles=All Files"}) + @Override + String getDisplayName() { + return Bundle.FileSearch_NoGroupingGroupKey_allFiles(); + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + // As long as the other key is the same type, they are equal + if (otherGroupKey instanceof NoGroupingGroupKey) { + return 0; + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this) { + return true; + } + // As long as the other key is the same type, they are equal + return otherKey instanceof NoGroupingGroupKey; + } + + @Override + public int hashCode() { + return 0; + } + } + + /** + * Enum for the attribute types that can be used for grouping. + */ + @NbBundle.Messages({ + "FileSearch.GroupingAttributeType.fileType.displayName=File Type", + "FileSearch.GroupingAttributeType.frequency.displayName=Past Occurrences", + "FileSearch.GroupingAttributeType.keywordList.displayName=Keyword", + "FileSearch.GroupingAttributeType.size.displayName=File Size", + "FileSearch.GroupingAttributeType.datasource.displayName=Data Source", + "FileSearch.GroupingAttributeType.parent.displayName=Parent Folder", + "FileSearch.GroupingAttributeType.hash.displayName=Hash Set", + "FileSearch.GroupingAttributeType.interestingItem.displayName=Interesting Item", + "FileSearch.GroupingAttributeType.tag.displayName=Tag", + "FileSearch.GroupingAttributeType.object.displayName=Object Detected", + "FileSearch.GroupingAttributeType.none.displayName=None"}) + enum GroupingAttributeType { + FILE_SIZE(new FileSizeAttribute(), Bundle.FileSearch_GroupingAttributeType_size_displayName()), + FREQUENCY(new FrequencyAttribute(), Bundle.FileSearch_GroupingAttributeType_frequency_displayName()), + KEYWORD_LIST_NAME(new KeywordListAttribute(), Bundle.FileSearch_GroupingAttributeType_keywordList_displayName()), + DATA_SOURCE(new DataSourceAttribute(), Bundle.FileSearch_GroupingAttributeType_datasource_displayName()), + PARENT_PATH(new ParentPathAttribute(), Bundle.FileSearch_GroupingAttributeType_parent_displayName()), + HASH_LIST_NAME(new HashHitsAttribute(), Bundle.FileSearch_GroupingAttributeType_hash_displayName()), + INTERESTING_ITEM_SET(new InterestingItemAttribute(), Bundle.FileSearch_GroupingAttributeType_interestingItem_displayName()), + FILE_TAG(new FileTagAttribute(), Bundle.FileSearch_GroupingAttributeType_tag_displayName()), + OBJECT_DETECTED(new ObjectDetectedAttribute(), Bundle.FileSearch_GroupingAttributeType_object_displayName()), + NO_GROUPING(new NoGroupingAttribute(), Bundle.FileSearch_GroupingAttributeType_none_displayName()); + + private final AttributeType attributeType; + private final String displayName; + + GroupingAttributeType(AttributeType attributeType, String displayName) { + this.attributeType = attributeType; + this.displayName = displayName; + } + + @Override + public String toString() { + return displayName; + } + + AttributeType getAttributeType() { + return attributeType; + } + + /** + * Get the list of enums that are valid for grouping images. + * + * @return enums that can be used to group images + */ + static List getOptionsForGrouping() { + return Arrays.asList(FILE_SIZE, FREQUENCY, PARENT_PATH, OBJECT_DETECTED, HASH_LIST_NAME, INTERESTING_ITEM_SET); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java new file mode 100644 index 0000000000..93d17b21bb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java @@ -0,0 +1,398 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.filequery; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.FileTypeUtils; + +/** + * Utility enums for FileSearch + */ +final class FileSearchData { + + private final static long BYTES_PER_MB = 1000000; + + /** + * Enum representing how often the file occurs in the Central Repository. + */ + @NbBundle.Messages({ + "FileSearchData.Frequency.unique.displayName=Unique (1)", + "FileSearchData.Frequency.rare.displayName=Rare (2-10)", + "FileSearchData.Frequency.common.displayName=Common (11 - 100)", + "FileSearchData.Frequency.verycommon.displayName=Very Common (100+)", + "FileSearchData.Frequency.known.displayName=Known (NSRL)", + "FileSearchData.Frequency.unknown.displayName=Unknown",}) + enum Frequency { + UNIQUE(0, 1, Bundle.FileSearchData_Frequency_unique_displayName()), + RARE(1, 10, Bundle.FileSearchData_Frequency_rare_displayName()), + COMMON(2, 100, Bundle.FileSearchData_Frequency_common_displayName()), + VERY_COMMON(3, 0, Bundle.FileSearchData_Frequency_common_displayName()), + KNOWN(4, 0, Bundle.FileSearchData_Frequency_known_displayName()), + UNKNOWN(5, 0, Bundle.FileSearchData_Frequency_unknown_displayName()); + + private final int ranking; + private final String displayName; + private final int maxOccur; + + Frequency(int ranking, int maxOccur, String displayName) { + this.ranking = ranking; + this.maxOccur = maxOccur; + this.displayName = displayName; + } + + /** + * Get the rank for sorting. + * + * @return the rank (lower should be displayed first) + */ + int getRanking() { + return ranking; + } + + /** + * Get the enum matching the given occurrence count. + * + * @param count Number of times a file is in the Central Repository. + * + * @return the corresponding enum + */ + static Frequency fromCount(long count) { + if (count <= UNIQUE.getMaxOccur()) { + return UNIQUE; + } else if (count <= RARE.getMaxOccur()) { + return RARE; + } else if (count <= COMMON.getMaxOccur()) { + return COMMON; + } + return VERY_COMMON; + } + + /** + * Get the list of enums that are valid for filtering when a CR is + * enabled. + * + * @return enums that can be used to filter with a CR. + */ + static List getOptionsForFilteringWithCr() { + return Arrays.asList(UNIQUE, RARE, COMMON, VERY_COMMON, KNOWN); + } + + /** + * Get the list of enums that are valid for filtering when no CR is + * enabled. + * + * @return enums that can be used to filter without a CR. + */ + static List getOptionsForFilteringWithoutCr() { + return Arrays.asList(KNOWN, UNKNOWN); + } + + @Override + public String toString() { + return displayName; + } + + /** + * @return the maxOccur + */ + int getMaxOccur() { + return maxOccur; + } + } + + /** + * Enum representing the file size + */ + @NbBundle.Messages({ + "FileSearchData.FileSize.XXLARGE_IMAGE.displayName=XXLarge: 200MB+", + "FileSearchData.FileSize.XLARGE_IMAGE.displayName=XLarge: 50-200MB", + "FileSearchData.FileSize.LARGE_IMAGE.displayName=Large: 1-50MB", + "FileSearchData.FileSize.MEDIUM_IMAGE.displayName=Medium: 100KB-1MB", + "FileSearchData.FileSize.SMALL_IMAGE.displayName=Small: 16-100KB", + "FileSearchData.FileSize.XSMALL_IMAGE.displayName=XSmall: 0-16KB", + "FileSearchData.FileSize.XXLARGE_VIDEO.displayName=XXLarge: 10GB+", + "FileSearchData.FileSize.XLARGE_VIDEO.displayName=XLarge: 5-10GB", + "FileSearchData.FileSize.LARGE_VIDEO.displayName=Large: 1-5GB", + "FileSearchData.FileSize.MEDIUM_VIDEO.displayName=Medium: 100MB-1GB", + "FileSearchData.FileSize.SMALL_VIDEO.displayName=Small: 500KB-100MB", + "FileSearchData.FileSize.XSMALL_VIDEO.displayName=XSmall: 0-500KB",}) + enum FileSize { + XXLARGE_VIDEO(0, 10000 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_VIDEO_displayName()), + XLARGE_VIDEO(1, 5000 * BYTES_PER_MB, 10000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_VIDEO_displayName()), + LARGE_VIDEO(2, 1000 * BYTES_PER_MB, 5000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_VIDEO_displayName()), + MEDIUM_VIDEO(3, 100 * BYTES_PER_MB, 1000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_VIDEO_displayName()), + SMALL_VIDEO(4, 500000, 100 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_SMALL_VIDEO_displayName()), + XSMALL_VIDEO(5, 0, 500000, Bundle.FileSearchData_FileSize_XSMALL_VIDEO_displayName()), + XXLARGE_IMAGE(6, 200 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_IMAGE_displayName()), + XLARGE_IMAGE(7, 50 * BYTES_PER_MB, 200 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_IMAGE_displayName()), + LARGE_IMAGE(8, 1 * BYTES_PER_MB, 50 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_IMAGE_displayName()), + MEDIUM_IMAGE(9, 100000, 1 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_IMAGE_displayName()), + SMALL_IMAGE(10, 16000, 100000, Bundle.FileSearchData_FileSize_SMALL_IMAGE_displayName()), + XSMALL_IMAGE(11, 0, 16000, Bundle.FileSearchData_FileSize_XSMALL_IMAGE_displayName()); + + private final int ranking; // Must be unique for each value + private final long minBytes; // Note that the size must be strictly greater than this to match + private final long maxBytes; + private final String displayName; + final static long NO_MAXIMUM = -1; + + FileSize(int ranking, long minB, long maxB, String displayName) { + this.ranking = ranking; + this.minBytes = minB; + if (maxB >= 0) { + this.maxBytes = maxB; + } else { + this.maxBytes = NO_MAXIMUM; + } + this.displayName = displayName; + } + + /** + * Get the enum corresponding to the given file size for image files. + * The file size must be strictly greater than minBytes. + * + * @param size the file size + * + * @return the enum whose range contains the file size + */ + static FileSize fromImageSize(long size) { + if (size > XXLARGE_IMAGE.getMinBytes()) { + return XXLARGE_IMAGE; + } else if (size > XLARGE_IMAGE.getMinBytes()) { + return XLARGE_IMAGE; + } else if (size > LARGE_IMAGE.getMinBytes()) { + return LARGE_IMAGE; + } else if (size > MEDIUM_IMAGE.getMinBytes()) { + return MEDIUM_IMAGE; + } else if (size > SMALL_IMAGE.getMinBytes()) { + return SMALL_IMAGE; + } else { + return XSMALL_IMAGE; + } + } + + /** + * Get the enum corresponding to the given file size for video files. + * The file size must be strictly greater than minBytes. + * + * @param size the file size + * + * @return the enum whose range contains the file size + */ + static FileSize fromVideoSize(long size) { + if (size > XXLARGE_VIDEO.getMinBytes()) { + return XXLARGE_VIDEO; + } else if (size > XLARGE_VIDEO.getMinBytes()) { + return XLARGE_VIDEO; + } else if (size > LARGE_VIDEO.getMinBytes()) { + return LARGE_VIDEO; + } else if (size > MEDIUM_VIDEO.getMinBytes()) { + return MEDIUM_VIDEO; + } else if (size > SMALL_VIDEO.getMinBytes()) { + return SMALL_VIDEO; + } else { + return XSMALL_VIDEO; + } + } + + /** + * Get the upper limit of the range. + * + * @return the maximum file size that will fit in this range. + */ + long getMaxBytes() { + return maxBytes; + } + + /** + * Get the lower limit of the range. + * + * @return the maximum file size that is not part of this range + */ + long getMinBytes() { + return minBytes; + } + + /** + * Get the rank for sorting. + * + * @return the rank (lower should be displayed first) + */ + int getRanking() { + return ranking; + } + + @Override + public String toString() { + return displayName; + } + + /** + * Get the list of enums that are valid for image sizes. + * + * @return enums that can be used to filter images by size. + */ + static List getOptionsForImages() { + return Arrays.asList(XXLARGE_IMAGE, XLARGE_IMAGE, LARGE_IMAGE, MEDIUM_IMAGE, SMALL_IMAGE, XSMALL_IMAGE); + } + + /** + * Get the list of enums that are valid for video sizes. + * + * @return enums that can be used to filter videos by size. + */ + static List getOptionsForVideos() { + return Arrays.asList(XXLARGE_VIDEO, XLARGE_VIDEO, LARGE_VIDEO, MEDIUM_VIDEO, SMALL_VIDEO, XSMALL_VIDEO); + } + } + + /** + * Enum representing the file type. We don't simply use + * FileTypeUtils.FileTypeCategory because: - Some file types categories + * overlap - It is convenient to have the "OTHER" option for files that + * don't match the given types + */ + @NbBundle.Messages({ + "FileSearchData.FileType.Audio.displayName=Audio", + "FileSearchData.FileType.Video.displayName=Video", + "FileSearchData.FileType.Image.displayName=Image", + "FileSearchData.FileType.Documents.displayName=Documents", + "FileSearchData.FileType.Executables.displayName=Executables", + "FileSearchData.FileType.Other.displayName=Other/Unknown"}) + enum FileType { + + IMAGE(0, Bundle.FileSearchData_FileType_Image_displayName(), FileTypeUtils.FileTypeCategory.IMAGE.getMediaTypes()), + AUDIO(1, Bundle.FileSearchData_FileType_Audio_displayName(), FileTypeUtils.FileTypeCategory.AUDIO.getMediaTypes()), + VIDEO(2, Bundle.FileSearchData_FileType_Video_displayName(), FileTypeUtils.FileTypeCategory.VIDEO.getMediaTypes()), + EXECUTABLE(3, Bundle.FileSearchData_FileType_Executables_displayName(), FileTypeUtils.FileTypeCategory.EXECUTABLE.getMediaTypes()), + DOCUMENTS(4, Bundle.FileSearchData_FileType_Documents_displayName(), FileTypeUtils.FileTypeCategory.DOCUMENTS.getMediaTypes()), + OTHER(5, Bundle.FileSearchData_FileType_Other_displayName(), new ArrayList<>()); + + private final int ranking; // For ordering in the UI + private final String displayName; + private final Collection mediaTypes; + + FileType(int value, String displayName, Collection mediaTypes) { + this.ranking = value; + this.displayName = displayName; + this.mediaTypes = mediaTypes; + } + + /** + * Get the MIME types matching this category. + * + * @return Collection of MIME type strings + */ + Collection getMediaTypes() { + return Collections.unmodifiableCollection(mediaTypes); + } + + @Override + public String toString() { + return displayName; + } + + /** + * Get the rank for sorting. + * + * @return the rank (lower should be displayed first) + */ + int getRanking() { + return ranking; + } + + /** + * Get the enum matching the given MIME type. + * + * @param mimeType The MIME type for the file + * + * @return the corresponding enum (will be OTHER if no types matched) + */ + static FileType fromMIMEtype(String mimeType) { + for (FileType type : FileType.values()) { + if (type.getMediaTypes().contains(mimeType)) { + return type; + } + } + return OTHER; + } + + /** + * Get the list of enums that are valid for filtering. + * + * @return enums that can be used to filter + */ + static List getOptionsForFiltering() { + return Arrays.asList(IMAGE, VIDEO); + } + } + + /** + * Enum representing the score of the file. + */ + @NbBundle.Messages({ + "FileSearchData.Score.notable.displayName=Notable", + "FileSearchData.Score.interesting.displayName=Interesting", + "FileSearchData.Score.unknown.displayName=Unknown",}) + enum Score { + NOTABLE(0, Bundle.FileSearchData_Score_notable_displayName()), + INTERESTING(1, Bundle.FileSearchData_Score_interesting_displayName()), + UNKNOWN(2, Bundle.FileSearchData_Score_unknown_displayName()); + + private final int ranking; + private final String displayName; + + Score(int ranking, String displayName) { + this.ranking = ranking; + this.displayName = displayName; + } + + /** + * Get the rank for sorting. + * + * @return the rank (lower should be displayed first) + */ + int getRanking() { + return ranking; + } + + /** + * Get the list of enums that are valid for filtering. + * + * @return enums that can be used to filter + */ + static List getOptionsForFiltering() { + return Arrays.asList(NOTABLE, INTERESTING); + } + + @Override + public String toString() { + return displayName; + } + } + + private FileSearchData() { + // Class should not be instantiated + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchException.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchException.java new file mode 100644 index 0000000000..2b9a79b39c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchException.java @@ -0,0 +1,45 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.filequery; + +/** + * Exception type used for FileSearch + */ +public class FileSearchException extends Exception { + private static final long serialVersionUID = 1L; + + /** + * Create exception from a string + * + * @param message + */ + public FileSearchException(String message) { + super(message); + } + + /** + * Create exception for a string and cause + * + * @param message + * @param cause + */ + public FileSearchException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java new file mode 100644 index 0000000000..b35434be0e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java @@ -0,0 +1,1010 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.filequery; + +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.filequery.FileSearchData.FileSize; +import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; +import org.sleuthkit.autopsy.filequery.FileSearchData.Frequency; +import org.sleuthkit.autopsy.filequery.FileSearchData.Score; + +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; +import org.openide.util.NbBundle; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskData; + +/** + * Run various filters to return a subset of files from the current case. + */ +class FileSearchFiltering { + + private final static Logger logger = Logger.getLogger(FileSearchFiltering.class.getName()); + + /** + * Run the given filters to get a list of matching files. + * + * @param filters The filters to run + * @param caseDb The case database + * @param crDb The central repo. Can be null as long as no filters need + * it. + * + * @return + */ + static List runQueries(List filters, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { + + if (caseDb == null) { + throw new FileSearchException("Case DB parameter is null"); // NON-NLS + } + + // Record the selected filters + String filterStr = ""; + for (FileFilter filter : filters) { + filterStr += " " + filter.getDesc() + "\n"; + } + logger.log(Level.INFO, "Running filters:\n{0}", filterStr); + + // Combine all the SQL queries from the filters into one query + String combinedQuery = ""; + for (FileFilter filter : filters) { + if (!filter.getWhereClause().isEmpty()) { + if (!combinedQuery.isEmpty()) { + combinedQuery += " AND "; // NON-NLS + } + combinedQuery += "(" + filter.getWhereClause() + ")"; // NON-NLS + } + } + + if (combinedQuery.isEmpty()) { + // The file search filter is required, so this should never be empty. + throw new FileSearchException("Selected filters do not include a case database query"); + } + try { + return getResultList(filters, combinedQuery, caseDb, centralRepoDb); + } catch (TskCoreException ex) { + throw new FileSearchException("Error querying case database", ex); // NON-NLS + } + } + + /** + * Private helper method for runQueries method to get the ResultFile list. + * + * @param filters The filters to run. + * @param combinedQuery The query to get results files for. + * @param caseDb The case database. + * @param crDb The central repo. Can be null as long as no filters + * need it. + * + * @return An ArrayList of ResultFiles returned by the query. + * + * @throws TskCoreException + * @throws FileSearchException + */ + private static List getResultList(List filters, String combinedQuery, SleuthkitCase caseDb, EamDb centralRepoDb) throws TskCoreException, FileSearchException { + // Get all matching abstract files + List resultList = new ArrayList<>(); + + logger.log(Level.INFO, "Running SQL query: {0}", combinedQuery); + List sqlResults = caseDb.findAllFilesWhere(combinedQuery); + + // If there are no results, return now + if (sqlResults.isEmpty()) { + return resultList; + } + + // Wrap each result in a ResultFile + for (AbstractFile abstractFile : sqlResults) { + resultList.add(new ResultFile(abstractFile)); + } + + // Now run any non-SQL filters. + for (FileFilter filter : filters) { + if (filter.useAlternateFilter()) { + resultList = filter.applyAlternateFilter(resultList, caseDb, centralRepoDb); + } + // There are no matches for the filters run so far, so return + if (resultList.isEmpty()) { + return resultList; + } + } + return resultList; + } + + /** + * Base class for the filters. + */ + static abstract class FileFilter { + + /** + * Returns part of a query on the tsk_files table that can be AND-ed + * with other pieces + * + * @return the SQL query or an empty string if there is no SQL query for + * this filter. + */ + abstract String getWhereClause(); + + /** + * Indicates whether this filter needs to use the secondary, non-SQL + * method applyAlternateFilter(). + * + * @return false by default + */ + boolean useAlternateFilter() { + return false; + } + + /** + * Run a secondary filter that does not operate on tsk_files. + * + * @param currentResults The current list of matching files; empty if no + * filters have yet been run. + * @param caseDb The case database + * @param centralRepoDb The central repo database. Can be null if the + * filter does not require it. + * + * @return The list of files that match this filter (and any that came + * before it) + * + * @throws FileSearchException + */ + List applyAlternateFilter(List currentResults, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + return new ArrayList<>(); + } + + /** + * Get a description of the selected filter. + * + * @return A description of the filter + */ + abstract String getDesc(); + } + + /** + * A filter for specifying the file size + */ + static class SizeFilter extends FileFilter { + + private final List fileSizes; + + /** + * Create the SizeFilter + * + * @param fileSizes the file sizes that should match + */ + SizeFilter(List fileSizes) { + this.fileSizes = fileSizes; + } + + @Override + String getWhereClause() { + String queryStr = ""; // NON-NLS + for (FileSize size : fileSizes) { + if (!queryStr.isEmpty()) { + queryStr += " OR "; // NON-NLS + } + if (size.getMaxBytes() != FileSize.NO_MAXIMUM) { + queryStr += "(size > \'" + size.getMinBytes() + "\' AND size <= \'" + size.getMaxBytes() + "\')"; // NON-NLS + } else { + queryStr += "(size >= \'" + size.getMinBytes() + "\')"; // NON-NLS + } + } + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0}", + "FileSearchFiltering.SizeFilter.or= or ", + "# {0} - Minimum bytes", + "# {1} - Maximum bytes", + "FileSearchFiltering.SizeFilter.range=({0} to {1})",}) + @Override + String getDesc() { + String desc = ""; // NON-NLS + for (FileSize size : fileSizes) { + if (!desc.isEmpty()) { + desc += Bundle.FileSearchFiltering_SizeFilter_or(); + } + desc += Bundle.FileSearchFiltering_SizeFilter_range(size.getMinBytes(), size.getMaxBytes()); + } + desc = Bundle.FileSearchFiltering_SizeFilter_desc(desc); + return desc; + } + } + + /** + * A utility class for the ParentFilter to store the search string and + * whether it is a full path or a substring. + */ + static class ParentSearchTerm { + + private final String searchStr; + private final boolean fullPath; + private final boolean included; + + /** + * Create the ParentSearchTerm object + * + * @param searchStr The string to search for in the file path + * @param isFullPath True if the path should exactly match the given + * string, false to do a substring search + * @param isIncluded True if the results must include the path, false if + * the path should be excluded from the results. + */ + ParentSearchTerm(String searchStr, boolean isFullPath, boolean isIncluded) { + this.searchStr = searchStr; + this.fullPath = isFullPath; + this.included = isIncluded; + } + + /** + * Get the SQL term to search for + * + * @return The SQL for a where clause to search for a matching path + */ + String getSQLForTerm() { + // TODO - these should really be prepared statements + if (isIncluded()) { + if (isFullPath()) { + return "parent_path=\'" + getSearchStr() + "\'"; // NON-NLS + } else { + return "parent_path LIKE \'%" + getSearchStr() + "%\'"; // NON-NLS + } + } else { + if (isFullPath()) { + return "parent_path!=\'" + getSearchStr() + "\'"; // NON-NLS + } else { + return "parent_path NOT LIKE \'%" + getSearchStr() + "%\'"; // NON-NLS + } + } + } + + @NbBundle.Messages({ + "FileSearchFiltering.ParentSearchTerm.fullString= (exact)", + "FileSearchFiltering.ParentSearchTerm.subString= (substring)", + "FileSearchFiltering.ParentSearchTerm.includeString= (include)", + "FileSearchFiltering.ParentSearchTerm.excludeString= (exclude)",}) + @Override + public String toString() { + String returnString = getSearchStr(); + if (isFullPath()) { + returnString += Bundle.FileSearchFiltering_ParentSearchTerm_fullString(); + } else { + returnString += Bundle.FileSearchFiltering_ParentSearchTerm_subString(); + } + if (isIncluded()) { + returnString += Bundle.FileSearchFiltering_ParentSearchTerm_includeString(); + } else { + returnString += Bundle.FileSearchFiltering_ParentSearchTerm_excludeString(); + } + return returnString; + } + + /** + * @return the fullPath + */ + boolean isFullPath() { + return fullPath; + } + + /** + * @return the included + */ + boolean isIncluded() { + return included; + } + + /** + * @return the searchStr + */ + String getSearchStr() { + return searchStr; + } + } + + /** + * A filter for specifying parent path (either full path or substring) + */ + static class ParentFilter extends FileFilter { + + private final List parentSearchTerms; + + /** + * Create the ParentFilter + * + * @param parentSearchTerms Full paths or substrings to filter on + */ + ParentFilter(List parentSearchTerms) { + this.parentSearchTerms = parentSearchTerms; + } + + @Override + String getWhereClause() { + String includeQueryStr = ""; // NON-NLS + String excludeQueryStr = ""; + for (ParentSearchTerm searchTerm : parentSearchTerms) { + if (searchTerm.isIncluded()) { + if (!includeQueryStr.isEmpty()) { + includeQueryStr += " OR "; // NON-NLS + } + includeQueryStr += searchTerm.getSQLForTerm(); + } else { + if (!excludeQueryStr.isEmpty()) { + excludeQueryStr += " AND "; // NON-NLS + } + excludeQueryStr += searchTerm.getSQLForTerm(); + } + } + if (!includeQueryStr.isEmpty()) { + includeQueryStr = "(" + includeQueryStr + ")"; + } + if (!excludeQueryStr.isEmpty()) { + excludeQueryStr = "(" + excludeQueryStr + ")"; + } + if (includeQueryStr.isEmpty() || excludeQueryStr.isEmpty()) { + return includeQueryStr + excludeQueryStr; + } else { + return includeQueryStr + " AND " + excludeQueryStr; + } + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0}", + "FileSearchFiltering.ParentFilter.or= or ", + "FileSearchFiltering.ParentFilter.exact=(exact match)", + "FileSearchFiltering.ParentFilter.substring=(substring)",}) + @Override + String getDesc() { + String desc = ""; // NON-NLS + for (ParentSearchTerm searchTerm : parentSearchTerms) { + if (!desc.isEmpty()) { + desc += Bundle.FileSearchFiltering_ParentFilter_or(); + } + if (searchTerm.isFullPath()) { + desc += searchTerm.getSearchStr() + Bundle.FileSearchFiltering_ParentFilter_exact(); + } else { + desc += searchTerm.getSearchStr() + Bundle.FileSearchFiltering_ParentFilter_substring(); + } + } + desc = Bundle.FileSearchFiltering_ParentFilter_desc(desc); + return desc; + } + } + + /** + * A filter for specifying data sources + */ + static class DataSourceFilter extends FileFilter { + + private final List dataSources; + + /** + * Create the DataSourceFilter + * + * @param dataSources the data sources to filter on + */ + DataSourceFilter(List dataSources) { + this.dataSources = dataSources; + } + + @Override + String getWhereClause() { + String queryStr = ""; // NON-NLS + for (DataSource ds : dataSources) { + if (!queryStr.isEmpty()) { + queryStr += ","; // NON-NLS + } + queryStr += "\'" + ds.getId() + "\'"; // NON-NLS + } + queryStr = "data_source_obj_id IN (" + queryStr + ")"; // NON-NLS + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0}", + "FileSearchFiltering.DataSourceFilter.or= or ", + "# {0} - Data source name", + "# {1} - Data source ID", + "FileSearchFiltering.DataSourceFilter.datasource={0}({1})",}) + @Override + String getDesc() { + String desc = ""; // NON-NLS + for (DataSource ds : dataSources) { + if (!desc.isEmpty()) { + desc += Bundle.FileSearchFiltering_DataSourceFilter_or(); + } + desc += Bundle.FileSearchFiltering_DataSourceFilter_datasource(ds.getName(), ds.getId()); + } + desc = Bundle.FileSearchFiltering_DataSourceFilter_desc(desc); + return desc; + } + } + + /** + * A filter for specifying keyword list names. A file must contain a keyword + * from one of the given lists to pass. + */ + static class KeywordListFilter extends FileFilter { + + private final List listNames; + + /** + * Create the KeywordListFilter + * + * @param listNames + */ + KeywordListFilter(List listNames) { + this.listNames = listNames; + } + + @Override + String getWhereClause() { + String keywordListPart = concatenateNamesForSQL(listNames); + + String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN " + + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = 9 AND attribute_type_ID = 37 " + + "AND (" + keywordListPart + "))))"; // NON-NLS + + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0}",}) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_KeywordListFilter_desc(concatenateSetNamesForDisplay(listNames)); + } + } + + /** + * A filter for specifying file types. + */ + static class FileTypeFilter extends FileFilter { + + private final List categories; + + /** + * Create the FileTypeFilter + * + * @param categories List of file types to filter on + */ + FileTypeFilter(List categories) { + this.categories = categories; + } + + /** + * Create the FileTypeFilter + * + * @param category the file type to filter on + */ + FileTypeFilter(FileType category) { + this.categories = new ArrayList<>(); + this.categories.add(category); + } + + @Override + String getWhereClause() { + String queryStr = ""; // NON-NLS + for (FileType cat : categories) { + for (String type : cat.getMediaTypes()) { + if (!queryStr.isEmpty()) { + queryStr += ","; // NON-NLS + } + queryStr += "\'" + type + "\'"; // NON-NLS + } + } + queryStr = "mime_type IN (" + queryStr + ")"; // NON-NLS + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.FileTypeFilter.desc=Files with type: {0}", + "FileSearchFiltering.FileTypeFilter.or= or ",}) + @Override + String getDesc() { + String desc = ""; + for (FileType cat : categories) { + if (!desc.isEmpty()) { + desc += Bundle.FileSearchFiltering_FileTypeFilter_or(); + } + desc += cat.toString(); + } + desc = Bundle.FileSearchFiltering_FileTypeFilter_desc(desc); + return desc; + } + } + + /** + * A filter for specifying frequency in the central repository. + */ + static class FrequencyFilter extends FileFilter { + + private final List frequencies; + + /** + * Create the FrequencyFilter + * + * @param frequencies List of frequencies that will pass the filter + */ + FrequencyFilter(List frequencies) { + this.frequencies = frequencies; + } + + @Override + String getWhereClause() { + // Since this relies on the central repository database, there is no + // query on the case database. + return ""; // NON-NLS + } + + @Override + boolean useAlternateFilter() { + return true; + } + + @Override + List applyAlternateFilter(List currentResults, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + // We have to have run some kind of SQL filter before getting to this point, + // and should have checked afterward to see if the results were empty. + if (currentResults.isEmpty()) { + throw new FileSearchException("Can not run on empty list"); // NON-NLS + } + + // Set the frequency for each file + FileSearch.FrequencyAttribute freqAttr = new FileSearch.FrequencyAttribute(); + freqAttr.addAttributeToResultFiles(currentResults, caseDb, centralRepoDb); + + // If the frequency matches the filter, add the file to the results + List frequencyResults = new ArrayList<>(); + for (ResultFile file : currentResults) { + if (frequencies.contains(file.getFrequency())) { + frequencyResults.add(file); + } + } + return frequencyResults; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0}", + "FileSearchFiltering.FrequencyFilter.or= or ",}) + @Override + String getDesc() { + String desc = ""; // NON-NLS + for (Frequency freq : frequencies) { + if (!desc.isEmpty()) { + desc += Bundle.FileSearchFiltering_FrequencyFilter_or(); + } + desc += freq.name(); + } + return Bundle.FileSearchFiltering_FrequencyFilter_desc(desc); + } + } + + /** + * A filter for specifying hash set names. A file must match one of the + * given sets to pass. + */ + static class HashSetFilter extends FileFilter { + + private final List setNames; + + /** + * Create the HashSetFilter + * + * @param setNames + */ + HashSetFilter(List setNames) { + this.setNames = setNames; + } + + @Override + String getWhereClause() { + String hashSetPart = concatenateNamesForSQL(setNames); + + String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN " + + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() + + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " " + + "AND (" + hashSetPart + "))))"; // NON-NLS + + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.HashSetFilter.desc=Files with hash set hits in set(s): {0}",}) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_HashSetFilter_desc(concatenateSetNamesForDisplay(setNames)); + } + } + + /** + * A filter for specifying interesting file set names. A file must match one + * of the given sets to pass. + */ + static class InterestingFileSetFilter extends FileFilter { + + private final List setNames; + + /** + * Create the InterestingFileSetFilter + * + * @param setNames + */ + InterestingFileSetFilter(List setNames) { + this.setNames = setNames; + } + + @Override + String getWhereClause() { + String intItemSetPart = concatenateNamesForSQL(setNames); + + String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN " + + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " " + + "AND (" + intItemSetPart + "))))"; // NON-NLS + + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.InterestingItemSetFilter.desc=Files with interesting item hits in set(s): {0}",}) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_InterestingItemSetFilter_desc(concatenateSetNamesForDisplay(setNames)); + } + } + + /** + * A filter for specifying object types detected. A file must match one of + * the given types to pass. + */ + static class ObjectDetectionFilter extends FileFilter { + + private final List typeNames; + + /** + * Create the ObjectDetectionFilter + * + * @param typeNames + */ + ObjectDetectionFilter(List typeNames) { + this.typeNames = typeNames; + } + + @Override + String getWhereClause() { + String objTypePart = concatenateNamesForSQL(typeNames); + + String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN " + + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID() + + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID() + " " + + "AND (" + objTypePart + "))))"; // NON-NLS + + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.ObjectDetectionFilter.desc=Files with objects detected in set(s): {0}",}) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_ObjectDetectionFilter_desc(concatenateSetNamesForDisplay(typeNames)); + } + } + + /** + * A filter for specifying the score. A file must have one of the given + * scores to pass + */ + static class ScoreFilter extends FileFilter { + + private final List scores; + + /** + * Create the ObjectDetectionFilter + * + * @param typeNames + */ + ScoreFilter(List scores) { + this.scores = scores; + } + + @Override + String getWhereClause() { + + // Current algorithm: + // "Notable" if the file is a match for a notable hashset or has been tagged with a notable tag. + // "Interesting" if the file has an interesting item match or has been tagged with a non-notable tag. + String hashsetQueryPart = ""; + String tagQueryPart = ""; + String intItemQueryPart = ""; + + if (scores.contains(Score.NOTABLE)) { + // do hashset + hashsetQueryPart = " (known = " + TskData.FileKnown.BAD.getFileKnownValue() + ") "; + } + + if (scores.contains(Score.INTERESTING)) { + // Matches interesting item artifact + intItemQueryPart = " (obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_type_id = " + + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + ")) "; + } + + if (scores.contains(Score.NOTABLE) && scores.contains(Score.INTERESTING)) { + // Any tag will work + tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags))"; + } else if (scores.contains(Score.NOTABLE)) { + // Notable tags + tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus = " + + TskData.FileKnown.BAD.getFileKnownValue() + ")))"; + } else if (scores.contains(Score.INTERESTING)) { + // Non-notable tags + tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus != " + + TskData.FileKnown.BAD.getFileKnownValue() + ")))"; + } + + String queryStr = hashsetQueryPart; + if (!intItemQueryPart.isEmpty()) { + if (!queryStr.isEmpty()) { + queryStr += " OR "; + } + queryStr += intItemQueryPart; + } + if (!tagQueryPart.isEmpty()) { + if (!queryStr.isEmpty()) { + queryStr += " OR "; + } + queryStr += tagQueryPart; + } + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0}",}) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_ScoreFilter_desc( + concatenateSetNamesForDisplay(scores.stream().map(p -> p.toString()).collect(Collectors.toList()))); + } + } + + /** + * A filter for specifying tag names. A file must contain one of the given + * tags to pass. + */ + static class TagsFilter extends FileFilter { + + private final List tagNames; + + /** + * Create the TagsFilter + * + * @param tagNames + */ + TagsFilter(List tagNames) { + this.tagNames = tagNames; + } + + @Override + String getWhereClause() { + String tagIDs = ""; // NON-NLS + for (TagName tagName : tagNames) { + if (!tagIDs.isEmpty()) { + tagIDs += ","; + } + tagIDs += tagName.getId(); + } + + String queryStr = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (" + tagIDs + ")))"; + + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - tag names", + "FileSearchFiltering.TagsFilter.desc=Files that have been tagged {0}", + "FileSearchFiltering.TagsFilter.or= or ",}) + @Override + String getDesc() { + String desc = ""; // NON-NLS + for (TagName name : tagNames) { + if (!desc.isEmpty()) { + desc += Bundle.FileSearchFiltering_TagsFilter_or(); + } + desc += name.getDisplayName(); + } + return Bundle.FileSearchFiltering_TagsFilter_desc(desc); + } + } + + /** + * A filter for specifying that the file must have EXIF data. + */ + static class ExifFilter extends FileFilter { + + /** + * Create the ExifFilter + */ + ExifFilter() { + // Nothing to save + } + + @Override + String getWhereClause() { + return "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN " + + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + + BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID() + ")))"; + } + + @NbBundle.Messages({ + "FileSearchFiltering.ExifFilter.desc=Files that contain EXIF data",}) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_ExifFilter_desc(); + } + } + + /** + * A filter for specifying that the file must have been marked as notable in + * the CR. + */ + static class NotableFilter extends FileFilter { + + /** + * Create the NotableFilter + */ + NotableFilter() { + // Nothing to save + } + + @Override + String getWhereClause() { + // Since this relies on the central repository database, there is no + // query on the case database. + return ""; // NON-NLS + } + + @Override + boolean useAlternateFilter() { + return true; + } + + @Override + List applyAlternateFilter(List currentResults, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + if (centralRepoDb == null) { + throw new FileSearchException("Can not run Previously Notable filter with null Central Repository DB"); // NON-NLS + } + + // We have to have run some kind of SQL filter before getting to this point, + // and should have checked afterward to see if the results were empty. + if (currentResults.isEmpty()) { + throw new FileSearchException("Can not run on empty list"); // NON-NLS + } + + // The matching files + List notableResults = new ArrayList<>(); + + try { + CorrelationAttributeInstance.Type type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID); + + for (ResultFile file : currentResults) { + if (file.getFirstInstance().getMd5Hash() != null && !file.getFirstInstance().getMd5Hash().isEmpty()) { + + // Check if this file hash is marked as notable in the CR + String value = file.getFirstInstance().getMd5Hash(); + if (centralRepoDb.getCountArtifactInstancesKnownBad(type, value) > 0) { + notableResults.add(file); + } + } + } + return notableResults; + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { + throw new FileSearchException("Error querying central repository", ex); // NON-NLS + } + } + + @NbBundle.Messages({ + "FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable",}) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc(); + } + } + + /** + * A filter for specifying if known files should be included. + */ + static class KnownFilter extends FileFilter { + + @Override + String getWhereClause() { + return "known!=" + TskData.FileKnown.KNOWN.getFileKnownValue(); // NON-NLS + } + + @NbBundle.Messages({ + "FileSearchFiltering.KnownFilter.desc=Files which are not known"}) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_KnownFilter_desc(); + } + } + + @NbBundle.Messages({ + "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ",}) + private static String concatenateSetNamesForDisplay(List setNames) { + String desc = ""; // NON-NLS + for (String setName : setNames) { + if (!desc.isEmpty()) { + desc += Bundle.FileSearchFiltering_concatenateSetNamesForDisplay_comma(); + } + desc += setName; + } + return desc; + } + + /** + * Concatenate the set names into an "OR" separated list. This does not do + * any SQL-escaping. + * + * @param setNames + * + * @return the list to use in the SQL query + */ + private static String concatenateNamesForSQL(List setNames) { + String result = ""; // NON-NLS + for (String setName : setNames) { + if (!result.isEmpty()) { + result += " OR "; // NON-NLS + } + result += "value_text = \'" + setName + "\'"; // NON-NLS + } + return result; + } + + private FileSearchFiltering() { + // Class should not be instantiated + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form new file mode 100644 index 0000000000..c8a68294e2 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form @@ -0,0 +1,1020 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java new file mode 100644 index 0000000000..d751911379 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java @@ -0,0 +1,1942 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import com.google.common.eventbus.Subscribe; +import java.awt.Cursor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.JCheckBox; +import javax.swing.JList; +import javax.swing.SwingUtilities; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.filequery.FileGroup.GroupSortingAlgorithm; +import org.sleuthkit.autopsy.filequery.FileSearch.GroupingAttributeType; +import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; +import org.sleuthkit.autopsy.filequery.FileSearchData.FileSize; +import org.sleuthkit.autopsy.filequery.FileSearchData.Frequency; +import org.sleuthkit.autopsy.filequery.FileSearchData.Score; +import org.sleuthkit.autopsy.filequery.FileSearchFiltering.ParentSearchTerm; +import org.sleuthkit.autopsy.filequery.FileSorter.SortingMethod; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TagName; + +/** + * Dialog to allow the user to choose filtering and grouping options. + */ +final class FileSearchPanel extends javax.swing.JPanel implements ActionListener { + + private static final long serialVersionUID = 1L; + private static final String[] DEFAULT_IGNORED_PATHS = {"/Windows/", "/Program Files/"}; //NON-NLS + private final static Logger logger = Logger.getLogger(FileSearchPanel.class.getName()); + private FileType fileType = FileType.IMAGE; + private DefaultListModel parentListModel; + private SearchWorker searchWorker = null; + + /** + * Creates new form FileSearchDialog + */ + @NbBundle.Messages({"FileSearchPanel.dialogTitle.text=Test file search"}) + FileSearchPanel() { + initComponents(); + for (GroupSortingAlgorithm groupSortAlgorithm : GroupSortingAlgorithm.values()) { + groupSortingComboBox.addItem(groupSortAlgorithm); + } + parentListModel = (DefaultListModel) parentList.getModel(); + for (String ignorePath : DEFAULT_IGNORED_PATHS) { + parentListModel.add(parentListModel.size(), new ParentSearchTerm(ignorePath, false, false)); + } + } + + /** + * Setup the data source filter settings. + * + * @param visible Boolean indicating if the filter should be + * visible. + * @param enabled Boolean indicating if the filter should be + * enabled. + * @param selected Boolean indicating if the filter should be + * selected. + * @param indicesSelected Array of integers indicating which list items are + * selected, null to indicate leaving selected items + * unchanged. + */ + private void dataSourceFilterSettings(boolean visible, boolean enabled, boolean selected, int[] indicesSelected) { + dataSourceCheckbox.setVisible(visible); + dataSourceScrollPane.setVisible(visible); + dataSourceList.setVisible(visible); + dataSourceCheckbox.setEnabled(enabled); + dataSourceCheckbox.setSelected(selected); + if (dataSourceCheckbox.isEnabled() && dataSourceCheckbox.isSelected()) { + dataSourceScrollPane.setEnabled(true); + dataSourceList.setEnabled(true); + if (indicesSelected != null) { + dataSourceList.setSelectedIndices(indicesSelected); + } + } else { + dataSourceScrollPane.setEnabled(false); + dataSourceList.setEnabled(false); + } + } + + /** + * Setup the file size filter settings. + * + * @param visible Boolean indicating if the filter should be + * visible. + * @param enabled Boolean indicating if the filter should be + * enabled. + * @param selected Boolean indicating if the filter should be + * selected. + * @param indicesSelected Array of integers indicating which list items are + * selected, null to indicate leaving selected items + * unchanged. + */ + private void sizeFilterSettings(boolean visible, boolean enabled, boolean selected, int[] indicesSelected) { + sizeCheckbox.setVisible(visible); + sizeScrollPane.setVisible(visible); + sizeList.setVisible(visible); + sizeCheckbox.setEnabled(enabled); + sizeCheckbox.setSelected(selected); + if (sizeCheckbox.isEnabled() && sizeCheckbox.isSelected()) { + sizeScrollPane.setEnabled(true); + sizeList.setEnabled(true); + if (indicesSelected != null) { + sizeList.setSelectedIndices(indicesSelected); + } + } else { + sizeScrollPane.setEnabled(false); + sizeList.setEnabled(false); + } + } + + /** + * Setup the central repository frequency filter settings. + * + * @param visible Boolean indicating if the filter should be + * visible. + * @param enabled Boolean indicating if the filter should be + * enabled. + * @param selected Boolean indicating if the filter should be + * selected. + * @param indicesSelected Array of integers indicating which list items are + * selected, null to indicate leaving selected items + * unchanged. + */ + private void crFrequencyFilterSettings(boolean visible, boolean enabled, boolean selected, int[] indicesSelected) { + crFrequencyCheckbox.setVisible(visible); + crFrequencyScrollPane.setVisible(visible); + crFrequencyList.setVisible(visible); + crFrequencyCheckbox.setEnabled(enabled); + crFrequencyCheckbox.setSelected(selected); + if (crFrequencyCheckbox.isEnabled() && crFrequencyCheckbox.isSelected()) { + crFrequencyScrollPane.setEnabled(true); + crFrequencyList.setEnabled(true); + if (indicesSelected != null) { + crFrequencyList.setSelectedIndices(indicesSelected); + } + } else { + crFrequencyScrollPane.setEnabled(false); + crFrequencyList.setEnabled(false); + } + } + + /** + * Setup the objects filter settings. + * + * @param visible Boolean indicating if the filter should be + * visible. + * @param enabled Boolean indicating if the filter should be + * enabled. + * @param selected Boolean indicating if the filter should be + * selected. + * @param indicesSelected Array of integers indicating which list items are + * selected, null to indicate leaving selected items + * unchanged. + */ + private void objectsFilterSettings(boolean visible, boolean enabled, boolean selected, int[] indicesSelected) { + objectsCheckbox.setVisible(visible); + objectsScrollPane.setVisible(visible); + objectsList.setVisible(visible); + boolean hasObjects = objectsList.getModel().getSize() > 0; + objectsCheckbox.setEnabled(enabled && hasObjects); + objectsCheckbox.setSelected(selected && hasObjects); + if (objectsCheckbox.isEnabled() && objectsCheckbox.isSelected()) { + objectsScrollPane.setEnabled(true); + objectsList.setEnabled(true); + if (indicesSelected != null) { + objectsList.setSelectedIndices(indicesSelected); + } + } else { + objectsScrollPane.setEnabled(false); + objectsList.setEnabled(false); + } + } + + /** + * Setup the hash set filter settings. + * + * @param visible Boolean indicating if the filter should be + * visible. + * @param enabled Boolean indicating if the filter should be + * enabled. + * @param selected Boolean indicating if the filter should be + * selected. + * @param indicesSelected Array of integers indicating which list items are + * selected, null to indicate leaving selected items + * unchanged. + */ + private void hashSetFilterSettings(boolean visible, boolean enabled, boolean selected, int[] indicesSelected) { + hashSetCheckbox.setVisible(visible); + hashSetScrollPane.setVisible(visible); + hashSetList.setVisible(visible); + boolean hasHashSets = hashSetList.getModel().getSize() > 0; + hashSetCheckbox.setEnabled(enabled && hasHashSets); + hashSetCheckbox.setSelected(selected && hasHashSets); + if (hashSetCheckbox.isEnabled() && hashSetCheckbox.isSelected()) { + hashSetScrollPane.setEnabled(true); + hashSetList.setEnabled(true); + if (indicesSelected != null) { + hashSetList.setSelectedIndices(indicesSelected); + } + } else { + hashSetScrollPane.setEnabled(false); + hashSetList.setEnabled(false); + } + } + + /** + * Setup the interesting items filter settings. + * + * @param visible Boolean indicating if the filter should be + * visible. + * @param enabled Boolean indicating if the filter should be + * enabled. + * @param selected Boolean indicating if the filter should be + * selected. + * @param indicesSelected Array of integers indicating which list items are + * selected, null to indicate leaving selected items + * unchanged. + */ + private void interestingItemsFilterSettings(boolean visible, boolean enabled, boolean selected, int[] indicesSelected) { + interestingItemsCheckbox.setVisible(visible); + interestingItemsScrollPane.setVisible(visible); + interestingItemsList.setVisible(visible); + boolean hasInterestingItems = interestingItemsList.getModel().getSize() > 0; + interestingItemsCheckbox.setEnabled(enabled && hasInterestingItems); + interestingItemsCheckbox.setSelected(selected && hasInterestingItems); + if (interestingItemsCheckbox.isEnabled() && interestingItemsCheckbox.isSelected()) { + interestingItemsScrollPane.setEnabled(true); + interestingItemsList.setEnabled(true); + if (indicesSelected != null) { + interestingItemsList.setSelectedIndices(indicesSelected); + } + } else { + interestingItemsScrollPane.setEnabled(false); + interestingItemsList.setEnabled(false); + } + } + + /** + * Setup the score filter settings. + * + * @param visible Boolean indicating if the filter should be + * visible. + * @param enabled Boolean indicating if the filter should be + * enabled. + * @param selected Boolean indicating if the filter should be + * selected. + * @param indicesSelected Array of integers indicating which list items are + * selected, null to indicate leaving selected items + * unchanged. + */ + private void scoreFilterSettings(boolean visible, boolean enabled, boolean selected, int[] indicesSelected) { + scoreCheckbox.setVisible(visible); + scoreScrollPane.setVisible(visible); + scoreList.setVisible(visible); + scoreCheckbox.setEnabled(enabled); + scoreCheckbox.setSelected(selected); + if (scoreCheckbox.isEnabled() && scoreCheckbox.isSelected()) { + scoreScrollPane.setEnabled(true); + scoreList.setEnabled(true); + if (indicesSelected != null) { + scoreList.setSelectedIndices(indicesSelected); + } + } else { + scoreScrollPane.setEnabled(false); + scoreList.setEnabled(false); + } + } + + /** + * Setup the parent path filter settings. + * + * @param visible Boolean indicating if the filter should be + * visible. + * @param enabled Boolean indicating if the filter should be + * enabled. + * @param selected Boolean indicating if the filter should be + * selected. + * @param indicesSelected Array of integers indicating which list items are + * selected, null to indicate leaving selected items + * unchanged. + */ + private void parentFilterSettings(boolean visible, boolean enabled, boolean selected, int[] indicesSelected) { + parentCheckbox.setVisible(visible); + parentScrollPane.setVisible(visible); + parentList.setVisible(visible); + parentCheckbox.setEnabled(enabled); + parentCheckbox.setSelected(selected); + if (parentCheckbox.isEnabled() && parentCheckbox.isSelected()) { + parentScrollPane.setEnabled(true); + includeRadioButton.setEnabled(true); + excludeRadioButton.setEnabled(true); + fullRadioButton.setEnabled(true); + substringRadioButton.setEnabled(true); + addButton.setEnabled(true); + deleteButton.setEnabled(!parentListModel.isEmpty()); + parentList.setEnabled(true); + parentTextField.setEnabled(true); + if (indicesSelected != null) { + parentList.setSelectedIndices(indicesSelected); + } + } else { + parentScrollPane.setEnabled(false); + parentList.setEnabled(false); + includeRadioButton.setEnabled(false); + excludeRadioButton.setEnabled(false); + fullRadioButton.setEnabled(false); + substringRadioButton.setEnabled(false); + addButton.setEnabled(false); + deleteButton.setEnabled(false); + parentTextField.setEnabled(false); + } + } + + /** + * Setup the tags filter settings. + * + * @param visible Boolean indicating if the filter should be + * visible. + * @param enabled Boolean indicating if the filter should be + * enabled. + * @param selected Boolean indicating if the filter should be + * selected. + * @param indicesSelected Array of integers indicating which list items are + * selected, null to indicate leaving selected items + * unchanged. + */ + private void tagsFilterSettings(boolean visible, boolean enabled, boolean selected, int[] indicesSelected) { + tagsCheckbox.setVisible(visible); + tagsScrollPane.setVisible(visible); + tagsList.setVisible(visible); + tagsCheckbox.setEnabled(enabled); + tagsCheckbox.setSelected(selected); + if (tagsCheckbox.isEnabled() && tagsCheckbox.isSelected()) { + tagsScrollPane.setEnabled(true); + tagsList.setEnabled(true); + if (indicesSelected != null) { + tagsList.setSelectedIndices(indicesSelected); + } + } else { + tagsScrollPane.setEnabled(false); + tagsList.setEnabled(false); + } + } + + /** + * Setup the keyword filter settings. + * + * @param visible Boolean indicating if the filter should be + * visible. + * @param enabled Boolean indicating if the filter should be + * enabled. + * @param selected Boolean indicating if the filter should be + * selected. + * @param indicesSelected Array of integers indicating which list items are + * selected, null to indicate leaving selected items + * unchanged. + */ + private void keywordFilterSettings(boolean visible, boolean enabled, boolean selected, int[] indicesSelected) { + keywordCheckbox.setVisible(visible); + keywordScrollPane.setVisible(visible); + keywordList.setVisible(visible); + keywordCheckbox.setEnabled(enabled); + keywordCheckbox.setSelected(selected); + if (keywordCheckbox.isEnabled() && keywordCheckbox.isSelected()) { + keywordScrollPane.setEnabled(true); + keywordList.setEnabled(true); + if (indicesSelected != null) { + keywordList.setSelectedIndices(indicesSelected); + } + } else { + keywordScrollPane.setEnabled(false); + keywordList.setEnabled(false); + } + } + + /** + * Setup the exif filter settings. + * + * @param visible Boolean indicating if the filter should be visible. + * @param enabled Boolean indicating if the filter should be enabled. + * @param selected Boolean indicating if the filter should be selected. + */ + private void exifFilterSettings(boolean visible, boolean enabled, boolean selected) { + exifCheckbox.setVisible(visible); + exifCheckbox.setEnabled(enabled); + exifCheckbox.setSelected(selected); + } + + /** + * Setup the known status filter settings. + * + * @param visible Boolean indicating if the filter should be visible. + * @param enabled Boolean indicating if the filter should be enabled. + * @param selected Boolean indicating if the filter should be selected. + */ + private void knownFilesFilterSettings(boolean visible, boolean enabled, boolean selected) { + knownFilesCheckbox.setVisible(visible); + knownFilesCheckbox.setEnabled(enabled); + knownFilesCheckbox.setSelected(selected); + } + + /** + * Setup the notable filter settings. + * + * @param visible Boolean indicating if the filter should be visible. + * @param enabled Boolean indicating if the filter should be enabled. + * @param selected Boolean indicating if the filter should be selected. + */ + private void notableFilterSettings(boolean visible, boolean enabled, boolean selected) { + notableCheckbox.setVisible(visible); + notableCheckbox.setEnabled(enabled); + notableCheckbox.setSelected(selected); + } + + /** + * Set the UI elements available to be the set of UI elements available when + * an Image search is being performed. + * + * @param enabled Boolean indicating if the filters present for images + * should be enabled. + * @param resetSelected Boolean indicating if selection of the filters + * present for images should be reset to their default + * status. + */ + private void imagesSelected(boolean enabled, boolean resetSelected) { + dataSourceFilterSettings(true, enabled, !resetSelected && dataSourceCheckbox.isSelected(), null); + int[] selectedSizeIndices = {1, 2, 3, 4, 5}; + sizeFilterSettings(true, enabled, resetSelected || sizeCheckbox.isSelected(), resetSelected == true ? selectedSizeIndices : null); + int[] selectedFrequencyIndices; + if (!EamDb.isEnabled()) { + selectedFrequencyIndices = new int[]{0}; + } else { + selectedFrequencyIndices = new int[]{1, 2, 3, 4, 5, 6, 7}; + } + crFrequencyFilterSettings(true, enabled, resetSelected || crFrequencyCheckbox.isSelected(), resetSelected == true ? selectedFrequencyIndices : null); + exifFilterSettings(true, enabled, !resetSelected && exifCheckbox.isSelected()); + objectsFilterSettings(true, enabled, !resetSelected && objectsCheckbox.isSelected(), null); + hashSetFilterSettings(true, enabled, !resetSelected && hashSetCheckbox.isSelected(), null); + interestingItemsFilterSettings(true, enabled, !resetSelected && interestingItemsCheckbox.isSelected(), null); + parentFilterSettings(true, enabled, !resetSelected && parentCheckbox.isSelected(), null); + scoreFilterSettings(false, false, false, null); + tagsFilterSettings(false, false, false, null); + keywordFilterSettings(false, false, false, null); + knownFilesFilterSettings(false, false, false); + notableFilterSettings(false, false, false); + } + + /** + * Set the UI elements available to be the set of UI elements available when + * a Video search is being performed. + * + * @param enabled Boolean indicating if the filters present for videos + * should be enabled. + * @param resetSelected Boolean indicating if selection of the filters + * present for videos should be reset to their default + * status. + */ + private void videosSelected(boolean enabled, boolean resetSelected) { + dataSourceFilterSettings(true, enabled, !resetSelected && dataSourceCheckbox.isSelected(), null); + sizeFilterSettings(true, enabled, !resetSelected && sizeCheckbox.isSelected(), null); + int[] selectedFrequencyIndices; + if (!EamDb.isEnabled()) { + selectedFrequencyIndices = new int[]{0}; + } else { + selectedFrequencyIndices = new int[]{1, 2, 3, 4, 5, 6, 7}; + } + crFrequencyFilterSettings(true, enabled, resetSelected || crFrequencyCheckbox.isSelected(), resetSelected == true ? selectedFrequencyIndices : null); + exifFilterSettings(true, enabled, !resetSelected && exifCheckbox.isSelected()); + objectsFilterSettings(true, enabled, !resetSelected && objectsCheckbox.isSelected(), null); + hashSetFilterSettings(true, enabled, !resetSelected && hashSetCheckbox.isSelected(), null); + interestingItemsFilterSettings(true, enabled, !resetSelected && interestingItemsCheckbox.isSelected(), null); + parentFilterSettings(true, enabled, !resetSelected && parentCheckbox.isSelected(), null); + scoreFilterSettings(false, false, false, null); + tagsFilterSettings(false, false, false, null); + keywordFilterSettings(false, false, false, null); + knownFilesFilterSettings(false, false, false); + notableFilterSettings(false, false, false); + } + + /** + * Set the type of search to perform. + * + * @param type The type of File to be found by the search. + */ + void setSelectedType(FileType type) { + fileType = type; + setUpSizeFilter(); + if (fileType == FileType.IMAGE) { + imagesSelected(true, true); + } else if (fileType == FileType.VIDEO) { + videosSelected(true, true); + } + validateFields(); + } + + FileType getSelectedType() { + return fileType; + } + + /** + * Reset the panel to its initial configuration. + */ + void resetPanel() { + + searchButton.setEnabled(false); + + // Set up the filters + setUpDataSourceFilter(); + setUpFrequencyFilter(); + setUpSizeFilter(); + setUpKWFilter(); + setUpParentPathFilter(); + setUpHashFilter(); + setUpInterestingItemsFilter(); + setUpTagsFilter(); + setUpObjectFilter(); + setUpScoreFilter(); + + groupByCombobox.removeAllItems(); + // Set up the grouping attributes + for (FileSearch.GroupingAttributeType type : FileSearch.GroupingAttributeType.getOptionsForGrouping()) { + if ((type != GroupingAttributeType.FREQUENCY || EamDb.isEnabled()) + && (type != GroupingAttributeType.OBJECT_DETECTED || objectsList.getModel().getSize() > 0) + && (type != GroupingAttributeType.INTERESTING_ITEM_SET || interestingItemsList.getModel().getSize() > 0) + && (type != GroupingAttributeType.HASH_LIST_NAME || hashSetList.getModel().getSize() > 0)) { + groupByCombobox.addItem(type); + } + } + + orderByCombobox.removeAllItems(); + // Set up the file order list + for (FileSorter.SortingMethod method : FileSorter.SortingMethod.getOptionsForOrdering()) { + if (method != SortingMethod.BY_FREQUENCY || EamDb.isEnabled()) { + orderByCombobox.addItem(method); + } + } + + groupSortingComboBox.setSelectedIndex(0); + setSelectedType(FileType.IMAGE); + validateFields(); + } + + /** + * Add listeners to the checkbox/list set if listeners have not already been + * added. Either can be null. + * + * @param checkBox + * @param list + */ + private void addListeners(JCheckBox checkBox, JList list) { + if (checkBox != null) { + checkBox.addActionListener(this); + } + if (list != null) { + list.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent evt) { + if (!evt.getValueIsAdjusting()) { + validateFields(); + } + } + }); + } + } + + /** + * Initialize the data source filter + */ + private void setUpDataSourceFilter() { + int count = 0; + try { + DefaultListModel dsListModel = (DefaultListModel) dataSourceList.getModel(); + dsListModel.removeAllElements(); + for (DataSource ds : Case.getCurrentCase().getSleuthkitCase().getDataSources()) { + dsListModel.add(count, new DataSourceItem(ds)); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading data sources", ex); + dataSourceCheckbox.setEnabled(false); + dataSourceList.setEnabled(false); + } + addListeners(dataSourceCheckbox, dataSourceList); + } + + /** + * Initialize the frequency filter + */ + private void setUpFrequencyFilter() { + int count = 0; + DefaultListModel frequencyListModel = (DefaultListModel) crFrequencyList.getModel(); + frequencyListModel.removeAllElements(); + if (!EamDb.isEnabled()) { + for (FileSearchData.Frequency freq : FileSearchData.Frequency.getOptionsForFilteringWithoutCr()) { + frequencyListModel.add(count, freq); + } + } else { + for (FileSearchData.Frequency freq : FileSearchData.Frequency.getOptionsForFilteringWithCr()) { + frequencyListModel.add(count, freq); + } + } + addListeners(crFrequencyCheckbox, crFrequencyList); + } + + /** + * Initialize the file size filter + */ + private void setUpSizeFilter() { + int count = 0; + DefaultListModel sizeListModel = (DefaultListModel) sizeList.getModel(); + sizeListModel.removeAllElements(); + if (null == fileType) { + for (FileSearchData.FileSize size : FileSearchData.FileSize.values()) { + sizeListModel.add(count, size); + } + } else { + List sizes; + switch (fileType) { + case VIDEO: + sizes = FileSearchData.FileSize.getOptionsForVideos(); + break; + case IMAGE: + sizes = FileSearchData.FileSize.getOptionsForImages(); + break; + default: + sizes = new ArrayList<>(); + break; + } + for (FileSearchData.FileSize size : sizes) { + sizeListModel.add(count, size); + } + } + addListeners(sizeCheckbox, sizeList); + } + + /** + * Initialize the keyword list names filter + */ + private void setUpKWFilter() { + int count = 0; + try { + DefaultListModel kwListModel = (DefaultListModel) keywordList.getModel(); + kwListModel.removeAllElements(); + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME); + for (String name : setNames) { + kwListModel.add(count, name); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading keyword list names", ex); + keywordCheckbox.setEnabled(false); + keywordList.setEnabled(false); + } + addListeners(keywordCheckbox, keywordList); + } + + /** + * Initialize the hash filter. + */ + private void setUpHashFilter() { + int count = 0; + try { + DefaultListModel hashListModel = (DefaultListModel) hashSetList.getModel(); + hashListModel.removeAllElements(); + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME); + for (String name : setNames) { + hashListModel.add(count, name); + count++; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading hash set names", ex); + hashSetCheckbox.setEnabled(false); + hashSetList.setEnabled(false); + } + addListeners(hashSetCheckbox, hashSetList); + } + + /** + * Initialize the interesting items filter. + */ + private void setUpInterestingItemsFilter() { + int count = 0; + try { + DefaultListModel intListModel = (DefaultListModel) interestingItemsList.getModel(); + intListModel.removeAllElements(); + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME); + for (String name : setNames) { + intListModel.add(count, name); + count++; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading interesting file set names", ex); + interestingItemsCheckbox.setEnabled(false); + interestingItemsList.setEnabled(false); + } + addListeners(interestingItemsCheckbox, interestingItemsList); + } + + /** + * Initialize the tags filter. + */ + private void setUpTagsFilter() { + int count = 0; + try { + DefaultListModel tagsListModel = (DefaultListModel) tagsList.getModel(); + tagsListModel.removeAllElements(); + List tagNames = Case.getCurrentCase().getSleuthkitCase().getTagNamesInUse(); + for (TagName name : tagNames) { + tagsListModel.add(count, name); + count++; + } + tagsList.setCellRenderer(new TagsListCellRenderer()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading tag names", ex); + tagsCheckbox.setEnabled(false); + tagsList.setEnabled(false); + } + addListeners(tagsCheckbox, tagsList); + } + + /** + * TagsListCellRenderer + */ + private class TagsListCellRenderer extends DefaultListCellRenderer { + + private static final long serialVersionUID = 1L; + + @Override + public java.awt.Component getListCellRendererComponent( + JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) { + Object newValue = value; + if (value instanceof TagName) { + newValue = ((TagName) value).getDisplayName(); + } + super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus); + return this; + } + } + + /** + * Initialize the object filter + */ + private void setUpObjectFilter() { + int count = 0; + try { + DefaultListModel objListModel = (DefaultListModel) objectsList.getModel(); + objListModel.removeAllElements(); + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION); + for (String name : setNames) { + objListModel.add(count, name); + count++; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading object detected set names", ex); + objectsCheckbox.setEnabled(false); + objectsList.setEnabled(false); + } + addListeners(objectsCheckbox, objectsList); + } + + /** + * Initialize the score filter + */ + private void setUpScoreFilter() { + + int count = 0; + DefaultListModel scoreListModel = (DefaultListModel) scoreList.getModel(); + scoreListModel.removeAllElements(); + for (Score score : Score.getOptionsForFiltering()) { + scoreListModel.add(count, score); + } + addListeners(scoreCheckbox, scoreList); + } + + /** + * Get the names of the sets which exist in the case database for the + * specified artifact and attribute types. + * + * @param artifactType The artifact type to get the list of sets for. + * @param setNameAttribute The attribute type which contains the set names. + * + * @return A list of set names which exist in the case for the specified + * artifact and attribute types. + * + * @throws TskCoreException + */ + private List getSetNames(BlackboardArtifact.ARTIFACT_TYPE artifactType, BlackboardAttribute.ATTRIBUTE_TYPE setNameAttribute) throws TskCoreException { + List arts = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(artifactType); + List setNames = new ArrayList<>(); + for (BlackboardArtifact art : arts) { + for (BlackboardAttribute attr : art.getAttributes()) { + if (attr.getAttributeType().getTypeID() == setNameAttribute.getTypeID()) { + String setName = attr.getValueString(); + if (!setNames.contains(setName)) { + setNames.add(setName); + } + } + } + } + Collections.sort(setNames); + return setNames; + } + + /** + * Initialize the parent path filter + */ + private void setUpParentPathFilter() { + fullRadioButton.setSelected(true); + includeRadioButton.setSelected(true); + parentListModel = (DefaultListModel) parentList.getModel(); + addListeners(parentCheckbox, parentList); + } + + /** + * Get a list of all filters selected by the user. + * + * @return the list of filters + */ + List getFilters() { + List filters = new ArrayList<>(); + filters.add(new FileSearchFiltering.FileTypeFilter(fileType)); + if (parentCheckbox.isSelected()) { + // For the parent paths, everything in the box is used (not just the selected entries) + filters.add(new FileSearchFiltering.ParentFilter(getParentPaths())); + } + + if (dataSourceCheckbox.isSelected()) { + List dataSources = dataSourceList.getSelectedValuesList().stream().map(t -> t.getDataSource()).collect(Collectors.toList()); + filters.add(new FileSearchFiltering.DataSourceFilter(dataSources)); + } + + if (crFrequencyCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.FrequencyFilter(crFrequencyList.getSelectedValuesList())); + } + + if (sizeCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.SizeFilter(sizeList.getSelectedValuesList())); + } + + if (keywordCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.KeywordListFilter(keywordList.getSelectedValuesList())); + } + + if (hashSetCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.HashSetFilter(hashSetList.getSelectedValuesList())); + } + + if (interestingItemsCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.InterestingFileSetFilter(interestingItemsList.getSelectedValuesList())); + } + + if (objectsCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.ObjectDetectionFilter(objectsList.getSelectedValuesList())); + } + + if (tagsCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.TagsFilter(tagsList.getSelectedValuesList())); + } + + if (exifCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.ExifFilter()); + } + + if (notableCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.NotableFilter()); + } + + if (knownFilesCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.KnownFilter()); + } + + if (scoreCheckbox.isSelected()) { + filters.add(new FileSearchFiltering.ScoreFilter(scoreList.getSelectedValuesList())); + } + + return filters; + } + + /** + * Utility method to get the parent path objects out of the JList. + * + * @return The list of entered ParentSearchTerm objects + */ + private List getParentPaths() { + List results = new ArrayList<>(); + for (int i = 0; i < parentListModel.getSize(); i++) { + results.add(parentListModel.get(i)); + } + return results; + } + + /** + * Get the attribute to group by + * + * @return the grouping attribute + */ + FileSearch.AttributeType getGroupingAttribute() { + return groupByCombobox.getItemAt(groupByCombobox.getSelectedIndex()).getAttributeType(); + } + + /** + * Get the sorting method for groups. + * + * @return the selected sorting method + */ + FileGroup.GroupSortingAlgorithm getGroupSortingMethod() { + return groupSortingComboBox.getItemAt(groupSortingComboBox.getSelectedIndex()); + + } + + /** + * Get the sorting method for files. + * + * @return the selected sorting method + */ + FileSorter.SortingMethod getFileSortingMethod() { + return (FileSorter.SortingMethod) orderByCombobox.getSelectedItem(); + } + + @Override + public void actionPerformed(ActionEvent e) { + validateFields(); + } + + /** + * Utility class to allow us to display the data source ID along with the + * name + */ + private class DataSourceItem { + + private final DataSource ds; + + DataSourceItem(DataSource ds) { + this.ds = ds; + } + + DataSource getDataSource() { + return ds; + } + + @Override + public String toString() { + return ds.getName() + " (ID: " + ds.getId() + ")"; + } + } + + /** + * Validate the form. If we use any of this in the final dialog we should + * use bundle messages. + */ + private void validateFields() { + // There will be a file type selected. + if (fileType == null) { + setInvalid("At least one file type must be selected"); + return; + } + // For most enabled filters, there should be something selected + if (dataSourceCheckbox.isSelected() && dataSourceList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one data source must be selected"); + return; + } + if (crFrequencyCheckbox.isSelected() && crFrequencyList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one CR frequency must be selected"); + return; + } + if (sizeCheckbox.isSelected() && sizeList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one size must be selected"); + return; + } + if (keywordCheckbox.isSelected() && keywordList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one keyword list name must be selected"); + return; + } + + // Parent uses everything in the box + if (parentCheckbox.isSelected() && getParentPaths().isEmpty()) { + setInvalid("At least one parent path must be entered"); + return; + } + + if (hashSetCheckbox.isSelected() && hashSetList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one hash set name must be selected"); + return; + } + + if (interestingItemsCheckbox.isSelected() && interestingItemsList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one interesting file set name must be selected"); + return; + } + + if (objectsCheckbox.isSelected() && objectsList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one object type name must be selected"); + return; + } + + if (tagsCheckbox.isSelected() && tagsList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one tag name must be selected"); + return; + } + + if (scoreCheckbox.isSelected() && scoreList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one score must be selected"); + return; + } + setValid(); + } + + /** + * The settings are valid so enable the Search button + */ + private void setValid() { + errorLabel.setText(""); + searchButton.setEnabled(true); + } + + /** + * The settings are not valid so disable the search button and display the + * given error message. + * + * @param error + */ + private void setInvalid(String error) { + errorLabel.setText(error); + searchButton.setEnabled(false); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + javax.swing.ButtonGroup parentPathButtonGroup = new javax.swing.ButtonGroup(); + javax.swing.ButtonGroup parentIncludeButtonGroup = new javax.swing.ButtonGroup(); + javax.swing.JScrollPane filtersScrollPane = new javax.swing.JScrollPane(); + javax.swing.JPanel filtersPanel = new javax.swing.JPanel(); + sizeCheckbox = new javax.swing.JCheckBox(); + dataSourceCheckbox = new javax.swing.JCheckBox(); + crFrequencyCheckbox = new javax.swing.JCheckBox(); + keywordCheckbox = new javax.swing.JCheckBox(); + parentCheckbox = new javax.swing.JCheckBox(); + dataSourceScrollPane = new javax.swing.JScrollPane(); + dataSourceList = new javax.swing.JList<>(); + substringRadioButton = new javax.swing.JRadioButton(); + addButton = new javax.swing.JButton(); + deleteButton = new javax.swing.JButton(); + sizeScrollPane = new javax.swing.JScrollPane(); + sizeList = new javax.swing.JList<>(); + crFrequencyScrollPane = new javax.swing.JScrollPane(); + crFrequencyList = new javax.swing.JList<>(); + keywordScrollPane = new javax.swing.JScrollPane(); + keywordList = new javax.swing.JList<>(); + javax.swing.JLabel parentLabel = new javax.swing.JLabel(); + parentScrollPane = new javax.swing.JScrollPane(); + parentList = new javax.swing.JList<>(); + hashSetCheckbox = new javax.swing.JCheckBox(); + hashSetScrollPane = new javax.swing.JScrollPane(); + hashSetList = new javax.swing.JList<>(); + objectsCheckbox = new javax.swing.JCheckBox(); + tagsCheckbox = new javax.swing.JCheckBox(); + interestingItemsCheckbox = new javax.swing.JCheckBox(); + scoreCheckbox = new javax.swing.JCheckBox(); + exifCheckbox = new javax.swing.JCheckBox(); + notableCheckbox = new javax.swing.JCheckBox(); + objectsScrollPane = new javax.swing.JScrollPane(); + objectsList = new javax.swing.JList<>(); + tagsScrollPane = new javax.swing.JScrollPane(); + tagsList = new javax.swing.JList<>(); + interestingItemsScrollPane = new javax.swing.JScrollPane(); + interestingItemsList = new javax.swing.JList<>(); + scoreScrollPane = new javax.swing.JScrollPane(); + scoreList = new javax.swing.JList<>(); + excludeRadioButton = new javax.swing.JRadioButton(); + knownFilesCheckbox = new javax.swing.JCheckBox(); + javax.swing.JPanel fullRadioPanel = new javax.swing.JPanel(); + fullRadioButton = new javax.swing.JRadioButton(); + javax.swing.JPanel includeRadioPanel = new javax.swing.JPanel(); + javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 32767)); + includeRadioButton = new javax.swing.JRadioButton(); + javax.swing.JPanel parentTextPanel = new javax.swing.JPanel(); + parentTextField = new javax.swing.JTextField(); + searchButton = new javax.swing.JButton(); + javax.swing.JPanel sortingPanel = new javax.swing.JPanel(); + groupByCombobox = new javax.swing.JComboBox<>(); + orderByCombobox = new javax.swing.JComboBox<>(); + javax.swing.JLabel orderGroupsByLabel = new javax.swing.JLabel(); + javax.swing.JLabel orderByLabel = new javax.swing.JLabel(); + javax.swing.JLabel groupByLabel = new javax.swing.JLabel(); + groupSortingComboBox = new javax.swing.JComboBox<>(); + errorLabel = new javax.swing.JLabel(); + cancelButton = new javax.swing.JButton(); + javax.swing.JLabel stepTwoLabel = new javax.swing.JLabel(); + javax.swing.JLabel stepThreeLabel = new javax.swing.JLabel(); + + setMinimumSize(new java.awt.Dimension(10, 0)); + setPreferredSize(new java.awt.Dimension(321, 400)); + + filtersScrollPane.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.filtersScrollPane.border.title"))); // NOI18N + filtersScrollPane.setPreferredSize(new java.awt.Dimension(309, 400)); + + filtersPanel.setMinimumSize(new java.awt.Dimension(280, 500)); + filtersPanel.setPreferredSize(new java.awt.Dimension(280, 540)); + filtersPanel.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(sizeCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.sizeCheckbox.text")); // NOI18N + sizeCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + sizeCheckboxActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(6, 6, 4, 0); + filtersPanel.add(sizeCheckbox, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(dataSourceCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.dataSourceCheckbox.text")); // NOI18N + dataSourceCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + dataSourceCheckboxActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); + filtersPanel.add(dataSourceCheckbox, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(crFrequencyCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.crFrequencyCheckbox.text")); // NOI18N + crFrequencyCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + crFrequencyCheckboxActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); + filtersPanel.add(crFrequencyCheckbox, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(keywordCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.keywordCheckbox.text")); // NOI18N + keywordCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + keywordCheckboxActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 12; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); + filtersPanel.add(keywordCheckbox, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(parentCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.parentCheckbox.text")); // NOI18N + parentCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + parentCheckboxActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 7; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); + filtersPanel.add(parentCheckbox, gridBagConstraints); + + dataSourceList.setModel(new DefaultListModel()); + dataSourceList.setEnabled(false); + dataSourceList.setVisibleRowCount(5); + dataSourceScrollPane.setViewportView(dataSourceList); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.weighty = 0.1; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); + filtersPanel.add(dataSourceScrollPane, gridBagConstraints); + + parentPathButtonGroup.add(substringRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.substringRadioButton.text")); // NOI18N + substringRadioButton.setEnabled(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 9; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.1; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 0); + filtersPanel.add(substringRadioButton, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(addButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.addButton.text")); // NOI18N + addButton.setEnabled(false); + addButton.setMaximumSize(new java.awt.Dimension(70, 23)); + addButton.setMinimumSize(new java.awt.Dimension(70, 23)); + addButton.setPreferredSize(new java.awt.Dimension(70, 23)); + addButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + addButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 3; + gridBagConstraints.gridy = 11; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 6, 6); + filtersPanel.add(addButton, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(deleteButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.deleteButton.text")); // NOI18N + deleteButton.setEnabled(false); + deleteButton.setMaximumSize(new java.awt.Dimension(70, 23)); + deleteButton.setMinimumSize(new java.awt.Dimension(70, 23)); + deleteButton.setPreferredSize(new java.awt.Dimension(70, 23)); + deleteButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 3; + gridBagConstraints.gridy = 10; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 4, 6); + filtersPanel.add(deleteButton, gridBagConstraints); + + sizeList.setModel(new DefaultListModel()); + sizeList.setEnabled(false); + sizeList.setVisibleRowCount(5); + sizeScrollPane.setViewportView(sizeList); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.weighty = 0.1; + gridBagConstraints.insets = new java.awt.Insets(6, 4, 4, 6); + filtersPanel.add(sizeScrollPane, gridBagConstraints); + + crFrequencyList.setModel(new DefaultListModel()); + crFrequencyList.setEnabled(false); + crFrequencyList.setVisibleRowCount(5); + crFrequencyScrollPane.setViewportView(crFrequencyList); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.weighty = 0.1; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); + filtersPanel.add(crFrequencyScrollPane, gridBagConstraints); + + keywordList.setModel(new DefaultListModel()); + keywordList.setEnabled(false); + keywordList.setVisibleRowCount(3); + keywordScrollPane.setViewportView(keywordList); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 12; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); + filtersPanel.add(keywordScrollPane, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(parentLabel, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.parentLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 8; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); + filtersPanel.add(parentLabel, gridBagConstraints); + + parentList.setModel(new DefaultListModel()); + parentList.setEnabled(false); + parentList.setMaximumSize(null); + parentList.setMinimumSize(new java.awt.Dimension(0, 30)); + parentList.setPreferredSize(new java.awt.Dimension(0, 30)); + parentList.setVisibleRowCount(4); + parentList.addListSelectionListener(new javax.swing.event.ListSelectionListener() { + public void valueChanged(javax.swing.event.ListSelectionEvent evt) { + parentListValueChanged(evt); + } + }); + parentScrollPane.setViewportView(parentList); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 7; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.gridheight = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.weighty = 0.05; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); + filtersPanel.add(parentScrollPane, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(hashSetCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.hashSetCheckbox.text")); // NOI18N + hashSetCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + hashSetCheckboxActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 4; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); + filtersPanel.add(hashSetCheckbox, gridBagConstraints); + + hashSetList.setModel(new DefaultListModel()); + hashSetList.setEnabled(false); + hashSetList.setMinimumSize(new java.awt.Dimension(0, 30)); + hashSetList.setPreferredSize(new java.awt.Dimension(0, 30)); + hashSetList.setVisibleRowCount(3); + hashSetScrollPane.setViewportView(hashSetList); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 4; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.weighty = 0.05; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); + filtersPanel.add(hashSetScrollPane, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(objectsCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.objectsCheckbox.text")); // NOI18N + objectsCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + objectsCheckboxActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 6; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); + filtersPanel.add(objectsCheckbox, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(tagsCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.tagsCheckbox.text")); // NOI18N + tagsCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + tagsCheckboxActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 13; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); + filtersPanel.add(tagsCheckbox, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(interestingItemsCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.interestingItemsCheckbox.text")); // NOI18N + interestingItemsCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + interestingItemsCheckboxActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 5; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); + filtersPanel.add(interestingItemsCheckbox, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(scoreCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.scoreCheckbox.text")); // NOI18N + scoreCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + scoreCheckboxActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 14; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); + filtersPanel.add(scoreCheckbox, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(exifCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.exifCheckbox.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 6); + filtersPanel.add(exifCheckbox, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(notableCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.notableCheckbox.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 15; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 6); + filtersPanel.add(notableCheckbox, gridBagConstraints); + + objectsList.setModel(new DefaultListModel()); + objectsList.setEnabled(false); + objectsList.setMinimumSize(new java.awt.Dimension(0, 30)); + objectsList.setPreferredSize(new java.awt.Dimension(0, 30)); + objectsList.setVisibleRowCount(2); + objectsScrollPane.setViewportView(objectsList); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 6; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.weighty = 0.05; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); + filtersPanel.add(objectsScrollPane, gridBagConstraints); + + tagsList.setModel(new DefaultListModel()); + tagsList.setEnabled(false); + tagsList.setVisibleRowCount(3); + tagsScrollPane.setViewportView(tagsList); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 13; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); + filtersPanel.add(tagsScrollPane, gridBagConstraints); + + interestingItemsList.setModel(new DefaultListModel()); + interestingItemsList.setEnabled(false); + interestingItemsList.setMinimumSize(new java.awt.Dimension(0, 30)); + interestingItemsList.setPreferredSize(new java.awt.Dimension(0, 30)); + interestingItemsList.setVisibleRowCount(2); + interestingItemsScrollPane.setViewportView(interestingItemsList); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 5; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.weighty = 0.05; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); + filtersPanel.add(interestingItemsScrollPane, gridBagConstraints); + + scoreList.setModel(new DefaultListModel()); + scoreList.setEnabled(false); + scoreList.setVisibleRowCount(3); + scoreScrollPane.setViewportView(scoreList); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 14; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); + filtersPanel.add(scoreScrollPane, gridBagConstraints); + + parentIncludeButtonGroup.add(excludeRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(excludeRadioButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.excludeRadioButton.text")); // NOI18N + excludeRadioButton.setEnabled(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 10; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.1; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 0); + filtersPanel.add(excludeRadioButton, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(knownFilesCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.knownFilesCheckbox.text")); // NOI18N + knownFilesCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.knownFilesCheckbox.toolTipText")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 16; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 6); + filtersPanel.add(knownFilesCheckbox, gridBagConstraints); + + parentPathButtonGroup.add(fullRadioButton); + fullRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(fullRadioButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.fullRadioButton.text")); // NOI18N + fullRadioButton.setEnabled(false); + + javax.swing.GroupLayout fullRadioPanelLayout = new javax.swing.GroupLayout(fullRadioPanel); + fullRadioPanel.setLayout(fullRadioPanelLayout); + fullRadioPanelLayout.setHorizontalGroup( + fullRadioPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, fullRadioPanelLayout.createSequentialGroup() + .addContainerGap(58, Short.MAX_VALUE) + .addComponent(fullRadioButton) + .addGap(20, 20, 20)) + ); + fullRadioPanelLayout.setVerticalGroup( + fullRadioPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(fullRadioPanelLayout.createSequentialGroup() + .addComponent(fullRadioButton) + .addGap(0, 4, Short.MAX_VALUE)) + ); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 9; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + filtersPanel.add(fullRadioPanel, gridBagConstraints); + + parentIncludeButtonGroup.add(includeRadioButton); + includeRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(includeRadioButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.includeRadioButton.text")); // NOI18N + includeRadioButton.setEnabled(false); + + javax.swing.GroupLayout includeRadioPanelLayout = new javax.swing.GroupLayout(includeRadioPanel); + includeRadioPanel.setLayout(includeRadioPanelLayout); + includeRadioPanelLayout.setHorizontalGroup( + includeRadioPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(includeRadioPanelLayout.createSequentialGroup() + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 58, Short.MAX_VALUE) + .addComponent(includeRadioButton)) + ); + includeRadioPanelLayout.setVerticalGroup( + includeRadioPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(includeRadioButton) + ); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 10; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + filtersPanel.add(includeRadioPanel, gridBagConstraints); + + parentTextField.setEnabled(false); + + javax.swing.GroupLayout parentTextPanelLayout = new javax.swing.GroupLayout(parentTextPanel); + parentTextPanel.setLayout(parentTextPanelLayout); + parentTextPanelLayout.setHorizontalGroup( + parentTextPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, parentTextPanelLayout.createSequentialGroup() + .addGap(0, 0, 0) + .addComponent(parentTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 71, Short.MAX_VALUE)) + ); + parentTextPanelLayout.setVerticalGroup( + parentTextPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(parentTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + ); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 11; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.weightx = 1.5; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 0); + filtersPanel.add(parentTextPanel, gridBagConstraints); + + filtersScrollPane.setViewportView(filtersPanel); + + org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.searchButton.text")); // NOI18N + searchButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + searchButtonActionPerformed(evt); + } + }); + + sortingPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.sortingPanel.border.title"))); // NOI18N + sortingPanel.setPreferredSize(new java.awt.Dimension(345, 112)); + + org.openide.awt.Mnemonics.setLocalizedText(orderGroupsByLabel, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.orderGroupsByLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(orderByLabel, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.orderByLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(groupByLabel, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.groupByLabel.text")); // NOI18N + + javax.swing.GroupLayout sortingPanelLayout = new javax.swing.GroupLayout(sortingPanel); + sortingPanel.setLayout(sortingPanelLayout); + sortingPanelLayout.setHorizontalGroup( + sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(sortingPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(orderGroupsByLabel) + .addGroup(sortingPanelLayout.createSequentialGroup() + .addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(groupByLabel) + .addComponent(orderByLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(groupSortingComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(orderByCombobox, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(groupByCombobox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addContainerGap()) + ); + sortingPanelLayout.setVerticalGroup( + sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(sortingPanelLayout.createSequentialGroup() + .addGap(6, 6, 6) + .addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(groupByCombobox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(groupByLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(groupSortingComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(orderGroupsByLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(orderByCombobox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(orderByLabel)) + .addContainerGap()) + ); + + errorLabel.setForeground(new java.awt.Color(255, 0, 0)); + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.cancelButton.text")); // NOI18N + cancelButton.setEnabled(false); + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(stepTwoLabel, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.stepTwoLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(stepThreeLabel, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.stepThreeLabel.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(6, 6, 6) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(stepTwoLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(searchButton)) + .addComponent(stepThreeLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(filtersScrollPane, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(sortingPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 309, Short.MAX_VALUE)) + .addGap(6, 6, 6)))) + ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, searchButton}); + + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(6, 6, 6) + .addComponent(stepTwoLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(filtersScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 201, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(stepThreeLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(sortingPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(errorLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cancelButton) + .addComponent(searchButton))) + .addGap(6, 6, 6)) + ); + }// //GEN-END:initComponents + + private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed + // Get the selected filters + List filters = getFilters(); + enableSearch(false); + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.SearchStartedEvent(fileType)); + + // Get the grouping attribute and group sorting method + FileSearch.AttributeType groupingAttr = getGroupingAttribute(); + FileGroup.GroupSortingAlgorithm groupSortAlgorithm = getGroupSortingMethod(); + + // Get the file sorting method + FileSorter.SortingMethod fileSort = getFileSortingMethod(); + EamDb centralRepoDb = null; + if (EamDb.isEnabled()) { + try { + centralRepoDb = EamDb.getInstance(); + } catch (EamDbException ex) { + centralRepoDb = null; + logger.log(Level.SEVERE, "Error loading central repository database, no central repository options will be available for File Discovery", ex); + } + } + searchWorker = new SearchWorker(centralRepoDb, filters, groupingAttr, groupSortAlgorithm, fileSort); + searchWorker.execute(); + }//GEN-LAST:event_searchButtonActionPerformed + + /** + * Set the enabled status of the search controls. + * + * @param enabled Boolean which indicates if the search should be enabled. + * True if the search button and controls should be enabled, + * false otherwise. + */ + private void enableSearch(boolean enabled) { + if (enabled) { + DiscoveryTopComponent.getTopComponent().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } else { + DiscoveryTopComponent.getTopComponent().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + if (fileType == FileType.IMAGE) { + imagesSelected(enabled, false); + } else if (fileType == FileType.VIDEO) { + videosSelected(enabled, false); + } + searchButton.setEnabled(enabled); + cancelButton.setEnabled(!enabled); + orderByCombobox.setEnabled(enabled); + groupByCombobox.setEnabled(enabled); + groupSortingComboBox.setEnabled(enabled); + } + + /** + * Update the user interface when a search has been cancelled. + * + * @param searchCancelledEvent The SearchCancelledEvent which was received. + */ + @Subscribe + void handleSearchCancelledEvent(DiscoveryEventUtils.SearchCancelledEvent searchCancelledEvent) { + SwingUtilities.invokeLater(() -> { + enableSearch(true); + }); + } + + /** + * Update the user interface when a search has been successfully completed. + * + * @param searchCompleteEvent The SearchCompleteEvent which was received. + */ + @Subscribe + void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) { + SwingUtilities.invokeLater(() -> { + enableSearch(true); + }); + } + + private void parentCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_parentCheckboxActionPerformed + parentFilterSettings(true, true, parentCheckbox.isSelected(), null); + }//GEN-LAST:event_parentCheckboxActionPerformed + + private void keywordCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_keywordCheckboxActionPerformed + keywordList.setEnabled(keywordCheckbox.isSelected()); + }//GEN-LAST:event_keywordCheckboxActionPerformed + + private void sizeCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sizeCheckboxActionPerformed + sizeList.setEnabled(sizeCheckbox.isSelected()); + }//GEN-LAST:event_sizeCheckboxActionPerformed + + private void crFrequencyCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_crFrequencyCheckboxActionPerformed + crFrequencyList.setEnabled(crFrequencyCheckbox.isSelected()); + }//GEN-LAST:event_crFrequencyCheckboxActionPerformed + + private void addButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addButtonActionPerformed + if (!parentTextField.getText().isEmpty()) { + ParentSearchTerm searchTerm; + searchTerm = new ParentSearchTerm(parentTextField.getText(), fullRadioButton.isSelected(), includeRadioButton.isSelected()); + parentListModel.add(parentListModel.size(), searchTerm); + validateFields(); + parentTextField.setText(""); + } + }//GEN-LAST:event_addButtonActionPerformed + + /** + * Cancel the current search. + */ + void cancelSearch() { + if (searchWorker != null) { + searchWorker.cancel(true); + } + } + + private void deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteButtonActionPerformed + int index = parentList.getSelectedIndex(); + if (index >= 0) { + parentListModel.remove(index); + } + validateFields(); + }//GEN-LAST:event_deleteButtonActionPerformed + + private void parentListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_parentListValueChanged + if (parentList.getSelectedValuesList().isEmpty()) { + deleteButton.setEnabled(false); + } else { + deleteButton.setEnabled(true); + } + }//GEN-LAST:event_parentListValueChanged + + private void dataSourceCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSourceCheckboxActionPerformed + dataSourceList.setEnabled(dataSourceCheckbox.isSelected()); + }//GEN-LAST:event_dataSourceCheckboxActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + cancelSearch(); + }//GEN-LAST:event_cancelButtonActionPerformed + + private void hashSetCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hashSetCheckboxActionPerformed + hashSetList.setEnabled(hashSetCheckbox.isSelected()); + }//GEN-LAST:event_hashSetCheckboxActionPerformed + + private void objectsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_objectsCheckboxActionPerformed + objectsList.setEnabled(objectsCheckbox.isSelected()); + }//GEN-LAST:event_objectsCheckboxActionPerformed + + private void tagsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tagsCheckboxActionPerformed + tagsList.setEnabled(tagsCheckbox.isSelected()); + }//GEN-LAST:event_tagsCheckboxActionPerformed + + private void interestingItemsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interestingItemsCheckboxActionPerformed + interestingItemsList.setEnabled(interestingItemsCheckbox.isSelected()); + }//GEN-LAST:event_interestingItemsCheckboxActionPerformed + + private void scoreCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_scoreCheckboxActionPerformed + scoreList.setEnabled(scoreCheckbox.isSelected()); + }//GEN-LAST:event_scoreCheckboxActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton addButton; + private javax.swing.JButton cancelButton; + private javax.swing.JCheckBox crFrequencyCheckbox; + private javax.swing.JList crFrequencyList; + private javax.swing.JScrollPane crFrequencyScrollPane; + private javax.swing.JCheckBox dataSourceCheckbox; + private javax.swing.JList dataSourceList; + private javax.swing.JScrollPane dataSourceScrollPane; + private javax.swing.JButton deleteButton; + private javax.swing.JLabel errorLabel; + private javax.swing.JRadioButton excludeRadioButton; + private javax.swing.JCheckBox exifCheckbox; + private javax.swing.JRadioButton fullRadioButton; + private javax.swing.JComboBox groupByCombobox; + private javax.swing.JComboBox groupSortingComboBox; + private javax.swing.JCheckBox hashSetCheckbox; + private javax.swing.JList hashSetList; + private javax.swing.JScrollPane hashSetScrollPane; + private javax.swing.JRadioButton includeRadioButton; + private javax.swing.JCheckBox interestingItemsCheckbox; + private javax.swing.JList interestingItemsList; + private javax.swing.JScrollPane interestingItemsScrollPane; + private javax.swing.JCheckBox keywordCheckbox; + private javax.swing.JList keywordList; + private javax.swing.JScrollPane keywordScrollPane; + private javax.swing.JCheckBox knownFilesCheckbox; + private javax.swing.JCheckBox notableCheckbox; + private javax.swing.JCheckBox objectsCheckbox; + private javax.swing.JList objectsList; + private javax.swing.JScrollPane objectsScrollPane; + private javax.swing.JComboBox orderByCombobox; + private javax.swing.JCheckBox parentCheckbox; + private javax.swing.JList parentList; + private javax.swing.JScrollPane parentScrollPane; + private javax.swing.JTextField parentTextField; + private javax.swing.JCheckBox scoreCheckbox; + private javax.swing.JList scoreList; + private javax.swing.JScrollPane scoreScrollPane; + private javax.swing.JButton searchButton; + private javax.swing.JCheckBox sizeCheckbox; + private javax.swing.JList sizeList; + private javax.swing.JScrollPane sizeScrollPane; + private javax.swing.JRadioButton substringRadioButton; + private javax.swing.JCheckBox tagsCheckbox; + private javax.swing.JList tagsList; + private javax.swing.JScrollPane tagsScrollPane; + // End of variables declaration//GEN-END:variables + +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSorter.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSorter.java new file mode 100644 index 0000000000..6d73c20b01 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSorter.java @@ -0,0 +1,292 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.filequery; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import org.openide.util.NbBundle; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Class used to sort ResultFiles using the supplied method. + */ +class FileSorter implements Comparator { + + private final List> comparators = new ArrayList<>(); + + /** + * Set up the sorter using the supplied sorting method. The sorting is + * defined by a list of ResultFile comparators. These comparators will be + * run in order until one returns a non-zero result. + * + * @param method The method that should be used to sort the files + */ + FileSorter(SortingMethod method) { + + // Set up the primary comparators that should applied to the files + switch (method) { + case BY_DATA_SOURCE: + comparators.add(getDataSourceComparator()); + break; + case BY_FILE_SIZE: + comparators.add(getFileSizeComparator()); + break; + case BY_FILE_TYPE: + comparators.add(getFileTypeComparator()); + comparators.add(getMIMETypeComparator()); + break; + case BY_FREQUENCY: + comparators.add(getFrequencyComparator()); + break; + case BY_KEYWORD_LIST_NAMES: + comparators.add(getKeywordListNameComparator()); + break; + case BY_FULL_PATH: + comparators.add(getParentPathComparator()); + break; + case BY_FILE_NAME: + comparators.add(getFileNameComparator()); + break; + default: + // The default comparator will be added afterward + break; + } + + // Add the default comparator to the end. This will ensure a consistent sort + // order regardless of the order the files were added to the list. + comparators.add(getDefaultComparator()); + } + + @Override + public int compare(ResultFile file1, ResultFile file2) { + + int result = 0; + for (Comparator comp : comparators) { + result = comp.compare(file1, file2); + if (result != 0) { + return result; + } + } + + // The files are the same + return result; + } + + /** + * Compare files using data source ID. Will order smallest to largest. + * + * @return -1 if file1 has the lower data source ID, 0 if equal, 1 otherwise + */ + private static Comparator getDataSourceComparator() { + return (ResultFile file1, ResultFile file2) -> Long.compare(file1.getFirstInstance().getDataSourceObjectId(), file2.getFirstInstance().getDataSourceObjectId()); + } + + /** + * Compare files using their FileType enum. Orders based on the ranking in + * the FileType enum. + * + * @return -1 if file1 has the lower FileType value, 0 if equal, 1 otherwise + */ + private static Comparator getFileTypeComparator() { + return (ResultFile file1, ResultFile file2) -> Integer.compare(file1.getFileType().getRanking(), file2.getFileType().getRanking()); + } + + /** + * Compare files using a concatenated version of keyword list names. + * Alphabetical by the list names with files with no keyword list hits going + * last. + * + * @return -1 if file1 has the earliest combined keyword list name, 0 if + * equal, 1 otherwise + */ + private static Comparator getKeywordListNameComparator() { + return (ResultFile file1, ResultFile file2) -> { + // Put empty lists at the bottom + if (file1.getKeywordListNames().isEmpty()) { + if (file2.getKeywordListNames().isEmpty()) { + return 0; + } + return 1; + } else if (file2.getKeywordListNames().isEmpty()) { + return -1; + } + + String list1 = String.join(",", file1.getKeywordListNames()); + String list2 = String.join(",", file2.getKeywordListNames()); + return compareStrings(list1, list2); + }; + } + + /** + * Compare files based on parent path. Order alphabetically. + * + * @return -1 if file1's path comes first alphabetically, 0 if equal, 1 + * otherwise + */ + private static Comparator getParentPathComparator() { + + return new Comparator() { + @Override + public int compare(ResultFile file1, ResultFile file2) { + String file1ParentPath; + try { + file1ParentPath = file1.getFirstInstance().getParent().getUniquePath(); + } catch (TskCoreException ingored) { + file1ParentPath = file1.getFirstInstance().getParentPath(); + } + String file2ParentPath; + try { + file2ParentPath = file2.getFirstInstance().getParent().getUniquePath(); + } catch (TskCoreException ingored) { + file2ParentPath = file2.getFirstInstance().getParentPath(); + } + return compareStrings(file1ParentPath.toLowerCase(), file2ParentPath.toLowerCase()); + } + }; + } + + /** + * Compare files based on number of occurrences in the central repository. + * Order from most rare to least rare Frequency enum. + * + * @return -1 if file1's rarity is lower than file2, 0 if equal, 1 otherwise + */ + private static Comparator getFrequencyComparator() { + return (ResultFile file1, ResultFile file2) -> Integer.compare(file1.getFrequency().getRanking(), file2.getFrequency().getRanking()); + } + + /** + * Compare files based on MIME type. Order is alphabetical. + * + * @return -1 if file1's MIME type comes before file2's, 0 if equal, 1 + * otherwise + */ + private static Comparator getMIMETypeComparator() { + return (ResultFile file1, ResultFile file2) -> compareStrings(file1.getFirstInstance().getMIMEType(), file2.getFirstInstance().getMIMEType()); + } + + /** + * Compare files based on size. Order large to small. + * + * @return -1 if file1 is larger than file2, 0 if equal, 1 otherwise + */ + private static Comparator getFileSizeComparator() { + return (ResultFile file1, ResultFile file2) -> -1 * Long.compare(file1.getFirstInstance().getSize(), file2.getFirstInstance().getSize()) // Sort large to small + ; + } + + /** + * Compare files based on file name. Order alphabetically. + * + * @return -1 if file1 comes before file2, 0 if equal, 1 otherwise + */ + private static Comparator getFileNameComparator() { + return (ResultFile file1, ResultFile file2) -> compareStrings(file1.getFirstInstance().getName().toLowerCase(), file2.getFirstInstance().getName().toLowerCase()); + } + + /** + * A final default comparison between two ResultFile objects. Currently this + * is on file name and then object ID. It can be changed but should always + * include something like the object ID to ensure a consistent sorting when + * the rest of the compared fields are the same. + * + * @return -1 if file1 comes before file2, 0 if equal, 1 otherwise + */ + private static Comparator getDefaultComparator() { + return (ResultFile file1, ResultFile file2) -> { + // Compare file names and then object ID (to ensure a consistent sort) + int result = getFileNameComparator().compare(file1, file2); + if (result == 0) { + return Long.compare(file1.getFirstInstance().getId(), file2.getFirstInstance().getId()); + } + return result; + }; + } + + /** + * Compare two strings alphabetically. Nulls are allowed. + * + * @param s1 + * @param s2 + * + * @return -1 if s1 comes before s2, 0 if equal, 1 otherwise + */ + private static int compareStrings(String s1, String s2) { + String string1 = s1 == null ? "" : s1; + String string2 = s2 == null ? "" : s2; + return string1.compareTo(string2); + } + + /** + * Enum for selecting the primary method for sorting result files. + */ + @NbBundle.Messages({ + "FileSorter.SortingMethod.datasource.displayName=Data Source", + "FileSorter.SortingMethod.filename.displayName=File Name", + "FileSorter.SortingMethod.filesize.displayName=File Size", + "FileSorter.SortingMethod.filetype.displayName=File Type", + "FileSorter.SortingMethod.frequency.displayName=Central Repo Frequency", + "FileSorter.SortingMethod.keywordlist.displayName=Keyword List Names", + "FileSorter.SortingMethod.fullPath.displayName=Full Path"}) + enum SortingMethod { + BY_FILE_NAME(new ArrayList<>(), + Bundle.FileSorter_SortingMethod_filename_displayName()), // Sort alphabetically by file name + BY_DATA_SOURCE(new ArrayList<>(), + Bundle.FileSorter_SortingMethod_datasource_displayName()), // Sort in increasing order of data source ID + BY_FILE_SIZE(new ArrayList<>(), + Bundle.FileSorter_SortingMethod_filesize_displayName()), // Sort in decreasing order of size + BY_FILE_TYPE(Arrays.asList(new FileSearch.FileTypeAttribute()), + Bundle.FileSorter_SortingMethod_filetype_displayName()), // Sort in order of file type (defined in FileType enum), with secondary sort on MIME type + BY_FREQUENCY(Arrays.asList(new FileSearch.FrequencyAttribute()), + Bundle.FileSorter_SortingMethod_frequency_displayName()), // Sort by decreasing rarity in the central repository + BY_KEYWORD_LIST_NAMES(Arrays.asList(new FileSearch.KeywordListAttribute()), + Bundle.FileSorter_SortingMethod_keywordlist_displayName()), // Sort alphabetically by list of keyword list names found + BY_FULL_PATH(new ArrayList<>(), + Bundle.FileSorter_SortingMethod_fullPath_displayName()); // Sort alphabetically by path + + private final String displayName; + private final List requiredAttributes; + + SortingMethod(List attributes, String displayName) { + this.requiredAttributes = attributes; + this.displayName = displayName; + } + + @Override + public String toString() { + return displayName; + } + + List getRequiredAttributes() { + return Collections.unmodifiableList(requiredAttributes); + } + + /** + * Get the list of enums that are valid for ordering images. + * + * @return enums that can be used to ordering images. + */ + static List getOptionsForOrdering() { + return Arrays.asList(BY_FILE_SIZE, BY_FULL_PATH, BY_FILE_NAME, BY_DATA_SOURCE); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/GroupListPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/GroupListPanel.form new file mode 100644 index 0000000000..9df241e5ab --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/GroupListPanel.form @@ -0,0 +1,71 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/GroupListPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/GroupListPanel.java new file mode 100644 index 0000000000..374c079590 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/GroupListPanel.java @@ -0,0 +1,193 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import com.google.common.eventbus.Subscribe; +import java.util.List; +import java.util.Map; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.filequery.FileSearch.GroupKey; +import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; + +/** + * Panel to display the list of groups which are provided by a search + */ +class GroupListPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + private FileType resultType = null; + private Map groupMap = null; + private List searchfilters; + private FileSearch.AttributeType groupingAttribute; + private FileGroup.GroupSortingAlgorithm groupSort; + private FileSorter.SortingMethod fileSortMethod; + private GroupKey selectedGroupKey; + + /** + * Creates new form GroupListPanel + */ + GroupListPanel() { + initComponents(); + } + + /** + * Subscribe to and reset the panel in response to SearchStartedEvents + * + * @param searchStartedEvent the SearchStartedEvent which was received + */ + @Subscribe + void handleSearchStartedEvent(DiscoveryEventUtils.SearchStartedEvent searchStartedEvent) { + resultType = searchStartedEvent.getType(); + groupKeyList.setListData(new GroupKey[0]); + } + + @Messages({"GroupsListPanel.noResults.message.text=No results were found for the selected filters.", + "GroupsListPanel.noResults.title.text=No results found"}) + /** + * Subscribe to and update list of groups in response to + * SearchCompleteEvents + * + * @param searchCompleteEvent the SearchCompleteEvent which was received + */ + @Subscribe + void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) { + groupMap = searchCompleteEvent.getGroupMap(); + searchfilters = searchCompleteEvent.getFilters(); + groupingAttribute = searchCompleteEvent.getGroupingAttr(); + groupSort = searchCompleteEvent.getGroupSort(); + fileSortMethod = searchCompleteEvent.getFileSort(); + groupKeyList.setListData(groupMap.keySet().toArray(new GroupKey[groupMap.keySet().size()])); + if (groupKeyList.getModel().getSize() > 0) { + groupKeyList.setSelectedIndex(0); + } else { + JOptionPane.showMessageDialog(DiscoveryTopComponent.getTopComponent(), + Bundle.GroupsListPanel_noResults_message_text(), + Bundle.GroupsListPanel_noResults_title_text(), + JOptionPane.INFORMATION_MESSAGE); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JScrollPane groupListScrollPane = new javax.swing.JScrollPane(); + groupKeyList = new javax.swing.JList<>(); + + groupKeyList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(GroupListPanel.class, "GroupListPanel.groupKeyList.border.title"))); // NOI18N + groupKeyList.setModel(new DefaultListModel()); + groupKeyList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + groupKeyList.setCellRenderer(new GroupListRenderer()); + groupKeyList.addListSelectionListener(new javax.swing.event.ListSelectionListener() { + public void valueChanged(javax.swing.event.ListSelectionEvent evt) { + groupSelected(evt); + } + }); + groupListScrollPane.setViewportView(groupKeyList); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 144, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(groupListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 144, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 300, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(groupListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + /** + * Reset the group list to be empty. + */ + void resetGroupList() { + groupKeyList.setListData(new GroupKey[0]); + } + + /** + * Respond to a group being selected by sending a PageRetrievedEvent + * + * @param evt the event which indicates a selection occurs in the list + */ + private void groupSelected(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_groupSelected + if (!evt.getValueIsAdjusting()) { + if (groupKeyList.getSelectedValue() != null) { + GroupKey selectedGroup = groupKeyList.getSelectedValue(); + for (GroupKey groupKey : groupMap.keySet()) { + if (selectedGroup.equals(groupKey)) { + selectedGroupKey = groupKey; + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.GroupSelectedEvent( + searchfilters, groupingAttribute, groupSort, fileSortMethod, selectedGroupKey, groupMap.get(selectedGroupKey), resultType)); + break; + } + } + } else { + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.NoResultsEvent()); + } + } + }//GEN-LAST:event_groupSelected + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JList groupKeyList; + // End of variables declaration//GEN-END:variables + + /** + * GroupListCellRenderer displays GroupKeys as their String value followed + * by the number of items in the group. + */ + private class GroupListRenderer extends DefaultListCellRenderer { + + private static final long serialVersionUID = 1L; + + @Override + public java.awt.Component getListCellRendererComponent( + JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) { + Object newValue = value; + if (newValue instanceof GroupKey) { + String valueString = newValue.toString(); + setToolTipText(valueString); + //if paths would be longer than 37 characters shorten them to be 37 characters + if (groupingAttribute instanceof FileSearch.ParentPathAttribute && valueString.length() > 37) { + valueString = valueString.substring(0, 16) + " ... " + valueString.substring(valueString.length() - 16); + } + newValue = valueString + " (" + groupMap.get(newValue) + ")"; + } + super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus); + return this; + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.form new file mode 100644 index 0000000000..e75effab93 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.form @@ -0,0 +1,134 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.java new file mode 100644 index 0000000000..a1d04c9621 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.java @@ -0,0 +1,249 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.event.MouseEvent; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.ListCellRenderer; +import org.openide.util.ImageUtilities; +import org.openide.util.NbBundle; + +/** + * Class which displays a thumbnail and information for an image file. + */ +public class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellRenderer { + + private static final long serialVersionUID = 1L; + private static final Color SELECTION_COLOR = new Color(0, 120, 215); + private static final int BYTE_UNIT_CONVERSION = 1000; + private static final int ICON_SIZE = 16; + private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png"; + private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png"; + private static final String DELETE_ICON_PATH = "/org/sleuthkit/autopsy/images/file-icon-deleted.png"; + private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false)); + private static final ImageIcon NOTABLE_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false)); + private static final ImageIcon DELETED_ICON = new ImageIcon(ImageUtilities.loadImage(DELETE_ICON_PATH, false)); + + /** + * Creates new form ImageThumbnailPanel + */ + public ImageThumbnailPanel() { + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JPanel thumbnailPanel = new javax.swing.JPanel(); + thumbnailLabel = new javax.swing.JLabel(); + fileSizeLabel = new javax.swing.JLabel(); + countLabel = new javax.swing.JLabel(); + isDeletedLabel = new javax.swing.JLabel(); + scoreLabel = new javax.swing.JLabel(); + + setToolTipText(""); + + thumbnailPanel.setToolTipText(""); + thumbnailPanel.setLayout(new java.awt.GridBagLayout()); + thumbnailPanel.add(thumbnailLabel, new java.awt.GridBagConstraints()); + + fileSizeLabel.setToolTipText(""); + + countLabel.setToolTipText(""); + countLabel.setMaximumSize(new java.awt.Dimension(159, 12)); + countLabel.setMinimumSize(new java.awt.Dimension(159, 12)); + countLabel.setPreferredSize(new java.awt.Dimension(159, 12)); + + isDeletedLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/file-icon-deleted.png"))); // NOI18N + isDeletedLabel.setToolTipText(""); + isDeletedLabel.setMaximumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + isDeletedLabel.setMinimumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + isDeletedLabel.setPreferredSize(new Dimension(ICON_SIZE,ICON_SIZE)); + + scoreLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/red-circle-exclamation.png"))); // NOI18N + scoreLabel.setToolTipText(""); + scoreLabel.setMaximumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + scoreLabel.setMinimumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + scoreLabel.setPreferredSize(new Dimension(ICON_SIZE,ICON_SIZE)); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(thumbnailPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 201, Short.MAX_VALUE) + .addComponent(fileSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(countLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(isDeletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(thumbnailPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 178, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(fileSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(isDeletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(countLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel countLabel; + private javax.swing.JLabel fileSizeLabel; + private javax.swing.JLabel isDeletedLabel; + private javax.swing.JLabel scoreLabel; + private javax.swing.JLabel thumbnailLabel; + // End of variables declaration//GEN-END:variables + + @NbBundle.Messages({ + "# {0} - numberOfInstances", + "ImageThumbnailPanel.countLabel.text=Number of Instances: {0}", + "ImageThumbnailPanel.isDeleted.text=All instances of file are deleted."}) + @Override + public Component getListCellRendererComponent(JList list, ImageThumbnailWrapper value, int index, boolean isSelected, boolean cellHasFocus) { + fileSizeLabel.setText(getFileSizeString(value.getResultFile().getFirstInstance().getSize())); + countLabel.setText(Bundle.ImageThumbnailPanel_countLabel_text(value.getResultFile().getAllInstances().size())); + thumbnailLabel.setIcon(new ImageIcon(value.getThumbnail())); + if (value.getResultFile().isDeleted()) { + isDeletedLabel.setIcon(DELETED_ICON); + isDeletedLabel.setToolTipText(Bundle.ImageThumbnailPanel_isDeleted_text()); + } else { + isDeletedLabel.setIcon(null); + isDeletedLabel.setToolTipText(null); + } + switch (value.getResultFile().getScore()) { + case NOTABLE_SCORE: + scoreLabel.setIcon(NOTABLE_SCORE_ICON); + break; + case INTERESTING_SCORE: + scoreLabel.setIcon(INTERESTING_SCORE_ICON); + break; + case NO_SCORE: // empty case - this is interpreted as an intentional fall-through + default: + scoreLabel.setIcon(null); + break; + } + scoreLabel.setToolTipText(value.getResultFile().getScoreDescription()); + setBackground(isSelected ? SELECTION_COLOR : list.getBackground()); + + return this; + } + + @NbBundle.Messages({"# {0} - fileSize", + "# {1} - units", + "ImageThumbnailPanel.sizeLabel.text=Size: {0} {1}", + "ImageThumbnailPanel.bytes.text=bytes", + "ImageThumbnailPanel.kiloBytes.text=KB", + "ImageThumbnailPanel.megaBytes.text=MB", + "ImageThumbnailPanel.gigaBytes.text=GB", + "ImageThumbnailPanel.terraBytes.text=TB"}) + /** + * Convert a size in bytes to a string with representing the size in the + * largest units which represent the value as being greater than or equal to + * one. Result will be rounded down to the nearest whole number of those + * units. + * + * @param bytes Size in bytes. + */ + private String getFileSizeString(long bytes) { + long size = bytes; + int unitsSwitchValue = 0; + while (size > BYTE_UNIT_CONVERSION && unitsSwitchValue < 4) { + size /= BYTE_UNIT_CONVERSION; + unitsSwitchValue++; + } + String units; + switch (unitsSwitchValue) { + case 1: + units = Bundle.ImageThumbnailPanel_kiloBytes_text(); + break; + case 2: + units = Bundle.ImageThumbnailPanel_megaBytes_text(); + break; + case 3: + units = Bundle.ImageThumbnailPanel_gigaBytes_text(); + break; + case 4: + units = Bundle.ImageThumbnailPanel_terraBytes_text(); + break; + default: + units = Bundle.ImageThumbnailPanel_bytes_text(); + break; + } + return Bundle.ImageThumbnailPanel_sizeLabel_text(size, units); + } + + @Override + public String getToolTipText(MouseEvent event) { + if (event != null) { + //gets tooltip of internal panel item mouse is over + Point point = event.getPoint(); + for (Component comp : getComponents()) { + if (isPointOnIcon(comp, point)) { + String toolTip = ((JComponent) comp).getToolTipText(); + if (toolTip == null || toolTip.isEmpty()) { + return null; + } else { + return toolTip; + } + } + } + } + return null; + } + + /** + * Helper method to see if point is on the icon. + * + * @param comp The component to check if the cursor is over the icon of + * @param point The point the cursor is at. + * + * @return True if the point is over the icon, false otherwise. + */ + private boolean isPointOnIcon(Component comp, Point point) { + return comp instanceof JComponent && point.x >= comp.getX() && point.x <= comp.getX() + ICON_SIZE && point.y >= comp.getY() && point.y <= comp.getY() + ICON_SIZE; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailViewer.form b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailViewer.form new file mode 100644 index 0000000000..4d065c3bf9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailViewer.form @@ -0,0 +1,50 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailViewer.java b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailViewer.java new file mode 100644 index 0000000000..e2688359c9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailViewer.java @@ -0,0 +1,122 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import java.util.ArrayList; +import java.util.List; +import javax.swing.DefaultListModel; +import javax.swing.event.ListSelectionListener; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * A JPanel to display image thumbnails. + * + */ +public class ImageThumbnailViewer extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + + private final DefaultListModel thumbnailListModel = new DefaultListModel<>(); + + /** + * Creates new form ImageThumbnailViewer + */ + public ImageThumbnailViewer() { + initComponents(); + + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + thumbnailListScrollPane = new javax.swing.JScrollPane(); + thumbnailList = new javax.swing.JList<>(); + + setLayout(new java.awt.BorderLayout()); + + thumbnailList.setModel(thumbnailListModel); + thumbnailList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + thumbnailList.setCellRenderer(new ImageThumbnailPanel()); + thumbnailList.setLayoutOrientation(javax.swing.JList.HORIZONTAL_WRAP); + thumbnailList.setVisibleRowCount(0); + thumbnailListScrollPane.setViewportView(thumbnailList); + + add(thumbnailListScrollPane, java.awt.BorderLayout.CENTER); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JList thumbnailList; + private javax.swing.JScrollPane thumbnailListScrollPane; + // End of variables declaration//GEN-END:variables + + /** + * Add a selection listener to the list of thumbnails being displayed. + * + * @param listener The ListSelectionListener to add to the selection model. + */ + void addListSelectionListener(ListSelectionListener listener) { + thumbnailList.getSelectionModel().addListSelectionListener(listener); + } + + /** + * Get the list of AbstractFiles which are represented by the selected image + * thumbnail. + * + * @return The list of AbstractFiles which are represented by the selected + * image thumbnail. + */ + List getInstancesForSelected() { + synchronized (this) { + if (thumbnailList.getSelectedIndex() == -1) { + return new ArrayList<>(); + } else { + return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances(); + } + } + } + + /** + * Clear the list of thumbnails being displayed. + */ + void clearViewer() { + synchronized (this) { + thumbnailListModel.removeAllElements(); + thumbnailListScrollPane.getVerticalScrollBar().setValue(0); + } + } + + /** + * Add the thumbnail for an image to the panel. + * + * @param thumbnailWrapper The object which contains the thumbnail which + * will be displayed. + */ + void addImage(ImageThumbnailWrapper thumbnailWrapper) { + synchronized (this) { + thumbnailListModel.addElement(thumbnailWrapper); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailWrapper.java b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailWrapper.java new file mode 100644 index 0000000000..dd1923007d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailWrapper.java @@ -0,0 +1,72 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import java.awt.Image; +import org.sleuthkit.autopsy.coreutils.ImageUtils; + +/** + * Class to wrap all the information necessary for an image thumbnail to be + * displayed. + */ +public class ImageThumbnailWrapper { + + private Image thumbnail; + private final ResultFile resultFile; + + /** + * Construct a new ImageThumbnailsWrapper. + * + * @param file The ResultFile which represents the image file which the + * thumbnails were created for. + */ + ImageThumbnailWrapper(ResultFile file) { + this.thumbnail = ImageUtils.getDefaultThumbnail(); + this.resultFile = file; + } + + /** + * Set the image thumbnail which exists. + * + * @param thumbnail The thumbnail which exists for this file. + */ + void setImageThumbnail(Image thumbnail) { + this.thumbnail = thumbnail; + } + + /** + * Get the ResultFile which represents the image file which the thumbnail + * was created for. + * + * @return The ResultFile which represents the image file which the + * thumbnail was created for. + */ + ResultFile getResultFile() { + return resultFile; + } + + /** + * Get the thumbnail for the image. + * + * @return The Image which is the thumbnail for the image. + */ + Image getThumbnail() { + return thumbnail; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/OpenFileDiscoveryAction.java b/Core/src/org/sleuthkit/autopsy/filequery/OpenFileDiscoveryAction.java new file mode 100644 index 0000000000..d315b85956 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/OpenFileDiscoveryAction.java @@ -0,0 +1,159 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import java.awt.Component; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JOptionPane; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionReferences; +import org.openide.awt.ActionRegistration; +import org.openide.util.HelpCtx; +import org.openide.util.NbBundle; +import org.openide.util.actions.CallableSystemAction; +import org.openide.util.actions.Presenter; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.IngestJobInfo; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Class to open the File Discovery top component. Allows the user to run + * searches and see results. + */ +@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.newpackage.OpenFileDiscoveryAction") +@ActionReferences(value = { + @ActionReference(path = "Menu/Tools", position = 103) + , + @ActionReference(path = "Toolbars/Case", position = 104)}) +@ActionRegistration(displayName = "#CTL_OpenFileDiscoveryAction", lazy = false) +@NbBundle.Messages({"CTL_OpenFileDiscoveryAction=File Discovery"}) +public final class OpenFileDiscoveryAction extends CallableSystemAction implements Presenter.Toolbar { + + private static final Logger logger = Logger.getLogger(OpenFileDiscoveryAction.class.getName()); + + private static final String DISPLAY_NAME = Bundle.CTL_OpenFileDiscoveryAction(); + private static final long serialVersionUID = 1L; + private final JButton toolbarButton = new JButton(); + + public OpenFileDiscoveryAction() { + toolbarButton.addActionListener(OpenFileDiscoveryAction.this::actionPerformed); + this.setEnabled(false); + } + + @Override + public boolean isEnabled() { + return Case.isCaseOpen(); + } + + @NbBundle.Messages({"OpenFileDiscoveryAction.resultsIncomplete.text=Results may be incomplete"}) + + @Override + @SuppressWarnings("fallthrough") + public void performAction() { + final DiscoveryTopComponent tc = DiscoveryTopComponent.getTopComponent(); + if (tc != null) { + if (tc.isOpened() == false) { + tc.open(); + tc.updateSearchSettings(); + displayErrorMessage(tc); + } + tc.toFront(); + tc.requestActive(); + + } + } + + /** + * Private helper method to display an error message when the results of the + * File Discovery Top component may be incomplete. + * + * @param tc The File Discovery Top component. + */ + private void displayErrorMessage(DiscoveryTopComponent tc) { + //check if modules run and assemble message + try { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + Map dataSourceIngestModules = new HashMap<>(); + for (DataSource dataSource : skCase.getDataSources()) { + dataSourceIngestModules.put(dataSource.getId(), new DataSourceModulesWrapper(dataSource.getName())); + } + + for (IngestJobInfo jobInfo : skCase.getIngestJobs()) { + dataSourceIngestModules.get(jobInfo.getObjectId()).updateModulesRun(jobInfo); + } + String message = ""; + for (DataSourceModulesWrapper dsmodulesWrapper : dataSourceIngestModules.values()) { + message += dsmodulesWrapper.getMessage(); + } + if (!message.isEmpty()) { + JOptionPane.showMessageDialog(tc, message, Bundle.OpenFileDiscoveryAction_resultsIncomplete_text(), JOptionPane.INFORMATION_MESSAGE); + } + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.WARNING, "Exception while determining which modules have been run for File Discovery", ex); + } + } + + /** + * Returns the toolbar component of this action. + * + * @return The toolbar button + */ + @Override + public Component getToolbarPresenter() { + ImageIcon icon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/discovery-icon-24.png")); //NON-NLS + toolbarButton.setIcon(icon); + toolbarButton.setText(this.getName()); + return toolbarButton; + } + + /** + * Set this action to be enabled/disabled + * + * @param value whether to enable this action or not + */ + @Override + public void setEnabled(boolean value) { + super.setEnabled(value); + toolbarButton.setEnabled(value); + } + + @Override + public String getName() { + return DISPLAY_NAME; + } + + @Override + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + public boolean asynchronous() { + return false; // run on edt + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/PageWorker.java b/Core/src/org/sleuthkit/autopsy/filequery/PageWorker.java new file mode 100644 index 0000000000..f90d11bd32 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/PageWorker.java @@ -0,0 +1,103 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import java.util.List; +import java.util.ArrayList; +import java.util.logging.Level; +import javax.swing.SwingWorker; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.filequery.FileSearch.GroupKey; + +/** + * SwingWorker to retrieve the contents of a page. + */ +final class PageWorker extends SwingWorker { + + private final static Logger logger = Logger.getLogger(PageWorker.class.getName()); + private static final String USER_NAME_PROPERTY = "user.name"; //NON-NLS + private final List searchfilters; + private final FileSearch.AttributeType groupingAttribute; + private final FileGroup.GroupSortingAlgorithm groupSort; + private final FileSorter.SortingMethod fileSortMethod; + private final GroupKey groupKey; + private final int startingEntry; + private final int pageSize; + private final FileSearchData.FileType resultType; + private final EamDb centralRepo; + private final List results = new ArrayList<>(); + + /** + * Construct a new PageWorker. + * + * @param searchfilters The search filters which were used by the + * search. + * @param groupingAttribute The grouping attribute used by the search. + * @param groupSort The sorting algorithm used for groups. + * @param fileSortMethod The sorting method used for files. + * @param groupKey The key which uniquely identifies the group + * which was selected. + * @param startingEntry The first entry in the group to include in this + * page. + * @param pageSize The number of files to include in this page. + * @param resultType The type of files which exist in the group. + * @param centralRepo The central repository to be used. + */ + PageWorker(List searchfilters, FileSearch.AttributeType groupingAttribute, + FileGroup.GroupSortingAlgorithm groupSort, FileSorter.SortingMethod fileSortMethod, GroupKey groupKey, + int startingEntry, int pageSize, FileSearchData.FileType resultType, EamDb centralRepo) { + this.searchfilters = searchfilters; + this.groupingAttribute = groupingAttribute; + this.groupSort = groupSort; + this.fileSortMethod = fileSortMethod; + this.groupKey = groupKey; + this.startingEntry = startingEntry; + this.pageSize = pageSize; + this.resultType = resultType; + this.centralRepo = centralRepo; + } + + @Override + protected Void doInBackground() throws Exception { + + try { + // Run the search + results.addAll(FileSearch.getFilesInGroup(System.getProperty(USER_NAME_PROPERTY), searchfilters, + groupingAttribute, + groupSort, + fileSortMethod, groupKey, startingEntry, pageSize, + Case.getCurrentCase().getSleuthkitCase(), centralRepo)); + } catch (FileSearchException ex) { + logger.log(Level.SEVERE, "Error running file search test", ex); + cancel(true); + } + return null; + } + + @Override + protected void done() { + if (!isCancelled()) { + int currentPage = startingEntry / pageSize; //integer division should round down to get page number correctly + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.PageRetrievedEvent(resultType, currentPage, results)); + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java new file mode 100644 index 0000000000..8b903bf8b9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java @@ -0,0 +1,371 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.filequery; + +import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; +import org.sleuthkit.datamodel.AbstractFile; +import java.util.ArrayList; +import java.util.Collections; +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.NoCurrentCaseException; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.HashUtility; +import org.sleuthkit.datamodel.Tag; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; + +/** + * Container for files that holds all necessary data for grouping and sorting + */ +class ResultFile { + + private final static Logger logger = Logger.getLogger(ResultFile.class.getName()); + private FileSearchData.Frequency frequency; + private final List keywordListNames; + private final List hashSetNames; + private final List tagNames; + private final List interestingSetNames; + private final List objectDetectedNames; + private final List instances = new ArrayList<>(); + private DataResultViewerTable.Score currentScore = DataResultViewerTable.Score.NO_SCORE; + private String scoreDescription = null; + private boolean deleted = false; + private FileType fileType; + + /** + * Create a ResultFile from an AbstractFile + * + * @param abstractFile + */ + ResultFile(AbstractFile abstractFile) { + //store the file the ResultFile was created for as the first value in the instances list + instances.add(abstractFile); + if (abstractFile.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC)) { + deleted = true; + } + updateScoreAndDescription(abstractFile); + this.frequency = FileSearchData.Frequency.UNKNOWN; + keywordListNames = new ArrayList<>(); + hashSetNames = new ArrayList<>(); + tagNames = new ArrayList<>(); + interestingSetNames = new ArrayList<>(); + objectDetectedNames = new ArrayList<>(); + fileType = FileType.fromMIMEtype(abstractFile.getMIMEType()); + } + + /** + * Get the frequency of this file in the central repository + * + * @return The Frequency enum + */ + FileSearchData.Frequency getFrequency() { + return frequency; + } + + /** + * Set the frequency of this file from the central repository + * + * @param frequency The frequency of the file as an enum + */ + void setFrequency(FileSearchData.Frequency frequency) { + this.frequency = frequency; + } + + /** + * Add an AbstractFile to the list of files which are instances of this + * file. + * + * @param duplicate The abstract file to add as a duplicate. + */ + void addDuplicate(AbstractFile duplicate) { + if (deleted && !duplicate.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC)) { + deleted = false; + } + if (fileType == FileType.OTHER) { + fileType = FileType.fromMIMEtype(duplicate.getMIMEType()); + } + updateScoreAndDescription(duplicate); + instances.add(duplicate); + } + + /** + * Get the aggregate score of this ResultFile. Calculated as the highest + * score among all instances it represents. + * + * @return The score of this ResultFile. + */ + DataResultViewerTable.Score getScore() { + return currentScore; + } + + /** + * Get the description for the score assigned to this item. + * + * @return The score description of this ResultFile. + */ + String getScoreDescription() { + return scoreDescription; + } + + /** + * Get the aggregate deleted status of this ResultFile. A file is identified + * as deleted if all instances of it are deleted. + * + * @return The deleted status of this ResultFile. + */ + boolean isDeleted() { + return deleted; + } + + /** + * Get the list of AbstractFiles which have been identified as instances of + * this file. + * + * @return The list of AbstractFiles which have been identified as instances + * of this file. + */ + List getAllInstances() { + return Collections.unmodifiableList(instances); + } + + /** + * Get the file type. + * + * @return The FileType enum. + */ + FileType getFileType() { + return fileType; + } + + /** + * Add a keyword list name that matched this file. + * + * @param keywordListName + */ + void addKeywordListName(String keywordListName) { + if (!keywordListNames.contains(keywordListName)) { + keywordListNames.add(keywordListName); + } + + // Sort the list so the getKeywordListNames() will be consistent regardless of the order added + Collections.sort(keywordListNames); + } + + /** + * Get the keyword list names for this file + * + * @return the keyword list names that matched this file. + */ + List getKeywordListNames() { + return Collections.unmodifiableList(keywordListNames); + } + + /** + * Add a hash set name that matched this file. + * + * @param hashSetName + */ + void addHashSetName(String hashSetName) { + if (!hashSetNames.contains(hashSetName)) { + hashSetNames.add(hashSetName); + } + + // Sort the list so the getHashHitNames() will be consistent regardless of the order added + Collections.sort(hashSetNames); + } + + /** + * Get the hash set names for this file + * + * @return The hash set names that matched this file. + */ + List getHashSetNames() { + return Collections.unmodifiableList(hashSetNames); + } + + /** + * Add a tag name that matched this file. + * + * @param tagName + */ + void addTagName(String tagName) { + if (!tagNames.contains(tagName)) { + tagNames.add(tagName); + } + + // Sort the list so the getTagNames() will be consistent regardless of the order added + Collections.sort(tagNames); + } + + /** + * Get the tag names for this file + * + * @return the tag names that matched this file. + */ + List getTagNames() { + return Collections.unmodifiableList(tagNames); + } + + /** + * Add an interesting file set name that matched this file. + * + * @param interestingSetName + */ + void addInterestingSetName(String interestingSetName) { + if (!interestingSetNames.contains(interestingSetName)) { + interestingSetNames.add(interestingSetName); + } + + // Sort the list so the getInterestingSetNames() will be consistent regardless of the order added + Collections.sort(interestingSetNames); + } + + /** + * Get the interesting item set names for this file + * + * @return the interesting item set names that matched this file. + */ + List getInterestingSetNames() { + return Collections.unmodifiableList(interestingSetNames); + } + + /** + * Add an object detected in this file. + * + * @param objectDetectedName + */ + void addObjectDetectedName(String objectDetectedName) { + if (!objectDetectedNames.contains(objectDetectedName)) { + objectDetectedNames.add(objectDetectedName); + } + + // Sort the list so the getObjectDetectedNames() will be consistent regardless of the order added + Collections.sort(objectDetectedNames); + } + + /** + * Get the objects detected for this file + * + * @return the objects detected in this file. + */ + List getObjectDetectedNames() { + return Collections.unmodifiableList(objectDetectedNames); + } + + /** + * Get the AbstractFile + * + * @return the AbstractFile object + */ + AbstractFile getFirstInstance() { + return instances.get(0); + } + + @Override + public String toString() { + return getFirstInstance().getName() + "(" + getFirstInstance().getId() + ") - " + + getFirstInstance().getSize() + ", " + getFirstInstance().getParentPath() + ", " + + getFirstInstance().getDataSourceObjectId() + ", " + frequency.toString() + ", " + + String.join(",", keywordListNames) + ", " + getFirstInstance().getMIMEType(); + } + + @Override + public int hashCode() { + if (this.getFirstInstance().getMd5Hash() == null + || HashUtility.isNoDataMd5(this.getFirstInstance().getMd5Hash()) + || !HashUtility.isValidMd5Hash(this.getFirstInstance().getMd5Hash())) { + return super.hashCode(); + } else { + //if the file has a valid MD5 use the hashcode of the MD5 for deduping files with the same MD5 + return this.getFirstInstance().getMd5Hash().hashCode(); + } + + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ResultFile) + || this.getFirstInstance().getMd5Hash() == null + || HashUtility.isNoDataMd5(this.getFirstInstance().getMd5Hash()) + || !HashUtility.isValidMd5Hash(this.getFirstInstance().getMd5Hash())) { + return super.equals(obj); + } else { + //if the file has a valid MD5 compare use the MD5 for equality check + return this.getFirstInstance().getMd5Hash().equals(((ResultFile) obj).getFirstInstance().getMd5Hash()); + } + } + + /** + * Get all tags from the case database that are associated with the file + * + * @return a list of tags that are associated with the file + */ + private List getContentTagsFromDatabase(AbstractFile file) { + List tags = new ArrayList<>(); + try { + tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(file)); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Failed to get tags for file " + file.getName(), ex); + } + return tags; + } + + @NbBundle.Messages({ + "ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable.", + "ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it.", + "ResultFile.score.taggedFile.description=At least one instance of the file has been tagged.", + "ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag."}) + private void updateScoreAndDescription(AbstractFile file) { + if (currentScore == DataResultViewerTable.Score.NOTABLE_SCORE) { + //already notable can return + return; + } + if (file.getKnown() == TskData.FileKnown.BAD) { + currentScore = DataResultViewerTable.Score.NOTABLE_SCORE; + scoreDescription = Bundle.ResultFile_score_notableFile_description(); + return; + } + try { + if (currentScore == DataResultViewerTable.Score.NO_SCORE && !file.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT).isEmpty()) { + currentScore = DataResultViewerTable.Score.INTERESTING_SCORE; + scoreDescription = Bundle.ResultFile_score_interestingResult_description(); + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error getting artifacts for file: " + file.getName(), ex); + } + List tags = getContentTagsFromDatabase(file); + if (!tags.isEmpty()) { + currentScore = DataResultViewerTable.Score.INTERESTING_SCORE; + scoreDescription = Bundle.ResultFile_score_taggedFile_description(); + for (Tag tag : tags) { + if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) { + currentScore = DataResultViewerTable.Score.NOTABLE_SCORE; + scoreDescription = Bundle.ResultFile_score_notableTaggedFile_description(); + return; + } + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form new file mode 100644 index 0000000000..c21dca33e5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form @@ -0,0 +1,364 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java new file mode 100644 index 0000000000..3e4f8b7a68 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java @@ -0,0 +1,713 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import com.google.common.eventbus.Subscribe; +import java.awt.Component; +import java.awt.Image; +import java.awt.event.ItemEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import javax.swing.DefaultComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import javax.swing.event.ListSelectionListener; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.coreutils.ImageUtils; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.FileNode; +import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; +import org.sleuthkit.autopsy.directorytree.ViewContextAction; +import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; +import org.sleuthkit.autopsy.filequery.FileSearch.GroupKey; +import org.sleuthkit.autopsy.modules.hashdatabase.AddContentToHashDbAction; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Panel for displaying of file discovery results and handling the paging of + * those results. + */ +public class ResultsPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + private final static Logger logger = Logger.getLogger(ResultsPanel.class.getName()); + private final VideoThumbnailViewer videoThumbnailViewer; + private final ImageThumbnailViewer imageThumbnailViewer; + private List searchFilters; + private FileSearch.AttributeType groupingAttribute; + private FileGroup.GroupSortingAlgorithm groupSort; + private FileSorter.SortingMethod fileSortMethod; + private GroupKey selectedGroupKey; + private int currentPage = 0; + private int previousPageSize = 10; + private FileSearchData.FileType resultType; + private int groupSize = 0; + private PageWorker pageWorker; + private final List> thumbnailWorkers = new ArrayList<>(); + private final DefaultListModel instancesListModel = new DefaultListModel<>(); + private ListSelectionListener listener = null; + + /** + * Creates new form ResultsPanel. + */ + @Messages({"ResultsPanel.viewFileInDir.name=View File in Directory", + "ResultsPanel.openInExternalViewer.name=Open in External Viewer"}) + public ResultsPanel() { + initComponents(); + imageThumbnailViewer = new ImageThumbnailViewer(); + videoThumbnailViewer = new VideoThumbnailViewer(); + videoThumbnailViewer.addListSelectionListener((e) -> { + if (resultType == FileSearchData.FileType.VIDEO) { + if (!e.getValueIsAdjusting()) { + populateInstancesList(); + } else { + instancesList.clearSelection(); + } + } + }); + imageThumbnailViewer.addListSelectionListener((e) -> { + if (resultType == FileSearchData.FileType.IMAGE) { + if (!e.getValueIsAdjusting()) { + populateInstancesList(); + } else { + instancesList.clearSelection(); + } + } + }); + //Add the context menu when right clicking + instancesList.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e)) { + SwingUtilities.invokeLater(() -> { + instancesList.setSelectedIndex(instancesList.locationToIndex(e.getPoint())); + Set files = new HashSet<>(); + files.add(instancesList.getSelectedValue()); + JPopupMenu menu = new JPopupMenu(); + menu.add(new ViewContextAction(Bundle.ResultsPanel_viewFileInDir_name(), instancesList.getSelectedValue())); + menu.add(new ExternalViewerAction(Bundle.ResultsPanel_openInExternalViewer_name(), new FileNode(instancesList.getSelectedValue()))); + menu.add(ViewFileInTimelineAction.createViewFileAction(instancesList.getSelectedValue())); + menu.add(new DiscoveryExtractAction(files)); + menu.add(AddContentTagAction.getInstance().getMenuForContent(files)); + menu.add(DeleteFileContentTagAction.getInstance().getMenuForFiles(files)); + menu.add(AddContentToHashDbAction.getInstance().getMenuForFiles(files)); + menu.show(instancesList, e.getPoint().x, e.getPoint().y); + }); + } + } + }); + + } + + /** + * Add a list selection listener to the instances list. + * + * @param listener The ListSelectionListener to add to the instances list. + */ + void addListSelectionListener(ListSelectionListener newListener) { + instancesList.removeListSelectionListener(listener); + listener = newListener; + instancesList.addListSelectionListener(listener); + } + + /** + * Populate the instances list. + */ + synchronized void populateInstancesList() { + SwingUtilities.invokeLater(() -> { + instancesList.removeListSelectionListener(listener); + instancesListModel.removeAllElements(); + for (AbstractFile file : getInstancesForSelected()) { + instancesListModel.addElement(file); + } + instancesList.addListSelectionListener(listener); + if (!instancesListModel.isEmpty()) { + instancesList.setSelectedIndex(0); + } + }); + } + + /** + * Get the AbstractFile for the item currently selected in the instances + * list. + * + * @return The AbstractFile which is currently selected. + */ + synchronized AbstractFile getSelectedFile() { + if (instancesList.getSelectedIndex() == -1) { + return null; + } else { + return instancesListModel.getElementAt(instancesList.getSelectedIndex()); + } + } + + /** + * Get the list of all instances for the the currently selected item in the + * results viewer area. + * + * @return The list of AbstractFiles which are represented by the item + * selected in the results viewer area. + */ + private List getInstancesForSelected() { + if (resultType == FileSearchData.FileType.VIDEO) { + return videoThumbnailViewer.getInstancesForSelected(); + } else if (resultType == FileSearchData.FileType.IMAGE) { + return imageThumbnailViewer.getInstancesForSelected(); + } + return new ArrayList<>(); + } + + /** + * Subscribe and respond to PageRetrievedEvents. + * + * @param pageRetrievedEvent The PageRetrievedEvent received. + */ + @Subscribe + void handlePageRetrievedEvent(DiscoveryEventUtils.PageRetrievedEvent pageRetrievedEvent) { + SwingUtilities.invokeLater(() -> { + populateInstancesList(); + currentPage = pageRetrievedEvent.getPageNumber(); + updateControls(); + resetResultViewer(); + if (pageRetrievedEvent.getType() == FileSearchData.FileType.IMAGE) { + populateImageViewer(pageRetrievedEvent.getSearchResults()); + resultsViewerPanel.add(imageThumbnailViewer); + } else if (pageRetrievedEvent.getType() == FileSearchData.FileType.VIDEO) { + populateVideoViewer(pageRetrievedEvent.getSearchResults()); + resultsViewerPanel.add(videoThumbnailViewer); + } + resultsViewerPanel.revalidate(); + resultsViewerPanel.repaint(); + } + ); + } + + /** + * Reset the result viewer and any associate workers to a default empty + * state. + */ + synchronized void resetResultViewer() { + resultsViewerPanel.remove(imageThumbnailViewer); + resultsViewerPanel.remove(videoThumbnailViewer); + //cancel any unfished thumb workers + for (SwingWorker thumbWorker : thumbnailWorkers) { + if (!thumbWorker.isDone()) { + thumbWorker.cancel(true); + } + } + //clear old thumbnails + thumbnailWorkers.clear(); + videoThumbnailViewer.clearViewer(); + imageThumbnailViewer.clearViewer(); + } + + /** + * Populate the video thumbnail viewer, cancelling any thumbnails which are + * currently being created first. + * + * @param files The list of ResultFiles to populate the video viewer with. + */ + synchronized void populateVideoViewer(List files) { + for (ResultFile file : files) { + VideoThumbnailWorker thumbWorker = new VideoThumbnailWorker(file); + thumbWorker.execute(); + //keep track of thumb worker for possible cancelation + thumbnailWorkers.add(thumbWorker); + } + } + + /** + * Populate the image thumbnail viewer, cancelling any thumbnails which are + * currently being created first. + * + * @param files The list of ResultFiles to populate the image viewer with. + */ + synchronized void populateImageViewer(List files) { + for (ResultFile file : files) { + ImageThumbnailWorker thumbWorker = new ImageThumbnailWorker(file); + thumbWorker.execute(); + //keep track of thumb worker for possible cancelation + thumbnailWorkers.add(thumbWorker); + } + } + + /** + * Subscribe and respond to GroupSelectedEvents. + * + * @param groupSelectedEvent The GroupSelectedEvent received. + */ + @Subscribe + void handleGroupSelectedEvent(DiscoveryEventUtils.GroupSelectedEvent groupSelectedEvent) { + SwingUtilities.invokeLater(() -> { + searchFilters = groupSelectedEvent.getFilters(); + groupingAttribute = groupSelectedEvent.getGroupingAttr(); + groupSort = groupSelectedEvent.getGroupSort(); + fileSortMethod = groupSelectedEvent.getFileSort(); + selectedGroupKey = groupSelectedEvent.getGroupKey(); + resultType = groupSelectedEvent.getResultType(); + groupSize = groupSelectedEvent.getGroupSize(); + setPage(0); + }); + } + + /** + * Handle and respond to NoResultsEvent, updating the panel to reflect that + * there were no results. + * + * @param noResultsEvent the NoResultsEvent received. + */ + @Subscribe + void handleNoResultsEvent(DiscoveryEventUtils.NoResultsEvent noResultsEvent) { + SwingUtilities.invokeLater(() -> { + groupSize = 0; + currentPage = 0; + updateControls(); + videoThumbnailViewer.clearViewer(); + imageThumbnailViewer.clearViewer(); + resultsViewerPanel.revalidate(); + resultsViewerPanel.repaint(); + }); + } + + /** + * Set the page number and retrieve its contents. + * + * @param startingEntry The index of the first file in the group to include + * in this page. + */ + private synchronized void setPage(int startingEntry) { + int pageSize = pageSizeComboBox.getItemAt(pageSizeComboBox.getSelectedIndex()); + synchronized (this) { + if (pageWorker != null && !pageWorker.isDone()) { + pageWorker.cancel(true); + } + EamDb centralRepo = null; + if (EamDb.isEnabled()) { + try { + centralRepo = EamDb.getInstance(); + } catch (EamDbException ex) { + centralRepo = null; + logger.log(Level.SEVERE, "Error loading central repository database, no central repository options will be available for File Discovery", ex); + } + } + pageWorker = new PageWorker(searchFilters, groupingAttribute, groupSort, fileSortMethod, selectedGroupKey, startingEntry, pageSize, resultType, centralRepo); + pageWorker.execute(); + } + } + + /** + * Enable the paging controls based on what exists in the page. + */ + @Messages({"# {0} - currentPage", + "# {1} - totalPages", + "ResultsPanel.currentPage.displayValue=Page: {0} of {1}"}) + private void updateControls() { + previousPageSize = pageSizeComboBox.getItemAt(pageSizeComboBox.getSelectedIndex()); + int pageSize = pageSizeComboBox.getItemAt(pageSizeComboBox.getSelectedIndex()); + //handle edge case where group size is 0 and we want the empty results to be labeled paged 1 of 1 not page 1 of 0 + double maxPageDouble = groupSize == 0 ? 1 : Math.ceil((double) groupSize / pageSize); + currentPageLabel.setText(Bundle.ResultsPanel_currentPage_displayValue(currentPage + 1, maxPageDouble)); + previousPageButton.setEnabled(currentPage != 0); + nextPageButton.setEnabled(groupSize > ((currentPage + 1) * pageSize)); + gotoPageField.setEnabled(groupSize > pageSize); + pageSizeComboBox.setEnabled(true); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JPanel pagingPanel = new javax.swing.JPanel(); + previousPageButton = new javax.swing.JButton(); + currentPageLabel = new javax.swing.JLabel(); + nextPageButton = new javax.swing.JButton(); + javax.swing.JLabel pageControlsLabel = new javax.swing.JLabel(); + javax.swing.JLabel gotoPageLabel = new javax.swing.JLabel(); + gotoPageField = new javax.swing.JTextField(); + javax.swing.JLabel pageSizeLabel = new javax.swing.JLabel(); + pageSizeComboBox = new javax.swing.JComboBox<>(); + javax.swing.JSplitPane resultsSplitPane = new javax.swing.JSplitPane(); + javax.swing.JPanel instancesPanel = new javax.swing.JPanel(); + javax.swing.JScrollPane instancesScrollPane = new javax.swing.JScrollPane(); + instancesList = new javax.swing.JList<>(); + resultsViewerPanel = new javax.swing.JPanel(); + + setPreferredSize(new java.awt.Dimension(777, 475)); + + pagingPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); + + previousPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N + previousPageButton.setBorder(null); + previousPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N + previousPageButton.setEnabled(false); + previousPageButton.setFocusable(false); + previousPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N + previousPageButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + previousPageButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(currentPageLabel, org.openide.util.NbBundle.getMessage(ResultsPanel.class, "ResultsPanel.currentPageLabel.text")); // NOI18N + currentPageLabel.setMaximumSize(new java.awt.Dimension(90, 23)); + currentPageLabel.setMinimumSize(new java.awt.Dimension(90, 23)); + currentPageLabel.setPreferredSize(new java.awt.Dimension(90, 23)); + + nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N + nextPageButton.setBorder(null); + nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N + nextPageButton.setEnabled(false); + nextPageButton.setFocusable(false); + nextPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N + nextPageButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + nextPageButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(pageControlsLabel, org.openide.util.NbBundle.getMessage(ResultsPanel.class, "ResultsPanel.pageControlsLabel.text")); // NOI18N + pageControlsLabel.setMaximumSize(new java.awt.Dimension(33, 23)); + pageControlsLabel.setMinimumSize(new java.awt.Dimension(33, 23)); + pageControlsLabel.setPreferredSize(new java.awt.Dimension(33, 23)); + + org.openide.awt.Mnemonics.setLocalizedText(gotoPageLabel, org.openide.util.NbBundle.getMessage(ResultsPanel.class, "ResultsPanel.gotoPageLabel.text")); // NOI18N + gotoPageLabel.setMaximumSize(new java.awt.Dimension(70, 23)); + gotoPageLabel.setMinimumSize(new java.awt.Dimension(70, 23)); + gotoPageLabel.setPreferredSize(new java.awt.Dimension(70, 23)); + + gotoPageField.setEnabled(false); + gotoPageField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + gotoPageFieldActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(pageSizeLabel, org.openide.util.NbBundle.getMessage(ResultsPanel.class, "ResultsPanel.pageSizeLabel.text")); // NOI18N + pageSizeLabel.setMaximumSize(new java.awt.Dimension(60, 23)); + pageSizeLabel.setMinimumSize(new java.awt.Dimension(60, 23)); + pageSizeLabel.setPreferredSize(new java.awt.Dimension(60, 23)); + + pageSizeComboBox.setModel(new DefaultComboBoxModel(new Integer[] {25,50,75,100,125,150,175,200})); + pageSizeComboBox.setSelectedIndex(3); + pageSizeComboBox.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + pageSizeChanged(evt); + } + }); + + javax.swing.GroupLayout pagingPanelLayout = new javax.swing.GroupLayout(pagingPanel); + pagingPanel.setLayout(pagingPanelLayout); + pagingPanelLayout.setHorizontalGroup( + pagingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pagingPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(pageControlsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(previousPageButton) + .addGap(0, 0, 0) + .addComponent(nextPageButton) + .addGap(18, 18, 18) + .addComponent(gotoPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(gotoPageField, javax.swing.GroupLayout.PREFERRED_SIZE, 64, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(pageSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 52, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pageSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 55, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + pagingPanelLayout.setVerticalGroup( + pagingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pagingPanelLayout.createSequentialGroup() + .addGap(4, 4, 4) + .addGroup(pagingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(nextPageButton, javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pagingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(previousPageButton, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(pageControlsLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pagingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(gotoPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(gotoPageField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(pageSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(pageSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGap(4, 4, 4)) + ); + + resultsSplitPane.setDividerLocation(380); + resultsSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); + resultsSplitPane.setResizeWeight(1.0); + resultsSplitPane.setToolTipText(org.openide.util.NbBundle.getMessage(ResultsPanel.class, "ResultsPanel.resultsSplitPane.toolTipText")); // NOI18N + resultsSplitPane.setLastDividerLocation(180); + resultsSplitPane.setOpaque(false); + resultsSplitPane.setPreferredSize(new java.awt.Dimension(777, 440)); + + instancesPanel.setPreferredSize(new java.awt.Dimension(775, 68)); + + instancesScrollPane.setPreferredSize(new java.awt.Dimension(775, 60)); + + instancesList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ResultsPanel.class, "ResultsPanel.instancesList.border.title"))); // NOI18N + instancesList.setModel(instancesListModel); + instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + instancesList.setCellRenderer(new InstancesCellRenderer()); + instancesList.setPreferredSize(new java.awt.Dimension(0, 50)); + instancesList.setVisibleRowCount(2); + instancesScrollPane.setViewportView(instancesList); + + javax.swing.GroupLayout instancesPanelLayout = new javax.swing.GroupLayout(instancesPanel); + instancesPanel.setLayout(instancesPanelLayout); + instancesPanelLayout.setHorizontalGroup( + instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 775, Short.MAX_VALUE) + .addGroup(instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(instancesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + instancesPanelLayout.setVerticalGroup( + instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 221, Short.MAX_VALUE) + .addGroup(instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, instancesPanelLayout.createSequentialGroup() + .addGap(0, 0, 0) + .addComponent(instancesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 221, Short.MAX_VALUE))) + ); + + resultsSplitPane.setRightComponent(instancesPanel); + + resultsViewerPanel.setPreferredSize(new java.awt.Dimension(0, 380)); + resultsViewerPanel.setLayout(new java.awt.BorderLayout()); + resultsSplitPane.setLeftComponent(resultsViewerPanel); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pagingPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(resultsSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(pagingPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, 0) + .addComponent(resultsSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(0, 0, 0)) + ); + }// //GEN-END:initComponents + + /** + * Action to perform when previous button is clicked. + * + * @param evt Event which occurs when button is clicked. + */ + private void previousPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_previousPageButtonActionPerformed + if (currentPage > 0) { + disablePagingControls(); + int previousPage = currentPage - 1; + int pageSize = pageSizeComboBox.getItemAt(pageSizeComboBox.getSelectedIndex()); + setPage(previousPage * pageSize); + } + }//GEN-LAST:event_previousPageButtonActionPerformed + + /** + * Action to perform when next button is clicked. + * + * @param evt Event which occurs when button is clicked. + */ + private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed + disablePagingControls(); + int nextPage = currentPage + 1; + int pageSize = pageSizeComboBox.getItemAt(pageSizeComboBox.getSelectedIndex()); + setPage(nextPage * pageSize); + }//GEN-LAST:event_nextPageButtonActionPerformed + + /** + * Navigate to the page number specified in the field + * + * @param evt The event which happens to field is used. + */ + @Messages({"# {0} - selectedPage", + "# {1} - maxPage", + "ResultsPanel.invalidPageNumber.message=The selected page number {0} does not exist. Please select a value from 1 to {1}.", + "ResultsPanel.invalidPageNumber.title=Invalid Page Number"}) + private void gotoPageFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_gotoPageFieldActionPerformed + int newPage; + try { + newPage = Integer.parseInt(gotoPageField.getText()); + } catch (NumberFormatException e) { + //ignore input + return; + } + int pageSize = pageSizeComboBox.getItemAt(pageSizeComboBox.getSelectedIndex()); + if ((newPage - 1) < 0 || groupSize <= ((newPage - 1) * pageSize)) { + JOptionPane.showMessageDialog(this, + Bundle.ResultsPanel_invalidPageNumber_message(newPage, Math.ceil((double) groupSize / pageSize)), + Bundle.ResultsPanel_invalidPageNumber_title(), + JOptionPane.WARNING_MESSAGE); + return; + } + disablePagingControls(); + setPage((newPage - 1) * pageSize); + }//GEN-LAST:event_gotoPageFieldActionPerformed + + private void pageSizeChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_pageSizeChanged + if (evt.getStateChange() == ItemEvent.SELECTED) { + disablePagingControls(); + int previousPage = currentPage - 1; + int pageSize = pageSizeComboBox.getItemAt(pageSizeComboBox.getSelectedIndex()); + if (previousPageSize != pageSize) { + previousPage = 0; + } + setPage(previousPage * pageSize); + } + }//GEN-LAST:event_pageSizeChanged + + /** + * Disable all the paging controls. + */ + private void disablePagingControls() { + nextPageButton.setEnabled(false); + previousPageButton.setEnabled(false); + gotoPageField.setEnabled(false); + pageSizeComboBox.setEnabled(false); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel currentPageLabel; + private javax.swing.JTextField gotoPageField; + private javax.swing.JList instancesList; + private javax.swing.JButton nextPageButton; + private javax.swing.JComboBox pageSizeComboBox; + private javax.swing.JButton previousPageButton; + private javax.swing.JPanel resultsViewerPanel; + // End of variables declaration//GEN-END:variables + + /** + * Swing worker to handle the retrieval of video thumbnails and population + * of the Video Thumbnail Viewer. + */ + private class VideoThumbnailWorker extends SwingWorker { + + private final VideoThumbnailsWrapper thumbnailWrapper; + + /** + * Construct a new VideoThumbnailWorker. + * + * @param file The ResultFile which represents the video file thumbnails + * are being retrieved for. + */ + VideoThumbnailWorker(ResultFile file) { + thumbnailWrapper = new VideoThumbnailsWrapper(file); + videoThumbnailViewer.addVideo(thumbnailWrapper); + } + + @Override + protected Void doInBackground() throws Exception { + FileSearch.getVideoThumbnails(thumbnailWrapper); + return null; + } + + @Override + protected void done() { + videoThumbnailViewer.repaint(); + } + } + + /** + * Swing worker to handle the retrieval of image thumbnails and population + * of the Image Thumbnail Viewer. + */ + private class ImageThumbnailWorker extends SwingWorker { + + private final ImageThumbnailWrapper thumbnailWrapper; + + /** + * Construct a new ImageThumbnailWorker. + * + * @param file The ResultFile which represents the image file thumbnails + * are being retrieved for. + */ + ImageThumbnailWorker(ResultFile file) { + thumbnailWrapper = new ImageThumbnailWrapper(file); + imageThumbnailViewer.addImage(thumbnailWrapper); + } + + @Override + protected Void doInBackground() throws Exception { + Image thumbnail = ImageUtils.getThumbnail(thumbnailWrapper.getResultFile().getFirstInstance(), ImageUtils.ICON_SIZE_LARGE); + if (thumbnail != null) { + thumbnailWrapper.setImageThumbnail(thumbnail); + } + return null; + } + + @Override + protected void done() { + imageThumbnailViewer.repaint(); + } + + } + + /** + * Cell renderer for the instances list. + */ + private class InstancesCellRenderer extends DefaultListCellRenderer { + + private static final long serialVersionUID = 1L; + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + String name = ""; + if (value instanceof AbstractFile) { + AbstractFile file = (AbstractFile) value; + try { + name = file.getUniquePath(); + } catch (TskCoreException ingored) { + name = file.getParentPath() + "/" + file.getName(); + } + + } + setText(name); + return this; + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/SearchResults.java b/Core/src/org/sleuthkit/autopsy/filequery/SearchResults.java new file mode 100644 index 0000000000..7cc9b8a596 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/SearchResults.java @@ -0,0 +1,172 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.filequery; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.sleuthkit.autopsy.filequery.FileSearch.GroupKey; + +/** + * Class to hold the results of the filtering/grouping/sorting operations + */ +class SearchResults { + + private final FileGroup.GroupSortingAlgorithm groupSortingType; + private final FileSearch.AttributeType attrType; + private final FileSorter fileSorter; + + private final Map groupMap = new HashMap<>(); + private List groupList = new ArrayList<>(); + + private static final long MAX_OUTPUT_FILES = 2000; // For debug UI - maximum number of lines to print + + /** + * Create an empty SearchResults object + * + * @param groupSortingType The method that should be used to + * sortGroupsAndFiles the groups + * @param attrType The attribute type to use for grouping + * @param fileSortingMethod The method that should be used to + * sortGroupsAndFiles the files in each group + */ + SearchResults(FileGroup.GroupSortingAlgorithm groupSortingType, FileSearch.AttributeType attrType, + FileSorter.SortingMethod fileSortingMethod) { + this.groupSortingType = groupSortingType; + this.attrType = attrType; + this.fileSorter = new FileSorter(fileSortingMethod); + } + + /** + * Create an dummy SearchResults object that can be used in the UI before + * the search is finished. + */ + SearchResults() { + this.groupSortingType = FileGroup.GroupSortingAlgorithm.BY_GROUP_NAME; + this.attrType = new FileSearch.FileSizeAttribute(); + this.fileSorter = new FileSorter(FileSorter.SortingMethod.BY_FILE_NAME); + } + + /** + * Add a list of ResultFile to the results + * + * @param files the ResultFiles + */ + void add(List files) { + for (ResultFile file : files) { + // Add the file to the appropriate group, creating it if necessary + FileSearch.GroupKey groupKey = attrType.getGroupKey(file); + + if (!groupMap.containsKey(groupKey)) { + groupMap.put(groupKey, new FileGroup(groupSortingType, groupKey)); + } + groupMap.get(groupKey).addFile(file); + } + } + + /** + * Run after all files have been added to sortGroupsAndFiles the groups and + * files. + */ + void sortGroupsAndFiles() { + + // First sortGroupsAndFiles the files + for (FileGroup group : groupMap.values()) { + group.sortFiles(fileSorter); + } + + // Now put the groups in a list and sortGroupsAndFiles them + groupList = new ArrayList<>(groupMap.values()); + Collections.sort(groupList); + } + + @Override + public String toString() { + String result = ""; + if (groupList == null) { + return result; + } + + long count = 0; + for (FileGroup group : groupList) { + result += group.getDisplayName() + "\n"; + + for (ResultFile file : group.getFiles()) { + result += " " + file.toString() + "\n"; + count++; + if (count > MAX_OUTPUT_FILES) { + result += "(truncated)"; + return result; + } + } + } + return result; + } + + /** + * Get the names of the groups with counts + * + * @return the group names + */ + List getGroupNamesWithCounts() { + return groupList.stream().map(p -> p.getDisplayName() + " (" + p.getFiles().size() + ")").collect(Collectors.toList()); + } + + /** + * Get the result files for the selected group + * + * @param groupName The name of the group. Can have the size appended. + * + * @return the list of result files + */ + List getResultFilesInGroup(String groupName) { + if (groupName != null) { + final String modifiedGroupName = groupName.replaceAll(" \\([0-9]+\\)$", ""); + + java.util.Optional fileGroup = groupList.stream().filter(p -> p.getDisplayName().equals(modifiedGroupName)).findFirst(); + if (fileGroup.isPresent()) { + return fileGroup.get().getFiles(); + } + } + return new ArrayList<>(); + } + + /** + * Transform the results into a LinkedHashMap with result files. + * + * @return the grouped and sorted results + */ + Map> toLinkedHashMap() throws FileSearchException { + Map> map = new LinkedHashMap<>(); + + // Sort the groups and files + sortGroupsAndFiles(); + + // groupList is sorted and a LinkedHashMap will preserve that order. + for (FileGroup group : groupList) { + map.put(group.getGroupKey(), group.getFiles()); + } + + return map; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/SearchWorker.java b/Core/src/org/sleuthkit/autopsy/filequery/SearchWorker.java new file mode 100644 index 0000000000..279a470f3a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/SearchWorker.java @@ -0,0 +1,86 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import java.util.LinkedHashMap; +import javax.swing.SwingWorker; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.filequery.FileSearch.GroupKey; + +/** + * SwingWorker to perform search on a background thread + */ +final class SearchWorker extends SwingWorker { + + private final static Logger logger = Logger.getLogger(SearchWorker.class.getName()); + private static final String USER_NAME_PROPERTY = "user.name"; //NON-NLS + private final List filters; + private final FileSearch.AttributeType groupingAttr; + private final FileSorter.SortingMethod fileSort; + private final FileGroup.GroupSortingAlgorithm groupSortAlgorithm; + private final EamDb centralRepoDb; + private final Map results = new LinkedHashMap<>(); + + /** + * Create a SwingWorker which performs a search + * + * @param centralRepo the central repository being used for the search + * @param searchfilters the FileFilters to use for the search + * @param groupingAttribute the AttributeType to group by + * @param groupSort the Algorithm to sort groups by + * @param fileSortMethod the SortingMethod to use for files + */ + SearchWorker(EamDb centralRepo, List searchfilters, FileSearch.AttributeType groupingAttribute, FileGroup.GroupSortingAlgorithm groupSort, FileSorter.SortingMethod fileSortMethod) { + centralRepoDb = centralRepo; + filters = searchfilters; + groupingAttr = groupingAttribute; + groupSortAlgorithm = groupSort; + fileSort = fileSortMethod; + } + + @Override + protected Void doInBackground() throws Exception { + try { + // Run the search + results.putAll(FileSearch.getGroupSizes(System.getProperty(USER_NAME_PROPERTY), filters, + groupingAttr, + groupSortAlgorithm, + fileSort, + Case.getCurrentCase().getSleuthkitCase(), centralRepoDb)); + } catch (FileSearchException ex) { + logger.log(Level.SEVERE, "Error running file search test", ex); + cancel(true); + } + return null; + } + + @Override + protected void done() { + if (isCancelled()) { + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.SearchCancelledEvent()); + } else { + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.SearchCompleteEvent(results, filters, groupingAttr, groupSortAlgorithm, fileSort)); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.form new file mode 100644 index 0000000000..86a30442f6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.form @@ -0,0 +1,105 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.java new file mode 100644 index 0000000000..72296f7ce8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.java @@ -0,0 +1,280 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Image; +import java.awt.GridBagConstraints; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.util.concurrent.TimeUnit; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; +import org.openide.util.ImageUtilities; +import org.openide.util.NbBundle.Messages; + +/** + * Class which displays thumbnails and information for a video file. + */ +final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRenderer { + + private static final int GAP_SIZE = 4; + private static final Color SELECTION_COLOR = new Color(0, 120, 215); + private static final int BYTE_UNIT_CONVERSION = 1000; + private static final int ICON_SIZE = 16; + private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png"; + private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png"; + private static final String DELETE_ICON_PATH = "/org/sleuthkit/autopsy/images/file-icon-deleted.png"; + private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false)); + private static final ImageIcon NOTABLE_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false)); + private static final ImageIcon DELETED_ICON = new ImageIcon(ImageUtilities.loadImage(DELETE_ICON_PATH, false)); + private static final long serialVersionUID = 1L; + + /** + * Creates new form VideoThumbnailPanel + */ + VideoThumbnailPanel() { + initComponents(); + this.setFocusable(true); + } + + /** + * Add the thumbnails to the panel. + * + * @param thumbnailWrapper + */ + private void addThumbnails(VideoThumbnailsWrapper thumbnailWrapper) { + imagePanel.removeAll(); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + imagePanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(GAP_SIZE, 0), new java.awt.Dimension(GAP_SIZE, 0), new java.awt.Dimension(GAP_SIZE, 32767)), gridBagConstraints); + gridBagConstraints.gridy = 1; + imagePanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(GAP_SIZE, 0), new java.awt.Dimension(GAP_SIZE, 0), new java.awt.Dimension(GAP_SIZE, 32767)), gridBagConstraints); + gridBagConstraints.gridx++; + int timeIndex = 0; + int[] timeStamps = thumbnailWrapper.getTimeStamps(); + for (Image image : thumbnailWrapper.getThumbnails()) { + gridBagConstraints.gridy = 0; + imagePanel.add(new JLabel(new ImageIcon(image)), gridBagConstraints); + gridBagConstraints.gridy = 1; + long millis = timeStamps[timeIndex]; + long hours = TimeUnit.MILLISECONDS.toHours(millis); + millis -= TimeUnit.HOURS.toMillis(hours); + long minutes = TimeUnit.MILLISECONDS.toMinutes(millis); + millis -= TimeUnit.MINUTES.toMillis(minutes); + long seconds = TimeUnit.MILLISECONDS.toSeconds(millis); + imagePanel.add(new JLabel(String.format("%01d:%02d:%02d", hours, minutes, seconds)), gridBagConstraints); + gridBagConstraints.gridx++; + gridBagConstraints.gridy = 0; + imagePanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(GAP_SIZE, 0), new java.awt.Dimension(GAP_SIZE, 0), new java.awt.Dimension(GAP_SIZE, 32767)), gridBagConstraints); + gridBagConstraints.gridy = 1; + imagePanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(GAP_SIZE, 0), new java.awt.Dimension(GAP_SIZE, 0), new java.awt.Dimension(GAP_SIZE, 32767)), gridBagConstraints); + gridBagConstraints.gridx++; + timeIndex++; + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + imagePanel = new javax.swing.JPanel(); + fileSizeLabel = new javax.swing.JLabel(); + countLabel = new javax.swing.JLabel(); + scoreLabel = new javax.swing.JLabel(); + deletedLabel = new javax.swing.JLabel(); + + setBorder(javax.swing.BorderFactory.createEtchedBorder()); + + imagePanel.setLayout(new java.awt.GridBagLayout()); + + scoreLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/red-circle-exclamation.png"))); // NOI18N + scoreLabel.setMaximumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + scoreLabel.setMinimumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + scoreLabel.setPreferredSize(new Dimension(ICON_SIZE,ICON_SIZE)); + + deletedLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/file-icon-deleted.png"))); // NOI18N + deletedLabel.setMaximumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + deletedLabel.setMinimumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + deletedLabel.setPreferredSize(new Dimension(ICON_SIZE,ICON_SIZE)); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(imagePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 776, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(fileSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 248, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(countLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 124, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(imagePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(fileSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(deletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(countLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel countLabel; + private javax.swing.JLabel deletedLabel; + private javax.swing.JLabel fileSizeLabel; + private javax.swing.JPanel imagePanel; + private javax.swing.JLabel scoreLabel; + // End of variables declaration//GEN-END:variables + + @Messages({ + "# {0} - numberOfInstances", + "VideoThumbnailPanel.countLabel.text=Number of Instances: {0}", + "VideoThumbnailPanel.deleted.text=All instances of file are deleted."}) + @Override + public Component getListCellRendererComponent(JList list, VideoThumbnailsWrapper value, int index, boolean isSelected, boolean cellHasFocus) { + fileSizeLabel.setText(getFileSizeString(value.getResultFile().getFirstInstance().getSize())); + countLabel.setText(Bundle.VideoThumbnailPanel_countLabel_text(value.getResultFile().getAllInstances().size())); + addThumbnails(value); + imagePanel.setBackground(isSelected ? SELECTION_COLOR : list.getBackground()); + if (value.getResultFile().isDeleted()) { + deletedLabel.setIcon(DELETED_ICON); + deletedLabel.setToolTipText(Bundle.VideoThumbnailPanel_deleted_text()); + } else { + deletedLabel.setIcon(null); + deletedLabel.setToolTipText(""); + } + switch (value.getResultFile().getScore()) { + case NOTABLE_SCORE: + scoreLabel.setIcon(NOTABLE_SCORE_ICON); + break; + case INTERESTING_SCORE: + scoreLabel.setIcon(INTERESTING_SCORE_ICON); + break; + case NO_SCORE: // empty case - this is interpreted as an intentional fall-through + default: + scoreLabel.setIcon(null); + break; + } + scoreLabel.setToolTipText(value.getResultFile().getScoreDescription()); + setBackground(isSelected ? SELECTION_COLOR : list.getBackground()); + return this; + } + + @Messages({"# {0} - fileSize", + "# {1} - units", + "VideoThumbnailPanel.sizeLabel.text=Size: {0} {1}", + "VideoThumbnailPanel.bytes.text=bytes", + "VideoThumbnailPanel.kiloBytes.text=KB", + "VideoThumbnailPanel.megaBytes.text=MB", + "VideoThumbnailPanel.gigaBytes.text=GB", + "VideoThumbnailPanel.terraBytes.text=TB"}) + /** + * Convert a size in bytes to a string with representing the size in the + * largest units which represent the value as being greater than or equal to + * one. Result will be rounded down to the nearest whole number of those + * units. + * + * @param bytes Size in bytes. + */ + private String getFileSizeString(long bytes) { + long size = bytes; + int unitsSwitchValue = 0; + while (size > BYTE_UNIT_CONVERSION && unitsSwitchValue < 4) { + size /= BYTE_UNIT_CONVERSION; + unitsSwitchValue++; + } + String units; + switch (unitsSwitchValue) { + case 1: + units = Bundle.VideoThumbnailPanel_kiloBytes_text(); + break; + case 2: + units = Bundle.VideoThumbnailPanel_megaBytes_text(); + break; + case 3: + units = Bundle.VideoThumbnailPanel_gigaBytes_text(); + break; + case 4: + units = Bundle.VideoThumbnailPanel_terraBytes_text(); + break; + default: + units = Bundle.VideoThumbnailPanel_bytes_text(); + break; + } + return Bundle.VideoThumbnailPanel_sizeLabel_text(size, units); + } + + @Override + public String getToolTipText(MouseEvent event) { + if (event != null) { + //gets tooltip of internal panel item mouse is over + Point point = event.getPoint(); + for (Component comp : getComponents()) { + if (isPointOnIcon(comp, point)) { + String toolTip = ((JComponent) comp).getToolTipText(); + if (toolTip == null || toolTip.isEmpty()) { + return null; + } else { + return toolTip; + } + } + } + } + return null; + } + + /** + * Helper method to see if point is on the icon. + * + * @param comp The component to check if the cursor is over the icon of + * @param point The point the cursor is at. + * + * @return True if the point is over the icon, false otherwise. + */ + private boolean isPointOnIcon(Component comp, Point point) { + return comp instanceof JComponent && point.x >= comp.getX() && point.x <= comp.getX() + ICON_SIZE && point.y >= comp.getY() && point.y <= comp.getY() + ICON_SIZE; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.form b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.form new file mode 100644 index 0000000000..931f7f1bd1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.form @@ -0,0 +1,48 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.java b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.java new file mode 100644 index 0000000000..a1dc742eab --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.java @@ -0,0 +1,119 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import java.util.ArrayList; +import java.util.List; +import javax.swing.DefaultListModel; +import javax.swing.event.ListSelectionListener; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * A JPanel to display video thumbnails. + * + */ +public class VideoThumbnailViewer extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + private final DefaultListModel thumbnailListModel = new DefaultListModel<>(); + + /** + * Creates new form VideoThumbnailViewer + */ + public VideoThumbnailViewer() { + initComponents(); + } + + /** + * Add a selection listener to the list of thumbnails being displayed. + * + * @param listener The ListSelectionListener to add to the selection model. + */ + void addListSelectionListener(ListSelectionListener listener) { + thumbnailList.getSelectionModel().addListSelectionListener(listener); + } + + /** + * Get the list of AbstractFiles which are represented by the selected Video + * thumbnails. + * + * @return The list of AbstractFiles which are represented by the selected + * Video thumbnails. + */ + List getInstancesForSelected() { + synchronized (this) { + if (thumbnailList.getSelectedIndex() == -1) { + return new ArrayList<>(); + } else { + return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances(); + } + } + } + + /** + * Clear the list of thumbnails being displayed. + */ + void clearViewer() { + synchronized (this) { + thumbnailListModel.removeAllElements(); + thumbnailListScrollPane.getVerticalScrollBar().setValue(0); + } + } + + /** + * Add thumbnails for a video to the panel. + * + * @param thumbnailWrapper The object which contains the thumbnails which + * will be displayed. + */ + void addVideo(VideoThumbnailsWrapper thumbnailWrapper) { + synchronized (this) { + thumbnailListModel.addElement(thumbnailWrapper); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + thumbnailListScrollPane = new javax.swing.JScrollPane(); + thumbnailList = new javax.swing.JList<>(); + + setLayout(new java.awt.BorderLayout()); + + thumbnailList.setModel(thumbnailListModel); + thumbnailList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + thumbnailList.setCellRenderer(new VideoThumbnailPanel()); + thumbnailListScrollPane.setViewportView(thumbnailList); + + add(thumbnailListScrollPane, java.awt.BorderLayout.CENTER); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JList thumbnailList; + private javax.swing.JScrollPane thumbnailListScrollPane; + // End of variables declaration//GEN-END:variables + +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailsWrapper.java b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailsWrapper.java new file mode 100644 index 0000000000..08c68611c0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailsWrapper.java @@ -0,0 +1,92 @@ +/* + * Autopsy + * + * Copyright 2019 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.filequery; + +import java.awt.Image; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Class to wrap all the information necessary for video thumbnails to be + * displayed. + */ +final class VideoThumbnailsWrapper { + + private final List thumbnails; + private final ResultFile resultFile; + private int[] timeStamps; + + /** + * Construct a new VideoThumbnailsWrapper. + * + * @param file The ResultFile which represents the video file which the + * thumbnails were created for. + */ + VideoThumbnailsWrapper(ResultFile file) { + this.thumbnails = new ArrayList<>(); + this.timeStamps = new int[4]; + this.resultFile = file; + } + + /** + * Get the ResultFile which represents the video file which the thumbnails + * were created for. + * + * @return The ResultFile which represents the video file which the + * thumbnails were created for. + */ + ResultFile getResultFile() { + return resultFile; + } + + /** + * Get the array containing thumbnail timestamps. Each timestamp is stored + * as the number of milliseconds into the video each thumbnail was created + * at. + * + * @return The array of timestamps in milliseconds from start of video. + */ + int[] getTimeStamps() { + return timeStamps.clone(); + } + + /** + * Get the list of thumbnails for the video. + * + * @return The list of Images which are the thumbnails for the video. + */ + List getThumbnails() { + return Collections.unmodifiableList(thumbnails); + } + + /** + * Set the thumbnails and their associated time stamps. + * + * @param videoThumbnails The list of Images which are the thumbnails for + * the video. + * @param framePositions An array containing the time in milliseconds into + * the video that each thumbnail created for. + */ + void setThumbnails(List videoThumbnails, int[] framePositions) { + this.thumbnails.addAll(videoThumbnails); + this.timeStamps = framePositions.clone(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties index bb89603859..f0dace8a8a 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties @@ -45,7 +45,6 @@ FileSearchPanel.search.validationErr.msg=Validation Error: {0} FileSearchPanel.emptyWhereClause.text=Invalid options, nothing to show. KnownStatusSearchFilter.noneSelectedMsg.text=At least one known status must be selected\! NameSearchFilter.emptyNameMsg.text=Must enter something for name search. -SearchNode.getName.text=Search Result SizeSearchPanel.sizeCompareComboBox.equalTo=equal to SizeSearchPanel.sizeCompareComboBox.greaterThan=greater than SizeSearchPanel.sizeCompareComboBox.lessThan=less than diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED index 08cc69c39c..c585d0edf5 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED @@ -14,7 +14,7 @@ KnownStatusSearchPanel.knownCheckBox.text=Known Status: KnownStatusSearchPanel.knownBadOptionCheckBox.text=Notable KnownStatusSearchPanel.knownOptionCheckBox.text=Known (NSRL or other) KnownStatusSearchPanel.unknownOptionCheckBox.text=Unknown -DateSearchFilter.noneSelectedMsg.text=At least one date type must be selected\! +DateSearchFilter.noneSelectedMsg.text=At least one date type must be selected! DateSearchPanel.dateCheckBox.text=Date: DateSearchPanel.jLabel4.text=Timezone: DateSearchPanel.jLabel3.text=*The date format is mm/dd/yyyy @@ -27,6 +27,7 @@ DateSearchPanel.jLabel1.text=to NameSearchPanel.nameCheckBox.text=Name: NameSearchPanel.noteNameLabel.text=*Note: Name match is case insensitive and matches any part of the file name. Regular expressions are not currently supported. NameSearchPanel.searchTextField.text= +SearchNode.getName.text=Search Result SizeSearchFilter.errorMessage.nonNegativeNumber=Input size data is a negative number. SizeSearchFilter.errorMessage.notANumber=Input size data is not a number. SizeSearchPanel.sizeCheckBox.text=Size: @@ -56,9 +57,8 @@ FileSearchPanel.search.results.details=Large number of matches may impact perfor FileSearchPanel.search.exception.noFilterSelected.msg=At least one filter must be selected. FileSearchPanel.search.validationErr.msg=Validation Error: {0} FileSearchPanel.emptyWhereClause.text=Invalid options, nothing to show. -KnownStatusSearchFilter.noneSelectedMsg.text=At least one known status must be selected\! +KnownStatusSearchFilter.noneSelectedMsg.text=At least one known status must be selected! NameSearchFilter.emptyNameMsg.text=Must enter something for name search. -SearchNode.getName.text=Search Result SizeSearchPanel.sizeCompareComboBox.equalTo=equal to SizeSearchPanel.sizeCompareComboBox.greaterThan=greater than SizeSearchPanel.sizeCompareComboBox.lessThan=less than diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle_ja.properties index 401f521568..9da6f45334 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle_ja.properties @@ -1,50 +1,74 @@ -OpenIDE-Module-Name=\u30d5\u30a1\u30a4\u30eb\u691c\u7d22 -KnownStatusSearchPanel.knownCheckBox.text=\u65e2\u77e5\u30b9\u30c6\u30fc\u30bf\u30b9\uff1a -KnownStatusSearchPanel.knownBadOptionCheckBox.text=\u65e2\u77e5\u306e\u60aa\u8cea -KnownStatusSearchPanel.knownOptionCheckBox.text=\u65e2\u77e5\uff08NSRL\u307e\u305f\u306f\u305d\u306e\u4ed6\uff09 +DataSourceFilter.errorMessage.emptyDataSource=\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u9078\u629e\u3057\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +DateSearchFilter.errorMessage.endDateBeforeStartDate=\u7d42\u4e86\u65e5\u306f\u958b\u59cb\u65e5\u3088\u308a\u5f8c\u306e\u65e5\u4ed8\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +DataSourceFilter.errorMessage.emptyDataSource=\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u30c7\u30fc\u30bf\u30bf\u30a4\u30d7\u306e\u30c1\u30a7\u30c3\u30af\u30dc\u30c3\u30af\u30b9\u3092\u9078\u629e\u3057\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +FileSearchPanel.emptyNode.display.text=\u8a72\u5f53\u3059\u308b\u7d50\u679c\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +HashSearchFilter.errorMessage.emptyHash=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u304c\u7a7a(\u672a\u5165\u529b)\u3067\u3059\u3002 +HashSearchFilter.errorMessage.wrongCharacter=MD5\u306b\u7121\u52b9\u306a16\u9032\u6570\u6587\u5b57\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002 +# {0} - \u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u9577 +HashSearchFilter.errorMessage.wrongLength=\u5165\u529b\u306e\u9577\u3055({0}) \u304cMD5\u306e\u9577\u3055(32)\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093\u3002 +KnownStatusSearchFilter.errorMessage.noKnownStatusCheckboxSelected=\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u65e2\u77e5\u306e\u30b9\u30c6\u30fc\u30bf\u30b9\u30c1\u30a7\u30c3\u30af\u30dc\u30c3\u30af\u30b9\u3092\u9078\u629e\u3057\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +MimeTypeFilter.errorMessage.emptyMimeType=\u5c11\u306a\u304f\u3068\u30821\u3064\u306eMIME\u30bf\u30a4\u30d7\u3092\u9078\u629e\u3057\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +NameSearchFilter.errorMessage.emtpyName=\u691c\u7d22\u3059\u308b\u540d\u524d\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +OpenIDE-Module-Name=FileSearch +KnownStatusSearchPanel.knownCheckBox.text=\u65e2\u77e5\u306e\u30b9\u30c6\u30fc\u30bf\u30b9: +KnownStatusSearchPanel.knownBadOptionCheckBox.text=\u9855\u8457 +KnownStatusSearchPanel.knownOptionCheckBox.text=\u65e2\u77e5(NSRL\u307e\u305f\u306f\u305d\u306e\u4ed6) KnownStatusSearchPanel.unknownOptionCheckBox.text=\u4e0d\u660e -DateSearchFilter.noneSelectedMsg.text=\u6700\u4f4e\u4e00\u3064\u306e\u30c7\u30fc\u30bf\u30bf\u30a4\u30d7\u3092\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\uff01 -DateSearchPanel.dateCheckBox.text=\u65e5\u4ed8\uff1a -DateSearchPanel.jLabel4.text=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\uff1a -DateSearchPanel.jLabel3.text=*\u65e5\u4ed8\u306e\u5f62\u5f0f\u306fmm/dd/yyyy -DateSearchPanel.jLabel2.text=*\u7a7a\u767d\u306e\u9805\u76ee\u306f\u300c\u5236\u9650\u306a\u3057\u300d\u3068\u3044\u3046\u610f\u5473\u3067\u3059 +DateSearchFilter.noneSelectedMsg.text=\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u30c7\u30fc\u30bf\u30bf\u30a4\u30d7\u3092\u9078\u629e\u3057\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093! +DateSearchPanel.dateCheckBox.text=\u65e5\u4ed8: +DateSearchPanel.jLabel4.text=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3: +DateSearchPanel.jLabel3.text=*\u65e5\u4ed8\u5f62\u5f0f\u306fmm/dd/yyyy\u3067\u3059 +DateSearchPanel.jLabel2.text=*\u7a7a(\u672a\u5165\u529b)\u306e\u30d5\u30a3\u30fc\u30c9\u306f\u300c\u5236\u9650\u306a\u3057\u300d\u3092\u610f\u5473\u3057\u307e\u3059 DateSearchPanel.createdCheckBox.text=\u4f5c\u6210\u6e08\u307f DateSearchPanel.accessedCheckBox.text=\u30a2\u30af\u30bb\u30b9\u6e08\u307f DateSearchPanel.changedCheckBox.text=\u5909\u66f4\u6e08\u307f DateSearchPanel.modifiedCheckBox.text=\u4fee\u6b63\u6e08\u307f -DateSearchPanel.jLabel1.text=to -NameSearchPanel.nameCheckBox.text=\u540d\u524d\uff1a -NameSearchPanel.noteNameLabel.text=*\u6ce8\u610f\uff1a\u540d\u524d\u30de\u30c3\u30c1\u306f\u5927\u6587\u5b57\u3068\u5c0f\u6587\u5b57\u3092\u533a\u5225\u3057\u307e\u3059\u3002\u307e\u305f\u3001
\u30d5\u30a1\u30a4\u30eb\u540d\u306e\u3044\u304b\u306a\u308b\u90e8\u5206\u3082\u30de\u30c3\u30c1\u3057\u307e\u3059\u3002\u6b63\u898f\u8868\u73fe\u306f
\u73fe\u5728\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 -SizeSearchPanel.sizeCheckBox.text=\u30b5\u30a4\u30ba\uff1a -NameSearchPanel.cutMenuItem.text=\u30ab\u30c3\u30c8 +DateSearchPanel.jLabel1.text=\u5b9b\u5148 +NameSearchPanel.nameCheckBox.text=\u540d\u524d: +NameSearchPanel.noteNameLabel.text=*\u6ce8: \u540d\u524d\u7167\u5408\u306f\u5927\u6587\u5b57\u5c0f\u6587\u5b57\u3092\u533a\u5225\u305b\u305a\u306b\u3001\u30d5\u30a1\u30a4\u30eb\u540d\u306e\u4e00\u90e8\u3068\u7167\u5408\u3057\u307e\u3059\u3002\u6b63\u898f\u8868\u73fe\u306f\u73fe\u5728\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +NameSearchPanel.searchTextField.text= +SizeSearchFilter.errorMessage.nonNegativeNumber=\u5165\u529b\u30b5\u30a4\u30ba\u30c7\u30fc\u30bf\u306f\u8ca0\u306e\u6570\u3067\u3059\u3002 +SizeSearchFilter.errorMessage.notANumber=\u5165\u529b\u30b5\u30a4\u30ba\u30c7\u30fc\u30bf\u306f\u6570\u5b57\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +SizeSearchPanel.sizeCheckBox.text=\u30b5\u30a4\u30ba: +NameSearchPanel.cutMenuItem.text=\u5207\u308a\u53d6\u308a NameSearchPanel.copyMenuItem.text=\u30b3\u30d4\u30fc NameSearchPanel.pasteMenuItem.text=\u8cbc\u308a\u4ed8\u3051 NameSearchPanel.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e SizeSearchPanel.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e SizeSearchPanel.pasteMenuItem.text=\u8cbc\u308a\u4ed8\u3051 SizeSearchPanel.copyMenuItem.text=\u30b3\u30d4\u30fc -SizeSearchPanel.cutMenuItem.text=\u30ab\u30c3\u30c8 -DateSearchPanel.cutMenuItem.text=\u30ab\u30c3\u30c8 +SizeSearchPanel.cutMenuItem.text=\u5207\u308a\u53d6\u308a +DateSearchPanel.cutMenuItem.text=\u5207\u308a\u53d6\u308a DateSearchPanel.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e DateSearchPanel.pasteMenuItem.text=\u8cbc\u308a\u4ed8\u3051 DateSearchPanel.copyMenuItem.text=\u30b3\u30d4\u30fc -FileSearchAction.getName.text=\u5c5e\u6027\u306b\u3088\u308b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22 -FileSearchDialog.frame.title=\u5c5e\u6027\u306b\u3088\u308b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22 -FileSearchDialog.frame.msg=\u5c5e\u6027\u306b\u3088\u308b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22 -FileSearchPanel.custComp.label.text=\u6b21\u306e\u6761\u4ef6\u306b\u4e00\u81f4\u3059\u308b\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22\uff1a +FileSearchAction.getName.text=\u5c5e\u6027\u5225\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 +FileSearchDialog.frame.title=\u5c5e\u6027\u5225\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 +FileSearchDialog.frame.msg=\u5c5e\u6027\u5225\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 +FileSearchPanel.custComp.label.text=\u6b21\u306e\u6761\u4ef6\u306b\u4e00\u81f4\u3059\u308b\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22: FileSearchPanel.filterTitle.name=\u540d\u524d FileSearchPanel.filterTitle.metadata=\u30e1\u30bf\u30c7\u30fc\u30bf -FileSearchPanel.filterTitle.knownStatus=\u65e2\u77e5\u30b9\u30c6\u30fc\u30bf\u30b9 -FileSearchPanel.search.results.title=\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u7d50\u679c{0} -FileSearchPanel.search.results.pathText=\u30d5\u30a1\u30a4\u30eb\u540d\u691c\u7d22\u7d50\u679c\uff1a -FileSearchPanel.search.results.msg=\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\uff1a{0}\u500b\u306e\u30de\u30c3\u30c1\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f -FileSearchPanel.search.results.details=\u591a\u304f\u306e\u30de\u30c3\u30c1\u304c\u3042\u308b\u5834\u5408\u3001\u4e00\u90e8\u306e\u51e6\u7406\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306b\u5f71\u97ff\u3092\u4e0e\u3048\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093 -FileSearchPanel.search.exception.noFilterSelected.msg=\u6700\u4f4e\uff11\u500b\u306e\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 -FileSearchPanel.search.validationErr.msg=\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u30a8\u30e9\u30fc\uff1a{0} -FileSearchPanel.emptyWhereClause.text=\u7121\u52b9\u306a\u30aa\u30d7\u30b7\u30e7\u30f3\u3067\u3059\u3002\u8868\u793a\u3059\u308b\u3082\u306e\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -KnownStatusSearchFilter.noneSelectedMsg.text=\u6700\u4f4e\uff11\u500b\u306e\u65e2\u77e5\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\uff01 -NameSearchFilter.emptyNameMsg.text=\u540d\u524d\u691c\u7d22\u306b\u4f55\u304b\u8a18\u5165\u3057\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002 +FileSearchPanel.filterTitle.knownStatus=\u65e2\u77e5\u306e\u30b9\u30c6\u30fc\u30bf\u30b9 +FileSearchPanel.search.results.title=\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u7d50\u679c {0} +FileSearchPanel.search.results.pathText=\u30d5\u30a1\u30a4\u30eb\u540d\u691c\u7d22\u7d50\u679c: +FileSearchPanel.search.results.msg=\u30d5\u30a1\u30a4\u30eb\u691c\u7d22: \u4e00\u81f4\u3059\u308b\u7d50\u679c\u304c {0} \u4ef6\u898b\u3064\u304b\u308a\u307e\u3057\u305f +FileSearchPanel.search.results.details=\u4e00\u81f4\u3059\u308b\u7d50\u679c\u6570\u304c\u591a\u3044\u3068\u3001\u4e00\u90e8\u306e\u64cd\u4f5c\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306b\u5f71\u97ff\u3059\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 +FileSearchPanel.search.exception.noFilterSelected.msg=\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u9078\u629e\u3057\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +FileSearchPanel.search.validationErr.msg=\u691c\u8a3c\u30a8\u30e9\u30fc: {0} +FileSearchPanel.emptyWhereClause.text=\u7121\u52b9\u306a\u30aa\u30d7\u30b7\u30e7\u30f3\u3067\u3059\u3002\u8868\u793a\u3059\u308b\u3082\u306e\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +KnownStatusSearchFilter.noneSelectedMsg.text=\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u65e2\u77e5\u306e\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u9078\u629e\u3057\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093! +NameSearchFilter.emptyNameMsg.text=\u540d\u524d\u691c\u7d22\u306b\u4f55\u304b\u5165\u529b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 SearchNode.getName.text=\u691c\u7d22\u7d50\u679c -SizeSearchPanel.sizeCompareComboBox.equalTo=\u4e0b\u8a18\u3068\u7b49\u3057\u3044 -SizeSearchPanel.sizeCompareComboBox.greaterThan=\u4e0b\u8a18\u3088\u308a\u5927\u304d\u3044 -SizeSearchPanel.sizeCompareComboBox.lessThan=\u4e0b\u8a18\u3088\u308a\u5c0f\u3055\u3044 +SizeSearchPanel.sizeCompareComboBox.equalTo=\u76f8\u5f53 +SizeSearchPanel.sizeCompareComboBox.greaterThan=\u4ee5\u4e0a +SizeSearchPanel.sizeCompareComboBox.lessThan=\u4ee5\u4e0b +MimeTypePanel.jLabel1.text=*\u6ce8: \u8907\u6570\u306eMIME\u30bf\u30a4\u30d7\u3092\u9078\u629e\u3067\u304d\u307e\u3059 +FileSearchPanel.searchButton.text=\u691c\u7d22 +MimeTypePanel.mimeTypeCheckBox.text=MIME\u30bf\u30a4\u30d7: +HashSearchPanel.md5CheckBox.text=MD5: +HashSearchPanel.emptyHashMsg.text=\u30cf\u30c3\u30b7\u30e5\u691c\u7d22\u306b\u4f55\u304b\u5165\u529b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +FileSearchPanel.errorLabel.text=\ +DataSourcePanel.dataSourceCheckBox.label=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9: +DataSourcePanel.dataSourceCheckBox.actionCommand=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9: +DataSourcePanel.dataSourceCheckBox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9: +DataSourcePanel.dataSourceNoteLabel.text=*\u6ce8: \u8907\u6570\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u9078\u629e\u3067\u304d\u307e\u3059 diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java index 06d2ef0b47..4801227416 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -67,7 +67,7 @@ class FileSearchPanel extends javax.swing.JPanel { /** * Creates new form FileSearchPanel */ - public FileSearchPanel() { + FileSearchPanel() { initComponents(); customizeComponents(); diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/SearchChildren.java b/Core/src/org/sleuthkit/autopsy/filesearch/SearchChildren.java index 9e31177826..123519c233 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/SearchChildren.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/SearchChildren.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,9 +21,7 @@ package org.sleuthkit.autopsy.filesearch; import java.util.List; import org.openide.nodes.Children; import org.openide.nodes.Node; -import org.sleuthkit.datamodel.Directory; import org.sleuthkit.autopsy.datamodel.DirectoryNode; -import org.sleuthkit.datamodel.File; import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/SearchNode.java b/Core/src/org/sleuthkit/autopsy/filesearch/SearchNode.java index 70007c7dd9..2cd8d7fc5a 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/SearchNode.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/SearchNode.java @@ -29,13 +29,11 @@ import org.sleuthkit.datamodel.AbstractFile; */ class SearchNode extends AbstractNode { - private SearchChildren children; - SearchNode(List keys) { super(new SearchChildren(true, keys)); - this.children = (SearchChildren) this.getChildren(); } + @NbBundle.Messages({"SearchNode.getName.text=Search Result"}) @Override public String getName() { return NbBundle.getMessage(this.getClass(), "SearchNode.getName.text"); diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties new file mode 100755 index 0000000000..e21db15ee9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties @@ -0,0 +1,38 @@ +CTL_OpenGeolocation=Geolocation +CTL_GeolocationTopComponentAction=GeolocationTopComponent +CTL_GeolocationTopComponent=Geolocation +RefreshPanel.refreshLabel.text=The geolocation data has been updated, the visualization may be out of date. +RefreshPanel.refreshButton.text=Refresh View +RefreshPanel.closeButton.text= +WaypointDetailPanel.closeButton.text= +WaypointDetailPanel.imageLabel.text= +GeoFilterPanel.waypointSettings.border.title= +GeoFilterPanel.allButton.text=Show All +GeoFilterPanel.mostRecentButton.text=Show only last +GeoFilterPanel.applyButton.text=Apply +GeoFilterPanel.showWaypointsWOTSCheckBox.text=Include waypoints with no time stamps +GeoFilterPanel.daysLabel.text=days of activity +CheckBoxListPanel.titleLabel.text=jLabel1 +CheckBoxListPanel.checkButton.text=Check All +CheckBoxListPanel.uncheckButton.text=Uncheck All +GeoFilterPanel.optionsLabel.text=Waypoints +OptionsCategory_Name_Geolocation=Geolocation +OptionsCategory_Keywords_Geolocation=Geolocation Settings +GeolocationSettingsPanel.tilePane.border.title=Map Tile Data Source +OptionsCategory_Keywords_Geolocation=Geolocation +GeolocationSettingsPanel.mbtileFileField.toolTipText= +GeolocationSettingsPanel.mbtileFileField.text= +GeolocationSettingsPanel.defaultDataSource.text=Default online tile server (bing.com/maps) +GeolocationSettingsPanel.osmServerRBnt.text=OpenStreetMap server +GeolocationSettingsPanel.zipFileRBnt.text=OpenStreetMap zip file +GeolocationSettingsPanel.zipFileRBnt.actionCommand=OpenStreeMap tile ZIP file +GeolocationSettingsPanel.mbtilesRBtn.text=MBTiles file +GeolocationSettingsPanel.osmServerAddressField.text= +GeolocationSettingsPanel.zipFilePathField.text= +GeolocationSettingsPanel.serverTestBtn.text=Test +GeolocationSettingsPanel.mbtilesBrowseBtn.text=Browse +GeolocationSettingsPanel.zipFileBrowseBnt.text=Browse +GeolocationSettingsPanel.mbtileTestBtn.text=Test +GeolocationTopComponent.reportButton.text=KML Report +GeolocationTopComponent.coordLabel.text= + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED new file mode 100755 index 0000000000..1fbba07220 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED @@ -0,0 +1,74 @@ +CTL_OpenGeolocation=Geolocation +CTL_GeolocationTopComponentAction=GeolocationTopComponent +CTL_GeolocationTopComponent=Geolocation +GeoFilterPanel_DataSource_List_Title=Data Sources +GeoFilterPanel_empty_dataSource=Data Source list is empty. +GeolocationSettings_mbtile_does_not_exist_message=The file supplied does not exist.\nPlease verify that the file exists and try again. +GeolocationSettings_mbtile_does_not_exist_title=File Not Found +GeolocationSettings_mbtile_not_valid_message=The supplied file is not a raster tile file. +GeolocationSettings_mbtile_not_valid_title=File Not Valid +GeolocationSettings_mbtile_test_success_message=The supplied file is a valid mbtile raster file. +GeolocationSettings_mbtile_test_success_title=Success +GeolocationSettings_path_not_valid_message=The supplied file path is empty.\nPlease supply a valid file path. +GeolocationSettings_path_not_valid_title=File Not Valid +GeolocationSettingsPanel_malformed_url_message=The supplies OSM tile server address is invalid.\nPlease supply a well formed url prefixed with http:// +GeolocationSettingsPanel_malformed_url_message_tile=Malformed URL +GeolocationSettingsPanel_osm_server_test_fail_message=OSM tile server test failed.\nUnable to connect to server. +GeolocationSettingsPanel_osm_server_test_fail_message_title=Error +GeolocationSettingsPanel_osm_server_test_success_message=The provide OSM tile server address is valid. +GeolocationSettingsPanel_osm_server_test_success_message_title=Success +GeolocationTC_connection_failure_message=Failed to connect to map title source.\nPlease review map source in Options dialog. +GeolocationTC_connection_failure_message_title=Connection Failure +GeolocationTC_empty_waypoint_message=Unable to generate KML report due to a lack of waypoints.\nPlease make sure there are waypoints visible before generating the KML report +GeolocationTC_KML_report_title=KML Report +GeolocationTC_report_progress_title=KML Report Progress +GeoTopComponent_filer_data_invalid_msg=Unable to run waypoint filter.\nPlease select one or more data sources. +GeoTopComponent_filer_data_invalid_Title=Filter Failure +GeoTopComponent_filter_exception_msg=Exception occured during waypoint filtering. +GeoTopComponent_filter_exception_Title=Filter Failure +GeoTopComponent_no_waypoints_returned_mgs=Applied filter failed to find waypoints that matched criteria.\nRevise filter options and try again. +GeoTopComponent_no_waypoints_returned_Title=No Waypoints Found +GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete. +GLTopComponent_name=Geolocation +HidingPane_default_title=Filters +MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source. +MapPanel_connection_failure_message_title=Connection Failure +MayWaypoint_ExternalViewer_label=Open in ExternalViewer +OpenGeolocationAction_displayName=Geolocation +OpenGeolocationAction_name=Geolocation +RefreshPanel.refreshLabel.text=The geolocation data has been updated, the visualization may be out of date. +RefreshPanel.refreshButton.text=Refresh View +RefreshPanel.closeButton.text= +WaypointDetailPanel.closeButton.text= +WaypointDetailPanel.imageLabel.text= +GeoFilterPanel.waypointSettings.border.title= +GeoFilterPanel.allButton.text=Show All +GeoFilterPanel.mostRecentButton.text=Show only last +GeoFilterPanel.applyButton.text=Apply +GeoFilterPanel.showWaypointsWOTSCheckBox.text=Include waypoints with no time stamps +GeoFilterPanel.daysLabel.text=days of activity +CheckBoxListPanel.titleLabel.text=jLabel1 +CheckBoxListPanel.checkButton.text=Check All +CheckBoxListPanel.uncheckButton.text=Uncheck All +GeoFilterPanel.optionsLabel.text=Waypoints +OptionsCategory_Name_Geolocation=Geolocation +OptionsCategory_Keywords_Geolocation=Geolocation Settings +GeolocationSettingsPanel.tilePane.border.title=Map Tile Data Source +OptionsCategory_Keywords_Geolocation=Geolocation +GeolocationSettingsPanel.mbtileFileField.toolTipText= +GeolocationSettingsPanel.mbtileFileField.text= +GeolocationSettingsPanel.defaultDataSource.text=Default online tile server (bing.com/maps) +GeolocationSettingsPanel.osmServerRBnt.text=OpenStreetMap server +GeolocationSettingsPanel.zipFileRBnt.text=OpenStreetMap zip file +GeolocationSettingsPanel.zipFileRBnt.actionCommand=OpenStreeMap tile ZIP file +GeolocationSettingsPanel.mbtilesRBtn.text=MBTiles file +GeolocationSettingsPanel.osmServerAddressField.text= +GeolocationSettingsPanel.zipFilePathField.text= +GeolocationSettingsPanel.serverTestBtn.text=Test +GeolocationSettingsPanel.mbtilesBrowseBtn.text=Browse +GeolocationSettingsPanel.zipFileBrowseBnt.text=Browse +GeolocationSettingsPanel.mbtileTestBtn.text=Test +GeolocationTopComponent.reportButton.text=KML Report +GeolocationTopComponent.coordLabel.text= + +WaypointExtractAction_label=Extract Files(s) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxJList.java b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxJList.java new file mode 100755 index 0000000000..2f4247f3e6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxJList.java @@ -0,0 +1,110 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.Component; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JCheckBox; +import javax.swing.JList; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; + +/** + * A JList that renders the list items as check boxes. + */ +final class CheckBoxJList extends JList { + + private static final long serialVersionUID = 1L; + + /** + * Simple interface that must be implement for an object to be displayed as + * a checkbox in CheckBoxJList. + * + */ + interface CheckboxListItem { + + /** + * Returns the checkbox state. + * + * @return True if the check box should be checked + */ + boolean isChecked(); + + /** + * Set the state of the check box. + * + * @param checked + */ + void setChecked(boolean checked); + + /** + * Returns String to display as the check box label + * + * @return + */ + String getDisplayName(); + } + + /** + * Construct a new JCheckBoxList. + */ + CheckBoxJList() { + initalize(); + } + + /** + * Do all of the UI initialization. + */ + private void initalize() { + setCellRenderer(new CellRenderer()); + addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + int index = locationToIndex(e.getPoint()); + if (index != -1) { + CheckBoxJList.CheckboxListItem element = getModel().getElementAt(index); + element.setChecked(!element.isChecked()); + repaint(); + } + } + }); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + } + + /** + * A ListCellRenderer that renders list elements as check boxes. + */ + class CellRenderer extends JCheckBox implements ListCellRenderer { + + private static final long serialVersionUID = 1L; + + @Override + public Component getListCellRendererComponent( + JList list, CheckBoxJList.CheckboxListItem value, int index, + boolean isSelected, boolean cellHasFocus) { + + setBackground(list.getBackground()); + setSelected(value.isChecked()); + setText(value.getDisplayName()); + setEnabled(list.isEnabled()); + return this; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.form new file mode 100755 index 0000000000..75fb1d4c62 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.form @@ -0,0 +1,71 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.java new file mode 100755 index 0000000000..0942a9e528 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.java @@ -0,0 +1,269 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Objects; +import javax.swing.DefaultListModel; +import javax.swing.Icon; + +/** + * A panel for showing Content objects in a check box list. + */ +final class CheckBoxListPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + + private final DefaultListModel> model = new DefaultListModel<>(); + private final CheckBoxJList> checkboxList; + + /** + * Creates new CheckboxFilterPanel + */ + CheckBoxListPanel() { + initComponents(); + + checkboxList = new CheckBoxJList<>(); + checkboxList.setModel(model); + scrollPane.setViewportView(checkboxList); + } + + /** + * Add a new element to the check box list. + * + * @param displayName display name for the checkbox + * @param obj Object that the checkbox represents + */ + void addElement(String displayName, T obj) { + ObjectCheckBox newCheckBox = new ObjectCheckBox<>(displayName, true, obj); + + if(!model.contains(newCheckBox)) { + model.addElement(newCheckBox); + } + } + + /** + * Remove all objects from the checkbox list. + */ + void clearList() { + model.removeAllElements(); + } + + @Override + public void setEnabled(boolean enabled) { + checkboxList.setEnabled(enabled); + checkButton.setEnabled(enabled); + uncheckButton.setEnabled(enabled); + checkboxList.setEnabled(enabled); + } + + /** + * Returns a list of all of the selected elements. + * + * @return List of selected elements. + */ + List getSelectedElements() { + List selectedElements = new ArrayList<>(); + Enumeration> elements = model.elements(); + + while (elements.hasMoreElements()) { + ObjectCheckBox element = elements.nextElement(); + if (element.isChecked()) { + selectedElements.add(element.getObject()); + } + } + + return selectedElements; + } + + /** + * Sets the selection state of the all the check boxes in the list. + * + * @param selected True to check the boxes, false to unchecked + */ + void setSetAllSelected(boolean selected) { + Enumeration> enumeration = model.elements(); + while (enumeration.hasMoreElements()) { + ObjectCheckBox element = enumeration.nextElement(); + element.setChecked(selected); + checkboxList.repaint(); + checkboxList.revalidate(); + + } + } + + /** + * Sets the panel title. + * + * @param title Panel title or null for no title. + */ + void setPanelTitle(String title) { + titleLabel.setText(title); + } + + /** + * Sets the panel title icon. + * + * @param icon Icon to set or null for no icon + */ + void setPanelTitleIcon(Icon icon) { + titleLabel.setIcon(icon); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + titleLabel = new javax.swing.JLabel(); + uncheckButton = new javax.swing.JButton(); + checkButton = new javax.swing.JButton(); + scrollPane = new javax.swing.JScrollPane(); + + setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(titleLabel, org.openide.util.NbBundle.getMessage(CheckBoxListPanel.class, "CheckBoxListPanel.titleLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + add(titleLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(uncheckButton, org.openide.util.NbBundle.getMessage(CheckBoxListPanel.class, "CheckBoxListPanel.uncheckButton.text")); // NOI18N + uncheckButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + uncheckButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 9); + add(uncheckButton, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(checkButton, org.openide.util.NbBundle.getMessage(CheckBoxListPanel.class, "CheckBoxListPanel.checkButton.text")); // NOI18N + checkButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + checkButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + add(checkButton, gridBagConstraints); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 0, 9, 0); + add(scrollPane, gridBagConstraints); + }// //GEN-END:initComponents + + private void uncheckButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_uncheckButtonActionPerformed + setSetAllSelected(false); + }//GEN-LAST:event_uncheckButtonActionPerformed + + private void checkButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkButtonActionPerformed + setSetAllSelected(true); + }//GEN-LAST:event_checkButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton checkButton; + private javax.swing.JScrollPane scrollPane; + private javax.swing.JLabel titleLabel; + private javax.swing.JButton uncheckButton; + // End of variables declaration//GEN-END:variables + + /** + * Wrapper around T that implements CheckboxListItem + * + * @param + */ + final class ObjectCheckBox implements CheckBoxJList.CheckboxListItem { + + private static final long serialVersionUID = 1L; + + private final T object; + private final String displayName; + private boolean checked; + + /** + * Constructs a new ObjectCheckBox + * + * @param displayName String to show as the check box label + * @param initialState Sets the initial state of the check box + * @param object Object that the check box represents. + */ + ObjectCheckBox(String displayName, boolean initialState, T object) { + this.displayName = displayName; + this.object = object; + this.checked = initialState; + } + + T getObject() { + return object; + } + + @Override + public boolean isChecked() { + return checked; + } + + @Override + public void setChecked(boolean checked) { + this.checked = checked; + } + + @Override + public String getDisplayName() { + return displayName; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof ObjectCheckBox) { + return object.equals(((ObjectCheckBox)obj).object); + } + return false; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + Objects.hashCode(this.object); + return hash; + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.form new file mode 100755 index 0000000000..1b80568453 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.form @@ -0,0 +1,176 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java new file mode 100755 index 0000000000..51238a033e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java @@ -0,0 +1,379 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.GridBagConstraints; +import java.awt.event.ActionListener; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import javax.swing.ImageIcon; +import javax.swing.SpinnerNumberModel; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * Panel to display the filter options for geolocation waypoints. + */ +class GeoFilterPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(GeoFilterPanel.class.getName()); + + private final SpinnerNumberModel numberModel; + private final CheckBoxListPanel checkboxPanel; + + /** + * Creates new GeoFilterPanel + */ + @Messages({ + "GeoFilterPanel_DataSource_List_Title=Data Sources" + }) + GeoFilterPanel() { + // numberModel is used in initComponents + numberModel = new SpinnerNumberModel(10, 1, Integer.MAX_VALUE, 1); + + initComponents(); + + // The gui builder cannot handle using CheckBoxListPanel due to its + // use of generics so we will initalize it here. + checkboxPanel = new CheckBoxListPanel<>(); + checkboxPanel.setPanelTitle(Bundle.GeoFilterPanel_DataSource_List_Title()); + checkboxPanel.setPanelTitleIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/image.png"))); + checkboxPanel.setSetAllSelected(true); + + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 15, 0, 15); + add(checkboxPanel, gridBagConstraints); + } + + @Override + public void setEnabled(boolean enabled) { + applyButton.setEnabled(enabled); + mostRecentButton.setEnabled(enabled); + allButton.setEnabled(enabled); + showWaypointsWOTSCheckBox.setEnabled(enabled && mostRecentButton.isSelected()); + checkboxPanel.setEnabled(enabled); + daysLabel.setEnabled(enabled); + daysSpinner.setEnabled(enabled); + } + + /** + * Update the data source list with the current data sources + */ + void updateDataSourceList() { + try { + initCheckboxList(); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Failed to initialize the CheckboxListPane", ex); //NON-NLS + } + } + + /** + * Clears the data source list. + */ + void clearDataSourceList() { + checkboxPanel.clearList(); + } + + /** + * Adds an actionListener to listen for the filter apply action + * + * @param listener + */ + void addActionListener(ActionListener listener) { + applyButton.addActionListener(listener); + } + + /** + * Returns the selected filter values. + * + * @return A GeoFilter object with the user selected filter values + * + * @throws GeoLocationUIException + */ + @Messages({ + "GeoFilterPanel_empty_dataSource=Data Source list is empty." + }) + GeoFilter getFilterState() throws GeoLocationUIException { + List dataSources = checkboxPanel.getSelectedElements(); + + if (dataSources.isEmpty()) { + throw new GeoLocationUIException(Bundle.GeoFilterPanel_empty_dataSource()); + } + return new GeoFilter(allButton.isSelected(), + showWaypointsWOTSCheckBox.isSelected(), + numberModel.getNumber().intValue(), + dataSources); + } + + /** + * Initialize the checkbox list panel + * + * @throws TskCoreException + */ + private void initCheckboxList() throws TskCoreException { + final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); + + for (DataSource dataSource : sleuthkitCase.getDataSources()) { + String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName(); + checkboxPanel.addElement(dsName, dataSource); + } + } + + /** + * Based on the state of mostRecent radio button Change the state of the cnt + * spinner and the time stamp checkbox. + */ + private void updateWaypointOptions() { + boolean selected = mostRecentButton.isSelected(); + showWaypointsWOTSCheckBox.setEnabled(selected); + daysSpinner.setEnabled(selected); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + javax.swing.ButtonGroup buttonGroup = new javax.swing.ButtonGroup(); + javax.swing.JPanel waypointSettings = new javax.swing.JPanel(); + allButton = new javax.swing.JRadioButton(); + mostRecentButton = new javax.swing.JRadioButton(); + showWaypointsWOTSCheckBox = new javax.swing.JCheckBox(); + daysSpinner = new javax.swing.JSpinner(numberModel); + daysLabel = new javax.swing.JLabel(); + javax.swing.JPanel buttonPanel = new javax.swing.JPanel(); + applyButton = new javax.swing.JButton(); + javax.swing.JLabel optionsLabel = new javax.swing.JLabel(); + + setLayout(new java.awt.GridBagLayout()); + + waypointSettings.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(GeoFilterPanel.class, "GeoFilterPanel.waypointSettings.border.title"))); // NOI18N + waypointSettings.setLayout(new java.awt.GridBagLayout()); + + buttonGroup.add(allButton); + allButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(allButton, org.openide.util.NbBundle.getMessage(GeoFilterPanel.class, "GeoFilterPanel.allButton.text")); // NOI18N + allButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + allButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + waypointSettings.add(allButton, gridBagConstraints); + + buttonGroup.add(mostRecentButton); + org.openide.awt.Mnemonics.setLocalizedText(mostRecentButton, org.openide.util.NbBundle.getMessage(GeoFilterPanel.class, "GeoFilterPanel.mostRecentButton.text")); // NOI18N + mostRecentButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + mostRecentButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); + waypointSettings.add(mostRecentButton, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(showWaypointsWOTSCheckBox, org.openide.util.NbBundle.getMessage(GeoFilterPanel.class, "GeoFilterPanel.showWaypointsWOTSCheckBox.text")); // NOI18N + showWaypointsWOTSCheckBox.setEnabled(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 30, 0, 0); + waypointSettings.add(showWaypointsWOTSCheckBox, gridBagConstraints); + + daysSpinner.setEnabled(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); + waypointSettings.add(daysSpinner, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(daysLabel, org.openide.util.NbBundle.getMessage(GeoFilterPanel.class, "GeoFilterPanel.daysLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 3; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(9, 5, 0, 0); + waypointSettings.add(daysLabel, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 15, 9, 15); + add(waypointSettings, gridBagConstraints); + + buttonPanel.setLayout(new java.awt.GridBagLayout()); + + applyButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/tick.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(applyButton, org.openide.util.NbBundle.getMessage(GeoFilterPanel.class, "GeoFilterPanel.applyButton.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.weightx = 1.0; + buttonPanel.add(applyButton, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(9, 15, 0, 15); + add(buttonPanel, gridBagConstraints); + + optionsLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/blueGeo16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(optionsLabel, org.openide.util.NbBundle.getMessage(GeoFilterPanel.class, "GeoFilterPanel.optionsLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(0, 15, 0, 0); + add(optionsLabel, gridBagConstraints); + }// //GEN-END:initComponents + + private void allButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allButtonActionPerformed + updateWaypointOptions(); + }//GEN-LAST:event_allButtonActionPerformed + + private void mostRecentButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mostRecentButtonActionPerformed + updateWaypointOptions(); + }//GEN-LAST:event_mostRecentButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JRadioButton allButton; + private javax.swing.JButton applyButton; + private javax.swing.JLabel daysLabel; + private javax.swing.JSpinner daysSpinner; + private javax.swing.JRadioButton mostRecentButton; + private javax.swing.JCheckBox showWaypointsWOTSCheckBox; + // End of variables declaration//GEN-END:variables + + /** + * Class to store the values of the Geolocation user set filter parameters + */ + final class GeoFilter { + + private final boolean showAll; + private final boolean showWithoutTimeStamp; + private final int mostRecentNumDays; + private final List dataSources; + + /** + * Construct a Geolocation filter. showAll and mostRecentNumDays are + * exclusive filters, ie they cannot be used together. + * + * withoutTimeStamp is only applicable if mostRecentNumDays is true. + * + * When using the filters "most recent days" means to include waypoints + * for the numbers of days after the most recent waypoint, not the + * current date. + * + * @param showAll True if all waypoints should be shown + * @param withoutTimeStamp True to show waypoints without timeStamps, + * this filter is only applicable if + * mostRecentNumDays is true + * @param mostRecentNumDays Show Waypoint for the most recent given + * number of days. This parameter is ignored if + * showAll is true. + * @param dataSources A list of dataSources to filter waypoint + * for. + */ + GeoFilter(boolean showAll, boolean withoutTimeStamp, int mostRecentNumDays, List dataSources) { + this.showAll = showAll; + this.showWithoutTimeStamp = withoutTimeStamp; + this.mostRecentNumDays = mostRecentNumDays; + this.dataSources = dataSources; + } + + /** + * Returns whether or not to show all waypoints. + * + * @return True if all waypoints should be shown. + */ + boolean showAllWaypoints() { + return showAll; + } + + /** + * Returns whether or not to include waypoints with time stamps. + * + * This filter is only applicable if "showAll" is true. + * + * @return True if waypoints with time stamps should be shown. + */ + boolean showWaypointsWithoutTimeStamp() { + return showWithoutTimeStamp; + } + + /** + * Returns the number of most recent days to show waypoints for. This + * value should be ignored if showAll is true. + * + * @return The number of most recent days to show waypoints for + */ + int getMostRecentNumDays() { + return mostRecentNumDays; + } + + /** + * Returns a list of data sources to filter the waypoints by, or null if + * all datasources should be include. + * + * @return A list of dataSources or null if all dataSources should be + * included. + */ + List getDataSources() { + return Collections.unmodifiableList(dataSources); + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeoLocationUIException.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeoLocationUIException.java new file mode 100755 index 0000000000..7ce6837914 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeoLocationUIException.java @@ -0,0 +1,46 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +/** + * + * An exception call for Exceptions that occure in the geolocation dialog. + */ +public class GeoLocationUIException extends Exception{ + private static final long serialVersionUID = 1L; + + /** + * Create exception containing the error message + * + * @param msg the message + */ + public GeoLocationUIException(String msg) { + super(msg); + } + + /** + * Create exception containing the error message and cause exception + * + * @param msg the message + * @param ex cause exception + */ + public GeoLocationUIException(String msg, Exception ex) { + super(msg, ex); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationOptionPanelController.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationOptionPanelController.java new file mode 100755 index 0000000000..725baea022 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationOptionPanelController.java @@ -0,0 +1,124 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import javax.swing.JComponent; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; + +@OptionsPanelController.TopLevelRegistration( + categoryName = "#OptionsCategory_Name_Geolocation", + iconBase = "org/sleuthkit/autopsy/images/blueGeo32.png", + keywords = "#OptionsCategory_Keywords_Geolocation", + keywordsCategory = "Geolcoation", + position = 18 +) +/** + * Controller for the Geolocation options pane in the Options dialog. + */ +public final class GeolocationOptionPanelController extends OptionsPanelController { + + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private boolean changed; + private GeolocationSettingsPanel panel; + + /** + * Returns the GeolcoationSettingPanel. + * + * @return + */ + GeolocationSettingsPanel getPanel() { + if (panel == null) { + panel = new GeolocationSettingsPanel(); + panel.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) { + updateChanged(); + } + } + + }); + } + return panel; + } + + @Override + public void update() { + getPanel().load(); + changed = false; + } + + @Override + public void applyChanges() { + getPanel().store(); + changed = false; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public boolean isChanged() { + return changed; + } + + @Override + public JComponent getComponent(Lookup masterLookup) { + return getPanel(); + } + + @Override + public HelpCtx getHelpCtx() { + return null; + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + /** + * Helper function for updating the change state. + */ + void updateChanged() { + if (!changed) { + changed = true; + pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true); + } + pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null); + } + + @Override + public void cancel() { + getPanel().cancelChanges(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.form new file mode 100755 index 0000000000..ed4de8758a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.form @@ -0,0 +1,245 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.java new file mode 100755 index 0000000000..c2819d4a4f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.java @@ -0,0 +1,476 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.Color; +import java.io.File; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.logging.Level; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import org.apache.commons.validator.routines.UrlValidator; +import org.jxmapviewer.OSMTileFactoryInfo; +import org.jxmapviewer.viewer.TileFactoryInfo; +import org.jxmapviewer.viewer.util.GeoUtil; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.GeneralFilter; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.corecomponents.OptionsPanel; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * A panel to allow the user to set the custom properties of the geolocation + * window. + * + */ +final class GeolocationSettingsPanel extends javax.swing.JPanel implements OptionsPanel { + + private static final long serialVersionUID = 1L; + + private static final Logger logger = Logger.getLogger(GeolocationSettingsPanel.class.getName()); + + /** + * Creates new GeolocationSettingsPanel + */ + GeolocationSettingsPanel() { + initComponents(); + updateControlState(); + } + + @Override + public void store() { + UserPreferences.setGeolocationTileOption(getDataSourceOption().getValue()); + UserPreferences.setGeolocationOsmZipPath(zipFilePathField.getText()); + UserPreferences.setGeolocationOsmServerAddress(osmServerAddressField.getText()); + UserPreferences.setGeolocationMBTilesFilePath(mbtileFileField.getText()); + } + + @Override + public void load() { + osmServerAddressField.setText(UserPreferences.getGeolocationOsmServerAddress()); + zipFilePathField.setText(UserPreferences.getGeolocationOsmZipPath()); + mbtileFileField.setText(UserPreferences.getGeolocationMBTilesFilePath()); + switch (GeolocationDataSourceType.getOptionForValue(UserPreferences.getGeolocationtTileOption())) { + case ONLINE_USER_DEFINED_OSM_SERVER: + osmServerRBnt.setSelected(true); + break; + case OFFLINE_OSM_ZIP: + zipFileRBnt.setSelected(true); + break; + case OFFILE_MBTILES_FILE: + mbtilesRBtn.setSelected(true); + break; + default: + defaultDataSource.setSelected(true); + break; + } + + updateControlState(); + } + + /** + * Update the state of the tile server options based on the radio button + * selection state. + */ + private void updateControlState() { + osmServerAddressField.setEnabled(osmServerRBnt.isSelected()); + serverTestBtn.setEnabled(osmServerRBnt.isSelected()); + zipFilePathField.setEnabled(zipFileRBnt.isSelected()); + zipFileBrowseBnt.setEnabled(zipFileRBnt.isSelected()); + mbtileFileField.setEnabled(mbtilesRBtn.isSelected()); + mbtilesBrowseBtn.setEnabled(mbtilesRBtn.isSelected()); + mbtileTestBtn.setEnabled(mbtilesRBtn.isSelected()); + } + + /** + * Returns the GEOLOCATION_TILE_OPTION based on the selection state of the + * option radio buttons. + * + * @return Current GEOLOCATION_TILE_OPTION + */ + private GeolocationDataSourceType getDataSourceOption() { + if (osmServerRBnt.isSelected()) { + return GeolocationDataSourceType.ONLINE_USER_DEFINED_OSM_SERVER; + } else if (zipFileRBnt.isSelected()) { + return GeolocationDataSourceType.OFFLINE_OSM_ZIP; + } else if (mbtilesRBtn.isSelected()) { + return GeolocationDataSourceType.OFFILE_MBTILES_FILE; + } + return GeolocationDataSourceType.ONLINE_DEFAULT_SERVER; + } + + /** + * Tests the validity of the given tile server address. + * + * This test assumes the server is able to serve up at least one tile, the + * tile at x=1, y=1 with a zoom level of 1. This tile should be the whole + * global. + * + * @param url String url to tile server. The string does not have to be + * prefixed with http:// + * + * @return True if the server was successfully contacted. + */ + private boolean testOSMServer(String url) { + TileFactoryInfo info = new OSMTileFactoryInfo("User Defined Server", url); //NON-NLS + return GeoUtil.isValidTile(1, 1, 1, info); + } + + void cancelChanges() { + load(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + javax.swing.ButtonGroup buttonGroup = new javax.swing.ButtonGroup(); + javax.swing.JPanel tilePane = new javax.swing.JPanel(); + defaultDataSource = new javax.swing.JRadioButton(); + osmServerRBnt = new javax.swing.JRadioButton(); + osmServerAddressField = new javax.swing.JTextField(); + zipFileRBnt = new javax.swing.JRadioButton(); + zipFilePathField = new javax.swing.JTextField(); + zipFileBrowseBnt = new javax.swing.JButton(); + serverTestBtn = new javax.swing.JButton(); + mbtilesRBtn = new javax.swing.JRadioButton(); + mbtileFileField = new javax.swing.JTextField(); + javax.swing.JPanel MBTilesBtnPanel = new javax.swing.JPanel(); + mbtilesBrowseBtn = new javax.swing.JButton(); + mbtileTestBtn = new javax.swing.JButton(); + + setLayout(new java.awt.GridBagLayout()); + + tilePane.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.tilePane.border.title"))); // NOI18N + tilePane.setLayout(new java.awt.GridBagLayout()); + + buttonGroup.add(defaultDataSource); + defaultDataSource.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(defaultDataSource, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.defaultDataSource.text")); // NOI18N + defaultDataSource.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + defaultDataSourceActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 9, 0); + tilePane.add(defaultDataSource, gridBagConstraints); + + buttonGroup.add(osmServerRBnt); + org.openide.awt.Mnemonics.setLocalizedText(osmServerRBnt, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.osmServerRBnt.text")); // NOI18N + osmServerRBnt.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + osmServerRBntActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + tilePane.add(osmServerRBnt, gridBagConstraints); + + osmServerAddressField.setText(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.osmServerAddressField.text")); // NOI18N + osmServerAddressField.setPreferredSize(new java.awt.Dimension(300, 26)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + tilePane.add(osmServerAddressField, gridBagConstraints); + + buttonGroup.add(zipFileRBnt); + org.openide.awt.Mnemonics.setLocalizedText(zipFileRBnt, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.zipFileRBnt.text")); // NOI18N + zipFileRBnt.setActionCommand(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.zipFileRBnt.actionCommand")); // NOI18N + zipFileRBnt.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + zipFileRBntActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + tilePane.add(zipFileRBnt, gridBagConstraints); + + zipFilePathField.setText(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.zipFilePathField.text")); // NOI18N + zipFilePathField.setPreferredSize(new java.awt.Dimension(300, 26)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + tilePane.add(zipFilePathField, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(zipFileBrowseBnt, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.zipFileBrowseBnt.text")); // NOI18N + zipFileBrowseBnt.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + zipFileBrowseBntActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(0, 9, 9, 9); + tilePane.add(zipFileBrowseBnt, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(serverTestBtn, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.serverTestBtn.text")); // NOI18N + serverTestBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + serverTestBtnActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(0, 9, 9, 9); + tilePane.add(serverTestBtn, gridBagConstraints); + + buttonGroup.add(mbtilesRBtn); + org.openide.awt.Mnemonics.setLocalizedText(mbtilesRBtn, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.mbtilesRBtn.text")); // NOI18N + mbtilesRBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + mbtilesRBtnActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + tilePane.add(mbtilesRBtn, gridBagConstraints); + + mbtileFileField.setText(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.mbtileFileField.text")); // NOI18N + mbtileFileField.setToolTipText(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.mbtileFileField.toolTipText")); // NOI18N + mbtileFileField.setPreferredSize(new java.awt.Dimension(300, 26)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 3; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + tilePane.add(mbtileFileField, gridBagConstraints); + + MBTilesBtnPanel.setLayout(new java.awt.GridLayout(1, 0, 5, 0)); + + org.openide.awt.Mnemonics.setLocalizedText(mbtilesBrowseBtn, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.mbtilesBrowseBtn.text")); // NOI18N + mbtilesBrowseBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + mbtilesBrowseBtnActionPerformed(evt); + } + }); + MBTilesBtnPanel.add(mbtilesBrowseBtn); + + org.openide.awt.Mnemonics.setLocalizedText(mbtileTestBtn, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.mbtileTestBtn.text")); // NOI18N + mbtileTestBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + mbtileTestBtnActionPerformed(evt); + } + }); + MBTilesBtnPanel.add(mbtileTestBtn); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 3; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(0, 9, 9, 9); + tilePane.add(MBTilesBtnPanel, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + add(tilePane, gridBagConstraints); + }// //GEN-END:initComponents + + private void zipFileBrowseBntActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zipFileBrowseBntActionPerformed + JFileChooser fileWindow = new JFileChooser(); + fileWindow.setFileSelectionMode(JFileChooser.FILES_ONLY); + GeneralFilter fileFilter = new GeneralFilter(Arrays.asList(".zip"), "Zips (*.zip)"); //NON-NLS + fileWindow.setDragEnabled(false); + fileWindow.setFileFilter(fileFilter); + fileWindow.setMultiSelectionEnabled(false); + int returnVal = fileWindow.showSaveDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File zipFile = fileWindow.getSelectedFile(); + zipFilePathField.setForeground(Color.BLACK); + zipFilePathField.setText(zipFile.getAbsolutePath()); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + }//GEN-LAST:event_zipFileBrowseBntActionPerformed + + private void defaultDataSourceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_defaultDataSourceActionPerformed + updateControlState(); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_defaultDataSourceActionPerformed + + private void osmServerRBntActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_osmServerRBntActionPerformed + updateControlState(); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_osmServerRBntActionPerformed + + private void zipFileRBntActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zipFileRBntActionPerformed + updateControlState(); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_zipFileRBntActionPerformed + + @Messages({ + "GeolocationSettingsPanel_malformed_url_message=The supplies OSM tile server address is invalid.\nPlease supply a well formed url prefixed with http://", + "GeolocationSettingsPanel_malformed_url_message_tile=Malformed URL", + "GeolocationSettingsPanel_osm_server_test_fail_message=OSM tile server test failed.\nUnable to connect to server.", + "GeolocationSettingsPanel_osm_server_test_fail_message_title=Error", + "GeolocationSettingsPanel_osm_server_test_success_message=The provide OSM tile server address is valid.", + "GeolocationSettingsPanel_osm_server_test_success_message_title=Success",}) + private void serverTestBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_serverTestBtnActionPerformed + String address = osmServerAddressField.getText(); + String message = Bundle.GeolocationSettingsPanel_osm_server_test_fail_message(); + String title = Bundle.GeolocationSettingsPanel_osm_server_test_fail_message_title(); + + String[] schemes = {"http", "https"}; //NON-NLS + UrlValidator urlValidator = new UrlValidator(schemes); + if (!urlValidator.isValid(address)) { + message = Bundle.GeolocationSettingsPanel_malformed_url_message(); + title = Bundle.GeolocationSettingsPanel_malformed_url_message_tile(); + } else if (testOSMServer(address)) { + message = Bundle.GeolocationSettingsPanel_osm_server_test_success_message(); + title = Bundle.GeolocationSettingsPanel_osm_server_test_success_message_title(); + } + + JOptionPane.showMessageDialog(this, message, title, JOptionPane.INFORMATION_MESSAGE); + }//GEN-LAST:event_serverTestBtnActionPerformed + + private void mbtilesRBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mbtilesRBtnActionPerformed + updateControlState(); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_mbtilesRBtnActionPerformed + + private void mbtilesBrowseBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mbtilesBrowseBtnActionPerformed + JFileChooser fileWindow = new JFileChooser(); + fileWindow.setFileSelectionMode(JFileChooser.FILES_ONLY); + GeneralFilter fileFilter = new GeneralFilter(Arrays.asList(".mbtiles"), "MBTiles (*.mbtiles)"); //NON-NLS + fileWindow.setDragEnabled(false); + fileWindow.setFileFilter(fileFilter); + fileWindow.setMultiSelectionEnabled(false); + int returnVal = fileWindow.showSaveDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File zipFile = fileWindow.getSelectedFile(); + mbtileFileField.setForeground(Color.BLACK); + mbtileFileField.setText(zipFile.getAbsolutePath()); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + }//GEN-LAST:event_mbtilesBrowseBtnActionPerformed + + @Messages({ + "GeolocationSettings_mbtile_does_not_exist_message=The file supplied does not exist.\nPlease verify that the file exists and try again.", + "GeolocationSettings_mbtile_does_not_exist_title=File Not Found", + "GeolocationSettings_mbtile_not_valid_message=The supplied file is not a raster tile file.", + "GeolocationSettings_mbtile_not_valid_title=File Not Valid", + "GeolocationSettings_path_not_valid_message=The supplied file path is empty.\nPlease supply a valid file path.", + "GeolocationSettings_path_not_valid_title=File Not Valid", + "GeolocationSettings_mbtile_test_success_message=The supplied file is a valid mbtile raster file.", + "GeolocationSettings_mbtile_test_success_title=Success", + }) + private void mbtileTestBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mbtileTestBtnActionPerformed + String mbtilePath = mbtileFileField.getText(); + + if(mbtilePath.isEmpty()) { + JOptionPane.showMessageDialog(this, Bundle.GeolocationSettings_path_not_valid_message(), Bundle.GeolocationSettings_path_not_valid_title(), JOptionPane.ERROR_MESSAGE); + return; + } + + File file = new File(mbtilePath); + if(!file.exists()) { + JOptionPane.showMessageDialog(this, Bundle.GeolocationSettings_mbtile_does_not_exist_message(), Bundle.GeolocationSettings_mbtile_does_not_exist_title(), JOptionPane.ERROR_MESSAGE); + return; + } + + try { + if(!MBTilesFileConnector.isValidMBTileRasterFile(mbtilePath)) { + JOptionPane.showMessageDialog(this, Bundle.GeolocationSettings_mbtile_not_valid_message(), Bundle.GeolocationSettings_mbtile_not_valid_title(), JOptionPane.ERROR_MESSAGE); + return; + } + } catch (SQLException ex) { + JOptionPane.showMessageDialog(this, Bundle.GeolocationSettings_mbtile_not_valid_message(), Bundle.GeolocationSettings_mbtile_not_valid_title(), JOptionPane.ERROR_MESSAGE); + logger.log(Level.WARNING, String.format("Exception thrown while testing mbtile file %s", mbtilePath), ex); + return; + } + + JOptionPane.showMessageDialog(this, Bundle.GeolocationSettings_mbtile_test_success_message(), Bundle.GeolocationSettings_mbtile_test_success_title(), JOptionPane.INFORMATION_MESSAGE); + }//GEN-LAST:event_mbtileTestBtnActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JRadioButton defaultDataSource; + private javax.swing.JTextField mbtileFileField; + private javax.swing.JButton mbtileTestBtn; + private javax.swing.JButton mbtilesBrowseBtn; + private javax.swing.JRadioButton mbtilesRBtn; + private javax.swing.JTextField osmServerAddressField; + private javax.swing.JRadioButton osmServerRBnt; + private javax.swing.JButton serverTestBtn; + private javax.swing.JButton zipFileBrowseBnt; + private javax.swing.JTextField zipFilePathField; + private javax.swing.JRadioButton zipFileRBnt; + // End of variables declaration//GEN-END:variables + + /** + * Tile server option enum. The enum was given values to simplify the + * storing of the user preference for a particular option. + */ + enum GeolocationDataSourceType{ + ONLINE_DEFAULT_SERVER(0), + ONLINE_USER_DEFINED_OSM_SERVER(1), + OFFLINE_OSM_ZIP(2), + OFFILE_MBTILES_FILE(3); + + private final int value; + + GeolocationDataSourceType(int value) { + this.value = value; + } + + int getValue() { + return value; + } + + static GeolocationDataSourceType getOptionForValue(int value) { + for (GeolocationDataSourceType option : GeolocationDataSourceType.values()) { + if (option.getValue() == value) { + return option; + } + } + + return ONLINE_DEFAULT_SERVER; + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.form b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.form new file mode 100755 index 0000000000..c51c8d496e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.form @@ -0,0 +1,86 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java new file mode 100755 index 0000000000..b1dee1cbb4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -0,0 +1,470 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.EnumSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.logging.Level; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import org.openide.filesystems.FileUtil; +import org.openide.util.NbBundle.Messages; +import org.openide.windows.RetainLocation; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.casemodule.Case; +import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.geolocation.GeoFilterPanel.GeoFilter; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; +import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint; +import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder; +import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder.WaypointFilterQueryCallBack; +import org.sleuthkit.autopsy.ingest.IngestManager; +import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.autopsy.report.ReportProgressPanel; +import org.sleuthkit.autopsy.report.modules.kml.KMLReport; +import org.sleuthkit.datamodel.BlackboardArtifact; + +/** + * Top component for the Geolocation Tool. + * + */ +@TopComponent.Description(preferredID = "GeolocationTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER) +@TopComponent.Registration(mode = "geolocation", openAtStartup = false) +@RetainLocation("geolocation") +@SuppressWarnings("PMD.SingularField") +public final class GeolocationTopComponent extends TopComponent { + + private static final long serialVersionUID = 1L; + + private static final Logger logger = Logger.getLogger(GeolocationTopComponent.class.getName()); + + private static final Set INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED); + + private final PropertyChangeListener ingestListener; + private final PropertyChangeListener caseEventListener; + private final GeoFilterPanel geoFilterPanel; + + final RefreshPanel refreshPanel = new RefreshPanel(); + + private static final String REPORT_PATH_FMT_STR = "%s" + File.separator + "%s %s %s" + File.separator; + + // This is the hardcoded report name from KMLReport.java + private static final String REPORT_KML = "ReportKML.kml"; + + @Messages({ + "GLTopComponent_name=Geolocation", + "GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete." + }) + + /** + * Constructs new GeoLocationTopComponent + */ + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) + public GeolocationTopComponent() { + initComponents(); + + setName(Bundle.GLTopComponent_name()); + + this.ingestListener = pce -> { + String eventType = pce.getPropertyName(); + if (eventType.equals(DATA_ADDED.toString())) { + // Indicate that a refresh may be needed for GPS data. + ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); + if (null != eventData + && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID() + || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH.getTypeID() + || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION.getTypeID() + || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID() + || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID() + || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK.getTypeID())) { + + showRefreshPanel(true); + } + } + }; + + this.caseEventListener = pce -> { + mapPanel.clearWaypoints(); + if (pce.getNewValue() != null) { + updateWaypoints(); + } + }; + + refreshPanel.addCloseActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showRefreshPanel(false); + } + }); + + refreshPanel.addRefreshActionListner(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + geoFilterPanel.updateDataSourceList(); + mapPanel.clearWaypoints(); + updateWaypoints(); + showRefreshPanel(false); + } + }); + + geoFilterPanel = new GeoFilterPanel(); + filterPane.setPanel(geoFilterPanel); + geoFilterPanel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + updateWaypoints(); + } + }); + + mapPanel.addPropertyChangeListener(MapPanel.CURRENT_MOUSE_GEOPOSITION, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String label = ""; + Object newValue = evt.getNewValue(); + if (newValue != null) { + label = newValue.toString(); + } + + coordLabel.setText(label); + } + + }); + } + + @Override + public void addNotify() { + super.addNotify(); + IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, ingestListener); + Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), caseEventListener); + } + + @Override + public void removeNotify() { + super.removeNotify(); + IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); + Case.removeEventTypeSubscriber(EnumSet.of(CURRENT_CASE), caseEventListener); + } + + @Override + public void componentOpened() { + super.componentOpened(); + WindowManager.getDefault().setTopComponentFloating(this, true); + + } + + @Messages({ + "GeolocationTC_connection_failure_message=Failed to connect to map title source.\nPlease review map source in Options dialog.", + "GeolocationTC_connection_failure_message_title=Connection Failure" + }) + @Override + public void open() { + super.open(); + geoFilterPanel.clearDataSourceList(); + geoFilterPanel.updateDataSourceList(); + try { + mapPanel.initMap(); + } catch (GeoLocationDataException ex) { + JOptionPane.showMessageDialog(this, + Bundle.GeolocationTC_connection_failure_message(), + Bundle.GeolocationTC_connection_failure_message_title(), + JOptionPane.ERROR_MESSAGE); + MessageNotifyUtil.Notify.error( + Bundle.GeolocationTC_connection_failure_message_title(), + Bundle.GeolocationTC_connection_failure_message()); + logger.log(Level.SEVERE, ex.getMessage(), ex); + return; // Doen't set the waypoints. + } + mapPanel.setWaypoints(new ArrayList<>()); + updateWaypoints(); + } + + /** + * Set the state of the refresh panel at the top of the mapPanel. + * + * @param show Whether to show or hide the panel. + */ + private void showRefreshPanel(boolean show) { + if (show) { + mapPanel.add(refreshPanel, BorderLayout.NORTH); + } else { + mapPanel.remove(refreshPanel); + } + mapPanel.revalidate(); + } + + /** + * Filters the list of waypoints based on the user selections in the filter + * pane. + */ + @Messages({ + "GeoTopComponent_no_waypoints_returned_mgs=Applied filter failed to find waypoints that matched criteria.\nRevise filter options and try again.", + "GeoTopComponent_no_waypoints_returned_Title=No Waypoints Found", + "GeoTopComponent_filter_exception_msg=Exception occured during waypoint filtering.", + "GeoTopComponent_filter_exception_Title=Filter Failure", + "GeoTopComponent_filer_data_invalid_msg=Unable to run waypoint filter.\nPlease select one or more data sources.", + "GeoTopComponent_filer_data_invalid_Title=Filter Failure" + }) + private void updateWaypoints() { + GeoFilter filters; + + // Show a warning message if the user has not selected a data source + try { + filters = geoFilterPanel.getFilterState(); + } catch (GeoLocationUIException ex) { + JOptionPane.showMessageDialog(this, + Bundle.GeoTopComponent_filer_data_invalid_msg(), + Bundle.GeoTopComponent_filer_data_invalid_Title(), + JOptionPane.INFORMATION_MESSAGE); + return; + } + + setWaypointLoading(true); + geoFilterPanel.setEnabled(false); + + Thread thread = new Thread(new WaypointRunner(filters)); + thread.start(); + } + + /** + * Show or hide the waypoint loading progress bar. + * + * @param loading + */ + void setWaypointLoading(boolean loading) { + progressBar.setEnabled(true); + progressBar.setVisible(loading); + progressBar.setString("Loading Waypoints"); + } + + /** + * Create the directory path for the KML report. + * + * This is a modified version of the similar private function from + * KMLReport. + * + * @return Path for the report + * + * @throws IOException + */ + private static String createReportDirectory() throws IOException { + Case currentCase = Case.getCurrentCase(); + + // Create the root reports directory path of the form: /Reports/ / + DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss", Locale.US); + Date date = new Date(); + String dateNoTime = dateFormat.format(date); + String reportPath = String.format(REPORT_PATH_FMT_STR, currentCase.getReportDirectory(), currentCase.getDisplayName(), "Goggle Earth KML", dateNoTime); + // Create the root reports directory. + try { + FileUtil.createFolder(new File(reportPath)); + } catch (IOException ex) { + throw new IOException("Failed to make report folder, unable to generate reports.", ex); + } + return reportPath; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + filterPane = new org.sleuthkit.autopsy.geolocation.HidingPane(); + statusBar = new javax.swing.JPanel(); + reportButton = new javax.swing.JButton(); + progressBar = new javax.swing.JProgressBar(); + coordLabel = new javax.swing.JLabel(); + mapPanel = new org.sleuthkit.autopsy.geolocation.MapPanel(); + + setLayout(new java.awt.BorderLayout()); + add(filterPane, java.awt.BorderLayout.WEST); + + statusBar.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(reportButton, org.openide.util.NbBundle.getMessage(GeolocationTopComponent.class, "GeolocationTopComponent.reportButton.text")); // NOI18N + reportButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + reportButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + statusBar.add(reportButton, gridBagConstraints); + + progressBar.setIndeterminate(true); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + statusBar.add(progressBar, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(coordLabel, org.openide.util.NbBundle.getMessage(GeolocationTopComponent.class, "GeolocationTopComponent.coordLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 0); + statusBar.add(coordLabel, gridBagConstraints); + + add(statusBar, java.awt.BorderLayout.SOUTH); + add(mapPanel, java.awt.BorderLayout.CENTER); + }// //GEN-END:initComponents + + @Messages({ + "GeolocationTC_empty_waypoint_message=Unable to generate KML report due to a lack of waypoints.\nPlease make sure there are waypoints visible before generating the KML report", + "GeolocationTC_KML_report_title=KML Report", + "GeolocationTC_report_progress_title=KML Report Progress" + }) + private void reportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_reportButtonActionPerformed + List visiblePoints = mapPanel.getVisibleWaypoints(); + if (visiblePoints.isEmpty()) { + JOptionPane.showConfirmDialog(this, Bundle.GeolocationTC_empty_waypoint_message(), Bundle.GeolocationTC_KML_report_title(), JOptionPane.OK_OPTION, JOptionPane.INFORMATION_MESSAGE); + return; + } + + try { + ReportProgressPanel progressPanel = new ReportProgressPanel(); + String reportBaseDir = createReportDirectory(); + + progressPanel.setLabels(REPORT_KML, reportBaseDir); + + SwingWorker worker = new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + KMLReport.getDefault().generateReport(reportBaseDir, progressPanel, MapWaypoint.getDataModelWaypoints(visiblePoints)); + return null; + } + }; + worker.execute(); + JOptionPane.showConfirmDialog(this, progressPanel, Bundle.GeolocationTC_report_progress_title(), JOptionPane.CLOSED_OPTION, JOptionPane.PLAIN_MESSAGE); + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to create KML report", ex); + } + }//GEN-LAST:event_reportButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel coordLabel; + private org.sleuthkit.autopsy.geolocation.HidingPane filterPane; + private org.sleuthkit.autopsy.geolocation.MapPanel mapPanel; + private javax.swing.JProgressBar progressBar; + private javax.swing.JButton reportButton; + private javax.swing.JPanel statusBar; + // End of variables declaration//GEN-END:variables + + /** + * A runnable class for getting waypoints based on the current filters. + */ + private class WaypointRunner implements Runnable { + + private final GeoFilter filters; + + /** + * Constructs the Waypoint Runner + * + * @param filters + */ + WaypointRunner(GeoFilter filters) { + this.filters = filters; + } + + @Override + public void run() { + Case currentCase = Case.getCurrentCase(); + try { + WaypointBuilder.getAllWaypoints(currentCase.getSleuthkitCase(), + filters.getDataSources(), + filters.showAllWaypoints(), + filters.getMostRecentNumDays(), + filters.showWaypointsWithoutTimeStamp(), + new WaypointCallBack()); + + } catch (GeoLocationDataException ex) { + logger.log(Level.SEVERE, "Failed to filter waypoints.", ex); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + JOptionPane.showMessageDialog(GeolocationTopComponent.this, + Bundle.GeoTopComponent_filter_exception_Title(), + Bundle.GeoTopComponent_filter_exception_msg(), + JOptionPane.ERROR_MESSAGE); + } + }); + } + } + + } + + /** + * Callback for getting waypoints. + */ + private class WaypointCallBack implements WaypointFilterQueryCallBack { + + @Override + public void process(List waypoints) { + // Make sure that the waypoints are added to the map panel in + // the correct thread. + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + // If the list is empty, tell the user and do not change + // the visible waypoints. + if (waypoints == null || waypoints.isEmpty()) { + JOptionPane.showMessageDialog(GeolocationTopComponent.this, + Bundle.GeoTopComponent_no_waypoints_returned_Title(), + Bundle.GeoTopComponent_no_waypoints_returned_mgs(), + JOptionPane.INFORMATION_MESSAGE); + + return; + } + mapPanel.setWaypoints(MapWaypoint.getWaypoints(waypoints)); + setWaypointLoading(false); + geoFilterPanel.setEnabled(true); + } + }); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/HidingPane.java b/Core/src/org/sleuthkit/autopsy/geolocation/HidingPane.java new file mode 100755 index 0000000000..e842e5df86 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/HidingPane.java @@ -0,0 +1,133 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import org.openide.util.NbBundle.Messages; + +/** + * + * A JTabbed pane with one tab that says "Filters". When the user clicks on that + * table the content of the tab will be hidden. + * + * The content pane provides support for scrolling. + */ +public final class HidingPane extends JTabbedPane { + + private static final long serialVersionUID = 1L; + + private final JScrollPane scrollPane; + private final JPanel panel; + private final JLabel tabLabel; + + private boolean panelVisible = true; + + /** + * Constructs a new HidingFilterPane + */ + @Messages({ + "HidingPane_default_title=Filters" + }) + public HidingPane() { + super(); + + scrollPane = new JScrollPane(); + panel = new JPanel(); + panel.setLayout(new BorderLayout()); + panel.add(scrollPane, BorderLayout.CENTER); + tabLabel = new JLabel(Bundle.HidingPane_default_title()); + tabLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/funnel.png"))); + tabLabel.setUI(new VerticalLabelUI(true)); + tabLabel.setOpaque(false); + Font font = tabLabel.getFont().deriveFont(18).deriveFont(Font.BOLD); + tabLabel.setFont(font); + + addTab(null, panel); + setTabComponentAt(0, tabLabel); + + this.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent evt) { + handleMouseClick(evt.getPoint()); + } + }); + + this.setTabPlacement(JTabbedPane.RIGHT); + } + + /** + * Change the title of the tab. + * + * @param title + */ + void setTitle(String title) { + tabLabel.setText(title); + } + + /** + * Set the icon that appears on the tab. + * + * @param icon + */ + void setIcon(Icon icon) { + tabLabel.setIcon(icon); + } + + /** + * Set the content for this panel. + * + * @param panel A panel to display in the tabbed pane. + */ + void setPanel(JPanel panel) { + scrollPane.setViewportView(panel); + } + + /** + * Handle the mouse click. + * + * @param point + */ + private void handleMouseClick(Point point) { + int index = indexAtLocation(point.x, point.y); + + if(index == -1) { + return; + } + + if(panelVisible) { + panel.removeAll(); + panel.revalidate(); + panelVisible = false; + } else { + panel.add(scrollPane, BorderLayout.CENTER); + panel.revalidate(); + panelVisible = true; + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java b/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java new file mode 100755 index 0000000000..656abc9489 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java @@ -0,0 +1,755 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import static java.lang.Math.cos; +import static java.lang.Math.sin; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +/** + * A k-d tree (short for k-dimensional tree) is a space-partitioning data + * structure for organizing points in a k-dimensional space. k-d trees are a + * useful data structure for several applications, such as searches involving a + * multidimensional search key (e.g. range searches and nearest neighbor + * searches). k-d trees are a special case of binary space partitioning trees. + *

+ * @see K-D Tree + * (Wikipedia) + *
+ * + * Original other was JustinWetherell . + * + * @see Original version + * + */ +public class KdTree implements Iterable { + + private int k = 3; + private KdNode root = null; + + private static final Comparator X_COMPARATOR = new Comparator() { + /** + * {@inheritDoc} + */ + @Override + public int compare(XYZPoint o1, XYZPoint o2) { + if (o1.x < o2.x) + return -1; + if (o1.x > o2.x) + return 1; + return 0; + } + }; + + private static final Comparator Y_COMPARATOR = new Comparator() { + /** + * {@inheritDoc} + */ + @Override + public int compare(XYZPoint o1, XYZPoint o2) { + if (o1.y < o2.y) + return -1; + if (o1.y > o2.y) + return 1; + return 0; + } + }; + + private static final Comparator Z_COMPARATOR = new Comparator() { + /** + * {@inheritDoc} + */ + @Override + public int compare(XYZPoint o1, XYZPoint o2) { + if (o1.z < o2.z) + return -1; + if (o1.z > o2.z) + return 1; + return 0; + } + }; + + protected static final int X_AXIS = 0; + protected static final int Y_AXIS = 1; + protected static final int Z_AXIS = 2; + + /** + * Default constructor. + */ + public KdTree() { } + + /** + * Constructor for creating a more balanced tree. It uses the + * "median of points" algorithm. + * + * @param list + * of XYZPoints. + */ + public KdTree(List list) { + super(); + root = createNode(list, k, 0); + } + + /** + * Constructor for creating a more balanced tree. It uses the + * "median of points" algorithm. + * + * @param list + * of XYZPoints. + * @param k + * of the tree. + */ + public KdTree(List list, int k) { + super(); + root = createNode(list, k, 0); + } + + /** + * Creates node from list of XYZPoints. + * + * @param list + * of XYZPoints. + * @param k + * of the tree. + * @param depth + * depth of the node. + * @return node created. + */ + private static KdNode createNode(List list, int k, int depth) { + if (list == null || list.size() == 0) + return null; + + int axis = depth % k; + if (axis == X_AXIS) + Collections.sort(list, X_COMPARATOR); + else if (axis == Y_AXIS) + Collections.sort(list, Y_COMPARATOR); + else + Collections.sort(list, Z_COMPARATOR); + + KdNode node = null; + List less = new ArrayList(list.size()); + List more = new ArrayList(list.size()); + if (list.size() > 0) { + int medianIndex = list.size() / 2; + node = new KdNode(list.get(medianIndex), k, depth); + // Process list to see where each non-median point lies + for (int i = 0; i < list.size(); i++) { + if (i == medianIndex) + continue; + XYZPoint p = list.get(i); + // Cannot assume points before the median are less since they could be equal + if (KdNode.compareTo(depth, k, p, node.id) <= 0) { + less.add(p); + } else { + more.add(p); + } + } + + if ((medianIndex-1 >= 0) && less.size() > 0) { + node.lesser = createNode(less, k, depth + 1); + node.lesser.parent = node; + } + + if ((medianIndex <= list.size()-1) && more.size() > 0) { + node.greater = createNode(more, k, depth + 1); + node.greater.parent = node; + } + } + + return node; + } + + /** + * Adds value to the tree. Tree can contain multiple equal values. + * + * @param value + * T to add to the tree. + * @return True if successfully added to tree. + */ + public boolean add(T value) { + if (value == null) + return false; + + if (root == null) { + root = new KdNode(value); + return true; + } + + KdNode node = root; + while (true) { + if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) { + // Lesser + if (node.lesser == null) { + KdNode newNode = new KdNode(value, k, node.depth + 1); + newNode.parent = node; + node.lesser = newNode; + break; + } + node = node.lesser; + } else { + // Greater + if (node.greater == null) { + KdNode newNode = new KdNode(value, k, node.depth + 1); + newNode.parent = node; + node.greater = newNode; + break; + } + node = node.greater; + } + } + + return true; + } + + /** + * Does the tree contain the value. + * + * @param value + * T to locate in the tree. + * @return True if tree contains value. + */ + public boolean contains(T value) { + if (value == null || root == null) + return false; + + KdNode node = getNode(this, value); + return (node != null); + } + + /** + * Locates T in the tree. + * + * @param tree + * to search. + * @param value + * to search for. + * @return KdNode or NULL if not found + */ + private static final KdNode getNode(KdTree tree, T value) { + if (tree == null || tree.root == null || value == null) + return null; + + KdNode node = tree.root; + while (true) { + if (node.id.equals(value)) { + return node; + } else if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) { + // Lesser + if (node.lesser == null) { + return null; + } + node = node.lesser; + } else { + // Greater + if (node.greater == null) { + return null; + } + node = node.greater; + } + } + } + + /** + * Removes first occurrence of value in the tree. + * + * @param value + * T to remove from the tree. + * @return True if value was removed from the tree. + */ + public boolean remove(T value) { + if (value == null || root == null) + return false; + + KdNode node = getNode(this, value); + if (node == null) + return false; + + KdNode parent = node.parent; + if (parent != null) { + if (parent.lesser != null && node.equals(parent.lesser)) { + List nodes = getTree(node); + if (nodes.size() > 0) { + parent.lesser = createNode(nodes, node.k, node.depth); + if (parent.lesser != null) { + parent.lesser.parent = parent; + } + } else { + parent.lesser = null; + } + } else { + List nodes = getTree(node); + if (nodes.size() > 0) { + parent.greater = createNode(nodes, node.k, node.depth); + if (parent.greater != null) { + parent.greater.parent = parent; + } + } else { + parent.greater = null; + } + } + } else { + // root + List nodes = getTree(node); + if (nodes.size() > 0) + root = createNode(nodes, node.k, node.depth); + else + root = null; + } + + return true; + } + + /** + * Gets the (sub) tree rooted at root. + * + * @param root + * of tree to get nodes for. + * @return points in (sub) tree, not including root. + */ + private static final List getTree(KdNode root) { + List list = new ArrayList(); + if (root == null) + return list; + + if (root.lesser != null) { + list.add(root.lesser.id); + list.addAll(getTree(root.lesser)); + } + if (root.greater != null) { + list.add(root.greater.id); + list.addAll(getTree(root.greater)); + } + + return list; + } + + /** + * Searches the K nearest neighbor. + * + * @param K + * Number of neighbors to retrieve. Can return more than K, if + * last nodes are equal distances. + * @param value + * to find neighbors of. + * @return Collection of T neighbors. + */ + @SuppressWarnings("unchecked") + public Collection nearestNeighbourSearch(int K, T value) { + if (value == null || root == null) + return Collections.EMPTY_LIST; + + // Map used for results + TreeSet results = new TreeSet(new EuclideanComparator(value)); + + // Find the closest leaf node + KdNode prev = null; + KdNode node = root; + while (node != null) { + if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) { + // Lesser + prev = node; + node = node.lesser; + } else { + // Greater + prev = node; + node = node.greater; + } + } + KdNode leaf = prev; + + if (leaf != null) { + // Used to not re-examine nodes + Set examined = new HashSet(); + + // Go up the tree, looking for better solutions + node = leaf; + while (node != null) { + // Search node + searchNode(value, node, K, results, examined); + node = node.parent; + } + } + + // Load up the collection of the results + Collection collection = new ArrayList(K); + for (KdNode kdNode : results) + collection.add((T) kdNode.id); + return collection; + } + + private static final void searchNode(T value, KdNode node, int K, TreeSet results, Set examined) { + examined.add(node); + + // Search node + KdNode lastNode = null; + Double lastDistance = Double.MAX_VALUE; + if (results.size() > 0) { + lastNode = results.last(); + lastDistance = lastNode.id.euclideanDistance(value); + } + Double nodeDistance = node.id.euclideanDistance(value); + if (nodeDistance.compareTo(lastDistance) < 0) { + if (results.size() == K && lastNode != null) + results.remove(lastNode); + results.add(node); + } else if (nodeDistance.equals(lastDistance)) { + results.add(node); + } else if (results.size() < K) { + results.add(node); + } + lastNode = results.last(); + lastDistance = lastNode.id.euclideanDistance(value); + + int axis = node.depth % node.k; + KdNode lesser = node.lesser; + KdNode greater = node.greater; + + // Search children branches, if axis aligned distance is less than + // current distance + if (lesser != null && !examined.contains(lesser)) { + examined.add(lesser); + + double nodePoint = Double.MIN_VALUE; + double valuePlusDistance = Double.MIN_VALUE; + if (axis == X_AXIS) { + nodePoint = node.id.x; + valuePlusDistance = value.x - lastDistance; + } else if (axis == Y_AXIS) { + nodePoint = node.id.y; + valuePlusDistance = value.y - lastDistance; + } else { + nodePoint = node.id.z; + valuePlusDistance = value.z - lastDistance; + } + boolean lineIntersectsCube = ((valuePlusDistance <= nodePoint) ? true : false); + + // Continue down lesser branch + if (lineIntersectsCube) + searchNode(value, lesser, K, results, examined); + } + if (greater != null && !examined.contains(greater)) { + examined.add(greater); + + double nodePoint = Double.MIN_VALUE; + double valuePlusDistance = Double.MIN_VALUE; + if (axis == X_AXIS) { + nodePoint = node.id.x; + valuePlusDistance = value.x + lastDistance; + } else if (axis == Y_AXIS) { + nodePoint = node.id.y; + valuePlusDistance = value.y + lastDistance; + } else { + nodePoint = node.id.z; + valuePlusDistance = value.z + lastDistance; + } + boolean lineIntersectsCube = ((valuePlusDistance >= nodePoint) ? true : false); + + // Continue down greater branch + if (lineIntersectsCube) + searchNode(value, greater, K, results, examined); + } + } + + /** + * Adds, in a specified queue, a given node and its related nodes (lesser, greater). + * + * @param node + * Node to check. May be null. + * + * @param results + * Queue containing all found entries. Must not be null. + */ + @SuppressWarnings("unchecked") + private static void search(final KdNode node, final Deque results) { + if (node != null) { + results.add((T) node.id); + search(node.greater, results); + search(node.lesser, results); + } + } + + protected static class EuclideanComparator implements Comparator { + + private final XYZPoint point; + + public EuclideanComparator(XYZPoint point) { + this.point = point; + } + + /** + * {@inheritDoc} + */ + @Override + public int compare(KdNode o1, KdNode o2) { + Double d1 = point.euclideanDistance(o1.id); + Double d2 = point.euclideanDistance(o2.id); + if (d1.compareTo(d2) < 0) + return -1; + else if (d2.compareTo(d1) < 0) + return 1; + return o1.id.compareTo(o2.id); + } + } + + /** + * Searches all entries from the first to the last entry. + * + * @return Iterator + * allowing to iterate through a collection containing all found entries. + */ + public Iterator iterator() { + final Deque results = new ArrayDeque(); + search(root, results); + return results.iterator(); + } + + /** + * Searches all entries from the last to the first entry. + * + * @return Iterator + * allowing to iterate through a collection containing all found entries. + */ + public Iterator reverse_iterator() { + final Deque results = new ArrayDeque(); + search(root, results); + return results.descendingIterator(); + } + + public static class KdNode implements Comparable { + + private final XYZPoint id; + private final int k; + private final int depth; + + private KdNode parent = null; + private KdNode lesser = null; + private KdNode greater = null; + + public KdNode(XYZPoint id) { + this.id = id; + this.k = 3; + this.depth = 0; + } + + public KdNode(XYZPoint id, int k, int depth) { + this.id = id; + this.k = k; + this.depth = depth; + } + + public static int compareTo(int depth, int k, XYZPoint o1, XYZPoint o2) { + int axis = depth % k; + if (axis == X_AXIS) + return X_COMPARATOR.compare(o1, o2); + if (axis == Y_AXIS) + return Y_COMPARATOR.compare(o1, o2); + return Z_COMPARATOR.compare(o1, o2); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return 31 * (this.k + this.depth + this.id.hashCode()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (!(obj instanceof KdNode)) + return false; + + KdNode kdNode = (KdNode) obj; + if (this.compareTo(kdNode) == 0) + return true; + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int compareTo(KdNode o) { + return compareTo(depth, k, this.id, o.id); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("k=").append(k); + builder.append(" depth=").append(depth); + builder.append(" id=").append(id.toString()); + return builder.toString(); + } + } + + public static class XYZPoint implements Comparable { + + protected final double x; + protected final double y; + protected final double z; + + /** + * z is defaulted to zero. + * + * @param x + * @param y + */ + public XYZPoint(double x, double y) { + this.x = x; + this.y = y; + this.z = 0; + } + + /** + * Default constructor + * + * @param x + * @param y + * @param z + */ + public XYZPoint(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Does not use R to calculate x, y, and z. Where R is the approximate radius of earth (e.g. 6371KM). + * @param latitude + * @param longitude + */ + public XYZPoint(Double latitude, Double longitude) { + x = cos(Math.toRadians(latitude)) * cos(Math.toRadians(longitude)); + y = cos(Math.toRadians(latitude)) * sin(Math.toRadians(longitude)); + z = sin(Math.toRadians(latitude)); + } + + public double getX() { + return x; + } + public double getY() { + return y; + } + public double getZ() { + return z; + } + + /** + * Computes the Euclidean distance from this point to the other. + * + * @param o1 + * other point. + * @return euclidean distance. + */ + public double euclideanDistance(XYZPoint o1) { + return euclideanDistance(o1, this); + } + + /** + * Computes the Euclidean distance from one point to the other. + * + * @param o1 + * first point. + * @param o2 + * second point. + * @return euclidean distance. + */ + private static final double euclideanDistance(XYZPoint o1, XYZPoint o2) { + return Math.sqrt(Math.pow((o1.x - o2.x), 2) + Math.pow((o1.y - o2.y), 2) + Math.pow((o1.z - o2.z), 2)); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return 31 * (int)(this.x + this.y + this.z); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (!(obj instanceof XYZPoint)) + return false; + + XYZPoint xyzPoint = (XYZPoint) obj; + if (Double.compare(this.x, xyzPoint.x)!=0) + return false; + if (Double.compare(this.y, xyzPoint.y)!=0) + return false; + if (Double.compare(this.z, xyzPoint.z)!=0) + return false; + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public int compareTo(XYZPoint o) { + int xComp = X_COMPARATOR.compare(this, o); + if (xComp != 0) + return xComp; + int yComp = Y_COMPARATOR.compare(this, o); + if (yComp != 0) + return yComp; + int zComp = Z_COMPARATOR.compare(this, o); + return zComp; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("("); + builder.append(x).append(", "); + builder.append(y).append(", "); + builder.append(z); + builder.append(")"); + return builder.toString(); + } + } +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesFileConnector.java b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesFileConnector.java new file mode 100755 index 0000000000..7990b09a29 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesFileConnector.java @@ -0,0 +1,138 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import org.jxmapviewer.OSMTileFactoryInfo; +import org.jxmapviewer.viewer.TileFactoryInfo; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; + +/** + * Wraps the connection to the MBTiles file\sqlite db. + */ +final class MBTilesFileConnector { + + private final static String DB_URL = "jdbc:sqlite:%s"; + private final static String TILE_QUERY = "SELECT tile_data FROM images WHERE tile_id = '%s'"; + private final static String FORMAT_QUERY = "SELECT value FROM metadata WHERE name='format'"; + private final TileFactoryInfo factoryInfo; + private final String connectionString; + + /** + * Returns whether or not the file at the given path is a mbtiles file. + * + * @param filePath Absolute path the the file. + * + * @return True if the file is an mbtiles file + * + * @throws SQLException + */ + static boolean isValidMBTileRasterFile(String filePath) throws SQLException { + Path p = Paths.get(filePath); + if (!p.toFile().exists()) { + return false; + } + + String path = filePath.replaceAll("\\\\", "/"); + String url = String.format(DB_URL, path); + + try (Connection connection = DriverManager.getConnection(url)) { + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(FORMAT_QUERY)) { + if (resultSet.next()) { + String format = resultSet.getString(1); + return format.equals("jpg"); + } + } + } + return false; + } + + /** + * Construct a new connection to the MBTile file. + * + * @param tileFilePath MBTiles file absolute path + * + * @throws GeoLocationDataException + */ + MBTilesFileConnector(String tileFilePath) throws GeoLocationDataException { + String path = tileFilePath.replaceAll("\\\\", "/"); + connectionString = String.format(DB_URL, path); + factoryInfo = new MBTilesInfo(); + } + + /** + * Returns the TileFacortyInfo object for the MBTile file. + * + * @return TileFactoryInfo object or null if the connection has been closed. + */ + TileFactoryInfo getInfo() { + return factoryInfo; + } + + /** + * Get the tile for the given tileID. + * + * @param tileID String tile ID in the format of zoom/x/y + * + * @return The tile image byte array or an empty array if the tile was not + * found. + * + * @throws GeoLocationDataException + */ + byte[] getTileBytes(String tileID) throws GeoLocationDataException { + String query = String.format(TILE_QUERY, tileID); + + try (Connection connection = DriverManager.getConnection(connectionString)) { + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(query)) { + if (resultSet.next()) { + return resultSet.getBytes(1); + } + } + } catch (SQLException ex) { + throw new GeoLocationDataException(String.format("Failed to get tile %s", tileID), ex); + } + return new byte[0]; + } + + /** + * Overload the existing OSMTileFacotyInfo to return a "url" specific to + * MBTiles. + */ + private final class MBTilesInfo extends OSMTileFactoryInfo { + + MBTilesInfo() { + super("MBTilesFile", ""); + } + + @Override + public String getTileUrl(int x, int y, int zoom) { + // OSM zoom levels are reversed from how the TileFactory deals with + // them. + int osmZoom = getTotalMapZoom() - zoom; + return String.format("%d/%d/%d", osmZoom, x, y); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTile.java b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTile.java new file mode 100755 index 0000000000..848f31511c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTile.java @@ -0,0 +1,122 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.image.BufferedImage; +import java.lang.ref.SoftReference; + +import org.jxmapviewer.viewer.Tile; + +/** + * Provides a MBTile specific implementation of Tile. + * + * This class borrows functionality from Tile. + */ +final class MBTilesTile extends Tile { + + private SoftReference image = new SoftReference<>(null); + private Priority priority = Priority.High; + private boolean loaded = false; + private final String tileID; + + /** + * Construct an empty tile. + * + * @param x + * @param y + * @param zoom + */ + MBTilesTile(int x, int y, int zoom) { + super(x, y, zoom); + tileID = null; + } + + /** + * Construct a new tile. + * + * @param x Tile row + * @param y Tile column + * @param zoom Tile Zoom level + * @param tileID Tile identifier + * @param priority Priority for loading the tile + */ + MBTilesTile(int x, int y, int zoom, String tileID, Priority priority) { + super(x, y, zoom); + this.priority = priority; + this.tileID = tileID; + } + + /** + * Sets the image for this Tile. + * + * @param image + */ + void setImage(BufferedImage image) { + this.image = new SoftReference<>(image); + setLoaded(true); + } + + /** + * Indicates if this tile's underlying image has been successfully loaded + * yet. + * + * @return true if the Tile has been loaded + */ + @Override + public synchronized boolean isLoaded() { + return loaded; + } + + synchronized void setLoaded(boolean loaded) { + boolean old = isLoaded(); + this.loaded = loaded; + firePropertyChange("loaded", old, isLoaded()); + } + + @Override + public BufferedImage getImage() { + BufferedImage img = image.get(); + if (img == null) { + setLoaded(false); + } + return img; + } + + @Override + public Priority getPriority() { + return priority; + } + + @Override + public void setPriority(Priority priority) { + this.priority = priority; + } + + /** + * Overloading the original version of this function to return the tileID + * for this tile. + * + * @return tileID or null if none was set + */ + @Override + public String getURL() { + return tileID; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTileFactory.java b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTileFactory.java new file mode 100755 index 0000000000..372d2e1561 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTileFactory.java @@ -0,0 +1,351 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.logging.Level; + +import javax.imageio.ImageIO; +import javax.swing.SwingUtilities; + +import org.jxmapviewer.viewer.Tile; +import org.jxmapviewer.viewer.TileCache; +import org.jxmapviewer.viewer.TileFactory; +import org.jxmapviewer.viewer.util.GeoUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; + +/** + * This TileFactory class borrows from org.jxmapviewer.viewer.AbstractTileFactory, changing to support + * the getting the tiles from a database instead the web. + * + * @see AbstractTileFactory + * + */ +final class MBTilesTileFactory extends TileFactory { + + private static final Logger logger = Logger.getLogger(MBTilesTileFactory.class.getName()); + + private volatile int pendingTiles = 0; + private static final int THREAD_POOL_SIZE = 4; + private ExecutorService service; + + private final Map tileMap; + + private final TileCache cache; + + private MBTilesFileConnector connector; + + /** + * Construct a new TileFactory for the MBTiles file at the given location. + * + * @param filePath + * + * @throws GeoLocationDataException + */ + MBTilesTileFactory(String filePath) throws GeoLocationDataException { + this(new MBTilesFileConnector(filePath)); + } + + /** + * Construct a new TileFacotry. + * + * @param connector + */ + private MBTilesTileFactory(MBTilesFileConnector connector) { + super(connector.getInfo()); + this.connector = connector; + cache = new TileCache(); + tileMap = new HashMap<>(); + } + + /** + * Returns the tile that is located at the given tile point for this zoom. + * + * @param x Tile column + * @param y Tile row + * @param zoom Current zoom level + * + * @return A new Tile object + * + */ + @Override + public Tile getTile(int x, int y, int zoom) { + return getTile(x, y, zoom, true); + } + + /** + * Returns the tile object for the given location. + * + * @param tpx + * @param tpy + * @param zoom + * @param eagerLoad + * + * @return + */ + private Tile getTile(int tpx, int tpy, int zoom, boolean eagerLoad) { + // wrap the tiles horizontally --> mod the X with the max width + // and use that + int tileX = tpx;// tilePoint.getX(); + int numTilesWide = (int) getMapSize(zoom).getWidth(); + if (tileX < 0) { + tileX = numTilesWide - (Math.abs(tileX) % numTilesWide); + } + + tileX %= numTilesWide; + int tileY = tpy; + + String url = getInfo().getTileUrl(tileX, tileY, zoom); + + Tile.Priority pri = Tile.Priority.High; + if (!eagerLoad) { + pri = Tile.Priority.Low; + } + Tile tile; + if (!tileMap.containsKey(url)) { + // If its not a valid tile location return an empty tile. + if (!GeoUtil.isValidTile(tileX, tileY, zoom, getInfo())) { + tile = new MBTilesTile(tileX, tileY, zoom); + } else { + tile = new MBTilesTile(tileX, tileY, zoom, url, pri); + startLoading(tile); + } + tileMap.put(url, tile); + } else { + tile = tileMap.get(url); + // If the tile is in the tileMap, but the image is not loaded yet, + // bump the priority. + if (tile.getPriority() == Tile.Priority.Low && eagerLoad && !tile.isLoaded()) { + promote(tile); + } + } + + return tile; + } + + /** + * Returns the TileCache. + * + * @return the tile cache + */ + TileCache getTileCache() { + return cache; + } + + /** + * ==== Threaded Tile loading code === + */ + /** + * Thread pool for loading the tiles + */ + private final BlockingQueue tileQueue = new PriorityBlockingQueue<>(5, new Comparator() { + @Override + public int compare(Tile o1, Tile o2) { + if (o1.getPriority() == Tile.Priority.Low && o2.getPriority() == Tile.Priority.High) { + return 1; + } + if (o1.getPriority() == Tile.Priority.High && o2.getPriority() == Tile.Priority.Low) { + return -1; + } + return 0; + + } + }); + + /** + * Subclasses may override this method to provide their own executor + * services. This method will be called each time a tile needs to be loaded. + * Implementations should cache the ExecutorService when possible. + * + * @return ExecutorService to load tiles with + */ + synchronized ExecutorService getService() { + if (service == null) { + // System.out.println("creating an executor service with a threadpool of size " + threadPoolSize); + service = Executors.newFixedThreadPool(THREAD_POOL_SIZE, new ThreadFactory() { + private int count = 0; + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r, "tile-pool-" + count++); + t.setPriority(Thread.MIN_PRIORITY); + t.setDaemon(true); + return t; + } + }); + } + return service; + } + + @Override + public void dispose() { + if (service != null) { + service.shutdown(); + service = null; + } + } + + @Override + protected synchronized void startLoading(Tile tile) { + if (tile.isLoading()) { + return; + } + pendingTiles++; + tile.setLoading(true); + try { + tileQueue.put(tile); + getService().submit(new TileRunner()); + } catch (InterruptedException ex) { + + } + } + + /** + * Increase the priority of this tile so it will be loaded sooner. + * + * @param tile the tile + */ + synchronized void promote(Tile tile) { + if (tileQueue.contains(tile)) { + try { + tileQueue.remove(tile); + tile.setPriority(Tile.Priority.High); + tileQueue.put(tile); + } catch (InterruptedException ex) { + + } + } + } + + /** + * @return the number of pending (loading or queues) tiles + */ + int getPendingTiles() { + return pendingTiles; + } + + /** + * An inner class which actually loads the tiles. Used by the thread queue. + * Subclasses can override this via {@link #createTileRunner(Tile)} if + * necessary. + */ + private class TileRunner implements Runnable { + + /** + * Gets the full URI of a tile. + * + * @param tile the tile + * + * @throws URISyntaxException if the URI is invalid + * @return a URI for the tile + */ + protected URI getURI(Tile tile) throws URISyntaxException { + if (tile.getURL() == null) { + return null; + } + return new URI(tile.getURL()); + } + + @Override + public void run() { + /* + * Attempt to load the tile from . If loading fails, retry two more + * times. If all attempts fail, nothing else is done. This way, if + * there is some kind of failure, the pooled thread can try to load + * other tiles. + */ + final Tile tile = tileQueue.remove(); + + int remainingAttempts = 3; + while (!tile.isLoaded() && remainingAttempts > 0) { + remainingAttempts--; + try { + URI uri = getURI(tile); + BufferedImage img = cache.get(uri); + if (img == null) { + img = getImage(uri); + } + if (img != null) { + addImageToTile(tile, img); + } + } catch (OutOfMemoryError memErr) { + cache.needMoreMemory(); + } catch (IOException | GeoLocationDataException | InterruptedException | InvocationTargetException | URISyntaxException ex) { + if (remainingAttempts == 0) { + logger.log(Level.SEVERE, String.format("Failed to load a tile at URL: %s, stopping", tile.getURL()), ex); + } else { + logger.log(Level.WARNING, "Failed to load a tile at URL: " + tile.getURL() + ", retrying", ex); + } + } + } + tile.setLoading(false); + } + } + + /** + * + * @param uri + * @return + * @throws IOException + * @throws GeoLocationDataException + */ + private BufferedImage getImage(URI uri ) throws IOException, GeoLocationDataException{ + BufferedImage img = null; + byte[] bimg = connector.getTileBytes(uri.toString()); + if (bimg != null && bimg.length > 0) { + img = ImageIO.read(new ByteArrayInputStream(bimg)); + cache.put(uri, bimg, img); + } + return img; + } + + /** + * + * @param tile + * @param image + * @throws InterruptedException + * @throws InvocationTargetException + */ + private void addImageToTile(Tile tile, BufferedImage image) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if (tile instanceof MBTilesTile) { + ((MBTilesTile) tile).setImage(image); + } + pendingTiles--; + fireTileLoadedEvent(tile); + } + }); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form new file mode 100755 index 0000000000..bea654be40 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form @@ -0,0 +1,95 @@ + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java new file mode 100755 index 0000000000..7be6ac25a8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -0,0 +1,675 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; +import javax.swing.JSeparator; +import javax.swing.Popup; +import javax.swing.PopupFactory; +import javax.swing.Timer; +import javax.swing.event.MouseInputListener; +import org.jxmapviewer.JXMapViewer; +import org.jxmapviewer.OSMTileFactoryInfo; +import org.jxmapviewer.VirtualEarthTileFactoryInfo; +import org.jxmapviewer.input.CenterMapListener; +import org.jxmapviewer.input.PanMouseInputListener; +import org.jxmapviewer.input.ZoomMouseWheelListenerCursor; +import org.jxmapviewer.viewer.DefaultTileFactory; +import org.jxmapviewer.viewer.GeoPosition; +import org.jxmapviewer.viewer.TileFactory; +import org.jxmapviewer.viewer.TileFactoryInfo; +import org.jxmapviewer.viewer.Waypoint; +import org.jxmapviewer.viewer.WaypointPainter; +import org.jxmapviewer.viewer.WaypointRenderer; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; +import org.sleuthkit.datamodel.TskCoreException; +import javax.imageio.ImageIO; +import org.jxmapviewer.viewer.DefaultWaypointRenderer; + +/** + * The map panel. This panel contains the jxmapviewer MapViewer + */ +final public class MapPanel extends javax.swing.JPanel { + + static final String CURRENT_MOUSE_GEOPOSITION = "CURRENT_MOUSE_GEOPOSITION"; + private static final Logger logger = Logger.getLogger(MapPanel.class.getName()); + + private static final long serialVersionUID = 1L; + private boolean zoomChanging; + private KdTree waypointTree; + + private Popup currentPopup; + private final PopupFactory popupFactory; + + private static final int POPUP_WIDTH = 300; + private static final int POPUP_HEIGHT = 200; + private static final int POPUP_MARGIN = 10; + + private MapWaypoint currentlySelectedWaypoint; + + /** + * Creates new form MapPanel + */ + @Messages({ + "MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source.", + "MapPanel_connection_failure_message_title=Connection Failure" + }) + public MapPanel() { + initComponents(); + + zoomChanging = false; + currentPopup = null; + popupFactory = new PopupFactory(); + + // ComponentListeners do not have a concept of resize event "complete" + // therefore if we move the popup as the window resizes there will be + // a weird blinking behavior. Using the CompnentResizeEndListener the + // popup will move to its corner one the resize is completed. + this.addComponentListener(new ComponentResizeEndListener() { + @Override + public void resizeTimedOut() { + showDetailsPopup(); + } + }); + + UserPreferences.addChangeListener(new PreferenceChangeListener() { + @Override + public void preferenceChange(PreferenceChangeEvent evt) { + try { + // Tell the old factory to cleanup + mapViewer.getTileFactory().dispose(); + + mapViewer.setTileFactory(getTileFactory()); + initializeZoomSlider(); + } catch (GeoLocationDataException ex) { + logger.log(Level.SEVERE, "Failed to connect to new geolocation tile server.", ex); //NON-NLS + JOptionPane.showMessageDialog(MapPanel.this, + Bundle.MapPanel_connection_failure_message(), + Bundle.MapPanel_connection_failure_message_title(), + JOptionPane.ERROR_MESSAGE); + MessageNotifyUtil.Notify.error( + Bundle.MapPanel_connection_failure_message_title(), + Bundle.MapPanel_connection_failure_message()); + } + } + }); + } + + /** + * Get a list of the waypoints that are currently visible in the viewport. + * + * @return A list of waypoints or empty list if none were found. + */ + List getVisibleWaypoints() { + + Rectangle viewport = mapViewer.getViewportBounds(); + List waypoints = new ArrayList<>(); + + Iterator iterator = waypointTree.iterator(); + while (iterator.hasNext()) { + MapWaypoint waypoint = iterator.next(); + if (viewport.contains(mapViewer.getTileFactory().geoToPixel(waypoint.getPosition(), mapViewer.getZoom()))) { + waypoints.add(waypoint); + } + } + + return waypoints; + } + + /** + * Initialize the map. + */ + void initMap() throws GeoLocationDataException { + + TileFactory tileFactory = getTileFactory(); + mapViewer.setTileFactory(tileFactory); + + // Add Mouse interactions + MouseInputListener mia = new PanMouseInputListener(mapViewer); + mapViewer.addMouseListener(mia); + mapViewer.addMouseMotionListener(mia); + + mapViewer.addMouseListener(new CenterMapListener(mapViewer)); + mapViewer.addMouseWheelListener(new ZoomMouseWheelListenerCursor(mapViewer)); + + // Listen to the map for a change in zoom so that we can update the slider. + mapViewer.addPropertyChangeListener("zoom", new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + zoomSlider.setValue(mapViewer.getZoom()); + } + }); + + zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel()); + zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel()); + + setZoom(tileFactory.getInfo().getMaximumZoomLevel() - 1); + + mapViewer.setCenterPosition(new GeoPosition(0, 0)); + + // Basic painters for the way points. + WaypointPainter waypointPainter = new WaypointPainter() { + @Override + public Set getWaypoints() { + //To assure that the currentlySelectedWaypoint is visible it needs + // to be painted last. LinkedHashSet has a predicable ordering. + Set set = new LinkedHashSet<>(); + if (waypointTree != null) { + Iterator iterator = waypointTree.iterator(); + while (iterator.hasNext()) { + MapWaypoint point = iterator.next(); + if (point != currentlySelectedWaypoint) { + set.add(point); + } + } + // Add the currentlySelectedWaypoint to the end so that + // it will be painted last. + if (currentlySelectedWaypoint != null) { + set.add(currentlySelectedWaypoint); + } + } + return set; + } + }; + + try { + waypointPainter.setRenderer(new MapWaypointRenderer()); + } catch (IOException ex) { + logger.log(Level.WARNING, "Failed to load waypoint image resource, using DefaultWaypointRenderer", ex); + waypointPainter.setRenderer(new DefaultWaypointRenderer()); + } + + mapViewer.setOverlayPainter(waypointPainter); + } + + /** + * Setup the zoom slider based on the current tileFactory. + */ + void initializeZoomSlider() { + TileFactory tileFactory = mapViewer.getTileFactory(); + zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel()); + zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel()); + + zoomSlider.repaint(); + zoomSlider.revalidate(); + } + + /** + * Create the TileFactory object based on the user preference. + * + * @return + */ + private TileFactory getTileFactory() throws GeoLocationDataException { + switch (GeolocationSettingsPanel.GeolocationDataSourceType.getOptionForValue(UserPreferences.getGeolocationtTileOption())) { + case ONLINE_USER_DEFINED_OSM_SERVER: + return new DefaultTileFactory(createOnlineOSMFactory(UserPreferences.getGeolocationOsmServerAddress())); + case OFFLINE_OSM_ZIP: + return new DefaultTileFactory(createOSMZipFactory(UserPreferences.getGeolocationOsmZipPath())); + case OFFILE_MBTILES_FILE: + return new MBTilesTileFactory(UserPreferences.getGeolocationMBTilesFilePath()); + default: + return new DefaultTileFactory(new VirtualEarthTileFactoryInfo(VirtualEarthTileFactoryInfo.MAP)); + } + } + + /** + * Create the TileFactoryInfo for an online OSM tile server. + * + * @param address Tile server address + * + * @return TileFactoryInfo object for server address. + * + * @throws GeoLocationDataException + */ + private TileFactoryInfo createOnlineOSMFactory(String address) throws GeoLocationDataException { + if (address.isEmpty()) { + throw new GeoLocationDataException("Invalid user preference for OSM user define tile server. Address is an empty string."); + } else { + TileFactoryInfo info = new OSMTileFactoryInfo("User Defined Server", address); + return info; + } + } + + /** + * Create the TileFactoryInfo for OSM zip File + * + * @param zipPath Path to zip file. + * + * @return TileFactoryInfo for zip file. + * + * @throws GeoLocationDataException + */ + private TileFactoryInfo createOSMZipFactory(String path) throws GeoLocationDataException { + if (path.isEmpty()) { + throw new GeoLocationDataException("Invalid OSM tile Zip file. User preference value is empty string."); + } else { + File file = new File(path); + if (!file.exists() || !file.canRead()) { + throw new GeoLocationDataException("Invalid OSM tile zip file. Unable to read file: " + path); + } + + String zipPath = path.replaceAll("\\\\", "/"); + + return new OSMTileFactoryInfo("ZIP archive", "jar:file:/" + zipPath + "!"); //NON-NLS + } + } + + /** + * Stores the given List of MapWaypoint in a KdTree object. + * + * @param waypoints List of waypoints + */ + void setWaypoints(List waypoints) { + waypointTree = new KdTree<>(); + + for (MapWaypoint waypoint : waypoints) { + waypointTree.add(waypoint); + } + + mapViewer.repaint(); + } + + /** + * Set the current zoom level. + * + * @param zoom + */ + void setZoom(int zoom) { + zoomChanging = true; + mapViewer.setZoom(zoom); + zoomSlider.setValue((zoomSlider.getMaximum() + zoomSlider.getMinimum()) - zoom); + zoomChanging = false; + } + + /** + * Remove all of the way points from the map. + */ + void clearWaypoints() { + waypointTree = null; + } + + /** + * Finds the waypoint nearest to the given and point and shows the popup + * menu for that waypoint. + * + * @param point Current mouse click location + */ + private void showPopupMenu(Point point) { + try { + MapWaypoint waypoint = findClosestWaypoint(point); + showPopupMenu(waypoint, point); + // Change the details popup to the currently selected point only if + // it the popup is currently visible + if (waypoint != null && !waypoint.equals(currentlySelectedWaypoint)) { + currentlySelectedWaypoint = waypoint; + if(currentPopup != null) { + showDetailsPopup(); + } + mapViewer.repaint(); + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Failed to show popup for waypoint", ex); + } + } + + /** + * Show the popup menu for the given waypoint and location. + * + * @param waypoint Selected waypoint + * @param point Current mouse click location + */ + private void showPopupMenu(MapWaypoint waypoint, Point point) throws TskCoreException { + if (waypoint == null) { + return; + } + + JMenuItem[] items = waypoint.getMenuItems(); + JPopupMenu popupMenu = new JPopupMenu(); + for (JMenuItem menu : items) { + + if (menu != null) { + popupMenu.add(menu); + } else { + popupMenu.add(new JSeparator()); + } + } + popupMenu.show(mapViewer, point.x, point.y); + } + + /** + * Show the detailsPopup for the currently selected waypoint. + */ + private void showDetailsPopup() { + if (currentlySelectedWaypoint != null) { + if (currentPopup != null) { + currentPopup.hide(); + } + + WaypointDetailPanel detailPane = new WaypointDetailPanel(); + detailPane.setWaypoint(currentlySelectedWaypoint); + detailPane.setPreferredSize(new Dimension(POPUP_WIDTH, POPUP_HEIGHT)); + detailPane.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (currentPopup != null) { + currentPopup.hide(); + currentPopup = null; + } + } + + }); + + Point popupLocation = getLocationForDetailsPopup(); + + currentPopup = popupFactory.getPopup(this, detailPane, popupLocation.x, popupLocation.y); + currentPopup.show(); + + mapViewer.repaint(); + } + } + + /** + * Calculate the upper left corner on the screen for the details popup. + * + * @return Upper left corner location for the details popup. + */ + private Point getLocationForDetailsPopup() { + Point panelCorner = this.getLocationOnScreen(); + int width = this.getWidth(); + + int popupX = panelCorner.x + width - POPUP_WIDTH - POPUP_MARGIN; + int popupY = panelCorner.y + POPUP_MARGIN; + + return new Point(popupX, popupY); + } + + /** + * Find the waypoint that is closest to the given mouse click point. + * + * @param mouseClickPoint The mouse click point + * + * @return A waypoint that is within 10 pixels of the given point, or null + * if none was found. + */ + private MapWaypoint findClosestWaypoint(Point mouseClickPoint) { + if (waypointTree == null) { + return null; + } + + // Convert the mouse click location to latitude & longitude + GeoPosition geopos = mapViewer.getTileFactory().pixelToGeo(mouseClickPoint, mapViewer.getZoom()); + + // Get the 5 nearest neightbors to the point + Collection waypoints = waypointTree.nearestNeighbourSearch(20, MapWaypoint.getDummyWaypoint(geopos)); + + if (waypoints == null || waypoints.isEmpty()) { + return null; + } + + Iterator iterator = waypoints.iterator(); + + // These maybe the points closest to lat/log was clicked but + // that doesn't mean they are close in terms of pixles. + while (iterator.hasNext()) { + MapWaypoint nextWaypoint = iterator.next(); + + Point2D point = mapViewer.getTileFactory().geoToPixel(nextWaypoint.getPosition(), mapViewer.getZoom()); + + Rectangle rect = mapViewer.getViewportBounds(); + Point converted_gp_pt = new Point((int) point.getX() - rect.x, + (int) point.getY() - rect.y); + + if (converted_gp_pt.distance(mouseClickPoint) < 10) { + return nextWaypoint; + } + } + + return null; + } + + /** + * Abstract listener class to listen for the completion of a resize event. + * + * ComponentListener does not provide support for listening for the + * completion of a resize event. In order to provide this functionality + * ComponentResizeEndListener as a time that is restarted every time + * componentResize is called. When the timer finally runs out the assumption + * is that the resize has completed. + */ + public abstract class ComponentResizeEndListener + extends ComponentAdapter + implements ActionListener { + + private final Timer timer; + private static final int DEFAULT_TIMEOUT = 200; + + /** + * Constructs a new Listener with a default timeout of DEFAULT_TIMEOUT + * milliseconds. + */ + public ComponentResizeEndListener() { + this(DEFAULT_TIMEOUT); + } + + /** + * Constructs a new listener with the given timeout value. + * + * @param delayMS timeout value in milliseconds + */ + public ComponentResizeEndListener(int delayMS) { + timer = new Timer(delayMS, this); + timer.setRepeats(false); + timer.setCoalesce(false); + } + + @Override + public void componentResized(ComponentEvent e) { + timer.restart(); + } + + @Override + public void actionPerformed(ActionEvent e) { + resizeTimedOut(); + } + + /** + * Called when the resize event has completed\timed out + */ + public abstract void resizeTimedOut(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + mapViewer = new org.jxmapviewer.JXMapViewer(); + zoomPanel = new javax.swing.JPanel(); + zoomSlider = new javax.swing.JSlider(); + + setFocusable(false); + setLayout(new java.awt.BorderLayout()); + + mapViewer.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() { + public void mouseMoved(java.awt.event.MouseEvent evt) { + mapViewerMouseMoved(evt); + } + }); + mapViewer.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + mapViewerMouseClicked(evt); + } + public void mousePressed(java.awt.event.MouseEvent evt) { + mapViewerMousePressed(evt); + } + public void mouseReleased(java.awt.event.MouseEvent evt) { + mapViewerMouseReleased(evt); + } + }); + mapViewer.setLayout(new java.awt.GridBagLayout()); + + zoomPanel.setFocusable(false); + zoomPanel.setOpaque(false); + zoomPanel.setRequestFocusEnabled(false); + + zoomSlider.setMaximum(15); + zoomSlider.setMinimum(10); + zoomSlider.setMinorTickSpacing(1); + zoomSlider.setOrientation(javax.swing.JSlider.VERTICAL); + zoomSlider.setPaintTicks(true); + zoomSlider.setSnapToTicks(true); + zoomSlider.setMinimumSize(new java.awt.Dimension(35, 100)); + zoomSlider.setOpaque(false); + zoomSlider.setPreferredSize(new java.awt.Dimension(35, 190)); + zoomSlider.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + zoomSliderStateChanged(evt); + } + }); + + javax.swing.GroupLayout zoomPanelLayout = new javax.swing.GroupLayout(zoomPanel); + zoomPanel.setLayout(zoomPanelLayout); + zoomPanelLayout.setHorizontalGroup( + zoomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(zoomPanelLayout.createSequentialGroup() + .addGap(0, 0, 0) + .addComponent(zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + zoomPanelLayout.setVerticalGroup( + zoomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, zoomPanelLayout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, 0)) + ); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + mapViewer.add(zoomPanel, gridBagConstraints); + + add(mapViewer, java.awt.BorderLayout.CENTER); + }// //GEN-END:initComponents + + private void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_zoomSliderStateChanged + if (!zoomChanging) { + setZoom(zoomSlider.getValue()); + } + }//GEN-LAST:event_zoomSliderStateChanged + + private void mapViewerMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMousePressed + if (evt.isPopupTrigger()) { + showPopupMenu(evt.getPoint()); + } + }//GEN-LAST:event_mapViewerMousePressed + + private void mapViewerMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseReleased + if (evt.isPopupTrigger()) { + showPopupMenu(evt.getPoint()); + } + }//GEN-LAST:event_mapViewerMouseReleased + + private void mapViewerMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseMoved + GeoPosition geopos = mapViewer.getTileFactory().pixelToGeo(evt.getPoint(), mapViewer.getZoom()); + firePropertyChange(CURRENT_MOUSE_GEOPOSITION, null, geopos); + }//GEN-LAST:event_mapViewerMouseMoved + + private void mapViewerMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseClicked + if(!evt.isPopupTrigger() && (evt.getButton() == MouseEvent.BUTTON1)) { + currentlySelectedWaypoint = findClosestWaypoint(evt.getPoint()); + showDetailsPopup(); + } + }//GEN-LAST:event_mapViewerMouseClicked + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private org.jxmapviewer.JXMapViewer mapViewer; + private javax.swing.JPanel zoomPanel; + private javax.swing.JSlider zoomSlider; + // End of variables declaration//GEN-END:variables + + /** + * Renderer for the map waypoints. + */ + private class MapWaypointRenderer implements WaypointRenderer { + private final BufferedImage defaultWaypointImage; + private final BufferedImage selectedWaypointImage; + + /** + * Construct a WaypointRenederer + * + * @throws IOException + */ + MapWaypointRenderer() throws IOException { + defaultWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_teal.png")); + selectedWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_yellow.png")); + } + + @Override + public void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, Waypoint waypoint) { + Point2D point = jxmv.getTileFactory().geoToPixel(waypoint.getPosition(), jxmv.getZoom()); + + int x = (int)point.getX(); + int y = (int)point.getY(); + + BufferedImage image = (waypoint == currentlySelectedWaypoint ? selectedWaypointImage: defaultWaypointImage); + + (gd.create()).drawImage(image, x -image.getWidth() / 2, y -image.getHeight(), null); + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java new file mode 100755 index 0000000000..61b4800d7a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java @@ -0,0 +1,380 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.event.ActionEvent; +import java.awt.image.BufferedImage; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.logging.Level; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ImageIcon; +import javax.swing.JMenuItem; +import org.jxmapviewer.viewer.GeoPosition; +import org.openide.util.NbBundle.Messages; +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.ImageUtils; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; +import org.sleuthkit.autopsy.datamodel.FileNode; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; +import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; +import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction; +import org.sleuthkit.autopsy.directorytree.ExtractAction; +import org.sleuthkit.autopsy.directorytree.actionhelpers.ExtractActionHelper; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; +import org.sleuthkit.autopsy.geolocation.datamodel.Route; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint; +import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder; +import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * A Wrapper for the datamodel Waypoint class that implements the jxmapviewer + * Waypoint interface. + * + */ +final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewer.Waypoint { + + private static final Logger logger = Logger.getLogger(MapWaypoint.class.getName()); + private final static String HTML_PROP_FORMAT = "%s: %s
"; + static private final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.US); + + private final Waypoint dataModelWaypoint; + private final GeoPosition position; + + /** + * Returns a list of waypoints for the currently open case. + * + * @param skCase Current case + * + * @return list of waypoints, list will be empty if no waypoints were found + * + * @throws GeoLocationDataException + */ + static List getWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + List points = WaypointBuilder.getAllWaypoints(skCase); + + List routes = Route.getRoutes(skCase); + for (Route route : routes) { + points.addAll(route.getRoute()); + } + + List mapPoints = new ArrayList<>(); + + for (Waypoint point : points) { + mapPoints.add(new MapWaypoint(point)); + } + + return mapPoints; + } + + /** + * Returns a list of of MapWaypoint objects for the given list of + * datamodel.Waypoint objects. + * + * @param dmWaypoints + * + * @return List of MapWaypoint objects. List will be empty if dmWaypoints + * was empty or null. + */ + static List getWaypoints(List dmWaypoints) { + List mapPoints = new ArrayList<>(); + + if (dmWaypoints != null) { + + for (Waypoint point : dmWaypoints) { + mapPoints.add(new MapWaypoint(point)); + } + } + + return mapPoints; + } + + /** + * Helper function to get a list of data model waypoints from a list of + * MapWaypoints. + * + * @param mapWaypoints + * + * @return A list of Waypoint objects, or empty list if mapWaypoints was + * null or empty. + */ + static List getDataModelWaypoints(List mapWaypoints) { + List waypoints = new ArrayList<>(); + + if (mapWaypoints != null) { + for (MapWaypoint point : mapWaypoints) { + waypoints.add(point.dataModelWaypoint); + } + } + + return waypoints; + } + + /** + * Returns a MapWaypoint without a reference to the datamodel waypoint. + * + * @param position Location for new waypoint + * + * @return New MapWaypoint with dataModelWaypoint set to null + */ + static MapWaypoint getDummyWaypoint(GeoPosition position) { + return new MapWaypoint(position); + } + + /** + * Private constructor for MapWaypoint. + * + * @param dataModelWaypoint datamodel.Waypoint to wrap + */ + private MapWaypoint(Waypoint dataModelWaypoint) { + super(dataModelWaypoint.getLatitude(), dataModelWaypoint.getLongitude()); + this.dataModelWaypoint = dataModelWaypoint; + position = new GeoPosition(dataModelWaypoint.getLatitude(), dataModelWaypoint.getLongitude()); + } + + /** + * Construct a waypoint from a GeoPosition with a null datamodel.Waypoint. + * + * @param position GeoPosition for the waypoint + */ + private MapWaypoint(GeoPosition position) { + super(position.getLatitude(), position.getLongitude()); + dataModelWaypoint = null; + this.position = position; + } + + /** + * Returns the waypoint image or null of one is not currently set. + * + * @return the image for this waypoint + */ + ImageIcon getImage() { + if (dataModelWaypoint.getImage() != null && ImageUtils.isImageThumbnailSupported(dataModelWaypoint.getImage())) { + BufferedImage buffImage = ImageUtils.getThumbnail(dataModelWaypoint.getImage(), 150); + return new ImageIcon(buffImage); + } + + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public GeoPosition getPosition() { + return position; + } + + /** + * Returns the display label for the waypoint. + * + * @return Waypoint label + */ + String getLabel() { + return dataModelWaypoint.getLabel(); + } + + /** + * Returns the details of this waypoint formatted as HTML. + * + * @return HTML formatted string + */ + String getHTMLFormattedWaypointDetails() { + return getFormattedDetails(dataModelWaypoint); + } + + /** + * Returns a list of JMenuItems for the waypoint. The list list may contain + * nulls which should be removed or replaced with JSeparators. + * + * @return List of menu items + * + * @throws TskCoreException + */ + JMenuItem[] getMenuItems() throws TskCoreException { + List menuItems = new ArrayList<>(); + BlackboardArtifact artifact = dataModelWaypoint.getArtifact(); + Content content = artifact.getSleuthkitCase().getContentById(artifact.getObjectID()); + + menuItems.addAll(getTimelineMenuItems(dataModelWaypoint.getArtifact())); + menuItems.addAll(getDataModelActionFactoryMenuItems(artifact, content)); + menuItems.add(DeleteFileContentTagAction.getInstance().getMenuForFiles(Arrays.asList((AbstractFile) content))); + menuItems.add(DeleteFileBlackboardArtifactTagAction.getInstance().getMenuForArtifacts(Arrays.asList(artifact))); + + return menuItems.toArray(new JMenuItem[0]); + } + + /** + * Gets the Timeline Menu Items for this artifact. + * + * @param artifact Selected artifact + * + * @return List of timeline menu items. + */ + private List getTimelineMenuItems(BlackboardArtifact artifact) { + List menuItems = new ArrayList<>(); + //if this artifact has a time stamp add the action to view it in the timeline + try { + if (ViewArtifactInTimelineAction.hasSupportedTimeStamp(artifact)) { + menuItems.add(new JMenuItem(new ViewArtifactInTimelineAction(artifact))); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error getting arttribute(s) from blackboard artifact %d.", artifact.getArtifactID()), ex); //NON-NLS + } + + return menuItems; + } + + /** + * Use the DateModelActionsFactory to get some of the basic actions for the + * waypoint. The advantage to using the DataModelActionsFactory is that the + * menu items can be put in a consistent order with other parts of the UI. + * + * @param artifact Artifact for the selected waypoint + * @param content Artifact content + * + * @return List of JMenuItems for the DataModelActionFactory actions + */ + @Messages({ + "MayWaypoint_ExternalViewer_label=Open in ExternalViewer" + }) + private List getDataModelActionFactoryMenuItems(BlackboardArtifact artifact, Content content) { + List menuItems = new ArrayList<>(); + + List actions = DataModelActionsFactory.getActions(content, true); + for (Action action : actions) { + if (action == null) { + menuItems.add(null); + } else if (action instanceof ExportCSVAction) { + // Do nothing we don't need this menu item. + } else if (action instanceof AddContentTagAction) { + menuItems.add(((AddContentTagAction) action).getMenuForContent(Arrays.asList((AbstractFile) content))); + } else if (action instanceof AddBlackboardArtifactTagAction) { + menuItems.add(((AddBlackboardArtifactTagAction) action).getMenuForContent(Arrays.asList(artifact))); + } else if (action instanceof ExternalViewerShortcutAction) { + // Replace with an ExternalViewerAction + ExternalViewerAction newAction = new ExternalViewerAction(Bundle.MayWaypoint_ExternalViewer_label(), new FileNode((AbstractFile) content)); + menuItems.add(new JMenuItem(newAction)); + } else if (action instanceof ExtractAction) { + menuItems.add(new JMenuItem(new WaypointExtractAction((AbstractFile) content))); + } else { + menuItems.add(new JMenuItem(action)); + } + } + return menuItems; + } + + /** + * Get the nicely formatted details for the given waypoint. + * + * @param point Waypoint object + * @param header String details header + * + * @return HTML formatted String of details for given waypoint + */ + private String getFormattedDetails(Waypoint point) { + StringBuilder result = new StringBuilder(); //NON-NLS + + result.append("").append(formatAttribute("Name", point.getLabel())); + + Long timestamp = point.getTimestamp(); + if (timestamp != null) { + result.append(formatAttribute("Timestamp", getTimeStamp(timestamp))); + } + + result.append(formatAttribute("Latitude", point.getLatitude().toString())) + .append(formatAttribute("Longitude", point.getLongitude().toString())); + + if (point.getAltitude() != null) { + result.append(formatAttribute("Altitude", point.getAltitude().toString())); + } + + List list = point.getOtherProperties(); + for (Waypoint.Property prop : list) { + String value = prop.getValue(); + if (value != null && !value.isEmpty()) { + result.append(formatAttribute(prop.getDisplayName(), value)); + } + } + + result.append(""); + + return result.toString(); + } + + /** + * Format a title value pair. + * + * @param title Title of the property + * @param value Value of the property + * + * @return Formatted string with the title and value + */ + private String formatAttribute(String title, String value) { + return String.format(HTML_PROP_FORMAT, title, value); + } + + /** + * Format a point time stamp (in seconds) to the report format. + * + * @param timeStamp The timestamp in epoch seconds. + * + * @return The formatted timestamp + */ + private String getTimeStamp(long timeStamp) { + return DATE_FORMAT.format(new java.util.Date(timeStamp * 1000)); + } + + /** + * An action class for Extracting artifact files. + */ + @Messages({ + "WaypointExtractAction_label=Extract Files(s)" + }) + final class WaypointExtractAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + final private AbstractFile file; + + WaypointExtractAction(AbstractFile file) { + super(Bundle.WaypointExtractAction_label()); + this.file = file; + } + + @Override + public void actionPerformed(ActionEvent e) { + ExtractActionHelper helper = new ExtractActionHelper(); + helper.extract(e, Arrays.asList(file)); + + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/OpenGeolocationAction.java b/Core/src/org/sleuthkit/autopsy/geolocation/OpenGeolocationAction.java new file mode 100755 index 0000000000..4e77ab5610 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/OpenGeolocationAction.java @@ -0,0 +1,96 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionReferences; +import org.openide.awt.ActionRegistration; +import org.openide.util.HelpCtx; +import org.openide.util.NbBundle.Messages; +import org.openide.util.actions.CallableSystemAction; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.RuntimeProperties; + +/** + * Action for opening the Geolocation window. This action is + * currently available through the Tools menu. + * + */ +@ActionID(category = "Tools", + id = "org.sleuthkit.autopsy.geolocation.OpenGeolocationAction") +@ActionRegistration(displayName = "#CTL_OpenGeolocation", lazy = false) +@ActionReferences(value = { + @ActionReference(path = "Menu/Tools", position = 102)}) +public class OpenGeolocationAction extends CallableSystemAction { + + private static final long serialVersionUID = 1L; + + @Messages({ + "OpenGeolocationAction_name=Geolocation", + "OpenGeolocationAction_displayName=Geolocation" + }) + + /** + * Constructs the new action of opening the Geolocation window. + */ + public OpenGeolocationAction() { + setEnabled(false); //disabled by default. Will be enabled in Case.java when a case is opened. + + PropertyChangeListener caseChangeListener = (PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { + setEnabled(RuntimeProperties.runningWithGUI() && evt.getNewValue() != null); + } + }; + + Case.addPropertyChangeListener(caseChangeListener); + } + + @Override + public void performAction() { + final TopComponent topComponent = WindowManager.getDefault().findTopComponent("GeolocationTopComponent"); + if (topComponent != null) { + if (topComponent.isOpened() == false) { + topComponent.open(); + } + topComponent.toFront(); + topComponent.requestActive(); + } + } + + @Override + public String getName() { + return Bundle.OpenGeolocationAction_displayName(); + } + + @Override + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + public boolean asynchronous() { + return false; // run on edt + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/RefreshPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/RefreshPanel.form new file mode 100755 index 0000000000..145c22444a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/RefreshPanel.form @@ -0,0 +1,86 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/RefreshPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/RefreshPanel.java new file mode 100755 index 0000000000..f1f93a620d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/RefreshPanel.java @@ -0,0 +1,115 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.event.ActionListener; +import javax.swing.JPanel; + +/** + * Message panel to inform the user that the MapPanel may need to be refreshed + * due to new artifacts. + * + */ +final class RefreshPanel extends JPanel { + + private static final long serialVersionUID = 1L; + + /** + * Creates new form RefreshPanel + */ + RefreshPanel() { + initComponents(); + } + + void addCloseActionListener(ActionListener listener) { + closeButton.addActionListener(listener); + } + + void addRefreshActionListner(ActionListener listener) { + refreshButton.addActionListener(listener); + } + + void removeCloseActionListner(ActionListener listener) { + closeButton.removeActionListener(listener); + } + + void removeRefreshActionListner(ActionListener listener) { + refreshButton.removeActionListener(listener); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + refreshLabel = new javax.swing.JLabel(); + refreshButton = new javax.swing.JButton(); + closeButton = new javax.swing.JButton(); + + setBackground(new java.awt.Color(0, 51, 51)); + setLayout(new java.awt.GridBagLayout()); + + refreshLabel.setForeground(new java.awt.Color(255, 255, 255)); + refreshLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/warning16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(refreshLabel, org.openide.util.NbBundle.getMessage(RefreshPanel.class, "RefreshPanel.refreshLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(15, 10, 15, 10); + add(refreshLabel, gridBagConstraints); + + refreshButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/arrow-circle-double-135.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(RefreshPanel.class, "RefreshPanel.refreshButton.text")); // NOI18N + refreshButton.setMargin(new java.awt.Insets(2, 5, 2, 5)); + add(refreshButton, new java.awt.GridBagConstraints()); + + closeButton.setBackground(new java.awt.Color(0, 0, 0)); + closeButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/close-icon.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(RefreshPanel.class, "RefreshPanel.closeButton.text")); // NOI18N + closeButton.setMargin(new java.awt.Insets(0, 0, 0, 0)); + closeButton.setOpaque(false); + closeButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + closeButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 5); + add(closeButton, gridBagConstraints); + }// //GEN-END:initComponents + + private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed + + }//GEN-LAST:event_closeButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton closeButton; + private javax.swing.JButton refreshButton; + private javax.swing.JLabel refreshLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/VerticalLabelUI.java b/Core/src/org/sleuthkit/autopsy/geolocation/VerticalLabelUI.java new file mode 100755 index 0000000000..1a76953267 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/VerticalLabelUI.java @@ -0,0 +1,124 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.plaf.basic.BasicLabelUI; + + +/** + * This class is an overload of BasicLabelUI to draw labels vertically. + * + * This code was found at: + * https://tech.chitgoks.com/2009/11/13/rotate-jlabel-vertically/ + * + */ +final class VerticalLabelUI extends BasicLabelUI { + + private static final Rectangle paintIconR = new Rectangle(); + private static final Rectangle paintTextR = new Rectangle(); + private static final Rectangle paintViewR = new Rectangle(); + private static Insets paintViewInsets = new Insets(0, 0, 0, 0); + + static { + labelUI = new VerticalLabelUI(false); + } + + final boolean clockwise; + + /** + * Construct a new VerticalLabelUI + * @param clockwise + */ + VerticalLabelUI(boolean clockwise) { + super(); + this.clockwise = clockwise; + } + + @Override + public Dimension getPreferredSize(JComponent c) { + Dimension dim = super.getPreferredSize(c); + return new Dimension( dim.height, dim.width ); + } + + @Override + public void paint(Graphics g, JComponent c) { + JLabel label = (JLabel)c; + String text = label.getText(); + Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon(); + + if ((icon == null) && (text == null)) { + return; + } + + FontMetrics fm = g.getFontMetrics(); + paintViewInsets = c.getInsets(paintViewInsets); + + paintViewR.x = paintViewInsets.left; + paintViewR.y = paintViewInsets.top; + + // Use inverted height & width + paintViewR.height = c.getWidth() - (paintViewInsets.left + paintViewInsets.right); + paintViewR.width = c.getHeight() - (paintViewInsets.top + paintViewInsets.bottom); + + paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0; + paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0; + + String clippedText = layoutCL(label, fm, text, icon, paintViewR, paintIconR, paintTextR); + + Graphics2D g2 = (Graphics2D) g; + AffineTransform tr = g2.getTransform(); + if (clockwise) { + g2.rotate( Math.PI / 2 ); + g2.translate( 0, - c.getWidth() ); + } else { + g2.rotate( - Math.PI / 2 ); + g2.translate( - c.getHeight(), 0 ); + } + + if (icon != null) { + icon.paintIcon(c, g, paintIconR.x, paintIconR.y); + } + + if (text != null) { + int textX = paintTextR.x; + int textY = paintTextR.y + fm.getAscent(); + + if (label.isEnabled()) { + paintEnabledText(label, g, clippedText, textX, textY); + } else { + paintDisabledText(label, g, clippedText, textX, textY); + } + } + g2.setTransform( tr ); + } + +} + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/WaypointDetailPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/WaypointDetailPanel.form new file mode 100755 index 0000000000..4093c16050 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/WaypointDetailPanel.form @@ -0,0 +1,123 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/WaypointDetailPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/WaypointDetailPanel.java new file mode 100755 index 0000000000..939ae81247 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/WaypointDetailPanel.java @@ -0,0 +1,254 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.Point; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import javax.swing.ImageIcon; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JSeparator; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * A panel for displaying the details of a waypoint. + */ +final class WaypointDetailPanel extends JPanel { + + private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(WaypointDetailPanel.class.getName()); + + private final List listeners; + + private MapWaypoint waypoint; + + /** + * Constructs new a WaypointDetailPanel. + */ + WaypointDetailPanel() { + initComponents(); + + listeners = new ArrayList<>(); + mainPanel.setBackground(detailsPane.getBackground()); + } + + /** + * Sets the current waypoint. + * + * @param waypoint The panel waypoint + */ + void setWaypoint(MapWaypoint waypoint) { + this.waypoint = waypoint; + + ImageIcon image = waypoint.getImage(); + if (image != null) { + imageLabel.setIcon(image); + } + detailsPane.setText(waypoint.getHTMLFormattedWaypointDetails()); + } + + /** + * Adds listeners for the panel close button. + * + * @param listener + */ + void addActionListener(ActionListener listener) { + listeners.add(listener); + } + + /** + * Show the popup menu if the given MouseEvent was a popup event. + * + * @param evt the current event + */ + void showPopupMenu(MouseEvent evt) { + if(evt.isPopupTrigger()) { + try { + showPopupMenu(evt.getPoint()); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to show popup menu for selected waypoint"); + } + } + } + + /** + * Show the popup menu at the given location for waypoint + * + * @param point Mouse click location + * + * @throws TskCoreException + */ + void showPopupMenu(Point point) throws TskCoreException { + JMenuItem[] items = waypoint.getMenuItems(); + JPopupMenu popupMenu = new JPopupMenu(); + for(JMenuItem menu: items) { + + if(menu != null) { + popupMenu.add(menu); + } else { + popupMenu.add(new JSeparator()); + } + } + popupMenu.show(this, point.x, point.y); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + buttonPanel = new javax.swing.JPanel(); + closeButton = new javax.swing.JButton(); + scrollPane = new javax.swing.JScrollPane(); + mainPanel = new javax.swing.JPanel(); + imageLabel = new javax.swing.JLabel(); + detailsPane = new javax.swing.JTextPane(); + + setMaximumSize(new java.awt.Dimension(300, 300)); + setLayout(new java.awt.GridBagLayout()); + + buttonPanel.setLayout(new java.awt.BorderLayout()); + + closeButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/close-icon.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(WaypointDetailPanel.class, "WaypointDetailPanel.closeButton.text")); // NOI18N + closeButton.setBorder(null); + closeButton.setOpaque(false); + closeButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + closeButtonActionPerformed(evt); + } + }); + buttonPanel.add(closeButton, java.awt.BorderLayout.EAST); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + gridBagConstraints.weightx = 1.0; + add(buttonPanel, gridBagConstraints); + + mainPanel.setMaximumSize(new java.awt.Dimension(300, 300)); + mainPanel.addMouseListener(new java.awt.event.MouseAdapter() { + public void mousePressed(java.awt.event.MouseEvent evt) { + mainPanelMousePressed(evt); + } + public void mouseReleased(java.awt.event.MouseEvent evt) { + mainPanelMouseReleased(evt); + } + }); + mainPanel.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(imageLabel, org.openide.util.NbBundle.getMessage(WaypointDetailPanel.class, "WaypointDetailPanel.imageLabel.text")); // NOI18N + imageLabel.addMouseListener(new java.awt.event.MouseAdapter() { + public void mousePressed(java.awt.event.MouseEvent evt) { + imageLabelMousePressed(evt); + } + public void mouseReleased(java.awt.event.MouseEvent evt) { + imageLabelMouseReleased(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + mainPanel.add(imageLabel, gridBagConstraints); + + detailsPane.setEditable(false); + detailsPane.setContentType("text/html"); + detailsPane.setMaximumSize(new java.awt.Dimension(300, 300)); + detailsPane.addMouseListener(new java.awt.event.MouseAdapter() { + public void mousePressed(java.awt.event.MouseEvent evt) { + detailsPaneMousePressed(evt); + } + public void mouseReleased(java.awt.event.MouseEvent evt) { + detailsPaneMouseReleased(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + mainPanel.add(detailsPane, gridBagConstraints); + + scrollPane.setViewportView(mainPanel); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + add(scrollPane, gridBagConstraints); + }// //GEN-END:initComponents + + private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed + for (ActionListener listener : listeners) { + listener.actionPerformed(evt); + } + }//GEN-LAST:event_closeButtonActionPerformed + + private void mainPanelMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mainPanelMousePressed + showPopupMenu(evt); + }//GEN-LAST:event_mainPanelMousePressed + + private void mainPanelMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mainPanelMouseReleased + showPopupMenu(evt); + }//GEN-LAST:event_mainPanelMouseReleased + + private void detailsPaneMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_detailsPaneMousePressed + showPopupMenu(evt); + }//GEN-LAST:event_detailsPaneMousePressed + + private void detailsPaneMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_detailsPaneMouseReleased + showPopupMenu(evt); + }//GEN-LAST:event_detailsPaneMouseReleased + + private void imageLabelMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageLabelMousePressed + showPopupMenu(evt); + }//GEN-LAST:event_imageLabelMousePressed + + private void imageLabelMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageLabelMouseReleased + showPopupMenu(evt); + }//GEN-LAST:event_imageLabelMouseReleased + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel buttonPanel; + private javax.swing.JButton closeButton; + private javax.swing.JTextPane detailsPane; + private javax.swing.JLabel imageLabel; + private javax.swing.JPanel mainPanel; + private javax.swing.JScrollPane scrollPane; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/BookmarkWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/BookmarkWaypoint.java new file mode 100755 index 0000000000..08fdfeccb6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/BookmarkWaypoint.java @@ -0,0 +1,78 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation.datamodel; + +import java.util.Map; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; + +/** + * Class to represent TSK_GPS_BOOKMARK waypoints + * + */ +final class BookmarkWaypoint extends Waypoint { + + /** + * Constructs a new BookmarkWaypoint from the given artifact. + * + * @param artifact BlackboardArtifact for this waypoint + * + * @throws GeoLocationDataException + */ + BookmarkWaypoint(BlackboardArtifact artifact) throws GeoLocationDataException { + this(artifact, getAttributesFromArtifactAsMap(artifact)); + } + + /** + * Constructs a new BookmarkWaypoint. + * + * @param artifact BlackboardArtifact for this waypoint + * @param attributeMap A Map of the BlackboardAttributes for the given + * artifact. + * + * @throws GeoLocationDataException + */ + private BookmarkWaypoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { + super(artifact, + getLabelFromArtifact(attributeMap), + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME).getValueLong() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, + null, attributeMap, null); + } + + /** + * Gets the label for this waypoint. + * + * @param artifact BlackboardArtifact for waypoint + * + * @return Returns a label for the waypoint, or empty string if no label was + * found. + */ + private static String getLabelFromArtifact(Map attributeMap) { + BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); + if (attribute != null) { + return attribute.getDisplayString(); + } + + return ""; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Bundle.properties-MERGED new file mode 100755 index 0000000000..86acb2fdb1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Bundle.properties-MERGED @@ -0,0 +1,6 @@ +LastKnownWaypoint_Label=Last Known Location +Route_End_Label=End +Route_Label=As-the-crow-flies Route +Route_Start_Label=Start +SearchWaypoint_DisplayLabel=GPS Search +TrackpointWaypoint_DisplayLabel=GPS Trackpoint diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/EXIFWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/EXIFWaypoint.java new file mode 100755 index 0000000000..3a1ec3e66e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/EXIFWaypoint.java @@ -0,0 +1,85 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation.datamodel; + +import java.util.Map; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Waypoint wrapper class for TSK_METADATA_EXIF artifacts. + */ +final class EXIFWaypoint extends Waypoint { + + /** + * Construct a way point with the given artifact. + * + * @param artifact BlackboardArtifact for waypoint + * + * @throws GeoLocationDataException + */ + EXIFWaypoint(BlackboardArtifact artifact) throws GeoLocationDataException { + this(artifact, getAttributesFromArtifactAsMap(artifact), getImageFromArtifact(artifact)); + } + + /** + * Constructs new waypoint using the given artifact and attribute map. + * + * @param artifact Waypoint BlackboardArtifact + * @param attributeMap Map of artifact attributes + * @param image EXIF AbstractFile image + * + * @throws GeoLocationDataException + */ + private EXIFWaypoint(BlackboardArtifact artifact, Map attributeMap, AbstractFile image) throws GeoLocationDataException { + super(artifact, + image != null ? image.getName() : "", + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED).getValueLong() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, + image, attributeMap, null); + } + + /** + * Gets the image from the given artifact. + * + * @param artifact BlackboardArtifact for waypoint + * + * @return AbstractFile image for this waypoint or null if one is not + * available + * + * @throws GeoLocationDataException + */ + private static AbstractFile getImageFromArtifact(BlackboardArtifact artifact) throws GeoLocationDataException { + AbstractFile abstractFile = null; + BlackboardArtifact.ARTIFACT_TYPE artifactType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); + if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF) { + try { + abstractFile = artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID()); + } catch (TskCoreException ex) { + throw new GeoLocationDataException(String.format("Unable to getAbstractFileByID for artifactID: %d", artifact.getArtifactID()), ex); + } + } + + return abstractFile; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoLocationDataException.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoLocationDataException.java new file mode 100755 index 0000000000..44aad62b58 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoLocationDataException.java @@ -0,0 +1,47 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation.datamodel; + +/** + * An exception class for the Geolocation dateModel; + * + */ +public class GeoLocationDataException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Create exception containing the error message + * + * @param msg the message + */ + public GeoLocationDataException(String msg) { + super(msg); + } + + /** + * Create exception containing the error message and cause exception + * + * @param msg the message + * @param ex cause exception + */ + public GeoLocationDataException(String msg, Exception ex) { + super(msg, ex); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java new file mode 100755 index 0000000000..7bf85874ff --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java @@ -0,0 +1,82 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation.datamodel; + +import java.util.Map; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; + +/** + * A Last Known Location Waypoint object. + */ +final class LastKnownWaypoint extends Waypoint { + + /** + * Constructs a new waypoint. + * + * @param artifact BlackboardArtifact from which to construct the waypoint + * + * @throws GeoLocationDataException + */ + LastKnownWaypoint(BlackboardArtifact artifact) throws GeoLocationDataException { + this(artifact, getAttributesFromArtifactAsMap(artifact)); + } + + /** + * Constructs a new waypoint with the given artifact and attribute map. + * + * @param artifact BlackboardArtifact from which to construct the + * waypoint + * @param attributeMap Map of artifact attributes + * + * @throws GeoLocationDataException + */ + private LastKnownWaypoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { + super(artifact, + getLabelFromArtifact(attributeMap), + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME).getValueLong() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, + null, attributeMap, null); + } + + /** + * Gets the label for a TSK_LAST_KNOWN_LOCATION. + * + * @param attributeMap Map of artifact attributes for this waypoint + * + * @return String value from attribute TSK_NAME or LastKnownWaypoint_Label + * + * @throws GeoLocationDataException + */ + @Messages({ + "LastKnownWaypoint_Label=Last Known Location",}) + private static String getLabelFromArtifact(Map attributeMap) throws GeoLocationDataException { + BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); + String label = attribute.getDisplayString(); + + if (label == null || label.isEmpty()) { + label = Bundle.LastKnownWaypoint_Label(); + } + + return label; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java new file mode 100755 index 0000000000..7c608d6f08 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java @@ -0,0 +1,192 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation.datamodel; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * A Route represents a TSK_GPS_ROUTE artifact which has a start and end point + * however the class was written with the assumption that routes may have + * more that two points. + * + */ +public final class Route { + private final List points; + private final Long timestamp; + + // This list is not expected to change after construction so the + // constructor will take care of creating an unmodifiable List + private final List immutablePropertiesList; + + /** + * Gets the list of Routes from the TSK_GPS_ROUTE artifacts. + * + * @param skCase Currently open SleuthkitCase + * + * @return List of Route objects, empty list will be returned if no Routes + * were found + * + * @throws GeoLocationDataException + */ + static public List getRoutes(SleuthkitCase skCase) throws GeoLocationDataException { + List artifacts = null; + try { + artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE); + } catch (TskCoreException ex) { + throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_BOOKMARK", ex); + } + + List routes = new ArrayList<>(); + for (BlackboardArtifact artifact : artifacts) { + Route route = new Route(artifact); + routes.add(route); + } + return routes; + } + + /** + * Construct a route for the given artifact. + * + * @param artifact TSK_GPS_ROUTE artifact object + */ + Route(BlackboardArtifact artifact) throws GeoLocationDataException { + points = new ArrayList<>(); + + Map attributeMap = Waypoint.getAttributesFromArtifactAsMap(artifact); + points.add(getRouteStartPoint(artifact, attributeMap)); + points.add(getRouteEndPoint(artifact, attributeMap)); + + BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); + timestamp = attribute != null ? attribute.getValueLong() : null; + + immutablePropertiesList = Collections.unmodifiableList(Waypoint.createGeolocationProperties(attributeMap)); + } + + /** + * Get the list of way points for this route; + * + * @return List an unmodifiableList of ArtifactWaypoints for this route + */ + public List getRoute() { + return Collections.unmodifiableList(points); + } + + /** + * Get the "Other attributes" for this route. The map will contain display + * name, formatted value pairs. This list is unmodifiable. + * + * @return Map of key, value pairs. + */ + public List getOtherProperties() { + return immutablePropertiesList; + } + + /** + * Get the route label. + */ + @Messages({ + // This is the original static hardcoded label from the + // original kml-report code + "Route_Label=As-the-crow-flies Route" + }) + public String getLabel() { + return Bundle.Route_Label(); + } + + public Long getTimestamp() { + return timestamp; + } + + /** + * Get the route start point. + * + * @param attributeMap Map of artifact attributes for this waypoint. + * + * An exception will be thrown if longitude or latitude is null. + * + * @return Start waypoint + * + * @throws GeoLocationDataException. + */ + @Messages({ + "Route_Start_Label=Start" + }) + + private Waypoint getRouteStartPoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { + BlackboardAttribute latitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START); + BlackboardAttribute longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START); + BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); + BlackboardAttribute pointTimestamp = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); + + if (latitude != null && longitude != null) { + return new Waypoint(artifact, + Bundle.Route_Start_Label(), + pointTimestamp != null ? pointTimestamp.getValueLong() : null, + latitude.getValueDouble(), + longitude.getValueDouble(), + altitude != null ? altitude.getValueDouble() : null, + null, attributeMap, this); + } else { + throw new GeoLocationDataException("Unable to create route start point, invalid longitude and/or latitude"); + } + } + + /** + * Get the route End point. + * + * An exception will be thrown if longitude or latitude is null. + * + * @param attributeMap Map of artifact attributes for this waypoint + * + * @return The end waypoint + * + * @throws GeoLocationDataException + */ + @Messages({ + "Route_End_Label=End" + }) + Waypoint getRouteEndPoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { + BlackboardAttribute latitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END); + BlackboardAttribute longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END); + BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); + BlackboardAttribute pointTimestamp = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); + + if (latitude != null && longitude != null) { + + return new Waypoint(artifact, + Bundle.Route_End_Label(), + pointTimestamp != null ? pointTimestamp.getValueLong() : null, + latitude.getValueDouble(), + longitude.getValueDouble(), + altitude != null ? altitude.getValueDouble() : null, + null, attributeMap, this); + } else { + throw new GeoLocationDataException("Unable to create route end point, invalid longitude and/or latitude"); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/SearchWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/SearchWaypoint.java new file mode 100755 index 0000000000..fb410e6574 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/SearchWaypoint.java @@ -0,0 +1,76 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation.datamodel; + +import java.util.Map; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; + +/** + * A SearchWaypoint is a subclass of Waypoint. + */ +final class SearchWaypoint extends Waypoint { + + /** + * Construct a waypoint for TSK_GPS_SEARCH artifact. + * + * @throws GeoLocationDataException + */ + SearchWaypoint(BlackboardArtifact artifact) throws GeoLocationDataException { + this(artifact, getAttributesFromArtifactAsMap(artifact)); + } + + private SearchWaypoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { + super(artifact, + getLabelFromArtifact(attributeMap), + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME).getValueLong() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, + null, attributeMap, null); + } + + /** + * Returns a Label for a GPS_SEARCH artifact. + * + * @param attributeMap Map of artifact attributes + * + * @return String label for the artifacts way point. + * + * @throws GeoLocationDataException + */ + @Messages({ + "SearchWaypoint_DisplayLabel=GPS Search" + }) + private static String getLabelFromArtifact(Map attributeMap) throws GeoLocationDataException { + BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); + if (attribute != null) { + return attribute.getDisplayString(); + } + + attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION); + if (attribute != null) { + return attribute.getDisplayString(); + } + + return Bundle.SearchWaypoint_DisplayLabel(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/TrackpointWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/TrackpointWaypoint.java new file mode 100755 index 0000000000..7009aa63a1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/TrackpointWaypoint.java @@ -0,0 +1,82 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation.datamodel; + +import java.util.Map; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; + +/** + * A wrapper class for TSK_GPS_TRACKPOINT artifacts. + */ +final class TrackpointWaypoint extends Waypoint { + /** + * Construct a waypoint for trackpoints. + * + * @throws GeoLocationDataException + */ + TrackpointWaypoint(BlackboardArtifact artifact) throws GeoLocationDataException { + this(artifact, getAttributesFromArtifactAsMap(artifact)); + } + + private TrackpointWaypoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { + super(artifact, + getLabelFromArtifact(attributeMap), + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME).getValueLong() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, + null, attributeMap, null); + } + + /** + * Returns a Label for a GPS_Trackpoint artifact. This function assumes the + * calling function has already checked TSK_NAME. + * + * @param artifact BlackboardArtifact for waypoint + * + * @return String label for the artifacts way point. + * + * @throws GeoLocationDataException + */ + @Messages({ + "TrackpointWaypoint_DisplayLabel=GPS Trackpoint" + }) + private static String getLabelFromArtifact(Map attributeMap) throws GeoLocationDataException { + + BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); + if (attribute != null) { + return attribute.getDisplayString(); + } + + attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME); + if (attribute != null) { + return attribute.getDisplayString(); + } + + attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG); + if (attribute != null) { + return attribute.getDisplayString(); + } + + return Bundle.TrackpointWaypoint_DisplayLabel(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java new file mode 100755 index 0000000000..8cc35555eb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java @@ -0,0 +1,298 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation.datamodel; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Representation of a Waypoint created from a BlackboardArtifact. + * + */ +public class Waypoint { + + final private Long timestamp; + final private Double longitude; + final private Double latitude; + final private Double altitude; + final private String label; + final private AbstractFile image; + final private BlackboardArtifact artifact; + final private Route route; + + // This list is not expected to change after construction. The + // constructor will take care of making an unmodifiable List + final private List immutablePropertiesList; + + /** + * This is a list of attributes that are already being handled by the by + * getter functions. + */ + static final private BlackboardAttribute.ATTRIBUTE_TYPE[] ALREADY_HANDLED_ATTRIBUTES = { + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END,}; + + /** + * Constructor that initializes all of the member variables. + * + * @param artifact BlackboardArtifact for this waypoint + * @param label String waypoint label + * @param timestamp Long timestamp, unix/java epoch seconds + * @param latitude Double waypoint latitude + * @param longitude Double waypoint longitude + * @param altitude Double waypoint altitude + * @param image AbstractFile image for waypoint, this maybe null + * @param attributeMap A Map of attributes for the given artifact + * + * @throws GeoLocationDataException Exception will be thrown if artifact did + * not have a valid longitude and latitude. + */ + Waypoint(BlackboardArtifact artifact, String label, Long timestamp, Double latitude, Double longitude, Double altitude, AbstractFile image, Map attributeMap, Route route) throws GeoLocationDataException { + if (longitude == null || latitude == null) { + throw new GeoLocationDataException("Invalid waypoint, null value passed for longitude or latitude"); + } + + this.artifact = artifact; + this.label = label; + this.image = image; + this.timestamp = timestamp; + this.longitude = longitude; + this.latitude = latitude; + this.altitude = altitude; + this.route = null; + + immutablePropertiesList = Collections.unmodifiableList(createGeolocationProperties(attributeMap)); + } + + /** + * Get the BlackboardArtifact that this waypoint represents. + * + * @return BlackboardArtifact for this waypoint. + */ + public BlackboardArtifact getArtifact() { + return artifact; + } + + /** + * Interface to describe a waypoint. A waypoint is made up of a longitude, + * latitude, label, timestamp, type, image and altitude. + * + * A good way point should have at minimum a longitude and latutude. + * + * @return Timestamp in java/unix epoch seconds or null if none was set. + */ + public Long getTimestamp() { + return timestamp; + } + + /** + * Get the label for this point object. + * + * @return String label for the point or null if none was set + */ + public String getLabel() { + return label; + } + + /** + * Get the latitude for this point. + * + * @return Returns the latitude for the point + */ + public Double getLatitude() { + return latitude; + } + + /** + * Get the longitude for this point. + * + * @return Returns the longitude for the point + */ + public Double getLongitude() { + return longitude; + } + + /** + * Get the altitude for this point. + * + * @return Returns the altitude for the point or null if none was set + */ + public Double getAltitude() { + return altitude; + } + + /** + * Get the image for this waypoint. + * + * @return AbstractFile image or null if one was not set + */ + public AbstractFile getImage() { + return image; + } + + /** + * Gets an unmodifiable List of other properties that may be interesting to + * this way point. The List will not include properties for which getter + * functions exist. + * + * @return A List of waypoint properties + */ + public List getOtherProperties() { + return immutablePropertiesList; + } + + /** + * Returns the route that this waypoint is apart of . + * + * @return The waypoint route or null if the waypoint is not apart of a + * route. + */ + public Route getRoute() { + return route; + } + + /** + * Gets the label for this waypoint. + * + * @param artifact BlackboardArtifact for waypoint + * + * @return Returns a label for the waypoint, or empty string if no label was + * found. + */ + private static String getLabelFromArtifact(Map attributeMap) { + BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); + if (attribute != null) { + return attribute.getDisplayString(); + } + + return ""; + } + + /** + * Gets the list of attributes from the artifact and puts them into a map + * with the ATRIBUTE_TYPE as the key. + * + * @param artifact BlackboardArtifact current artifact + * + * @return A Map of BlackboardAttributes for the given artifact with + * ATTRIBUTE_TYPE as the key. + * + * @throws GeoLocationDataException + */ + static Map getAttributesFromArtifactAsMap(BlackboardArtifact artifact) throws GeoLocationDataException { + Map attributeMap = new HashMap<>(); + try { + List attributeList = artifact.getAttributes(); + for (BlackboardAttribute attribute : attributeList) { + BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID()); + attributeMap.put(type, attribute); + } + } catch (TskCoreException ex) { + throw new GeoLocationDataException("Unable to get attributes from artifact", ex); + } + + return attributeMap; + } + + /** + * Get a list of Waypoint.Property objects for the given artifact. This list + * will not include attributes that the Waypoint interfact has get functions + * for. + * + * @param artifact Blackboard artifact to get attributes\properties from + * + * @return A List of Waypoint.Property objects + * + * @throws GeoLocationDataException + */ + static public List createGeolocationProperties(Map attributeMap) throws GeoLocationDataException { + List list = new ArrayList<>(); + + Set keys = new HashSet<>(attributeMap.keySet()); + + for (BlackboardAttribute.ATTRIBUTE_TYPE type : ALREADY_HANDLED_ATTRIBUTES) { + keys.remove(type); + } + + for (BlackboardAttribute.ATTRIBUTE_TYPE type : keys) { + String key = type.getDisplayName(); + String value = attributeMap.get(type).getDisplayString(); + + list.add(new Waypoint.Property(key, value)); + } + return list; + } + + /** + * Simple property class for waypoint properties that a purely + * informational. + */ + public static final class Property { + + private final String displayName; + private final String value; + + /** + * Construct a Property object. + * + * @param displayName String display name for property. Ideally not null + * or empty string. + * @param value String value for property. Can be null. + */ + private Property(String displayName, String value) { + this.displayName = displayName; + this.value = value; + } + + /** + * Get the display name for this property. + * + * @return String display name. + */ + public String getDisplayName() { + return displayName; + } + + /** + * Get the property value. + * + * @return String value. + */ + public String getValue() { + return value; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java new file mode 100755 index 0000000000..86539412be --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java @@ -0,0 +1,601 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation.datamodel; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.CaseDbAccessManager; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.DataSource; + +/** + * Class for building lists of waypoints. + * + */ +public final class WaypointBuilder { + + private static final Logger logger = Logger.getLogger(WaypointBuilder.class.getName()); + + // SELECT statement for getting a list of waypoints. + final static String GEO_ARTIFACT_QUERY + = "SELECT artifact_id, artifact_type_id " + + "FROM blackboard_attributes " + + "WHERE attribute_type_id IN (%d, %d) "; //NON-NLS + + // SELECT statement to get only artifact_ids + final static String GEO_ARTIFACT_QUERY_ID_ONLY + = "SELECT artifact_id " + + "FROM blackboard_attributes " + + "WHERE attribute_type_id IN (%d, %d) "; //NON-NLS + + // This Query will return a list of waypoint artifacts + final static String GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY + = "SELECT blackboard_attributes.artifact_id " + + "FROM blackboard_attributes, blackboard_artifacts " + + "WHERE blackboard_attributes.attribute_type_id IN(%d, %d) " + + "AND data_source_obj_id IN (%s)"; //NON-NLS + + // Select will return the "most recent" timestamp from all waypoings + final static String MOST_RECENT_TIME + = "SELECT MAX(value_int64) - (%d * 86400)" //86400 is the number of seconds in a day. + + "FROM blackboard_attributes " + + "WHERE attribute_type_id IN(%d, %d) " + + "AND artifact_id " + + "IN ( " + + "%s" //GEO_ARTIFACT with or without data source + + " )"; + + // Returns a list of artifacts with no time stamp + final static String SELECT_WO_TIMESTAMP + = "SELECT DISTINCT artifact_id, artifact_type_id " + + "FROM blackboard_attributes " + + "WHERE artifact_id NOT IN (%s) " + + "AND artifact_id IN (%s)"; //NON-NLS + + /** + * A callback interface to process the results of waypoint filtering. + */ + public interface WaypointFilterQueryCallBack { + + /** + * This function will be called after the waypoints have been filtered. + * + * @param wwaypoints This of waypoints. + */ + void process(List wwaypoints); + } + + /** + * private constructor + */ + private WaypointBuilder() { + + } + + /** + * Returns a list of Waypoints for the artifacts with geolocation + * information. + * + * List will include artifacts of type: TSK_GPS_TRACKPOINT TSK_GPS_SEARCH + * TSK_GPS_LAST_KNOWN_LOCATION TSK_GPS_BOOKMARK TSK_METADATA_EXIF + * + * @param skCase Currently open SleuthkitCase + * + * @return List of Waypoint + * + * @throws GeoLocationDataException + */ + public static List getAllWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + List points = new ArrayList<>(); + + points.addAll(getTrackpointWaypoints(skCase)); + points.addAll(getEXIFWaypoints(skCase)); + points.addAll(getSearchWaypoints(skCase)); + points.addAll(getLastKnownWaypoints(skCase)); + points.addAll(getBookmarkWaypoints(skCase)); + + return points; + } + + /** + * Returns a list of routes from the given list of waypoints. + * + * @param waypoints A list of waypoints + * + * @return A list of routes or an empty list if none were found. + */ + public static List getRoutes(List waypoints) { + List routeList = new ArrayList<>(); + for (Waypoint point : waypoints) { + Route route = point.getRoute(); + if (route != null && !routeList.contains(route)) { + routeList.add(route); + } + } + + return routeList; + } + + /** + * Gets a list of Waypoints for TSK_GPS_TRACKPOINT artifacts. + * + * @param skCase Currently open SleuthkitCase + * + * @return List of Waypoint + * + * @throws GeoLocationDataException + */ + public static List getTrackpointWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + List artifacts = null; + try { + artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT); + } catch (TskCoreException ex) { + throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_TRACKPOINT", ex);//NON-NLS + } + + List points = new ArrayList<>(); + for (BlackboardArtifact artifact : artifacts) { + try { + Waypoint point = new TrackpointWaypoint(artifact); + points.add(point); + } catch (GeoLocationDataException ex) { + logger.log(Level.WARNING, String.format("No longitude or latitude available for TSK_GPS_TRACKPOINT artifactID: %d", artifact.getArtifactID()));//NON-NLS + } + } + return points; + } + + /** + * Returns a list of waypoints that come from TSK_GEO_TRACKPOINT artifacts. + * + * @param waypoints A list of waypoints + * + * @return A list of trackpoint waypoints or empty list if none were found. + */ + public static List getTrackpointWaypoints(List waypoints) { + List specificPoints = new ArrayList<>(); + + for (Waypoint point : waypoints) { + if (point instanceof TrackpointWaypoint) { + specificPoints.add(point); + } + } + + return specificPoints; + } + + /** + * Gets a list of Waypoints for TSK_METADATA_EXIF artifacts. + * + * @param skCase Currently open SleuthkitCase + * + * @return List of Waypoint + * + * @throws GeoLocationDataException + */ + static public List getEXIFWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + List artifacts = null; + try { + artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF); + } catch (TskCoreException ex) { + throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_LAST_KNOWN_LOCATION", ex);//NON-NLS + } + + List points = new ArrayList<>(); + if (artifacts != null) { + for (BlackboardArtifact artifact : artifacts) { + try { + Waypoint point = new EXIFWaypoint(artifact); + points.add(point); + } catch (GeoLocationDataException ex) { + // I am a little relucant to log this error because I suspect + // this will happen more often than not. It is valid for + // METADAT_EXIF to not have longitude and latitude + } + } + } + return points; + } + + /** + * Returns a list of waypoints that come from TSK_METADATA_EXIF artifacts. + * + * @param waypoints A list of waypoints + * + * @return A list of trackpoint waypoints or empty list if none were found. + */ + public static List getEXIFWaypoints(List waypoints) { + List specificPoints = new ArrayList<>(); + + for (Waypoint point : waypoints) { + if (point instanceof EXIFWaypoint) { + specificPoints.add(point); + } + } + + return specificPoints; + } + + /** + * Gets a list of Waypoints for TSK_GPS_SEARCH artifacts. + * + * @param skCase Currently open SleuthkitCase + * + * @return List of Waypoint + * + * @throws GeoLocationDataException + */ + public static List getSearchWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + List artifacts = null; + try { + artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH); + } catch (TskCoreException ex) { + throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_SEARCH", ex);//NON-NLS + } + + List points = new ArrayList<>(); + if (artifacts != null) { + for (BlackboardArtifact artifact : artifacts) { + try { + Waypoint point = new SearchWaypoint(artifact); + points.add(point); + } catch (GeoLocationDataException ex) { + logger.log(Level.WARNING, String.format("No longitude or latitude available for TSK_GPS_SEARCH artifactID: %d", artifact.getArtifactID()));//NON-NLS + } + } + } + return points; + } + + /** + * Returns a list of waypoints that come from TSK_GPS_SEARCH artifacts. + * + * @param waypoints A list of waypoints + * + * @return A list of trackpoint waypoints or empty list if none were found. + */ + public static List getSearchWaypoints(List waypoints) { + List specificPoints = new ArrayList<>(); + + for (Waypoint point : waypoints) { + if (point instanceof SearchWaypoint) { + specificPoints.add(point); + } + } + + return specificPoints; + } + + /** + * Gets a list of Waypoints for TSK_GPS_LAST_KNOWN_LOCATION artifacts. + * + * @param skCase Currently open SleuthkitCase + * + * @return List of Waypoint + * + * @throws GeoLocationDataException + */ + public static List getLastKnownWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + List artifacts = null; + try { + artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION); + } catch (TskCoreException ex) { + throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_LAST_KNOWN_LOCATION", ex);//NON-NLS + } + + List points = new ArrayList<>(); + if (artifacts != null) { + for (BlackboardArtifact artifact : artifacts) { + try { + Waypoint point = new LastKnownWaypoint(artifact); + points.add(point); + } catch (GeoLocationDataException ex) { + logger.log(Level.WARNING, String.format("No longitude or latitude available for TSK_GPS_LAST_KNOWN_LOCATION artifactID: %d", artifact.getArtifactID()));//NON-NLS + } + } + } + return points; + } + + /** + * Returns a list of waypoints that come from TSK_GPS_LAST_KNOWN_LOCATION + * artifacts. + * + * @param waypoints A list of waypoints + * + * @return A list of trackpoint waypoints or empty list if none were found. + */ + public static List getLastKnownWaypoints(List waypoints) { + List specificPoints = new ArrayList<>(); + + for (Waypoint point : waypoints) { + if (point instanceof LastKnownWaypoint) { + specificPoints.add(point); + } + } + + return specificPoints; + } + + /** + * Gets a list of Waypoints for TSK_GPS_BOOKMARK artifacts. + * + * @param skCase Currently open SleuthkitCase + * + * @return List of Waypoint + * + * @throws GeoLocationDataException + */ + public static List getBookmarkWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + List artifacts = null; + try { + artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK); + } catch (TskCoreException ex) { + throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_BOOKMARK", ex);//NON-NLS + } + + List points = new ArrayList<>(); + if (artifacts != null) { + for (BlackboardArtifact artifact : artifacts) { + try { + Waypoint point = new BookmarkWaypoint(artifact); + points.add(point); + } catch (GeoLocationDataException ex) { + logger.log(Level.WARNING, String.format("No longitude or latitude available for TSK_GPS_BOOKMARK artifactID: %d", artifact.getArtifactID()), ex);//NON-NLS + } + } + } + return points; + } + + /** + * Returns a list of waypoints that come from TSK_GPS_LAST_KNOWN_LOCATION + * artifacts. + * + * @param waypoints A list of waypoints + * + * @return A list of trackpoint waypoints or empty list if none were found. + */ + public static List getBookmarkWaypoints(List waypoints) { + List specificPoints = new ArrayList<>(); + + for (Waypoint point : waypoints) { + if (point instanceof BookmarkWaypoint) { + specificPoints.add(point); + } + } + + return specificPoints; + } + + /** + * Get a filtered list of waypoints. + * + * If showAll is true, the values of cntDaysFromRecent and notTimeStamp will + * be ignored. + * + * To include data from all dataSources pass a null or empty dataSource + * list. + * + * + * @param skCase Currently open sleuthkit case. + * @param dataSources This of data sources to filter the waypoints by. + * Pass a null or empty list to show way points for + * all dataSources. + * + * @param showAll True to get all waypoints. + * + * @param cntDaysFromRecent Number of days from the most recent time stamp + * to get waypoints for. This parameter will be + * ignored if showAll is true; + * + * @param noTimeStamp True to include waypoints without timestamp. + * This parameter will be ignored if showAll is + * true. + * + * @param queryCallBack Function to call after the DB query has + * completed. + * + * @throws GeoLocationDataException + */ + static public void getAllWaypoints(SleuthkitCase skCase, List dataSources, boolean showAll, int cntDaysFromRecent, boolean noTimeStamp, WaypointFilterQueryCallBack queryCallBack) throws GeoLocationDataException { + String query = buildQuery(dataSources, showAll, cntDaysFromRecent, noTimeStamp); + + logger.log(Level.INFO, query); + + try { + // The CaseDBAccessManager.select function will add a SELECT + // to the beginning of the query + if (query.startsWith("SELECT")) { //NON-NLS + query = query.replaceFirst("SELECT", ""); //NON-NLS + } + + skCase.getCaseDbAccessManager().select(query, new CaseDbAccessManager.CaseDbAccessQueryCallback() { + @Override + public void process(ResultSet rs) { + List waypoints = new ArrayList<>(); + try { + while (rs.next()) { + int artifact_type_id = rs.getInt("artifact_type_id"); //NON-NLS + long artifact_id = rs.getLong("artifact_id"); //NON-NLS + + BlackboardArtifact.ARTIFACT_TYPE type = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact_type_id); + + waypoints.addAll(getWaypointForArtifact(skCase.getBlackboardArtifact(artifact_id), type)); + + } + queryCallBack.process(waypoints); + } catch (GeoLocationDataException | SQLException | TskCoreException ex) { + logger.log(Level.WARNING, "Failed to filter waypoint.", ex); //NON-NLS + } + + } + }); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Failed to filter waypoint.", ex); //NON-NLS + } + } + + /** + * Create the query for getting a list of waypoints that do not have time + * stamps. + * + * @param dataSources List of data Sources to filter by + * + * @return SQL SELECT statement + */ + static private String buildQueryForWaypointsWOTimeStamps(List dataSources) { + return String.format(SELECT_WO_TIMESTAMP, + String.format(GEO_ARTIFACT_QUERY_ID_ONLY, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID()), + getWaypointListQuery(dataSources)); + } + + /** + * Build the query to filter the list of waypoints. + * + * If showAll is true, the values of cntDaysFromRecent and noTimeStamp are + * ignored. + * + * @param dataSources This of data sources to filter the waypoints by. + * Pass a null or empty list to show way points for + * all dataSources. + * + * @param showAll True to get all waypoints. + * + * @param cntDaysFromRecent Number of days from the most recent time stamp + * to get waypoints for. This parameter will be + * ignored if showAll is true; + * + * @param noTimeStamp True to include waypoints without timestamp. + * This parameter will be ignored if showAll is + * true. + * + * @return + */ + static private String buildQuery(List dataSources, boolean showAll, int cntDaysFromRecent, boolean noTimeStamp) { + String mostRecentQuery = ""; + + if (!showAll && cntDaysFromRecent > 0) { + mostRecentQuery = String.format("AND value_int64 > (%s)", //NON-NLS + String.format(MOST_RECENT_TIME, + cntDaysFromRecent, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(), + getWaypointListQuery(dataSources) + )); + } + + // This givens us all artifact_ID that have time stamp + String query = String.format(GEO_ARTIFACT_QUERY, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID()); + + // That are in the list of artifacts for the given data Sources + query += String.format("AND artifact_id IN(%s)", getWaypointListQuery(dataSources)); //NON-NLS + query += mostRecentQuery; + + if (showAll || noTimeStamp) { + query = String.format("%s UNION %s", buildQueryForWaypointsWOTimeStamps(dataSources), query); //NON-NLS + } + + return query; + } + + /** + * Returns the query to get a list of waypoints filted by the given data + * sources. + * + * An artifact is assumed to be a "waypoint" if it has the attributes + * TSK_GEO_LATITUDE or TSK_GEO_LATITUDE_START + * + * @param dataSources A list of data sources to filter by. If the list is + * null or empty the data source list will be ignored. + * + * @return + */ + static private String getWaypointListQuery(List dataSources) { + + if (dataSources == null || dataSources.isEmpty()) { + return String.format(GEO_ARTIFACT_QUERY, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID()); + } + + String dataSourceList = ""; + for (DataSource source : dataSources) { + dataSourceList += Long.toString(source.getId()) + ","; + } + + if (!dataSourceList.isEmpty()) { + // Remove the last , + dataSourceList = dataSourceList.substring(0, dataSourceList.length() - 1); + } + + return String.format(GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID(), + dataSourceList); + } + + /** + * Create a Waypoint object for the given Blackboard artifact. + * + * @param artifact The artifact to create the waypoint from + * @param type The type of artifact + * + * @return A new waypoint object + * + * @throws GeoLocationDataException + */ + static private List getWaypointForArtifact(BlackboardArtifact artifact, BlackboardArtifact.ARTIFACT_TYPE type) throws GeoLocationDataException { + List waypoints = new ArrayList<>(); + switch (type) { + case TSK_METADATA_EXIF: + waypoints.add(new EXIFWaypoint(artifact)); + break; + case TSK_GPS_BOOKMARK: + waypoints.add(new BookmarkWaypoint(artifact)); + break; + case TSK_GPS_TRACKPOINT: + waypoints.add(new TrackpointWaypoint(artifact)); + break; + case TSK_GPS_SEARCH: + waypoints.add(new SearchWaypoint(artifact)); + break; + case TSK_GPS_ROUTE: + Route route = new Route(artifact); + waypoints.addAll(route.getRoute()); + break; + default: + throw new GeoLocationDataException(String.format("Unable to create waypoint for artifact of type %s", type.toString())); + } + + return waypoints; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/guiutils/Bundle_ja.properties new file mode 100644 index 0000000000..20895b6374 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/guiutils/Bundle_ja.properties @@ -0,0 +1,3 @@ +StatusIconCellRenderer.tooltiptext.error=\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +StatusIconCellRenderer.tooltiptext.ok=OK +StatusIconCellRenderer.tooltiptext.warning=\u8b66\u544a\u304c\u767a\u751f\u3057\u307e\u3057\u305f diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/healthmonitor/Bundle_ja.properties new file mode 100644 index 0000000000..ec245205c5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/Bundle_ja.properties @@ -0,0 +1,28 @@ +HealthMonitorDashboard.createAdminPanel.disableButton=\u30e2\u30cb\u30bf\u30fc\u3092\u7121\u52b9\u5316 +HealthMonitorDashboard.createAdminPanel.enableButton=\u30e2\u30cb\u30bf\u30fc\u3092\u6709\u52b9\u5316 +HealthMonitorDashboard.createTimingControlPanel.filterByHost=\u30db\u30b9\u30c8\u5225\u306b\u30d5\u30a3\u30eb\u30bf\u30fc +HealthMonitorDashboard.createTimingControlPanel.maxDays=\u6700\u5927\u8868\u793a\u65e5\u6570 +HealthMonitorDashboard.createTimingControlPanel.showTrendLine=\u30c8\u30ec\u30f3\u30c9\u30e9\u30a4\u30f3\u3092\u8868\u793a +HealthMonitorDashboard.createTimingControlPanel.skipOutliers=\u5916\u308c\u5024\u3092\u30d7\u30ed\u30c3\u30c8\u3057\u306a\u3044 +HealthMonitorDashboard.createTimingPanel.noData=\u8868\u793a\u3059\u308b\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093 - \u30e2\u30cb\u30bf\u30fc\u304c\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093 +HealthMonitorDashboard.createTimingPanel.timingMetricsTitle=\u30bf\u30a4\u30df\u30f3\u30b0\u6307\u6a19 +HealthMonitorDashboard.createUserControlPanel.maxDays=\u6700\u5927\u8868\u793a\u65e5\u6570 +HealthMonitorDashboard.createUserPanel.noData=\u8868\u793a\u3059\u308b\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093 - \u30e2\u30cb\u30bf\u30fc\u304c\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093 +HealthMonitorDashboard.createUserPanel.userMetricsTitle=\u30e6\u30fc\u30b6\u30fc\u6307\u6a19 +HealthMonitorDashboard.DateRange.oneDay=1\u65e5 +HealthMonitorDashboard.DateRange.oneMonth=1\u30f5\u6708 +HealthMonitorDashboard.DateRange.oneWeek=1\u9031\u9593 +HealthMonitorDashboard.DateRange.twoWeeks=2\u9031\u9593 +HealthMonitorDashboard.display.dashboardTitle=\u6b63\u5e38\u6027\u30e2\u30cb\u30bf\u30fc +HealthMonitorDashboard.display.errorCreatingDashboard=\u6b63\u5e38\u6027\u30e2\u30cb\u30bf\u30fc\u30c0\u30c3\u30b7\u30e5\u30dc\u30fc\u30c9\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +HealthMonitorDashboard.updateTimingMetricGraphs.noData=\u8868\u793a\u3059\u308b\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093 +HealthMonitorDashboard.updateUserMetricGraphs.noData=\u8868\u793a\u3059\u308b\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093 +TimeZonePanel.title=\u6b21\u306e\u5358\u4f4d\u3067\u6642\u523b\u3092\u8868\u793a: +TimingMetricGraphPanel.paintComponent.hours=\u6642 +TimingMetricGraphPanel.paintComponent.microseconds=\u30de\u30a4\u30af\u30ed\u79d2 +TimingMetricGraphPanel.paintComponent.milliseconds=\u30df\u30ea\u79d2 +TimingMetricGraphPanel.paintComponent.minutes=\u5206 +TimingMetricGraphPanel.paintComponent.nanoseconds=\u30ca\u30ce\u79d2 +TimingMetricGraphPanel.paintComponent.seconds=\u79d2 +UserMetricGraphPanel.constructor.casesOpen=\u30b1\u30fc\u30b9\u304c\u958b\u3044\u3066\u3044\u307e\u3059 +UserMetricGraphPanel.constructor.loggedIn=Users logged in - \u8abf\u67fb\u54e1\u30ce\u30fc\u30c9\u306f\u9752\u8272\u3067\u3001\u81ea\u52d5\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30ce\u30fc\u30c9\u306f\u7dd1\u8272\u3067\u3059 diff --git a/Core/src/org/sleuthkit/autopsy/images/arrow-circle-double-135.png b/Core/src/org/sleuthkit/autopsy/images/arrow-circle-double-135.png new file mode 100755 index 0000000000..4f40ba5206 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/arrow-circle-double-135.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/blueGeo16.png b/Core/src/org/sleuthkit/autopsy/images/blueGeo16.png new file mode 100755 index 0000000000..b89f013c41 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/blueGeo16.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/blueGeo32.png b/Core/src/org/sleuthkit/autopsy/images/blueGeo32.png new file mode 100755 index 0000000000..b7cf458039 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/blueGeo32.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/blueGeo64.png b/Core/src/org/sleuthkit/autopsy/images/blueGeo64.png new file mode 100755 index 0000000000..cbbfbf3764 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/blueGeo64.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/discovery-icon-24.png b/Core/src/org/sleuthkit/autopsy/images/discovery-icon-24.png new file mode 100644 index 0000000000..6ae4e602dc Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/discovery-icon-24.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/document-question-16.png b/Core/src/org/sleuthkit/autopsy/images/document-question-16.png new file mode 100644 index 0000000000..c084ead81a Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/document-question-16.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/funnel.png b/Core/src/org/sleuthkit/autopsy/images/funnel.png new file mode 100755 index 0000000000..1f69604528 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/funnel.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/pictures-icon.png b/Core/src/org/sleuthkit/autopsy/images/pictures-icon.png new file mode 100644 index 0000000000..b706ce3d22 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/pictures-icon.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/pool-icon.png b/Core/src/org/sleuthkit/autopsy/images/pool-icon.png new file mode 100644 index 0000000000..7300066c2d Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/pool-icon.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/url-16.png b/Core/src/org/sleuthkit/autopsy/images/url-16.png new file mode 100644 index 0000000000..414cff505d Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/url-16.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/video-icon.png b/Core/src/org/sleuthkit/autopsy/images/video-icon.png new file mode 100644 index 0000000000..67b2f87549 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/video-icon.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/waypoint_teal.png b/Core/src/org/sleuthkit/autopsy/images/waypoint_teal.png new file mode 100755 index 0000000000..3860d1973c Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/waypoint_teal.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/waypoint_yellow.png b/Core/src/org/sleuthkit/autopsy/images/waypoint_yellow.png new file mode 100755 index 0000000000..1cd715c101 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/waypoint_yellow.png differ diff --git a/Core/src/org/sleuthkit/autopsy/imagewriter/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/imagewriter/Bundle_ja.properties new file mode 100644 index 0000000000..2a979c7f04 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/imagewriter/Bundle_ja.properties @@ -0,0 +1,10 @@ +# \u3053\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u30d8\u30c3\u30c0\u30fc\u3092\u5909\u66f4\u3059\u308b\u306b\u306f\u3001[\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u30d7\u30ed\u30d1\u30c6\u30a3] \u3067 [\u30e9\u30a4\u30bb\u30f3\u30b9\u30d8\u30c3\u30c0\u30fc] \u3092\u9078\u629e\u3057\u307e\u3059\u3002 +# \u3053\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u5909\u66f4\u3059\u308b\u306b\u306f\u3001[\u30c4\u30fc\u30eb | \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8] \u3092\u9078\u629e\u3057\u3001 +# \u30a8\u30c7\u30a3\u30bf\u30fc\u3067\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u958b\u304d\u307e\u3059\u3002 + +# {0} - \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d +ImageWriter.progressBar.message=Finishing acquisition of {0} \u306e\u53d6\u5f97\u3092\u5b8c\u4e86\u4e2d\u3067\u3059(\u53d6\u308a\u6d88\u3059\u306b\u306f\u30c7\u30d0\u30a4\u30b9\u306e\u96fb\u6e90\u30d7\u30e9\u30b0\u3092\u629c\u304d\u307e\u3059) +ImageWriterService.serviceName=\u30a4\u30e1\u30fc\u30b8\u30e9\u30a4\u30bf\u30fc +ImageWriterService.waitingForVHDs=VHD\u304c\u5b8c\u4e86\u3059\u308b\u306e\u3092\u5f85\u3063\u3066\u3044\u307e\u3059 +ImageWriterService.shouldWait=\u9032\u884c\u4e2d\u306eVHD\u304c\u5b8c\u4e86\u3059\u308b\u306e\u3092\u5f85\u3063\u3066\u3044\u307e\u3059 +ImageWriterService.localDisk=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u30a4\u30e1\u30fc\u30b8\u30b3\u30d4\u30fc diff --git a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED index 9e4f612b6b..6be3e48e71 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED @@ -140,7 +140,7 @@ IngestJob.cancelReason.outOfDiskSpace.text=Out of disk space IngestJob.cancelReason.servicesDown.text=Services Down IngestJob.cancelReason.caseClosed.text=Case closed IngestJobSettingsPanel.globalSettingsButton.text=Global Settings -gest +gest= IngestJobSettingsPanel.globalSettingsButton.actionCommand=Advanced IngestJobSettingsPanel.globalSettingsButton.text=Global Settings IngestJobSettingsPanel.pastJobsButton.text=History diff --git a/Core/src/org/sleuthkit/autopsy/ingest/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/ingest/Bundle_ja.properties index 2fc82d0dd9..215b536e3d 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/ingest/Bundle_ja.properties @@ -1,117 +1,157 @@ +CTL_RunIngestAction=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c +IngestJobSettingsPanel.IngestModulesTableRenderer.info.message=\u4ee5\u524d\u306b\u3053\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u4e0a\u3067\u3053\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u65e7\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u5b9f\u884c\u3055\u308c\u307e\u3057\u305f\u3002 +IngestJobSettingsPanel.IngestModulesTableRenderer.warning.message=\u4ee5\u524d\u306b\u3053\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u4e0a\u3067\u3053\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5b9f\u884c\u3055\u308c\u307e\u3057\u305f\u3002 +IngestJobSettingsPanel.noPerRunSettings=\u9078\u629e\u3055\u308c\u305f\u30e2\u30b8\u30e5\u30fc\u30eb\u306b\u306f\u5b9f\u884c\u3054\u3068\u306e\u8a2d\u5b9a\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +IngestJobSettingsPanel.pastJobsButton.action.frame.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5c65\u6b74 +IngestManager.startupErr.dlgErrorList=\u30a8\u30e9\u30fc: +IngestManager.startupErr.dlgMsg=1\u3064\u4ee5\u4e0a\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u53d6\u308a\u6d88\u3055\u308c\u307e\u3057\u305f\u3002 +IngestManager.startupErr.dlgSolution=\u5931\u6557\u3057\u305f\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u7121\u52b9\u306b\u3059\u308b\u304b\u3001\u30a8\u30e9\u30fc\u3092\u4fee\u6b63\u3057\u3066\u304b\u3089\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u518d\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +IngestManager.startupErr.dlgTitle=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u8d77\u52d5\u5931\u6557 +IngestOptionsPanel.fileFiltersTab.text=\u30d5\u30a1\u30a4\u30eb\u30d5\u30a3\u30eb\u30bf\u30fc +IngestOptionsPanel.fileFiltersTab.toolTipText=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u4f5c\u6210\u304a\u3088\u3073\u7de8\u96c6\u3059\u308b\u305f\u3081\u306e\u8a2d\u5b9a\u3067\u3059\u3002 +IngestOptionsPanel.profilesTab.text=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb +IngestOptionsPanel.profilesTab.toolTipText=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3092\u4f5c\u6210\u304a\u3088\u3073\u7de8\u96c6\u3059\u308b\u305f\u3081\u306e\u8a2d\u5b9a\u3067\u3059\u3002 +IngestOptionsPanel.settingsTab.text=\u8a2d\u5b9a +IngestOptionsPanel.settingsTab.toolTipText=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u63d0\u4f9b\u53ef\u80fd\u306a\u30ea\u30bd\u30fc\u30b9\u306b\u95a2\u3059\u308b\u8a2d\u5b9a\u3067\u3059\u3002 +OpenIDE-Module-Name=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 +Menu/Tools/RunIngestModules=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c CTL_IngestMessageTopComponent=\u30e1\u30c3\u30bb\u30fc\u30b8 HINT_IngestMessageTopComponent=\u30e1\u30c3\u30bb\u30fc\u30b8\u30a6\u30a3\u30f3\u30c9\u30a6 -IngestDialog.closeButton.title=\u9589\u3058\u308b -IngestDialog.startButton.title=\u958b\u59cb -IngestJob.progress.cancelling=\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026 -IngestJob.progress.dataSourceIngest.displayName={1}\u306e{0} -IngestJob.progress.fileIngest.displayName={0}\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u4e2d -IngestManager.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -IngestManager.moduleErr.errListenToUpdates.msg=Ingest Manager\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -IngestManager.StartIngestJobsTask.run.cancelling={0}\uff08\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026\uff09 -IngestManager.StartIngestJobsTask.run.displayName=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u958b\u59cb\u4e2d -IngestMessage.exception.srcSubjDetailsDataNotNull.msg=\u30bd\u30fc\u30b9\u3001\u30b5\u30d6\u30b8\u30a7\u30af\u30c8\u3001\u8a73\u7d30\u304a\u3088\u3073\u30c7\u30fc\u30bf\u306f\u30cc\u30eb\u3067\u306f\u3044\u3051\u307e\u305b\u3093 -IngestMessage.exception.srcSubjNotNull.msg=\u30bd\u30fc\u30b9\u304a\u3088\u3073\u30b5\u30d6\u30b8\u30a7\u30af\u30c8\u306f\u30cc\u30eb\u3067\u306f\u3044\u3051\u307e\u305b\u3093 -IngestMessage.exception.typeSrcSubjNotNull.msg=\u30e1\u30c3\u30bb\u30fc\u30b8\u30bf\u30a4\u30d7\u3001\u30bd\u30fc\u30b9\u304a\u3088\u3073\u30b5\u30d6\u30b8\u30a7\u30af\u30c8\u306f\u30cc\u30eb\u3067\u306f\u3044\u3051\u307e\u305b\u3093 -IngestMessage.toString.data.text=\ \u30c7\u30fc\u30bf\uff1a{0} -IngestMessage.toString.date.text=\ \u65e5\u4ed8\uff1a{0} -IngestMessage.toString.details.text=\ \u8a73\u7d30\uff1a{0} -IngestMessage.toString.subject.text=\ \u30b5\u30d6\u30b8\u30a7\u30af\u30c8\uff1a{0} -IngestMessage.toString.type.text=\u30bf\u30a4\u30d7\uff1a{0} +IngestMessageDetailsPanel.backButton.text= +IngestMessageDetailsPanel.viewArtifactButton.text=\u7d50\u679c\u306b\u79fb\u52d5 +IngestMessageDetailsPanel.viewContentButton.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u306b\u79fb\u52d5 +IngestMessagePanel.sortByLabel.text=\u6b21\u3067\u30bd\u30fc\u30c8: +IngestMessagePanel.sortByComboBox.toolTipText=\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u6642\u9593(\u6642\u7cfb\u5217\u9806)\u307e\u305f\u306f\u30e1\u30c3\u30bb\u30fc\u30b8\u512a\u5148\u5ea6\u3067\u30bd\u30fc\u30c8 +IngestMessageDetailsPanel.messageDetailsPane.contentType=\u30c6\u30ad\u30b9\u30c8/html +IngestMessageDetailsPanel.messageDetailsPane.toolTipText= +IngestMessagesToolbar.toolTipText= IngestMessageDetailsPanel.copyMenuItem.text=\u30b3\u30d4\u30fc -IngestMessageDetailsPanel.messageDetailsPane.contentType=\u30c6\u30ad\u30b9\u30c8\uff0fhtml -IngestMessageDetailsPanel.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e -IngestMessageDetailsPanel.viewArtifactButton.text=\u7d50\u679c\u3078\u79fb\u52d5 -IngestMessageDetailsPanel.viewContentButton.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3078\u79fb\u52d5 -IngestMessagePanel.BooleanRenderer.exception.nonBoolVal.msg=\u30d6\u30fc\u30eb\u5024\u3067\u306f\u306a\u3044\u3082\u306e\u306bBooleanRenderer\u3092\u4f7f\u7528\u3057\u3088\u3046\u3068\u3057\u307e\u3057\u305f\u3002 -IngestMessagePanel.DateRenderer.exception.nonDateVal.text=\u65e5\u4ed8\u3067\u306f\u306a\u3044\u3082\u306e\u306bDateRenderer\u3092\u4f7f\u7528\u3057\u3088\u3046\u3068\u3057\u307e\u3057\u305f\u3002 -IngestMessagePanel.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -IngestMessagePanel.moduleErr.errListenUpdates.text=IngestMessagePanel\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -IngestMessagePanel.MsgTableMod.colNames.module=\u30e2\u30b8\u30e5\u30fc\u30eb -IngestMessagePanel.MsgTableMod.colNames.new=\u65b0\u898f\uff1f -IngestMessagePanel.MsgTableMod.colNames.num=\u756a\u53f7 -IngestMessagePanel.MsgTableMod.colNames.subject=\u30b5\u30d6\u30b8\u30a7\u30af\u30c8 -IngestMessagePanel.MsgTableMod.colNames.timestamp=\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7 -IngestMessagePanel.sortByComboBox.model.priority=\u512a\u5148\u5ea6 -IngestMessagePanel.sortByComboBox.model.time=\u6642\u9593 -IngestMessagePanel.sortByComboBox.toolTipText=\u6642\u9593\u9806\uff08\u6642\u7cfb\u5217\uff09\u307e\u305f\u306f\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u512a\u5148\u5ea6\u3067\u30bd\u30fc\u30c8 -IngestMessagePanel.sortByLabel.text=\u6b21\u3067\u30bd\u30fc\u30c8\uff1a -IngestMessagePanel.totalMessagesNameLabel.text=\u5408\u8a08\uff1a +IngestMessageDetailsPanel.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e +IngestMessageTopComponent.displayName=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30dc\u30c3\u30af\u30b9 +IngestMessagePanel.totalMessagesNameLabel.text=\u5408\u8a08: IngestMessagePanel.totalMessagesNameVal.text=- -IngestMessagePanel.totalUniqueMessagesNameLabel.text=\u30e6\u30cb\u30fc\u30af\uff1a +IngestMessagePanel.totalUniqueMessagesNameLabel.text=\u4e00\u610f: IngestMessagePanel.totalUniqueMessagesNameVal.text=- -IngestMessagesToolbar.customizeButton.toolTipText=\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 -IngestMessageTopComponent.displayName=\u30a4\u30f3\u30dc\u30c3\u30af\u30b9\u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 -IngestMessageTopComponent.displayReport.option.GenRpt=\u30ec\u30dd\u30fc\u30c8\u3092\u751f\u6210 +IngestJob.progress.dataSourceIngest.initialDisplayName={0} \u3092\u89e3\u6790\u4e2d\u3067\u3059 +IngestJob.progress.dataSourceIngest.displayName={1} \u306e {0} +IngestJob.progress.fileIngest.displayName={0} \u304b\u3089\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u4e2d\u3067\u3059 +IngestJob.progress.cancelling=\u53d6\u308a\u6d88\u3057\u4e2d\u3067\u3059... +IngestJob.cancellationDialog.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u53d6\u308a\u6d88\u3059 +IngestDialog.startButton.title=\u958b\u59cb +IngestDialog.closeButton.title=\u7d42\u4e86 +IngestManager.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc +IngestManager.moduleErr.errListenToUpdates.msg=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30de\u30cd\u30fc\u30b8\u30e3\u30fc\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +IngestMessage.toString.type.text=\u30bf\u30a4\u30d7: {0} +IngestMessage.toString.date.text=\ \u65e5\u4ed8: {0} +IngestMessage.toString.subject.text=\ \u4ef6\u540d: {0} +IngestMessage.toString.details.text=\ \u8a73\u7d30: {0} +IngestMessage.toString.date.text=\ \u30c7\u30fc\u30bf: {0} +IngestMessage.exception.typeSrcSubjNotNull.msg=\u30e1\u30c3\u30bb\u30fc\u30b8\u30bf\u30a4\u30d7\u3001\u30bd\u30fc\u30b9\u304a\u3088\u3073\u4ef6\u540d\u3092null\u306b\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093 +IngestMessage.exception.srcSubjNotNull.msg=\u30bd\u30fc\u30b9\u3068\u4ef6\u540d\u3092null\u306b\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093 +IngestMessage.exception.srcSubjDetailsDataNotNull.msg=\u30bd\u30fc\u30b9\u3001\u4ef6\u540d\u3001\u8a73\u7d30\u304a\u3088\u3073\u30c7\u30fc\u30bf\u3092null\u306b\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093 +IngestMessagePanel.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc +IngestMessagePanel.moduleErr.errListenUpdates.text=IngestMessagePanel\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +IngestMessagePanel.MsgTableMod.colNames.module=\u30e2\u30b8\u30e5\u30fc\u30eb +IngestMessagePanel.MsgTableMod.colNames.num=\u6570\u5b57 +IngestMessagePanel.MsgTableMod.colNames.new=\u65b0\u898f? +IngestMessagePanel.MsgTableMod.colNames.subject=\u4ef6\u540d +IngestMessagePanel.MsgTableMod.colNames.timestamp=\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7 +IngestMessagePanel.BooleanRenderer.exception.nonBoolVal.msg=\u30d6\u30fc\u30eb\u5024\u4ee5\u5916\u306e\u5024\u3067BooleanRenderer\u3092\u4f7f\u7528\u3057\u3088\u3046\u3068\u8a66\u307f\u307e\u3057\u305f\u3002 +IngestMessagePanel.DateRenderer.exception.nonDateVal.text=\u65e5\u4ed8\u5024\u4ee5\u5916\u306e\u5024\u3067DateRenderer\u3092\u4f7f\u7528\u3057\u3088\u3046\u3068\u8a66\u307f\u307e\u3057\u305f\u3002 IngestMessageTopComponent.displayReport.option.OK=OK -IngestMessageTopComponent.initComponents.name=\u30a4\u30f3\u30dc\u30c3\u30af\u30b9\u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 -IngestMessageTopComponent.msgDlg.ingestRpt.text=\u30ec\u30dd\u30fc\u30c8\u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 -IngestMonitor.mgrErrMsg.lowDiskSpace.msg=\u30c7\u30a3\u30b9\u30af{0}\u306e\u30c7\u30a3\u30b9\u30af\u9818\u57df\u4e0d\u8db3\u306e\u305f\u3081\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u4e2d\u6b62\u3057\u307e\u3059\u3002\n\u30b1\u30fc\u30b9\u30c9\u30e9\u30a4\u30d6\u306b\u6700\u4f4e1GB\u306e\u7a7a\u304d\u9818\u57df\u304c\u3042\u308b\u306e\u3092\u78ba\u8a8d\u3057\u3001\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u518d\u5b9f\u884c\u3057\u3066\u4e0b\u3055\u3044\u3002 -IngestMonitor.mgrErrMsg.lowDiskSpace.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u4e2d\u6b62\u3055\u308c\u307e\u3057\u305f\u30fc{0}\u306e\u30c7\u30a3\u30b9\u30af\u9818\u57df\u4e0d\u8db3 -OpenIDE-Module-Name=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 -IngestManager.StartIngestJobsTask.run.startupErr.dlgErrorList=\n\u30a8\u30e9\u30fc\uff1a\n{0} -IngestManager.StartIngestJobsTask.run.startupErr.dlgMsg=\u5358\u6570\u307e\u305f\u306f\u8907\u6570\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u30b9\u30bf\u30fc\u30c8\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u306f\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u307e\u3057\u305f\u3002 -IngestManager.StartIngestJobsTask.run.startupErr.dlgSolution=\u5931\u6557\u3057\u305f\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u7121\u52b9\u5316\u3059\u308b\u304b\u30a8\u30e9\u30fc\u3092\u89e3\u6c7a\u3057\u3001\u305d\u306e\u5f8c\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3001\n\u300c\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u5b9f\u884c\u300d\u3092\u9078\u629e\u3057\u3066\u3001\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u518d\u5b9f\u884c\u3057\u3066\u4e0b\u3055\u3044\u3002 -IngestManager.StartIngestJobsTask.run.startupErr.dlgTitle=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5931\u6557 -IngestJobSettings.createModuleSettingsFolder.warning=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u8a2d\u5b9a\u30d5\u30a9\u30eb\u30c0\u306e\u4f5c\u6210\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3002 -IngestJob.progress.dataSourceIngest.initialDisplayName={0}\u3092\u89e3\u6790\u4e2d -IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.dataSource=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 -IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.elapsedTime=\u7d4c\u904e\u6642\u9593\uff08\u6642\uff1a\u5206\uff1a\u79d2\uff09 -IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.file=\u30d5\u30a1\u30a4\u30eb -IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.startTime=\u958b\u59cb\u6642\u9593 -IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.threadID=\u30b9\u30ec\u30c3\u30c9ID -IngestManager.IngestMessage.ErrorMessageLimitReached.msg=\u6700\u5927\u6570({0})\u306e\u30a8\u30e9\u30fc\u304a\u3088\u3073\u307e\u305f\u306f\u8b66\u544a\u30e1\u30c3\u30bb\u30fc\u30b8\u304c\u63b2\u8f09\u3055\u308c\u307e\u3057\u305f\u3002\u3055\u3089\u306a\u308b\u30a8\u30e9\u30fc\uff0f\u8b66\u544a\u306f\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\uff08\u30d8\u30eb\u30d7->\u30ed\u30b0\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u958b\u304f\uff09 -IngestManager.IngestMessage.ErrorMessageLimitReached.subject=\u6700\u5927\u6570\u306e\u30a8\u30e9\u30fc\u304c\u63b2\u8f09\u3055\u308c\u307e\u3057\u305f -IngestManager.IngestMessage.ErrorMessageLimitReached.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30de\u30cd\u30b8\u30e3\u30fc -IngestManager.IngestThreadActivitySnapshot.idleThread=\u30a2\u30a4\u30c9\u30eb -IngestProgressSnapshotDialog.title.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d7\u30ed\u30b0\u30ec\u30b9\u30fb\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8 -IngestProgressSnapshotPanel.closeButton.text=\u9589\u3058\u308b -IngestProgressSnapshotPanel.refreshButton.text=\u30ea\u30d5\u30ec\u30c3\u30b7\u30e5 -IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.activity=\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 -ModuleTableModel.colName.duration=\u6240\u8981\u6642\u9593 -IngestJob.cancellationDialog.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u30ad\u30e3\u30f3\u30bb\u30eb -IngestJobSettings.missingModule.warning=\u4ee5\u524d\u306b\u8aad\u307f\u8fbc\u3093\u3060{0}\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -DataSourceIngestCancellationPanel.cancelAllModulesRadioButton.text=\u5168\u3066\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u30ad\u30e3\u30f3\u30bb\u30eb -DataSourceIngestCancellationPanel.cancelCurrentModuleRadioButton.text=\u73fe\u5728\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u307f\u30ad\u30e3\u30f3\u30bb\u30eb -FileIngestCancellationPanel.cancelFileIngestRadioButton.text=\u30d5\u30a1\u30a4\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u307f\u30ad\u30e3\u30f3\u30bb\u30eb -FileIngestCancellationPanel.cancelIngestJobRadioButton.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304a\u3088\u3073\u30d5\u30a1\u30a4\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u30ad\u30e3\u30f3\u30bb\u30eb -IngestJobSettings.moduleSettingsLoad.warning={1}\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e{0}\u30e2\u30b8\u30e5\u30fc\u30eb\u7528\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u8a2d\u5b9a\u3092\u4f7f\u7528\u3057\u3066\u3044\u307e\u3059\u3002 -IngestJobSettings.save.warning={0}\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -IngestJobTableModel.colName.dataSource=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 -IngestJobTableModel.colName.dirQueued=\u30ad\u30e5\u30fc\u3055\u308c\u305f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea -IngestJobTableModel.colName.filesPerSec=\u30d5\u30a1\u30a4\u30eb\uff0f\u79d2 -IngestJobTableModel.colName.filesQueued=\u30ad\u30e5\u30fc\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb -IngestJobTableModel.colName.inProgress=\u51e6\u7406\u4e2d -IngestJobTableModel.colName.jobID=\u30b8\u30e7\u30d6ID -IngestJobTableModel.colName.numProcessed=\u51e6\u7406\u3055\u308c\u305f\u6570 -IngestJobTableModel.colName.rootQueued=\u30ad\u30e5\u30fc\u3055\u308c\u305f\u30eb\u30fc\u30c8 -IngestJobTableModel.colName.start=\u30b9\u30bf\u30fc\u30c8 -IngestModuleFactoryLoader.errorMessages.duplicateDisplayName=\u5225\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u540d\u524d\u3092\u91cd\u8907\u3059\u308b\u3001{0}\u306e\u540d\u524d\u3092\u6301\u3064\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u4f7f\u7528\u3057\u307e\u305b\u3093\u3002 -IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.jobID=\u30b8\u30e7\u30d6ID -ModuleTableModel.colName.module=\u30e2\u30b8\u30e5\u30fc\u30eb - -Menu/Tools/RunIngestModules=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c -IngestManager.OpenEventChannel.Fail.ErrMsg=\u3053\u306e\u30b1\u30fc\u30b9\u3067\u4f7f\u308f\u308c\u3066\u3044\u308b\u304b\u3082\u3057\u308c\u306a\u3044\u4ed6\u306e\u30ce\u30fc\u30c9\u306b\u89e3\u6790\u30d7\u30ed\u30bb\u30b9\u304c\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +IngestMessageTopComponent.displayReport.option.GenRpt=\u30ec\u30dd\u30fc\u30c8\u751f\u6210 +IngestMessageTopComponent.msgDlg.ingestRpt.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30ec\u30dd\u30fc\u30c8 +IngestMonitor.mgrErrMsg.lowDiskSpace.title=Ingest stopped - {0} \u306e\u30c7\u30a3\u30b9\u30af\u9818\u57df\u304c\u5c11\u306a\u304f\u306a\u3063\u3066\u3044\u307e\u3059 +IngestMonitor.mgrErrMsg.lowDiskSpace.msg=\u30c7\u30a3\u30b9\u30af {0} \u306e\u30c7\u30a3\u30b9\u30af\u9818\u57df\u304c\u5c11\u306a\u304f\u306a\u3063\u3066\u3044\u308b\u305f\u3081\u3001\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u4e2d\u6b62\u3057\u307e\u3057\u305f\u3002\n\u30b1\u30fc\u30b9\u306e\u30c9\u30e9\u30a4\u30d6\u306e\u7a7a\u304d\u9818\u57df\u304c\u5c11\u306a\u304f\u30681GB\u3042\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u304b\u3089\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u518d\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +IngestManager.StartIngestJobsTask.run.displayName=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u8d77\u52d5\u4e2d\u3067\u3059 +IngestManager.StartIngestJobsTask.run.cancelling={0} (\u53d6\u308a\u6d88\u3057\u4e2d\u3067\u3059...) +IngestMessagePanel.sortByComboBox.model.time=\u6642\u523b +IngestMessagePanel.sortByComboBox.model.priority=\u512a\u5148\u5ea6 +IngestMessagesToolbar.customizeButton.toolTipText=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e1\u30c3\u30bb\u30fc\u30b8 +IngestMessageTopComponent.initComponents.name=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30dc\u30c3\u30af\u30b9 +IngestManager.OpenEventChannel.Fail.ErrMsg=\u30b1\u30fc\u30b9\u3067\u9023\u643a\u3057\u3066\u3044\u308b\u53ef\u80fd\u6027\u306e\u3042\u308b\u305d\u306e\u4ed6\u306e\u30ce\u30fc\u30c9\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 IngestManager.OpenEventChannel.Fail.Title=\u63a5\u7d9a\u5931\u6557 -IngestJobSettings.moduleSettingsSave.warning={1}\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e{0}\u30e2\u30b8\u30e5\u30fc\u30eb\u7528\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -IngestJobTableModel.colName.dsQueued=\u30ad\u30e5\u30fc\u3055\u308c\u305fDS -IngestJobSettingsPanel.jButtonSelectAll.text=\u5168\u3066\u9078\u629e -IngestJobSettingsPanel.jButtonDeselectAll.text=\u5168\u3066\u9078\u629e\u89e3\u9664 -IngestManager.cancellingIngest.msgDlg.text=\u73fe\u5728\u5b9f\u884c\u4e2d\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d -IngestManager.serviceIsDown.msgDlg.text={0}\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u307e\u3059 -RunIngestSubMenu.menuItem.empty=\u30fc\u7a7a\u767d\u30fc +IngestJobSettings.createModuleSettingsFolder.warning=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u8a2d\u5b9a\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3002 +IngestJobSettings.missingModule.warning=\u4ee5\u524d\u306b\u8aad\u307f\u8fbc\u3093\u3060 {0} \u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +IngestJobSettings.save.warning={0} \u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +IngestJobSettings.moduleSettingsLoad.warning=\u30c7\u30d5\u30a9\u30eb\u30c8\u8a2d\u5b9a\u3067\u3001{1} \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e {0} \u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +IngestJobSettings.moduleSettingsSave.warning={1} \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306e {0} \u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u8a2d\u5b9a\u3092\u4fdd\u5b58\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +IngestProgressSnapshotDialog.title.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u9032\u6357\u72b6\u6cc1\u306e\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8 +IngestProgressSnapshotPanel.refreshButton.text=\u66f4\u65b0 +IngestProgressSnapshotPanel.closeButton.text=\u7d42\u4e86 +IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.threadID=\u30b9\u30ec\u30c3\u30c9ID +IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.dataSource=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 +IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.activity=\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.file=\u30d5\u30a1\u30a4\u30eb +IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.startTime=\u958b\u59cb\u6642\u523b +IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.elapsedTime=\u7d4c\u904e\u6642\u9593(H:M:S) +IngestManager.IngestThreadActivitySnapshot.idleThread=\u30a2\u30a4\u30c9\u30eb\u4e2d +IngestManager.IngestMessage.ErrorMessageLimitReached.subject=\u63d0\u793a\u3055\u308c\u305f\u6700\u5927\u30a8\u30e9\u30fc\u6570 +IngestManager.IngestMessage.ErrorMessageLimitReached.msg=\u63d0\u793a\u3055\u308c\u305f\u30a8\u30e9\u30fc\u304a\u3088\u3073/\u307e\u305f\u306f\u8b66\u544a\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u6700\u5927\u6570 ({0}) \u3067\u3059\u3002\u305d\u306e\u4ed6\u306e\u30a8\u30e9\u30fc/\u8b66\u544a\u306f\u3001\u30ed\u30b0\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044([\u30d8\u30eb\u30d7] -> [\u30ed\u30b0\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u958b\u304f])\u3002 +IngestManager.IngestMessage.ErrorMessageLimitReached.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30de\u30cd\u30fc\u30b8\u30e3\u30fc +IngestModuleFactoryLoader.errorMessages.duplicateDisplayName={0} \u306e\u540d\u524d\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002\u5225\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u540d\u3068\u91cd\u8907\u3057\u3066\u3044\u308b\u5834\u5408\u3001\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u4f7f\u7528\u3055\u308c\u307e\u305b\u3093\u3002 +DataSourceIngestCancellationPanel.cancelCurrentModuleRadioButton.text=\u73fe\u5728\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u307f\u3092\u53d6\u308a\u6d88\u3059 +FileIngestCancellationPanel.cancelIngestJobRadioButton.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3068\u30d5\u30a1\u30a4\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u53d6\u308a\u6d88\u3059 +FileIngestCancellationPanel.cancelFileIngestRadioButton.text=\u30d5\u30a1\u30a4\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u307f\u3092\u53d6\u308a\u6d88\u3059 +DataSourceIngestCancellationPanel.cancelAllModulesRadioButton.text=\u3059\u3079\u3066\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u53d6\u308a\u6d88\u3059 +IngestProgressSnapshotPanel.SnapshotsTableModel.colNames.jobID=\u30b8\u30e7\u30d6ID +IngestJobTableModel.colName.jobID=\u30b8\u30e7\u30d6ID +IngestJobTableModel.colName.dataSource=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 +IngestJobTableModel.colName.start=\u958b\u59cb +IngestJobTableModel.colName.numProcessed=\u51e6\u7406\u3057\u305f\u6570 +IngestJobTableModel.colName.filesPerSec=\u30d5\u30a1\u30a4\u30eb/\u79d2 +IngestJobTableModel.colName.inProgress=\u9032\u884c\u4e2d +IngestJobTableModel.colName.filesQueued=\u30ad\u30e5\u30fc\u6e08\u307f\u30d5\u30a1\u30a4\u30eb +IngestJobTableModel.colName.dirQueued=\u30ad\u30e5\u30fc\u6e08\u307f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc +IngestJobTableModel.colName.rootQueued=\u30ad\u30e5\u30fc\u6e08\u307f\u30eb\u30fc\u30c8 +IngestJobTableModel.colName.dsQueued=\u30ad\u30e5\u30fc\u6e08\u307f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 +ModuleTableModel.colName.module=\u30e2\u30b8\u30e5\u30fc\u30eb +ModuleTableModel.colName.duration=\u671f\u9593 +IngestJobSettingsPanel.jButtonSelectAll.text=\u3059\u3079\u3066\u3092\u9078\u629e +IngestJobSettingsPanel.jButtonDeselectAll.text=\u3059\u3079\u3066\u306e\u9078\u629e\u3092\u89e3\u9664 +IngestManager.cancellingIngest.msgDlg.text=\u73fe\u5728\u5b9f\u884c\u4e2d\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u3092\u3059\u3079\u3066\u53d6\u308a\u6d88\u3057\u4e2d\u3067\u3059 +IngestManager.serviceIsDown.msgDlg.text={0} \u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u307e\u3059 +ProfilePanel.messages.profileNameContainsIllegalCharacter=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u540d\u306b\u4e0d\u6b63\u306a\u6587\u5b57\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059 +ProfilePanel.messages.profilesMustBeNamed=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u306b\u540d\u524d\u3092\u4ed8\u3051\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +ProfilePanel.newProfileText=NewEmptyProfile +ProfilePanel.profileDescLabel.text=\u8aac\u660e: +ProfilePanel.profileNameLabel.text=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u540d: +ProfilePanel.title.text=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb +ProfileSettingsPanel.deleteProfileButton.text=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3092\u524a\u9664 +# {0} - \u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u540d +ProfileSettingsPanel.doFileSetsDialog.duplicateProfile.text={0} \u306e\u540d\u524d\u306e\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 +ProfileSettingsPanel.editProfileButton.text=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3092\u7de8\u96c6 +ProfileSettingsPanel.filterNameLabel.text=\u30d5\u30a3\u30eb\u30bf\u30fc: +ProfileSettingsPanel.infoTextArea.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u304c\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5185\u306e\u4e00\u90e8\u307e\u305f\u306f\u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u4e0a\u3067\u4e8b\u524d\u69cb\u6210\u6e08\u307f\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u30bb\u30c3\u30c8\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002\u30d5\u30a1\u30a4\u30eb\u306e\u30b5\u30d6\u30bb\u30c3\u30c8\u4e0a\u3067\u540c\u3058\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u30bb\u30c3\u30c8\u3092\u983b\u7e41\u306b\u5b9f\u884c\u3059\u308b\u5834\u5408\u306f\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3092\u4f5c\u6210\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +ProfileSettingsPanel.messages.filterLoadFailed=\u30d5\u30a1\u30a4\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f +ProfileSettingsPanel.newProfileButton.text=\u65b0\u898f\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb +ProfileSettingsPanel.profileDescLabel.text=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u306e\u8aac\u660e: +ProfileSettingsPanel.profileListLabel.text=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb: +ProfileSettingsPanel.selectedModulesLabel.text=\u9078\u629e\u3057\u305f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb: +ProfileSettingsPanel.title=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u8a2d\u5b9a +RunIngestSubMenu.menuItem.empty=-\u7a7a(\u672a\u5165\u529b)- RunIngestModulesMenu.getName.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c -DataSourceIngestPipeline.moduleError.title.text={0}\u30a8\u30e9\u30fc -FileIngestPipeline.moduleError.title.text={0}\u30a8\u30e9\u30fc -IngestJob.cancelReason.notCancelled.text=\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u3066\u3044\u307e\u305b\u3093 -IngestJob.cancelReason.cancelledByUser.text=\u30e6\u30fc\u30b6\u30fc\u304c\u30ad\u30e3\u30f3\u30bb\u30eb\u3057\u307e\u3057\u305f -IngestJob.cancelReason.ingestModStartFail.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u8d77\u52d5\u306b\u5931\u6557 -IngestJob.cancelReason.outOfDiskSpace.text=\u30c7\u30a3\u30b9\u30af\u30b9\u30da\u30fc\u30b9\u304c\u8db3\u308a\u307e\u305b\u3093 +DataSourceIngestPipeline.moduleError.title.text={0} \u30a8\u30e9\u30fc +FileIngestPipeline.moduleError.title.text={0} \u30a8\u30e9\u30fc +IngestJob.cancelReason.notCancelled.text=\u53d6\u308a\u6d88\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +IngestJob.cancelReason.cancelledByUser.text=\u30e6\u30fc\u30b6\u30fc\u306b\u3088\u3063\u3066\u53d6\u308a\u6d88\u3055\u308c\u307e\u3057\u305f +IngestJob.cancelReason.ingestModStartFail.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +IngestJob.cancelReason.outOfDiskSpace.text=\u30c7\u30a3\u30b9\u30af\u9818\u57df\u304c\u3042\u308a\u307e\u305b\u3093 +IngestJob.cancelReason.servicesDown.text=\u30b5\u30fc\u30d3\u30b9\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u307e\u3059 IngestJob.cancelReason.caseClosed.text=\u30b1\u30fc\u30b9\u3092\u9589\u3058\u307e\u3057\u305f -IngestJobSettingsPanel.globalSettingsButton.actionCommand=\u30a2\u30c9\u30d0\u30f3\u30b9 -IngestJobSettingsPanel.globalSettingsButton.text=\u30a2\u30c9\u30d0\u30f3\u30b9 -IngestSettingsPanel.jLabelNumThreads.text=\u30d5\u30a1\u30a4\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u4f7f\u7528\u3059\u308b\u30b9\u30ec\u30c3\u30c9\u6570\uff1a +IngestJobSettingsPanel.globalSettingsButton.text=\u30b0\u30ed\u30fc\u30d0\u30eb\u8a2d\u5b9a +\u30b2\u30b9\u30c8 +IngestJobSettingsPanel.globalSettingsButton.actionCommand=\u9ad8\u5ea6 +IngestJobSettingsPanel.globalSettingsButton.text=\u30b0\u30ed\u30fc\u30d0\u30eb\u8a2d\u5b9a +IngestJobSettingsPanel.pastJobsButton.text=\u5c65\u6b74 +IngestJobSettingsPanel.fileIngestFilterLabel.text=\u6b21\u3067\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c: +OptionsCategory_Name_IngestOptions=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 +OptionsCategory_Keywords_IngestOptions=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 IngestSettingsPanel.jLabelProcessTimeOutUnits.text=\u6642\u9593 -IngestSettingsPanel.jLabelSetProcessTimeOut.text=\u4e00\u5b9a\u306e\u6642\u9593\u304c\u904e\u304e\u305f\u5f8c\u306b\u81ea\u52d5\u7684\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u505c\u6b62\u3067\u304d\u308b\u3088\u3046\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3092\u6709\u52b9\u5316\uff1a -IngestSettingsPanel.restartRequiredLabel.text=\u3053\u306e\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u30fc\u3067\u306f\u6700\u5927{0}\u306e\u30d5\u30a1\u30a4\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b9\u30ec\u30c3\u30c9\u3092\u4f7f\u7528\u3059\u3079\u304d\u3067\u3059\u3002\u6709\u52b9\u306b\u3059\u308b\u306b\u306f\u518d\u8d77\u52d5\u304c\u5fc5\u8981\u3067\u3059\u3002 -IngestSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u3067\u3059\u3002\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 -ProfileSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u3067\u3059\u3002\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +IngestSettingsPanel.jFormattedTextFieldProcTimeOutHrs.text=60 +IngestSettingsPanel.jCheckBoxEnableProcTimeout.text= +IngestSettingsPanel.jLabelSetProcessTimeOut.text=\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3092\u6709\u52b9\u306b\u3059\u308b\u3068\u3001\u8a2d\u5b9a\u3057\u305f\u6642\u9593\u306e\u7d4c\u904e\u5f8c\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u81ea\u52d5\u7684\u306b\u7d42\u4e86\u3067\u304d\u307e\u3059\u3002 +IngestSettingsPanel.restartRequiredLabel.text=\u3053\u306e\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u30fc\u306e\u5834\u5408\u3001\u30d5\u30a1\u30a4\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b9\u30ec\u30c3\u30c9 {0} \u306e\u6700\u5927\u6570\u3092\u4f7f\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u6709\u52b9\u306b\u3059\u308b\u306b\u306f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8d77\u52d5\u304c\u5fc5\u8981\u3067\u3059\u3002 +IngestSettingsPanel.jLabelNumThreads.text=\u30d5\u30a1\u30a4\u30eb\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u4f7f\u7528\u3067\u304d\u308b\u30b9\u30ec\u30c3\u30c9\u6570: +IngestSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +ProfileSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/ingest/GetFilesContentVisitor.java b/Core/src/org/sleuthkit/autopsy/ingest/GetFilesContentVisitor.java index e0cb7176f9..7dfbdacb4c 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/GetFilesContentVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/GetFilesContentVisitor.java @@ -30,6 +30,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.datamodel.LocalDirectory; +import org.sleuthkit.datamodel.Pool; import org.sleuthkit.datamodel.Report; import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.Volume; @@ -71,6 +72,11 @@ abstract class GetFilesContentVisitor implements ContentVisitor visit(VolumeSystem vs) { return getAllFromChildren(vs); } + + @Override + public Collection visit(Pool pool) { + return getAllFromChildren(pool); + } @Override public Collection visit(Report r) { diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java index 3117ef2f98..7bad9ebf98 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java @@ -110,7 +110,6 @@ public final class IngestServices { @Deprecated public void fireModuleDataEvent(ModuleDataEvent moduleDataEvent) { IngestManager.getInstance().fireIngestModuleDataEvent(moduleDataEvent); - } /** diff --git a/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/Bundle_ja.properties index 39d7a2632b..431b60a8b1 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/Bundle_ja.properties @@ -1 +1,8 @@ -RunIngestModulesAction.name=\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 \ No newline at end of file +IngestModulesConfigWizardPanel.name.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u69cb\u6210 +IngestProfileSelectionPanel.customSettings.description=\u30a6\u30a3\u30b6\u30fc\u30c9\u306e\u6b21\u306e\u30b9\u30c6\u30c3\u30d7\u3067\u500b\u3005\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u8a2d\u5b9a\u3092\u69cb\u6210 +IngestProfileSelectionPanel.customSettings.name=\u30ab\u30b9\u30bf\u30e0\u8a2d\u5b9a +IngestProfileSelectionPanel.ingestSettingsButton.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u8a2d\u5b9a +IngestProfileSelectionPanel.profileListLabel.text=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e: +IngestProfileWizardPanel.panelName=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u9078\u629e\u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 +RunIngestModulesAction.actionPerformed.errorMessage=\u9078\u629e\u3057\u305f\u9805\u76ee\u306e\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30af\u30a8\u30ea\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +RunIngestModulesAction.name=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c diff --git a/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java b/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java index fdd85b4609..981abe05de 100644 --- a/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java +++ b/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java @@ -28,11 +28,11 @@ import org.sleuthkit.datamodel.TskCoreException; /** * An interface for implementations of a keyword search service. You can find * the implementations by using Lookup, such as: - * + * * Lookup.getDefault().lookup(KeywordSearchService.class) - * + * * although most clients should obtain a keyword search service by calling: - * + * * Case.getCurrentCase().getServices().getKeywordSearchService() * * TODO (AUT-2158): This interface should not extend Closeable. @@ -82,7 +82,7 @@ public interface KeywordSearchService extends Closeable { * @throws KeywordSearchServiceException if unable to delete. */ public void deleteTextIndex(CaseMetadata metadata) throws KeywordSearchServiceException; - + /** * Closes the keyword search service. * @@ -95,6 +95,15 @@ public interface KeywordSearchService extends Closeable { * No-op maintained for backwards compatibility. Clients should not * attempt to close case services. */ - } + } + + /** + * Deletes the keyword search text for a specific data source. + * + * @param dataSourceId The data source id to be deleted. + * + * @throws KeywordSearchServiceException if unable to delete. + */ + void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException; } diff --git a/Core/src/org/sleuthkit/autopsy/menuactions/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/menuactions/Bundle_ja.properties index 9bbd244f08..76dff54faf 100644 --- a/Core/src/org/sleuthkit/autopsy/menuactions/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/menuactions/Bundle_ja.properties @@ -1,7 +1,7 @@ -OpenIDE-Module-Name=\u30E1\u30CB\u30E5\u30FC\u30A2\u30AF\u30B7\u30E7\u30F3 -DataContentDynamicMenu.menu.dataContentWin.text=\u30C7\u30FC\u30BF\u30B3\u30F3\u30C6\u30F3\u30C4\u30A6\u30A3\u30F3\u30C9\u30A6 -DataContentMenu.getName.text=\u30C7\u30FC\u30BF\u30B3\u30F3\u30C6\u30F3\u30C4\u30E1\u30CB\u30E5\u30FC -DataExplorerMenu.getName.text=\u30C7\u30FC\u30BF\u30A8\u30AF\u30B9\u30D7\u30ED\u30FC\u30E9\u30C4\u30FC\u30EB -DataResultMenu.menu.dataResWin.text=\u30C7\u30FC\u30BF\u7D50\u679C\u30A6\u30A3\u30F3\u30C9\u30A6 -DataResultMenu.getName.text=\u30C7\u30FC\u30BF\u7D50\u679C\u30E1\u30CB\u30E5\u30FC -SearchResultMenu.menu.dataRes.text=\u30C7\u30FC\u30BF\u7D50\u679C \ No newline at end of file +OpenIDE-Module-Name=\u30e1\u30cb\u30e5\u30fc\u30a2\u30af\u30b7\u30e7\u30f3 +DataContentDynamicMenu.menu.dataContentWin.text=\u30c7\u30fc\u30bf\u30b3\u30f3\u30c6\u30f3\u30c4\u30a6\u30a3\u30f3\u30c9\u30a6 +DataContentMenu.getName.text=\u30c7\u30fc\u30bf\u30b3\u30f3\u30c6\u30f3\u30c4\u30e1\u30cb\u30e5\u30fc +DataExplorerMenu.getName.text=\u30c7\u30fc\u30bf\u30a8\u30af\u30b9\u30d7\u30ed\u30fc\u30e9\u30c4\u30fc\u30eb +DataResultMenu.menu.dataResWin.text=\u30c7\u30fc\u30bf\u7d50\u679c\u30a6\u30a3\u30f3\u30c9\u30a6 +DataResultMenu.getName.text=\u30c7\u30fc\u30bf\u7d50\u679c\u30e1\u30cb\u30e5\u30fc +SearchResultMenu.menu.dataRes.text=\u30c7\u30fc\u30bf\u7d50\u679c diff --git a/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/Bundle_ja.properties index cbd02b8693..f73bf77a55 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/Bundle_ja.properties @@ -1,14 +1,50 @@ -OpenIDE-Module-Name=EWFVerify -DataSourceIntegrityIngestModule.process.errProcImg={0}\u3092\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -DataSourceIntegrityModuleFactory.moduleName.text=E01\u8a8d\u8a3c\u30c4\u30fc\u30eb -DataSourceIntegrityModuleFactory.moduleDesc.text=E01\u30d5\u30a1\u30a4\u30eb\u306e\u6574\u5408\u6027\u3092\u8a8d\u8a3c\u3057\u307e\u3059\u3002 -DataSourceIntegrityIngestModule.process.skipNonEwf=E01\u30a4\u30e1\u30fc\u30b8\u3067\u306f\u306a\u3044{0}\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u3066\u3044\u307e\u3059 -DataSourceIntegrityIngestModule.process.noStoredHash=\u30a4\u30e1\u30fc\u30b8{0}\u306f\u4fdd\u5b58\u3055\u308c\u3066\u3044\u308b\u30cf\u30c3\u30b7\u30e5\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -DataSourceIntegrityIngestModule.process.startingImg={0}\u3092\u958b\u59cb\u4e2d -DataSourceIntegrityIngestModule.process.errReadImgAtChunk={0}\u306e\u30c1\u30e3\u30f3\u30af{1}\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -DataSourceIntegrityIngestModule.shutDown.calcHashLi=
  • \u8a08\u7b97\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u5024\uff1a{0}
  • -DataSourceIntegrityIngestModule.shutDown.notVerified=\u8a8d\u8a3c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -DataSourceIntegrityIngestModule.shutDown.resultLi=
  • \u7d50\u679c\uff1a{0}
  • -DataSourceIntegrityIngestModule.shutDown.storedHashLi=
  • \u4fdd\u5b58\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\uff1a {0}
  • -DataSourceIntegrityIngestModule.shutDown.verifyResultsHeader=

    {0}\u306eEWF\u30d9\u30ea\u30d5\u30a3\u30b1\u30fc\u30b7\u30e7\u30f3\u7d50\u679c

    -EwfVerifyIDataSourceIntegrityIngestModulengestModule.shutDown.verified=\u8a8d\u8a3c\u3055\u308c\u307e\u3057\u305f \ No newline at end of file +# {0} - hashName +# {1} - calculatedHashValue +DataSourceIntegrityIngestModule.process.calcHashWithType=
  • \u8a08\u7b97\u6e08\u307f {0} \u30cf\u30c3\u30b7\u30e5: {1}
  • +# {0} - imageName +DataSourceIntegrityIngestModule.process.calculateHashDone=

    {0} \u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30cf\u30c3\u30b7\u30e5\u8a08\u7b97\u7d50\u679c

    +# {0} - imageName +DataSourceIntegrityIngestModule.process.errorLoadingHashes=\ \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u30a4\u30e1\u30fc\u30b8 {0} \u306e\u30cf\u30c3\u30b7\u30e5\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +# {0} - imageName +DataSourceIntegrityIngestModule.process.errorSavingHashes=\ \u30a4\u30e1\u30fc\u30b8 {0} \u306e\u30cf\u30c3\u30b7\u30e5\u3092\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u4fdd\u5b58\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +# {0} - hashName +DataSourceIntegrityIngestModule.process.hashAlgorithmError={0} \u30a2\u30eb\u30b4\u30ea\u30ba\u30e0\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +DataSourceIntegrityIngestModule.process.hashesCalculated=\ \u8a08\u7b97\u6e08\u307f\u30cf\u30c3\u30b7\u30e5 +# {0} - hashAlgorithm +# {1} - calculatedHashValue +# {2} - storedHashValue +DataSourceIntegrityIngestModule.process.hashFailedForArtifact={0} \u30cf\u30c3\u30b7\u30e5\u306e\u691c\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f:\n \u8a08\u7b97\u6e08\u307f\u30cf\u30c3\u30b7\u30e5: {1}\N \u4fdd\u5b58\u6e08\u307f\u30cf\u30c3\u30b7\u30e5: {2}\n +# {0} - calculatedHashValue +# {1} - storedHashValue +DataSourceIntegrityIngestModule.process.hashList=
    • \u8a08\u7b97\u6e08\u307f\u30cf\u30c3\u30b7\u30e5: {0}
    • \u4fdd\u5b58\u6e08\u307f\u30cf\u30c3\u30b7\u30e5: {1}
    +# {0} - hashName +DataSourceIntegrityIngestModule.process.hashMatch=
  • {0} \u691c\u8a3c\u6e08\u307f\u30cf\u30c3\u30b7\u30e5
  • +# {0} - hashName +DataSourceIntegrityIngestModule.process.hashNonMatch=
  • {0} \u30cf\u30c3\u30b7\u30e5\u304c\u691c\u8a3c\u3055\u308c\u3066\u3044\u307e\u305b\u3093
  • +# {0} - imageName +DataSourceIntegrityIngestModule.process.skipCompute=\u30aa\u30d7\u30b7\u30e7\u30f3\u304c\u7121\u52b9\u3060\u3063\u305f\u305f\u3081 {0} \u306e\u65b0\u898f\u30cf\u30c3\u30b7\u30e5\u306e\u8a08\u7b97\u304c\u884c\u308f\u308c\u3066\u3044\u307e\u305b\u3093 +# {0} - imageName +DataSourceIntegrityIngestModule.process.skipVerify=\u30aa\u30d7\u30b7\u30e7\u30f3\u304c\u7121\u52b9\u3060\u3063\u305f\u305f\u3081 {0} \u306e\u65e2\u5b58\u30cf\u30c3\u30b7\u30e5\u306e\u691c\u8a3c\u304c\u884c\u308f\u308c\u3066\u3044\u307e\u305b\u3093 +# {0} - imageName +DataSourceIntegrityIngestModule.process.verificationFailure={0} \u304c\u5b8c\u5168\u6027\u691c\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f +# {0} - imageName +DataSourceIntegrityIngestModule.process.verificationSuccess={0} \u306e\u5b8c\u5168\u6027\u304c\u691c\u8a3c\u3055\u308c\u307e\u3057\u305f +DataSourceIntegrityIngestModule.startup.noCheckboxesSelected=\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u30c1\u30a7\u30c3\u30af\u30dc\u30c3\u30af\u30b9\u3092\u9078\u629e\u3057\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093 +OpenIDE-Module-Name=ewfVerify +DataSourceIntegrityModuleFactory.moduleName.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5b8c\u5168\u6027 +DataSourceIntegrityModuleFactory.moduleDesc.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u30cf\u30c3\u30b7\u30e5\u3092\u8a08\u7b97\u304a\u3088\u3073\u691c\u8a3c\u3057\u307e\u3059\u3002 +DataSourceIntegrityIngestModule.process.errProcImg={0} \u306e\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +DataSourceIntegrityIngestModule.process.skipNonEwf=\u975e\u30c7\u30a3\u30b9\u30af\u30a4\u30e1\u30fc\u30b8\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 {0} \u3092\u30b9\u30ad\u30c3\u30d7\u4e2d\u3067\u3059 +DataSourceIntegrityIngestModule.process.noStoredHash=\u30a4\u30e1\u30fc\u30b8 {0} \u306b\u4fdd\u5b58\u6e08\u307f\u30cf\u30c3\u30b7\u30e5\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +DataSourceIntegrityIngestModule.process.startingImg={0} \u3092\u8d77\u52d5\u4e2d\u3067\u3059 +DataSourceIntegrityIngestModule.process.errReadImgAtChunk=\u30c1\u30e3\u30f3\u30af {1} \u306e {0} \u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +DataSourceIntegrityIngestModule.shutDown.verified=\ \u691c\u8a3c\u6e08\u307f +DataSourceIntegrityIngestModule.shutDown.notVerified=\ \u691c\u8a3c\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +DataSourceIntegrityIngestModule.shutDown.verifyResultsHeader=

    {0} \u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u691c\u8a3c\u7d50\u679c

    +DataSourceIntegrityIngestModule.shutDown.resultLi=
  • \u7d50\u679c:{0}
  • +DataSourceIntegrityIngestModule.shutDown.calcHashLi=
  • \u8a08\u7b97\u6e08\u307f\u30cf\u30c3\u30b7\u30e5: {0}
  • +DataSourceIntegrityIngestModule.shutDown.storedHashLi=
  • \u4fdd\u5b58\u6e08\u307f\u30cf\u30c3\u30b7\u30e5: {0}
  • +DataSourceIntegrityIngestSettingsPanel.computeHashesCheckbox.text=\u4f55\u3082\u306a\u3044\u5834\u5408\u306f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30cf\u30c3\u30b7\u30e5\u3092\u8a08\u7b97 +DataSourceIntegrityIngestSettingsPanel.jLabel1.text=\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u8ad6\u7406\u30d5\u30a1\u30a4\u30eb\u4e0a\u3067\u306f\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093 +DataSourceIntegrityIngestSettingsPanel.jLabel3.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u8a2d\u5b9a +DataSourceIntegrityIngestSettingsPanel.verifyHashesCheckbox.text=\u65e2\u5b58\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30cf\u30c3\u30b7\u30e5\u3092\u691c\u8a3c diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED index d73865ac3e..4729293fb9 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED @@ -11,12 +11,7 @@ ExtractArchiveWithPasswordAction.progress.text=Unpacking contents of archive: {0 ExtractArchiveWithPasswordAction.prompt.text=Enter Password ExtractArchiveWithPasswordAction.prompt.title=Enter Password OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=\ - Embedded File Extraction Ingest Module\n\nThe Embedded File Extraction Ingest Module processes document files (such as doc, docx, ppt, pptx, xls, xlsx) and archive files (such as zip and others archive types supported by the 7zip extractor).\n\ - Contents of these files are extracted and the derived files are added back to the current ingest to be processed by the configured ingest modules.\n\ - If the derived file happens to be an archive file, it will be re-processed by the 7zip extractor - the extractor will process archive files N-levels deep.\n\n\ - The extracted files are navigable in the directory tree.\n\n\ - The module is supported on Windows, Linux and Mac operating systems. +OpenIDE-Module-Long-Description=Embedded File Extraction Ingest Module\n\nThe Embedded File Extraction Ingest Module processes document files (such as doc, docx, ppt, pptx, xls, xlsx) and archive files (such as zip and others archive types supported by the 7zip extractor).\nContents of these files are extracted and the derived files are added back to the current ingest to be processed by the configured ingest modules.\nIf the derived file happens to be an archive file, it will be re-processed by the 7zip extractor - the extractor will process archive files N-levels deep.\n\nThe extracted files are navigable in the directory tree.\n\nThe module is supported on Windows, Linux and Mac operating systems. OpenIDE-Module-Name=Embedded File Extraction OpenIDE-Module-Short-Description=Embedded File Extraction Ingest Module EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=Invalid seek origin: {0} diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle_ja.properties index 66063448fd..355c94be83 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle_ja.properties @@ -1,40 +1,55 @@ +CannotCreateOutputFolder=\u51fa\u529b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002 +CannotRunFileTypeDetection=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u691c\u51fa\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 +EmbeddedFileExtractorIngestModule.ArchiveExtractor.moduleDesc.text=\u57cb\u3081\u8fbc\u307f\u30d5\u30a1\u30a4\u30eb(doc, docx, ppt, pptx, xls, xlsx, zip, rar, arj, 7z, gzip, bzip2, tar)\u3092\u62bd\u51fa\u3057\u3001\u30d5\u30a1\u30a4\u30eb\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u30b9\u30b1\u30b8\u30e5\u30fc\u30eb\u3057\u3001\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u30c4\u30ea\u30fc\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u5165\u529b\u3057\u307e\u3059\u3002 +EmbeddedFileExtractorIngestModule.ArchiveExtractor.moduleName=\u57cb\u3081\u8fbc\u307f\u30d5\u30a1\u30a4\u30eb\u62bd\u51fa\u30c4\u30fc\u30eb +EmbeddedFileExtractorIngestModule.NoOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +EmbeddedFileExtractorIngestModule.UnableToGetMSOfficeExtractor.errMsg=MSOfficeEmbeddedContentExtractor\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002 +ExtractArchiveWithPasswordAction.extractFailed.title=\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u5c01\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +ExtractArchiveWithPasswordAction.name.text=\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u89e3\u51cd +# {0} - archiveFile +ExtractArchiveWithPasswordAction.progress.text=\u6b21\u306e\u30a2\u30fc\u30ab\u30a4\u30d6\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u958b\u5c01\u4e2d\u3067\u3059: {0} +ExtractArchiveWithPasswordAction.prompt.text=\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5165\u529b +ExtractArchiveWithPasswordAction.prompt.title=\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5165\u529b OpenIDE-Module-Display-Category=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb OpenIDE-Module-Long-Description=\ - \u57cb\u3081\u8fbc\u307f\u30d5\u30a1\u30a4\u30eb\u62bd\u51fa\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\n\n\u57cb\u3081\u8fbc\u307f\u30d5\u30a1\u30a4\u30eb\u62bd\u51fa\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d5\u30a1\u30a4\u30eb\uff08doc\u3001docx\u3001ppt\u3001pptx\u3001xls\u3001xlsx\u7b49\uff09\u3084\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\uff08zip\u3084\u305d\u306e\u4ed6\u306e7zip\u30a8\u30af\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u3067\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\uff09\u3092\u51e6\u7406\u3057\u307e\u3059\u3002\n\ - 7Zip\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\n\n7Zip\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u3057\u307e\u3059\uff08zip\u30847zip\u30a8\u30af\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u306b\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u305d\u306e\u4ed6\u306e\u30a2\u30fc\u30ab\u30a4\u30d6\u30bf\u30a4\u30d7\u306a\u3069\uff09\u3002\n\ - \u30a2\u30fc\u30ab\u30a4\u30d6\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u304c\u62bd\u51fa\u3055\u308c\u3001\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u306f\u5b9f\u884c\u4e2d\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u8ffd\u52a0\u3055\u308c\u3001\u8a2d\u5b9a\u3055\u308c\u305f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3067\u51e6\u7406\u3055\u308c\u307e\u3059\u3002\n\ - \u3082\u3057\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u304c\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\u3067\u3042\u308c\u3070\u30017Zip\u30a8\u30af\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u306b\u3088\u308a\u3001\u518d\u5ea6\u51e6\u7406\u3055\u308c\u307e\u3059 - \u30a8\u30af\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u306f\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\u3092N-\u30ec\u30d9\u30eb\u306e\u6df1\u3055\u3067\u51e6\u7406\u3057\u307e\u3059\u3002\n\n\ - \u62bd\u51fa\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c4\u30ea\u30fc\u3067\u30ca\u30d3\u30b2\u30fc\u30c8\u3067\u304d\u307e\u3059\u3002\n\n\ - \u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306fWindows\u3001Linux\u3001Mac\u306e\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0\u74b0\u5883\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u3059\u3002 + Embedded File Extraction Ingest Module\n\n\u57cb\u3081\u8fbc\u307f\u30d5\u30a1\u30a4\u30eb\u62bd\u51fa\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u3001\u6587\u66f8\u30d5\u30a1\u30a4\u30eb(\u4f8b: doc, docx, ppt, pptx, xls, xlsx)\u3068\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb(\u4f8b: zip\u3001\u5727\u7e2e\u30fb\u89e3\u51cd\u30bd\u30d5\u30c87zip\u3067\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u305d\u306e\u4ed6\u30a2\u30fc\u30ab\u30a4\u30d6\u30bf\u30a4\u30d7)\u3092\u51e6\u7406\u3057\u307e\u3059\u3002\n\ + \u3053\u308c\u3089\u306e\u30d5\u30a1\u30a4\u30eb\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u62bd\u51fa\u3057\u3001\u69cb\u6210\u6e08\u307f\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306b\u3088\u3063\u3066\u51e6\u7406\u3055\u308c\u308b\u73fe\u5728\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u3092\u623b\u3057\u307e\u3059\u3002\n\ + \u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u304c\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\u306e\u5834\u5408\u3001\u5727\u7e2e\u30fb\u89e3\u51cd\u30bd\u30d5\u30c87zip\u3067\u518d\u51e6\u7406\u3055\u308c\u307e\u3059\u3002\u5727\u7e2e\u30fb\u89e3\u51cd\u30bd\u30d5\u30c8\u304cN\u30ec\u30d9\u30eb\u306e\u6df1\u3055\u3067\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u3057\u307e\u3059\u3002\n\n\ + \u62bd\u51fa\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u30c4\u30ea\u30fc\u3067\u53c2\u7167\u3067\u304d\u307e\u3059\u3002\n\n\ + \u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u3002Windows\u3001Linux\u3001Mac\u306e\u5404\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0\u3067\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002 OpenIDE-Module-Name=\u57cb\u3081\u8fbc\u307f\u30d5\u30a1\u30a4\u30eb\u62bd\u51fa -OpenIDE-Module-Short-Description=\u57cb\u3081\u8fbc\u307f\u30d5\u30a1\u30a4\u30eb\u62bd\u51fa\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb -SevenZipContentReadStream.seek.exception.invalidOrigin=\u7121\u52b9\u306a\u30b7\u30fc\u30af\u539f\u70b9\uff1a {0} -SevenZipContentReadStream.read.exception.errReadStream=\u30b3\u30f3\u30c6\u30f3\u30c4\u30b9\u30c8\u30ea\u30fc\u30e0\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=\u7121\u52b9\u306a\u30b7\u30fc\u30af\u306e\u539f\u70b9\: {0} +OpenIDE-Module-Short-Description=\u57cb\u3081\u8fbc\u307f\u30d5\u30a1\u30a4\u30eb\u62bd\u51fa\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb +EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=\u7121\u52b9\u306a\u30b7\u30fc\u30af\u30aa\u30ea\u30b8\u30f3: {0} EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.read.exception.errReadStream=\u30b3\u30f3\u30c6\u30f3\u30c4\u30b9\u30c8\u30ea\u30fc\u30e0\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.moduleName=\u57cb\u3081\u8fbc\u307f\u30d5\u30a1\u30a4\u30eb\u306e\u62bd\u51fa\u30c4\u30fc\u30eb -EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=\u30d5\u30a1\u30a4\u30eb\u30ec\u30d9\u30eb\u3067\u306e\u6697\u53f7\u5316 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull=\u5b8c\u5168\u306a\u6697\u53f7\u5316 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\: {0}\: {1}\u3092\u8d77\u52d5\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=\u30a2\u30fc\u30ab\u30a4\u30d6\: {0}, item\: {1}\u306bZIP\u7206\u5f3e\u304b\u3082\u3057\u308c\u306a\u3044\u3082\u306e\u3092\u767a\u898b -EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=\u5727\u7e2e\u7387\u304c{0}\u3001{1}\u306b\u3042\u308b\u30a2\u30a4\u30c6\u30e0\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb=ZIP\u7206\u5f3e\u304b\u3082\u3057\u308c\u306a\u3044\u3082\u306e\u3092\u767a\u898b\uff1a{0} -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb=\u30a2\u30fc\u30ab\u30a4\u30d6\u306f{0}\u30ec\u30d9\u30eb\u306e\u6df1\u3055\u3067\u3059\u3002{1}\u306e\u51e6\u7406\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\: {0}\u306b\u4e0d\u660e\u306a\u30a2\u30a4\u30c6\u30e0\u30d1\u30b9\u304c\u3042\u308a\u307e\u3059\u3002{1}\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\u30a2\u30a4\u30c6\u30e0\: {0}, {1}\u3092\u89e3\u51cd\u3059\u308b\u306e\u306b\u30c7\u30a3\u30b9\u30af\u30b9\u30da\u30fc\u30b9\u304c\u8db3\u308a\u307e\u305b\u3093 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details=\u30a2\u30fc\u30ab\u30a4\u30d6\u30a2\u30a4\u30c6\u30e0\u304c\u89e3\u51cd\u3059\u308b\u306e\u306b\u5927\u304d\u3059\u304e\u307e\u3059\u3002\u3053\u306e\u30a2\u30a4\u30c6\u30e0\u306e\u89e3\u51cd\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg={0}\u306e\u89e3\u51cd\u30a8\u30e9\u30fc -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details={0}. {1}\u3092\u89e3\u51cd\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\u306b\u6697\u53f7\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u767a\u898b\u3057\u307e\u3057\u305f\u3002 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details=\u30a2\u30fc\u30ab\u30a4\u30d6\: {0}\u306e\u4e00\u90e8\u306e\u30d5\u30a1\u30a4\u30eb\u306f\u6697\u53f7\u5316\u3055\u308c\u3066\u3044\u307e\u3059\u3002 {1} \u30a8\u30af\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u304c\u3053\u306e\u30a2\u30fc\u30ab\u30a4\u30d6\u306e\u5168\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg=\u89e3\u51cd\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3092\: {0}\u306b\u66f8\u304d\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\:{0}\u306b\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -EmbeddedFileExtractorIngestModule.ImageExtractor.docContainer.init.err=\u8aad\u307f\u53d6\u308a\u4e2d\u306bDoc\u30b3\u30f3\u30c6\u30a4\u30ca\u30fc\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -EmbeddedFileExtractorIngestModule.ImageExtractor.docxContainer.init.err={0}\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306bDocx\u30b3\u30f3\u30c6\u30a4\u30ca\u30fc\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -EmbeddedFileExtractorIngestModule.ImageExtractor.pptContainer.init.err={0}\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306bPpt\u30b3\u30f3\u30c6\u30a4\u30ca\u30fc\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -EmbeddedFileExtractorIngestModule.ImageExtractor.pptxContainer.init.err={0}\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306bPptx\u30b3\u30f3\u30c6\u30a4\u30ca\u30fc\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -EmbeddedFileExtractorIngestModule.ImageExtractor.xlsContainer.init.err={0}\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306bXls\u30b3\u30f3\u30c6\u30a4\u30ca\u30fc\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -EmbeddedFileExtractorIngestModule.ImageExtractor.xlsxContainer.init.err={0}\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306bXlsx\u30b3\u30f3\u30c6\u30a4\u30ca\u30fc\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -EmbeddedFileExtractorIngestModule.ImageExtractor.extractImage.addToDB.exception.msg=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -EmbeddedFileExtractorIngestModule.ImageExtractor.getOutputFolderPath.exception.msg=\u30a2\u30d6\u30b9\u30c8\u30e9\u30af\u30c8\u30d5\u30a1\u30a4\u30eb\: {0}\u304b\u3089\u753b\u50cf\u62bd\u51fa\u306e\u30d1\u30b9\u3092\u5165\u624b\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg=\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u30c7\u30a3\u30b9\u30af\u306b\u66f8\u304d\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u307f\u306e\u6697\u53f7\u5316(\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb) +EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull=\u5b8c\u5168\u306a\u6697\u53f7\u5316(\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb) +EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=\u30a8\u30e9\u30fc\u307e\u305f\u306f\u51fa\u529b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u306e\u521d\u671f\u5316\u4e2d\u3067\u3059: {0}: {1} +EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=\u30a2\u30fc\u30ab\u30a4\u30d6\u3067ZIP\u7206\u5f3e\u304c\u691c\u51fa\u3055\u308c\u305f\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059: {0}\u3001\u9805\u76ee: {1} +EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=\u5727\u7e2e\u7387\u306f {0} \u3067\u3059\u3002{1} \u306e\u9805\u76ee\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002 +EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb=ZIP\u7206\u5f3e\u304c\u691c\u51fa\u3055\u308c\u305f\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059: {0} +EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb=\u30a2\u30fc\u30ab\u30a4\u30d6\u306f {0} \u30ec\u30d9\u30eb\u306e\u6df1\u3055\u3067\u3059\u3002{1} \u306e\u51e6\u7406\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059 +EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\u306e\u4e0d\u660e\u306a\u9805\u76ee\u30d1\u30b9: {0}\u3001\u6b21\u3092\u4f7f\u7528\u3057\u307e\u3059: {1} +EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\u9805\u76ee\u3092\u958b\u5c01\u3059\u308b\u305f\u3081\u306e\u5341\u5206\u306a\u30c7\u30a3\u30b9\u30af\u9818\u57df\u304c\u3042\u308a\u307e\u305b\u3093: {0}\u3001{1} +EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details=\u30a2\u30fc\u30ab\u30a4\u30d6\u9805\u76ee\u304c\u5927\u304d\u3059\u304e\u3067\u958b\u5c01\u3067\u304d\u307e\u305b\u3093\u3002\u3053\u306e\u9805\u76ee\u306e\u958b\u5c01\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002 +EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg={0} \u306e\u958b\u5c01\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details={0} \u306e\u958b\u5c01\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002{1} +EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\u306b\u6697\u53f7\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f\u3002 +EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details=\u30a2\u30fc\u30ab\u30a4\u30d6\u306e\u3044\u304f\u3064\u304b\u306e\u30d5\u30a1\u30a4\u30eb: {0} \u304c\u6697\u53f7\u5316\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5727\u7e2e\u30fb\u89e3\u51cd\u30bd\u30d5\u30c8 {1} \u306f\u3053\u306e\u30a2\u30fc\u30ab\u30a4\u30d6\u304b\u3089\u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg=\u30d5\u30a1\u30a4\u30eb\u3092\u6b21\u306b\u958b\u5c01\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg=\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u3092\u6b21\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f:{0} +EmbeddedFileExtractorIngestModule.ImageExtractor.docContainer.init.err=\u8aad\u307f\u53d6\u308a\u4e2d\u306bDOC\u30b3\u30f3\u30c6\u30ca\u30fc\u3092\u521d\u671f\u5316\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +EmbeddedFileExtractorIngestModule.ImageExtractor.docxContainer.init.err=\u6b21\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306bDOCX\u30b3\u30f3\u30c6\u30ca\u30fc\u3092\u521d\u671f\u5316\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +EmbeddedFileExtractorIngestModule.ImageExtractor.pptContainer.init.err=\u6b21\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306bPPT\u30b3\u30f3\u30c6\u30ca\u30fc\u3092\u521d\u671f\u5316\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +EmbeddedFileExtractorIngestModule.ImageExtractor.pptxContainer.init.err=\u8aad\u307f\u53d6\u308a\u4e2d\u306bPPTX\u30b3\u30f3\u30c6\u30ca\u30fc\u3092\u521d\u671f\u5316\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +EmbeddedFileExtractorIngestModule.ImageExtractor.xlsContainer.init.err=\u6b21\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306bXLS\u30b3\u30f3\u30c6\u30ca\u30fc\u3092\u521d\u671f\u5316\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +EmbeddedFileExtractorIngestModule.ImageExtractor.xlsxContainer.init.err=\u6b21\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306bXLSX\u30b3\u30f3\u30c6\u30ca\u30fc\u3092\u521d\u671f\u5316\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +EmbeddedFileExtractorIngestModule.ImageExtractor.extractImage.addToDB.exception.msg=\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u3092\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093. +EmbeddedFileExtractorIngestModule.ImageExtractor.getOutputFolderPath.exception.msg=\u6b21\u306e\u62bd\u8c61\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u306e\u30a4\u30e1\u30fc\u30b8\u62bd\u51fa\u30d1\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +EmbeddedFileExtractorIngestModule.ImageExtractor.getOutputFolderPath.exception.msg=\u6b21\u306e\u62bd\u8c61\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u306e\u30a4\u30e1\u30fc\u30b8\u62bd\u51fa\u30d1\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg=\u30c7\u30a3\u30b9\u30af\u306b\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u66f8\u304d\u8fbc\u3081\u307e\u305b\u3093\u3002\u5341\u5206\u306a\u9818\u57df\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +SevenZipContentReadStream.seek.exception.invalidOrigin=\u7121\u52b9\u306a\u30aa\u30ea\u30b8\u30f3 {0} +SevenZipContentReadStream.read.exception.errReadStream=\u30b9\u30c8\u30ea\u30fc\u30e0\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +SevenZipExtractor.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001\u6697\u53f7\u5316\u691c\u51fa\u6e08\u307f\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +# {0} - rootArchive +SevenZipExtractor.zipBombArtifactCreation.text=Zip\u7206\u5f3e\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f {0} +UnableToInitializeLibraries=7Zip\u30e9\u30a4\u30d6\u30e9\u30ea\u30fc\u3092\u521d\u671f\u5316\u3067\u304d\u307e\u305b\u3093\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index 0ccd35cef9..958813a905 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.modules.embeddedfileextractor; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -37,12 +38,14 @@ import net.sf.sevenzipjbinding.ExtractAskMode; import net.sf.sevenzipjbinding.ExtractOperationResult; import net.sf.sevenzipjbinding.IArchiveExtractCallback; import net.sf.sevenzipjbinding.ICryptoGetTextPassword; +import net.sf.sevenzipjbinding.IInArchive; import net.sf.sevenzipjbinding.ISequentialOutStream; -import net.sf.sevenzipjbinding.ISevenZipInArchive; import net.sf.sevenzipjbinding.PropID; import net.sf.sevenzipjbinding.SevenZip; import net.sf.sevenzipjbinding.SevenZipException; import net.sf.sevenzipjbinding.SevenZipNativeInitializationException; +import org.apache.tika.parser.txt.CharsetDetector; +import org.apache.tika.parser.txt.CharsetMatch; import org.netbeans.api.progress.ProgressHandle; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; @@ -76,6 +79,7 @@ import org.sleuthkit.datamodel.TskData; class SevenZipExtractor { private static final Logger logger = Logger.getLogger(SevenZipExtractor.class.getName()); + private static final String MODULE_NAME = EmbeddedFileExtractorModuleFactory.getModuleName(); //encryption type strings @@ -185,7 +189,7 @@ class SevenZipExtractor { * * @return true if potential zip bomb, false otherwise */ - private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap depthMap, String escapedFilePath) { + private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, IInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap depthMap, String escapedFilePath) { //If a file is corrupted as a result of reconstructing it from unallocated space, then //7zip does a poor job estimating the original uncompressed file size. //As a result, many corrupted files have wonky compression ratios and could flood the UI @@ -399,18 +403,16 @@ class SevenZipExtractor { /** * Get the path in the archive of the specified item * - * @param item - the item to get the path for - * @param itemNumber - the item number to help provide uniqueness to the - * path - * @param archiveFile - the archive file the item exists in + * @param archive - the archive to get the path for + * @param inArchiveItemIndex - the item index to help provide uniqueness to the path + * @param archiveFile - the archive file the item exists in * * @return a string representing the path to the item in the archive * * @throws SevenZipException */ - private String getPathInArchive(ISevenZipInArchive archive, int inArchiveItemIndex, AbstractFile archiveFile) throws SevenZipException { - String pathInArchive = (String) archive.getProperty( - inArchiveItemIndex, PropID.PATH); + private String getPathInArchive(IInArchive archive, int inArchiveItemIndex, AbstractFile archiveFile) throws SevenZipException { + String pathInArchive = (String) archive.getProperty(inArchiveItemIndex, PropID.PATH); if (pathInArchive == null || pathInArchive.isEmpty()) { //some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path @@ -454,6 +456,10 @@ class SevenZipExtractor { } return pathInArchive; } + + private byte[] getPathBytesInArchive(IInArchive archive, int inArchiveItemIndex, AbstractFile archiveFile) throws SevenZipException { + return (byte[]) archive.getProperty(inArchiveItemIndex, PropID.PATH_BYTES); + } /* * Get the String that will represent the key for the hashmap which keeps @@ -505,7 +511,7 @@ class SevenZipExtractor { final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath); HashMap statusMap = new HashMap<>(); List unpackedFiles = Collections.emptyList(); - ISevenZipInArchive inArchive = null; + currentArchiveName = archiveFile.getName(); SevenZipContentReadStream stream = null; @@ -520,7 +526,6 @@ class SevenZipExtractor { return unpackSuccessful; } try { - List existingFiles = getAlreadyExtractedFiles(archiveFile, archiveFilePath); for (AbstractFile file : existingFiles) { statusMap.put(getKeyAbstractFile(file), new ZipFileStatusWrapper(file, ZipFileStatus.EXISTS)); @@ -553,6 +558,7 @@ class SevenZipExtractor { return unpackSuccessful; } } + IInArchive inArchive = null; try { stream = new SevenZipContentReadStream(new ReadContentInputStream(archiveFile)); // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive @@ -600,7 +606,8 @@ class SevenZipExtractor { } String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile); - UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive); + byte[] pathBytesInArchive = getPathBytesInArchive(inArchive, inArchiveItemIndex, archiveFile); + UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive, pathBytesInArchive); final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED); @@ -801,6 +808,34 @@ class SevenZipExtractor { return unpackSuccessful; } + private Charset detectFilenamesCharset(List byteDatas) { + Charset detectedCharset = null; + CharsetDetector charsetDetector = new CharsetDetector(); + int byteSum = 0; + int fileNum = 0; + for (byte[] byteData : byteDatas) { + fileNum++; + byteSum += byteData.length; + // Only read ~1000 bytes of filenames in this directory + if (byteSum >= 1000) { + break; + } + } + byte[] allBytes = new byte[byteSum]; + int start = 0; + for (int i = 0; i < fileNum; i++) { + byte[] byteData = byteDatas.get(i); + System.arraycopy(byteData, 0, allBytes, start, byteData.length); + start += byteData.length; + } + charsetDetector.setText(allBytes); + CharsetMatch cm = charsetDetector.detect(); + if (cm.getConfidence() >= 90 && Charset.isSupported(cm.getName())) { + detectedCharset = Charset.forName(cm.getName()); + } + return detectedCharset; + } + /** * Produce a list of archive indices needed for the call to extract, which * will open the archive and begin unpacking the files. @@ -906,7 +941,7 @@ class SevenZipExtractor { implements IArchiveExtractCallback, ICryptoGetTextPassword { private final AbstractFile archiveFile; - private final ISevenZipInArchive inArchive; + private final IInArchive inArchive; private UnpackStream unpackStream = null; private final Map archiveDetailsMap; private final ProgressHandle progressHandle; @@ -922,11 +957,10 @@ class SevenZipExtractor { private boolean unpackSuccessful = true; - StandardIArchiveExtractCallback(ISevenZipInArchive inArchive, - AbstractFile archiveFile, ProgressHandle progressHandle, - Map archiveDetailsMap, - String password, long freeDiskSpace) { - + StandardIArchiveExtractCallback(IInArchive inArchive, + AbstractFile archiveFile, ProgressHandle progressHandle, + Map archiveDetailsMap, + String password, long freeDiskSpace) { this.inArchive = inArchive; this.progressHandle = progressHandle; this.archiveFile = archiveFile; @@ -998,7 +1032,7 @@ class SevenZipExtractor { final Date accessTime = (Date) inArchive.getProperty( inArchiveItemIndex, PropID.LAST_ACCESS_TIME); final Date writeTime = (Date) inArchive.getProperty( - inArchiveItemIndex, PropID.LAST_WRITE_TIME); + inArchiveItemIndex, PropID.LAST_MODIFICATION_TIME); createTimeInSeconds = createTime == null ? 0L : createTime.getTime() / 1000; @@ -1100,7 +1134,6 @@ class SevenZipExtractor { * @param localPathRoot Path in module output folder that files will be * saved to * @param archiveFile Archive file being extracted - * @param fileManager */ UnpackedTree(String localPathRoot, AbstractFile archiveFile) { this.rootNode = new UnpackedNode(); @@ -1118,7 +1151,7 @@ class SevenZipExtractor { * * @return child node for the last file token in the filePath */ - UnpackedNode addNode(String filePath) { + UnpackedNode addNode(String filePath, byte[] filePathBytes) { String[] toks = filePath.split("[\\/\\\\]"); List tokens = new ArrayList<>(); for (int i = 0; i < toks.length; ++i) { @@ -1126,7 +1159,36 @@ class SevenZipExtractor { tokens.add(toks[i]); } } - return addNode(rootNode, tokens); + + List byteTokens = null; + if (filePathBytes == null) { + return addNode(rootNode, tokens, null); + } else { + byteTokens = new ArrayList<>(tokens.size()); + int last = 0; + for (int i = 0; i < filePathBytes.length; i++) { + if (filePathBytes[i] == '/') { + int len = i - last; + byte[] arr = new byte[len]; + System.arraycopy(filePathBytes, last, arr, 0, len); + byteTokens.add(arr); + last = i + 1; + } + } + int len = filePathBytes.length - last; + if (len > 0) { + byte[] arr = new byte[len]; + System.arraycopy(filePathBytes, last, arr, 0, len); + byteTokens.add(arr); + } + + if (tokens.size() != byteTokens.size()) { + logger.log(Level.WARNING, "Could not map path bytes to path string"); + return addNode(rootNode, tokens, null); + } + } + + return addNode(rootNode, tokens, byteTokens); } /** @@ -1137,7 +1199,8 @@ class SevenZipExtractor { * * @return */ - private UnpackedNode addNode(UnpackedNode parent, List tokenPath) { + private UnpackedNode addNode(UnpackedNode parent, + List tokenPath, List tokenPathBytes) { // we found all of the tokens if (tokenPath.isEmpty()) { return parent; @@ -1145,15 +1208,20 @@ class SevenZipExtractor { // get the next name in the path and look it up String childName = tokenPath.remove(0); + byte[] childNameBytes = null; + if (tokenPathBytes != null) { + childNameBytes = tokenPathBytes.remove(0); + } UnpackedNode child = parent.getChild(childName); // create new node if (child == null) { child = new UnpackedNode(childName, parent); + child.setFileNameBytes(childNameBytes); parent.addChild(child); } // go down one more level - return addNode(child, tokenPath); + return addNode(child, tokenPath, tokenPathBytes); } /** @@ -1254,6 +1322,32 @@ class SevenZipExtractor { NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg", node.getFileName()), ex); } + + // Determine encoding of children + if (node.getChildren().size() > 0) { + String names = ""; + ArrayList byteDatas = new ArrayList<>(); + for (UnpackedNode child : node.getChildren()) { + byte[] childBytes = child.getFileNameBytes(); + if (childBytes != null) { + byteDatas.add(childBytes); + } + names += child.getFileName(); + } + Charset detectedCharset = detectFilenamesCharset(byteDatas); + + // If a charset was detected, transcode filenames accordingly + if (detectedCharset != null && detectedCharset.canEncode()) { + for (UnpackedNode child : node.getChildren()) { + byte[] childBytes = child.getFileNameBytes(); + if (childBytes != null) { + String decodedName = new String(childBytes, detectedCharset); + child.setFileName(decodedName); + } + } + } + } + //recurse adding the children if this file was incomplete the children presumably need to be added for (UnpackedNode child : node.getChildren()) { updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath)); @@ -1266,6 +1360,7 @@ class SevenZipExtractor { private class UnpackedNode { private String fileName; + private byte[] fileNameBytes; private AbstractFile file; private final List children = new ArrayList<>(); private String localRelPath = ""; @@ -1396,6 +1491,19 @@ class SevenZipExtractor { boolean isIsFile() { return isFile; } + + void setFileNameBytes(byte[] fileNameBytes) { + if (fileNameBytes != null) { + this.fileNameBytes = Arrays.copyOf(fileNameBytes, fileNameBytes.length); + } + } + + byte[] getFileNameBytes() { + if (fileNameBytes == null) { + return null; + } + return Arrays.copyOf(fileNameBytes, fileNameBytes.length); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle_ja.properties new file mode 100644 index 0000000000..8d75ef4fad --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle_ja.properties @@ -0,0 +1,17 @@ +EncryptionDetectionDataSourceIngestModule.artifactComment.bitlocker=Bitlocker\u6697\u53f7\u5316\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f\u3002 +EncryptionDetectionDataSourceIngestModule.artifactComment.suspected=\u9ad8\u30a8\u30f3\u30c8\u30ed\u30d4\u30fc(%f)\u306b\u3088\u308a\u6697\u53f7\u5316\u304c\u7591\u308f\u308c\u307e\u3057\u305f\u3002 +EncryptionDetectionDataSourceIngestModule.processing.message=\u6697\u53f7\u5316\u306e\u30a4\u30e1\u30fc\u30b8\u3092\u30c1\u30a7\u30c3\u30af\u4e2d\u3067\u3059\u3002 +EncryptionDetectionFileIngestModule.artifactComment.password=\u30d1\u30b9\u30ef\u30fc\u30c9\u4fdd\u8b77\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f\u3002 +EncryptionDetectionFileIngestModule.artifactComment.suspected=\u9ad8\u30a8\u30f3\u30c8\u30ed\u30d4\u30fc(%f)\u306b\u3088\u308a\u6697\u53f7\u5316\u304c\u7591\u308f\u308c\u307e\u3057\u305f\u3002 +EncryptionDetectionFileIngestModule.getDesc.text=\u6307\u5b9a\u3057\u305f\u6700\u5c0f\u30a8\u30f3\u30c8\u30ed\u30d4\u30fc\u3092\u4f7f\u3063\u3066\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22\u3057\u307e\u3059\u3002 +EncryptionDetectionFileIngestModule.moduleName.text=\u6697\u53f7\u5316\u691c\u51fa +EncryptionDetectionIngestJobSettingsPanel.minimumEntropyLabel.text=\u6700\u5c0f\u30a8\u30f3\u30c8\u30ed\u30d4\u30fc: +EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeLabel.text=\u6700\u5c0f\u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba: +EncryptionDetectionIngestJobSettingsPanel.fileSizeMultiplesEnforcedCheckbox.text=512\u306e\u500d\u6570\u306b\u306a\u308b\u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba\u306e\u307f\u3092\u691c\u8a0e\u3057\u307e\u3059\u3002 +EncryptionDetectionIngestJobSettingsPanel.slackFilesAllowedCheckbox.text=\u30b9\u30e9\u30c3\u30af\u9818\u57df\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u8a0e\u3057\u307e\u3059\u3002 +EncryptionDetectionIngestJobSettingsPanel.mbLabel.text=MB +EncryptionDetectionIngestJobSettingsPanel.detectionSettingsLabel.text=\u691c\u51fa\u8a2d\u5b9a +EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeTextbox.text= +EncryptionDetectionIngestJobSettingsPanel.minimumEntropyTextbox.text= +EncryptionDetectionTools.errorMessage.minimumEntropyInput=\u6700\u5c0f\u30a8\u30f3\u30c8\u30ed\u30d4\u30fc\u306e\u5165\u529b\u306f6.0\uff5e8.0\u306e\u6570\u5b57\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +EncryptionDetectionTools.errorMessage.minimumFileSizeInput=\u6700\u5c0f\u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba\u306e\u5165\u529b\u306f1\u4ee5\u4e0a\u306e\u6574\u6570(\u5358\u4f4d:\u30e1\u30ac\u30d0\u30a4\u30c8)\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED index 5971ab0d82..2fdb54d654 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED @@ -1,9 +1,7 @@ CannotRunFileTypeDetection=Cannot run file type detection. ExifParserFileIngestModule.indexError.message=Failed to post EXIF Metadata artifact(s). OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=\ - Exif metadata ingest module. \n\n\ - The ingest module analyzes image files, extracts Exif information and posts the Exif data as results. +OpenIDE-Module-Long-Description=Exif metadata ingest module. \n\nThe ingest module analyzes image files, extracts Exif information and posts the Exif data as results. OpenIDE-Module-Name=ExifParser OpenIDE-Module-Short-Description=Exif metadata ingest module ExifParserFileIngestModule.moduleName.text=Exif Parser diff --git a/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle_ja.properties index 4200bd7f58..fb0ec85c09 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle_ja.properties @@ -1,8 +1,10 @@ +CannotRunFileTypeDetection=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u691c\u51fa\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 +ExifParserFileIngestModule.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001EXIF\u30e1\u30bf\u30c7\u30fc\u30bf\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 OpenIDE-Module-Display-Category=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb OpenIDE-Module-Long-Description=\ - Exif\u30e1\u30bf\u30c7\u30fc\u30bf\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3002\n\n\ - \u3053\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u753b\u50cf\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u3057\u3001Exif\u60c5\u5831\u3092\u62bd\u51fa\u3057\u3001Exif\u60c5\u5831\u3092\u7d50\u679c\u3068\u3057\u3066\u30dd\u30b9\u30c8\u3057\u307e\u3059\u3002 -OpenIDE-Module-Name=Exif\u30d1\u30fc\u30b5 -OpenIDE-Module-Short-Description=Exif\u30e1\u30bf\u30c7\u30fc\u30bf\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb -ExifParserFileIngestModule.moduleName.text=Exif\u30d1\u30fc\u30b5 -ExifParserFileIngestModule.getDesc.text=JPEG\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u3057\u3001\u305d\u308c\u3089\u306eEXIF\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\u3057\u307e\u3059\u3002 + EXIF\u30e1\u30bf\u30c7\u30fc\u30bf\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3067\u3059\u3002 \n\n\ + \u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u3057\u3001Exif\u60c5\u5831\u3092\u62bd\u51fa\u3057\u3001Exif\u30c7\u30fc\u30bf\u3092\u7d50\u679c\u3068\u3057\u3066\u6295\u7a3f\u3057\u307e\u3059\u3002 +OpenIDE-Module-Name=ExifParser +OpenIDE-Module-Short-Description=Exif\u30e1\u30bf\u30c7\u30fc\u30bf\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3067\u3059\u3002 +ExifParserFileIngestModule.moduleName.text=Exif\u89e3\u6790\u30c4\u30fc\u30eb +ExifParserFileIngestModule.getDesc.text=Ingests JPEG\u30d5\u30a1\u30a4\u30eb\u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3057\u3001EXIF\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u56de\u53ce\u3057\u307e\u3059\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserModuleFactory.java index 8f1b1a4efe..4b18db33c7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserModuleFactory.java @@ -38,7 +38,7 @@ public class ExifParserModuleFactory extends IngestModuleFactoryAdapter { return getModuleName(); } - static String getModuleName() { + public static String getModuleName() { return NbBundle.getMessage(ExifParserFileIngestModule.class, "ExifParserFileIngestModule.moduleName.text"); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED index cfaadf1635..5063bd55fa 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED @@ -36,27 +36,27 @@ FileExtMismatchSettingsPanel.jLabel1.text=File Types: FileExtMismatchSettingsPanel.newExtButton.text=New Extension FileExtMismatchSettingsPanel.newMimePrompt.message=Add a new MIME file type: FileExtMismatchSettingsPanel.newMimePrompt.title=New MIME -FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.message=MIME type text is empty\! +FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.message=MIME type text is empty! FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.title=Empty type -FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.message=MIME type not supported\! +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.message=MIME type not supported! FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.title=Type not supported -FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.message=MIME type already exists\! +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.message=MIME type already exists! FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.title=Type already exists FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotDetectable.message=MIME type is not detectable by this module. FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotDetectable.title=Type not detectable -FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.message=No MIME type selected\! +FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.message=No MIME type selected! FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.title=No type selected FileExtMismatchSettingsPanel.newExtPrompt.message=Add an allowed extension: FileExtMismatchSettingsPanel.newExtPrompt.title=New allowed extension -FileExtMismatchSettingsPanel.newExtPrompt.empty.message=Extension text is empty\! +FileExtMismatchSettingsPanel.newExtPrompt.empty.message=Extension text is empty! FileExtMismatchSettingsPanel.newExtPrompt.empty.title=Extension text empty -FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.message=No MIME type selected\! +FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.message=No MIME type selected! FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.title=No MIME type selected -FileExtMismatchSettingsPanel.newExtPrompt.extExists.message=Extension already exists\! +FileExtMismatchSettingsPanel.newExtPrompt.extExists.message=Extension already exists! FileExtMismatchSettingsPanel.newExtPrompt.extExists.title=Extension already exists -FileExtMismatchSettingsPanel.removeExtButton.noneSelected.message=No extension selected\! +FileExtMismatchSettingsPanel.removeExtButton.noneSelected.message=No extension selected! FileExtMismatchSettingsPanel.removeExtButton.noneSelected.title=No extension selected -FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.message=No MIME type selected\! +FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.message=No MIME type selected! FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.title=No MIME type selected FileExtMismatchSettingsPanel.removeTypeButton.toolTipText= FileExtMismatchModuleSettingsPanel.checkAllRadioButton.text=Check all file types diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle_ja.properties index fc35bab029..24969e973b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle_ja.properties @@ -1,44 +1,64 @@ -OpenIDE-Module-Name=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4 +AddFileExtensionAction.writeError.message=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u8a2d\u5b9a\u3092\u66f8\u304d\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +CannotRunFileTypeDetection=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u691c\u51fa\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 +FileExtMismatchIngestModule.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +FileExtMismatchIngestModule.readError.message=\u8a2d\u5b9a\u3092\u8aad\u307f\u53d6\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +FileExtMismatchSettingsPanel.Title=\u30b0\u30ed\u30fc\u30d0\u30eb\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4\u8b58\u5225\u8a2d\u5b9a +OpenIDE-Module-Name=FileExtMismatch OptionsCategory_Name_FileExtMismatchOptions=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4 OptionsCategory_FileExtMismatch=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4 -AddFileExtensionAction.msgDlg.msg=XML\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3092\u66f8\u304f\u306e\u3092\u5931\u6557\u3057\u307e\u3057\u305f\u3002 -AddFileExtensionAction.msgDlg.title=\u4e0d\u4e00\u81f4\u62e1\u5f35\u5b50\u306e\u30a8\u30e9\u30fc\u3092\u8ffd\u52a0 -FileExtMismatchConfigPanel.addExtButton.errLabel.empty=\u62e1\u5f35\u5b50\u30c6\u30ad\u30b9\u30c8\u304c\u7a7a\u767d\u3067\u3059\uff01 -FileExtMismatchConfigPanel.addExtButton.errLabel.noMimeType=MIME\u30bf\u30a4\u30d7\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\uff01 -FileExtMismatchConfigPanel.addExtButton.errLabel.extExists=\u62e1\u5f35\u5b50\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\uff01 -FileExtMismatchConfigPanel.addExtButton.errLabel.extAdded=\u62e1\u5f35\u5b50{0}\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002 -FileExtMismatchConfigPanel.addTypeButton.empty=MIME\u30bf\u30a4\u30d7\u30c6\u30ad\u30b9\u30c8\u304c\u7a7a\u767d\u3067\u3059\uff01 -FileExtMismatchConfigPanel.addTypeButton.mimeTypeNotSupported=MIME\u30bf\u30a4\u30d7\u304c\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\uff01 -FileExtMismatchConfigPanel.addTypeButton.mimeTypeExists=MIME\u30bf\u30a4\u30d7\u304c\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\uff01 -FileExtMismatchConfigPanel.addTypeButton.mimeTypeNotDetectable=MIME\u30bf\u30a4\u30d7\u304c\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u3067\u306f\u691c\u51fa\u3067\u304d\u307e\u305b\u3093\u3002 -FileExtMismatchConfigPanel.addTypeButton.mimeTypeAdded=MIME\u30bf\u30a4\u30d7{0}\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002 -FileExtMismatchConfigPanel.removeTypeButton.noneSelected=MIME\u30bf\u30a4\u30d7\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\uff01 -FileExtMismatchConfigPanel.remoteTypeButton.deleted=MIME\u30bf\u30a4\u30d7{0}\u304c\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002 -FileExtMismatchConfigPanel.removeExtButton.noneSelected=\u62e1\u5f35\u5b50\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\uff01 -FileExtMismatchConfigPanel.removeExtButton.noMimeTypeSelected=MIME\u30bf\u30a4\u30d7\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\uff01 -FileExtMismatchConfigPanel.removeExtButton.deleted=\u62e1\u5f35\u5b50{0}\u304c\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002 -FileExtMismatchConfigPanel.store.msg=\u4fdd\u5b58\u3055\u308c\u307e\u3057\u305f\u3002 -FileExtMismatchConfigPanel.store.msgDlg.msg=XML\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3092\u66f8\u304f\u306e\u3092\u5931\u6557\u3057\u307e\u3057\u305f\u3002 -FileExtMismatchConfigPanel.save.msgDlg.title=\u4fdd\u5b58\u30a8\u30e9\u30fc -FileExtMismatchConfigPanel.ok.confDlg.msg=\u8a2d\u5b9a\u5909\u66f4\u3092\u4fdd\u5b58\u3057\u307e\u3059\u304b\uff1f +AddFileExtensionAction.msgDlg.msg2=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4\u8a2d\u5b9a\u304c\u8aad\u307f\u53d6\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u62e1\u5f35\u5b50\u306e\u66f4\u65b0\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +AddFileExtensionAction.msgDlg.title=\u4e0d\u4e00\u81f4\u62e1\u5f35\u5b50\u30a8\u30e9\u30fc\u3092\u8ffd\u52a0 +AddFileExtensionAction.extHeaderLbl.text=\u62e1\u5f35\u5b50\u306e\u8a31\u53ef\u5148 +FileExtMismatchConfigPanel.store.msg=\u4fdd\u5b58\u3057\u307e\u3057\u305f\u3002 +FileExtMismatchConfigPanel.store.msgDlg.msg=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +FileExtMismatchConfigPanel.save.msgDlg.title=\u30a8\u30e9\u30fc\u3092\u4fdd\u5b58 +FileExtMismatchConfigPanel.ok.confDlg.msg=\u69cb\u6210\u306e\u5909\u66f4\u3092\u4fdd\u5b58\u3057\u307e\u3059\u304b? FileExtMismatchConfigPanel.confDlg.title=\u4fdd\u5b58\u3055\u308c\u3066\u3044\u306a\u3044\u5909\u66f4 FileExtMismatchConfigPanel.mimeTableModel.colName=MIME\u30bf\u30a4\u30d7 FileExtMismatchConfigPanel.extTableModel.colName=\u62e1\u5f35\u5b50 -FileExtMismatchContextMenuActionsProvider.menuItemStr=\u62e1\u5f35\u5b50{0}\u3092MIME\u30bf\u30a4\u30d7{1}\u306e\u4e00\u81f4\u3068\u3057\u3066\u8ffd\u52a0 -FileExtMismatchIngestModule.moduleName=\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4\u30c7\u30a3\u30c6\u30af\u30bf\u30fc -FileExtMismatchIngestModule.moduleDesc.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306b\u57fa\u3065\u3044\u3066\u3001\u6a19\u6e96\u7684\u3067\u306f\u306a\u3044\u62e1\u5f35\u5b50\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u30d5\u30e9\u30b0\u3057\u307e\u3059\u3002d +FileExtMismatchContextMenuActionsProvider.menuItemStr=\u4e00\u81f4\u3059\u308bMIME\u30bf\u30a4\u30d7 {1} \u3068\u3057\u3066\u62e1\u5f35\u5b50 {0} \u3092\u8ffd\u52a0 +FileExtMismatchIngestModule.moduleName=\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4\u691c\u51fa\u30c4\u30fc\u30eb +FileExtMismatchIngestModule.moduleDesc.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306b\u57fa\u3065\u304d\u3001\u975e\u6a19\u6e96\u306e\u62e1\u5f35\u5b50\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u306b\u30d5\u30e9\u30b0\u3092\u7acb\u3066\u307e\u3059\u3002 FileExtMismatchIngestModule.complete.totalProcTime=\u5408\u8a08\u51e6\u7406\u6642\u9593 -FileExtMismatchIngestModule.complete.totalFiles=\u5408\u8a08\u51e6\u7406\u30d5\u30a1\u30a4\u30eb\u6570 +FileExtMismatchIngestModule.complete.totalFiles=\u30d5\u30a1\u30a4\u30eb\u51e6\u7406\u306b\u8cbb\u3084\u3057\u305f\u5408\u8a08\u6642\u9593 FileExtMismatchIngestModule.complete.svcMsg.text=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4\u7d50\u679c FileExtMismatchOptionsPanelController.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -FileExtMismatchOptionsPanelController.moduleErr.msg=FileExtMismatchOptionsPanelController\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u306e\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -AddFileExtensionAction.extHeaderLbl.text=\u4e0b\u8a18\u7528\u306b\u8a31\u53ef\u3059\u308b\u62e1\u5f35\u5b50 -FileExtMismatchModuleSettingsPanel.skipNoExtCheckBox.text=\u62e1\u5f35\u5b50\u306e\u7121\u3044\u30d5\u30a1\u30a4\u30eb\u3092\u30b9\u30ad\u30c3\u30d7 -FileExtMismatchSettingsPanel.extHeaderLabel.text=\u8a31\u53ef\u3059\u308b\u62e1\u5f35\u5b50\uff1a -FileExtMismatchSettingsPanel.removeExtButton.text=\u9078\u629e\u3057\u305f\u62e1\u5f35\u5b50\u3092\u524a\u9664 -FileExtMismatchDetectorModuleFactory.getIngestJobSettingsPanel.exception.msg=\u8a2d\u5b9a\u3092\u884c\u3046\u70ba\u306e\u60f3\u5b9a\u3055\u308c\u308b\u5f15\u6570\u306finstanceof FileExtMismatchDetectorModuleSettings\u3067\u3059\u3002 -FileExtMismatchDetectorModuleFactory.createFileIngestModule.exception.msg=\u8a2d\u5b9a\u3092\u884c\u3046\u70ba\u306e\u60f3\u5b9a\u3055\u308c\u308b\u5f15\u6570\u306finstanceof FileExtMismatchDetectorModuleSettings\u3067\u3059\u3002 -FileExtMismatchSettingsPanel.removeTypeButton.text=\u9078\u629e\u3057\u305f\u30bf\u30a4\u30d7\u3092\u524a\u9664 -FileExtMismatchSettingsPanel.newTypeButton.text=\u30bf\u30a4\u30d7\u3092\u8ffd\u52a0 -FileExtMismatchSettingsPanel.jLabel1.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\uff1a -FileExtMismatchSettingsPanel.newExtButton.text=\u62e1\u5f35\u5b50\u3092\u8ffd\u52a0 +FileExtMismatchOptionsPanelController.moduleErr.msg=FileExtMismatchOptionsPanelController\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +FileExtMismatchModuleSettingsPanel.skipNoExtCheckBox.text=\u62e1\u5f35\u5b50\u306e\u306a\u3044\u30d5\u30a1\u30a4\u30eb\u3092\u30b9\u30ad\u30c3\u30d7 +FileExtMismatchSettingsPanel.extHeaderLabel.text=\u8a31\u53ef\u3055\u308c\u3066\u3044\u308b\u62e1\u5f35\u5b50: +FileExtMismatchSettingsPanel.removeExtButton.text=\u62e1\u5f35\u5b50\u3092\u524a\u9664 +FileExtMismatchDetectorModuleFactory.getIngestJobSettingsPanel.exception.msg=instanceof FileExtMismatchDetectorModuleSettings\u306b\u306a\u308b\u3068\u4e88\u60f3\u3055\u308c\u308b\u8a2d\u5b9a\u5f15\u6570 +FileExtMismatchDetectorModuleFactory.createFileIngestModule.exception.msg=instanceof FileExtMismatchDetectorModuleSettings\u306b\u306a\u308b\u3068\u4e88\u60f3\u3055\u308c\u308b\u8a2d\u5b9a\u5f15\u6570 +FileExtMismatchModuleSettingsPanel.skipKnownFiles.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\u3092\u30b9\u30ad\u30c3\u30d7 +FileExtMismatchSettingsPanel.removeTypeButton.text=\u30bf\u30a4\u30d7\u3092\u524a\u9664 +FileExtMismatchSettingsPanel.newTypeButton.text=\u65b0\u898f\u30bf\u30a4\u30d7 +FileExtMismatchSettingsPanel.jLabel1.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7: +FileExtMismatchSettingsPanel.newExtButton.text=\u65b0\u898f\u62e1\u5f35\u5b50 +FileExtMismatchSettingsPanel.newMimePrompt.message=\u65b0\u898fMIME\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u8ffd\u52a0: +FileExtMismatchSettingsPanel.newMimePrompt.title=\u65b0\u898fMIME +FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.message=MIME\u30bf\u30a4\u30d7\u306e\u30c6\u30ad\u30b9\u30c8\u304c\u7a7a(\u672a\u5165\u529b)\u3067\u3059\! +FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.title=\u7a7a(\u672a\u5165\u529b)\u306e\u30bf\u30a4\u30d7 +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.message=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044MIME\u30bf\u30a4\u30d7\u3067\u3059\! +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.title=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u30bf\u30a4\u30d7 +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.message=MIME\u30bf\u30a4\u30d7\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\! +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.title=\u30bf\u30a4\u30d7\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059 +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotDetectable.message=\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u3067\u306fMIME\u30bf\u30a4\u30d7\u3092\u691c\u51fa\u3067\u304d\u307e\u305b\u3093\u3002 +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotDetectable.title=\u691c\u51fa\u4e0d\u80fd\u306e\u30bf\u30a4\u30d7 +FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.message=MIME\u30bf\u30a4\u30d7\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\! +FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.title=\u30bf\u30a4\u30d7\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +FileExtMismatchSettingsPanel.newExtPrompt.message=\u8a31\u53ef\u3055\u308c\u3066\u3044\u308b\u62e1\u5f35\u5b50\u3092\u8ffd\u52a0: +FileExtMismatchSettingsPanel.newExtPrompt.title=\u65b0\u898f\u8a31\u53ef\u3055\u308c\u305f\u62e1\u5f35\u5b50 +FileExtMismatchSettingsPanel.newExtPrompt.empty.message=\u62e1\u5f35\u5b50\u306e\u30c6\u30ad\u30b9\u30c8\u304c\u7a7a(\u672a\u5165\u529b)\u3067\u3059\! +FileExtMismatchSettingsPanel.newExtPrompt.empty.title=\u62e1\u5f35\u5b50\u306e\u30c6\u30ad\u30b9\u30c8\u304c\u7a7a\u5165\u529b)\u3067\u3059 +FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.message=MIME\u30bf\u30a4\u30d7\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\! +FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.title=MIME\u30bf\u30a4\u30d7\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +FileExtMismatchSettingsPanel.newExtPrompt.extExists.message=\u62e1\u5f35\u5b50\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\! +FileExtMismatchSettingsPanel.newExtPrompt.extExists.title=\u62e1\u5f35\u5b50\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059 +FileExtMismatchSettingsPanel.removeExtButton.noneSelected.message=\u62e1\u5f35\u5b50\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\! +FileExtMismatchSettingsPanel.removeExtButton.noneSelected.title=\u62e1\u5f35\u5b50\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.message=MIME\u30bf\u30a4\u30d7\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\! +FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.title=MIME\u30bf\u30a4\u30d7\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +FileExtMismatchSettingsPanel.removeTypeButton.toolTipText= +FileExtMismatchModuleSettingsPanel.checkAllRadioButton.text=\u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u30c1\u30a7\u30c3\u30af +FileExtMismatchModuleSettingsPanel.checkNoTextRadioButton.text=\u30c6\u30ad\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb\u4ee5\u5916\u306e\u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u30c1\u30a7\u30c3\u30af +FileExtMismatchModuleSettingsPanel.checkMediaExeRadioButton.text=\u30de\u30eb\u30c1\u30e1\u30c7\u30a3\u30a2\u30d5\u30a1\u30a4\u30eb\u3068\u5b9f\u884c\u53ef\u80fd\u30d5\u30a1\u30a4\u30eb\u306e\u307f\u3092\u30c1\u30a7\u30c3\u30af diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchDetectorModuleSettings.java b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchDetectorModuleSettings.java index 2e11bccc41..5b979623c2 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchDetectorModuleSettings.java +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchDetectorModuleSettings.java @@ -61,7 +61,25 @@ final class FileExtMismatchDetectorModuleSettings implements IngestModuleIngestJ "application/exe", "application/x-dosexec", "application/x-exe", - "application/x-msdownload").collect(Collectors.toSet()); + "application/x-msdownload", + "application/msword", + "application/pdf", + "application/rtf", + "application/vnd.ms-excel", + "application/vnd.ms-powerpoint", + "application/vnd.oasis.opendocument.presentation", + "application/vnd.oasis.opendocument.spreadsheet", + "application/vnd.oasis.opendocument.text", + "application/x-msoffice", + "application/x-ooxml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.openxmlformats-officedocument.wordprocessingml.template", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.spreadsheetml.template", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/vnd.openxmlformats-officedocument.presentationml.template", + "application/vnd.openxmlformats-officedocument.presentationml.slideshow" + ).collect(Collectors.toSet()); /** * Constructs an object with the ingest options for the file extension @@ -176,5 +194,5 @@ final class FileExtMismatchDetectorModuleSettings implements IngestModuleIngestJ versionNumber = 2; } } - + } diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/mismatch_config.xml b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/mismatch_config.xml index a89cb7f421..fe2868e3ab 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/mismatch_config.xml +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/mismatch_config.xml @@ -469,4 +469,25 @@ avi + + docx + + + dotx + + + xlsx + + + xltx + + + pptx + + + potx + + + ppsx + diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties index 61626e69aa..8620a7ca3d 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties @@ -1,42 +1,75 @@ -OpenIDE-Module-Name=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7ID -FileTypeIdIngestModule.moduleName.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u7279\u5b9a -FileTypeIdIngestModule.moduleDesc.text=\u30d0\u30a4\u30ca\u30ea\u30b7\u30b0\u30cd\u30c1\u30e3\u306b\u57fa\u3065\u3044\u3066\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u4e00\u81f4\u3057\u307e\u3059\u3002 -FileTypeIdIngestModule.complete.totalProcTime=\u5408\u8a08\u51e6\u7406\u6642\u9593 -FileTypeIdIngestModule.complete.totalFiles=\u5408\u8a08\u51e6\u7406\u30d5\u30a1\u30a4\u30eb\u6570 -FileTypeIdIngestModule.complete.srvMsg.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7ID\u306e\u7d50\u679c -FileTypeIdModuleFactory.getIngestJobSettingsPanel.exception.msg=\u8a2d\u5b9a\u3092\u884c\u3046\u70ba\u306e\u60f3\u5b9a\u3055\u308c\u308b\u5f15\u6570\u306finstanceof FileTypeIdModuleSettings\u3067\u3059\u3002 -FileTypeIdModuleFactory.createFileIngestModule.exception.msg=\u8a2d\u5b9a\u3092\u884c\u3046\u70ba\u306e\u60f3\u5b9a\u3055\u308c\u308b\u5f15\u6570\u306finstanceof FileTypeIdModuleSettings\u3067\u3059\u3002 -FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.toolTipText=\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u6570\u306b\u3088\u3063\u3066\u306f\u3001\u3053\u306e\u30dc\u30c3\u30af\u30b9\u3092\u9078\u629e\u3059\u308b\u3053\u3068\u306b\u3088\u308a\u3001\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u7279\u5b9a\u3092\u52a0\u901f\u3057\u307e\u3059\u3002 -FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\uff08NSRL\uff09\u3092\u30b9\u30ad\u30c3\u30d7 -FileTypeIdGlobalSettingsPanel.deleteTypeButton.text=\u524a\u9664 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.title=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u540d\u304c\u6b20\u3051\u3066\u3044\u307e\u3059 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidMIMEType.message=MIME\u30bf\u30a4\u30d7\u304c\u5fc5\u8981\u3067\u3059\u3002 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidMIMEType.title=MIME\u30bf\u30a4\u30d7\u304c\u6b20\u3051\u3066\u3044\u307e\u3059 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.message=\u30aa\u30d5\u30bb\u30c3\u30c8\u306f\u6b63\u6574\u6570\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.title=\u7121\u52b9\u306a\u30aa\u30d5\u30bb\u30c3\u30c8 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidRawSignatureBytes.message=\u3053\u306e\u30b7\u30b0\u30cd\u30c1\u30e3\u306b\u4e00\u3064\u4ee5\u4e0a\u306e\u7121\u52b9\u306a16\u9032\u6570\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignature.message=\u30b7\u30b0\u30cd\u30c1\u30e3\u304c\u5fc5\u8981\u3067\u3059\u3002 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignature.title=\u30b7\u30b0\u30cd\u30c1\u30e3\u304c\u6b20\u3051\u3066\u3044\u307e\u3059 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignatureBytes.title=\u30b7\u30b0\u30cd\u30c1\u30e3\u304c\u7121\u52b9\u3067\u3059 -FileTypeIdGlobalSettingsPanel.JOptionPane.loadFailed.title=\u8aad\u307f\u8fbc\u307f\u304c\u5931\u6557\u3057\u307e\u3057\u305f -FileTypeIdGlobalSettingsPanel.JOptionPane.storeFailed.title=\u4fdd\u5b58\u304c\u5931\u6557\u3057\u307e\u3057\u305f -FileTypeIdGlobalSettingsPanel.newTypeButton.text=\u65b0\u898f\u30bf\u30a4\u30d7 -FileTypeIdGlobalSettingsPanel.signatureComboBox.asciiItem=\u30b9\u30c8\u30ea\u30f3\u30b0\uff08ASCII\uff09 -FileTypeIdGlobalSettingsPanel.signatureComboBox.rawItem=\u30d0\u30a4\u30c8\uff08HEX\uff09 -OptionsCategory_Keywords_FileTypeId=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7ID +# {0} - \u30bf\u30a4\u30d7\u540d +AddFileTypePanel.containsIllegalCharacter.message=\u4ee3\u308f\u308a\u306bMIME\u30bf\u30a4\u30d7\u306e\u7121\u52b9\u306a\u6587\u5b57 {0} \u304c\u63d0\u6848\u3055\u308c\u307e\u3057\u305f +AddFileTypePanel.containsIllegalCharacter.title=MIME\u30bf\u30a4\u30d7\u306e\u7121\u52b9\u306a\u6587\u5b57 +AddFileTypePanel.mimeFormatLabel.text=MIME\u30bf\u30a4\u30d7\u306e\u5f62\u5f0f\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059: \u30e1\u30c7\u30a3\u30a2\u30bf\u30a4\u30d7/\u30e1\u30c7\u30a3\u30a2\u30b5\u30d6\u30bf\u30a4\u30d7 +# {0} - \u30e1\u30c7\u30a3\u30a2\u30b5\u30d6\u30bf\u30a4\u30d7 +AddFileTypePanel.nonStandardMIMEType.message=MIME\u30bf\u30a4\u30d7\u306e\u5f62\u5f0f\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093: \u30e1\u30c7\u30a3\u30a2\u30bf\u30a4\u30d7/\u30e1\u30c7\u30a3\u30a2\u30b5\u30d6\u30bf\u30a4\u30d7\u3002\u4ee3\u308f\u308a\u306b\u30ab\u30b9\u30bf\u30e0/{0} \u304c\u63d0\u6848\u3055\u308c\u307e\u3057\u305f\u3002 +AddFileTypePanel.nonStandardMIMEType.title=\u975e\u6a19\u6e96\u306eMIME\u30bf\u30a4\u30d7 +AddFileTypeSignatureDialog.addButton.title=OK +AddFileTypeSignatureDialog.cancelButton.title=\u53d6\u308a\u6d88\u3057 +AddFileTypeSignatureDialog.invalidSignature.message=\u7121\u52b9\u306a\u7f72\u540d +AddFileTypeSignaturePanel.signatureStringFail.text=\u7f72\u540d\u6587\u5b57\u5217\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +AddMimeTypeDialog.addButton.title=OK +AddMimeTypeDialog.cancelButton.title=\u53d6\u308a\u6d88\u3057 +AddMimeTypedialog.title=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 +AddMimeTypePanel.emptySetName.message=\u30a2\u30e9\u30fc\u30c8\u304c\u8981\u6c42\u3055\u308c\u305f\u5834\u5408\u3001\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u540d\u304c\u5fc5\u8981\u3067\u3059\u3002 +AddMimeTypePanel.emptySetName.title=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u540d\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +AddMimeTypePanel.emptySigList.message=\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u7f72\u540d\u304c\u5fc5\u8981\u3067\u3059\u3002 +AddMimeTypePanel.emptySigList.title=\u7121\u52b9\u306a\u7f72\u540d\u30ea\u30b9\u30c8 +CannotRunFileTypeDetection=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u691c\u51fa\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 +FileTypeIdGlobalSettingsPanel.Title=\u30b0\u30ed\u30fc\u30d0\u30eb\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u8b58\u5225\u8a2d\u5b9a OptionsCategory_Name_FileTypeId=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 -FileTypeIdGlobalSettingsPanel.loadFileTypes.errorMessage=\u65e2\u5b58\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u306e\u8aad\u307f\u8fbc\u307f\u306b\u5931\u6557\u3057\u307e\u3057\u305f -FileTypeIdGlobalSettingsPanel.saveFileTypes.errorMessage=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u306e\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.message=\u30a2\u30e9\u30fc\u30c8\u3092\u8a2d\u5b9a\u3059\u308b\u306b\u306f\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u540d\u304c\u5fc5\u8981\u3067\u3059\u3002 +OptionsCategory_Keywords_FileTypeId=FileTypeId +OpenIDE-Module-Name=FileTypeId +FileTypeIdIngestModule.moduleName.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u8b58\u5225 +FileTypeIdIngestModule.moduleDesc.text=\u30d0\u30a4\u30ca\u30ea\u30fc\u7f72\u540d\u306b\u57fa\u3065\u304d\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u7167\u5408\u3057\u307e\u3059\u3002 +FileTypeIdIngestModule.complete.totalProcTime=\u5408\u8a08\u51e6\u7406\u6642\u9593 +FileTypeIdIngestModule.complete.totalFiles=\u30d5\u30a1\u30a4\u30eb\u51e6\u7406\u306b\u8cbb\u3084\u3057\u305f\u5408\u8a08\u6642\u9593 +FileTypeIdIngestModule.complete.srvMsg.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7ID\u7d50\u679c +FileTypeIdModuleFactory.getIngestJobSettingsPanel.exception.msg=instanceof FileTypeIdModuleSettings\u306b\u306a\u308b\u3068\u4e88\u60f3\u3055\u308c\u308b\u8a2d\u5b9a\u5f15\u6570 +FileTypeIdModuleFactory.createFileIngestModule.exception.msg=instanceof FileTypeIdModuleSettings\u306b\u306a\u308b\u3068\u4e88\u60f3\u3055\u308c\u308b\u8a2d\u5b9a\u5f15\u6570 +FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.toolTipText=\u65e2\u77e5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u6570\u306b\u5fdc\u3058\u3066\u3001\u3053\u306e\u30dc\u30c3\u30af\u30b9\u3092\u30c1\u30a7\u30c3\u30af\u3059\u308b\u3068\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u8b58\u5225\u901f\u5ea6\u304c\u5411\u4e0a\u3057\u307e\u3059\u3002 +FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb(NSRL)\u3092\u30b9\u30ad\u30c3\u30d7 +FileTypeIdGlobalSettingsPanel.deleteTypeButton.text=\u30bf\u30a4\u30d7\u3092\u524a\u9664 +FileTypeIdGlobalSettingsPanel.signatureComboBox.rawItem=\u30d0\u30a4\u30c8(16\u9032\u6570) +FileTypeIdGlobalSettingsPanel.signatureComboBox.asciiItem=\u6587\u5b57\u5217(ASCII) FileTypeIdGlobalSettingsPanel.offsetComboBox.startItem=\u958b\u59cb -FileTypeIdGlobalSettingsPanel.offsetComboBox.endItem=\u505c\u6b62 -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.length=\u30aa\u30d5\u30bb\u30c3\u30c8\u306f\u30b7\u30b0\u30cd\u30c1\u30e3\u30b5\u30a4\u30ba\u3088\u308a\u5c0f\u3055\u304f\u3066\u306f\u3044\u3051\u307e\u305b\u3093\u3002 -FileTypeIdGlobalSettingsPanel.jLabel2.text=MIME\u30bf\u30a4\u30d7\uff1a -FileTypeIdGlobalSettingsPanel.startUp.fileTypeDetectorInitializationException.msg=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u30c7\u30a3\u30c6\u30af\u30bf\u3092\u8d77\u52d5\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -AddFileTypeSignaturePanel.signatureTypeLabel.text=\u30b7\u30b0\u30cd\u30c1\u30e3\u30bf\u30a4\u30d7 -AddFileTypeSignaturePanel.signatureLabel.text=\u30b7\u30b0\u30cd\u30c1\u30e3 -AddFileTypeSignaturePanel.offsetRelativeToLabel.text=\u30aa\u30d5\u30bb\u30c3\u30c8\u306f\u6b21\u3068\u76f8\u5bfe\u7684 +FileTypeIdGlobalSettingsPanel.offsetComboBox.endItem=\u7d42\u4e86 +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidMIMEType.message=MIME\u30bf\u30a4\u30d7\u304c\u5fc5\u8981\u3067\u3059\u3002 +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidMIMEType.title=MIME\u30bf\u30a4\u30d7\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignature.message=\u7f72\u540d\u304c\u5fc5\u8981\u3067\u3059\u3002 +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignature.title=\u7f72\u540d\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.length=\u30aa\u30d5\u30bb\u30c3\u30c8\u306f\u7f72\u540d\u30b5\u30a4\u30ba\u3088\u308a\u3082\u5c0f\u3055\u304f\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.message=\u30aa\u30d5\u30bb\u30c3\u30c8\u306f\u6b63\u306e\u6574\u6570\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.title=\u7121\u52b9\u306a\u30aa\u30d5\u30bb\u30c3\u30c8 +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidRawSignatureBytes.message=\u7f72\u540d\u306b1\u3064\u4ee5\u4e0a\u306e\u7121\u52b9\u306a16\u9032\u6570\u306e\u6841\u304c\u3042\u308a\u307e\u3059\u3002 +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignatureBytes.title=\u7121\u52b9\u306a\u7f72\u540d +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.message= +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.title=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u540d\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +FileTypeIdGlobalSettingsPanel.JOptionPane.storeFailed.title=\u4fdd\u5b58\u5931\u6557 +FileTypeIdGlobalSettingsPanel.JOptionPane.loadFailed.title=\u8aad\u307f\u8fbc\u307f\u5931\u6557 +FileTypeIdGlobalSettingsPanel.loadFileTypes.errorMessage=\u65e2\u5b58\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +FileTypeIdGlobalSettingsPanel.saveFileTypes.errorMessage=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +FileTypeIdGlobalSettingsPanel.newTypeButton.text=\u65b0\u898f\u30bf\u30a4\u30d7 +FileTypeIdGlobalSettingsPanel.jLabel2.text=\u30ab\u30b9\u30bf\u30e0MIME\u30bf\u30a4\u30d7: +FileTypeIdGlobalSettingsPanel.startUp.fileTypeDetectorInitializationException.msg=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u691c\u51fa\u30c4\u30fc\u30eb\u306e\u521d\u671f\u5316\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 AddFileTypeSignaturePanel.offsetLabel.text=\u30d0\u30a4\u30c8\u30aa\u30d5\u30bb\u30c3\u30c8 +AddFileTypeSignaturePanel.signatureTextField.text= +AddFileTypeSignaturePanel.signatureTypeLabel.text=\u7f72\u540d\u30bf\u30a4\u30d7 +AddFileTypeSignaturePanel.signatureLabel.text=\u7f72\u540d +AddFileTypeSignaturePanel.hexPrefixLabel.text=0x +AddFileTypeSignaturePanel.offsetRelativeToLabel.text=\u30aa\u30d5\u30bb\u30c3\u30c8\u76f8\u5bfe\u5148 +AddFileTypeSignaturePanel.offsetTextField.text= +FileTypeIdGlobalSettingsPanel.jLabel1.text=\u7f72\u540d +FileTypeIdGlobalSettingsPanel.editTypeButton.text=\u30bf\u30a4\u30d7\u3092\u7de8\u96c6 +AddFileTypePanel.mimeTypeTextField.text= AddFileTypePanel.mimeTypeLabel.text=MIME\u30bf\u30a4\u30d7 -FileTypeIdGlobalSettingsPanel.ingestRunningWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u4e2d\u306b\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093\uff01 -FileTypeIdGlobalSettingsPanel.jLabel3.text=Autopsy\u306f\u81ea\u52d5\u7684\u306b\u591a\u304f\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u691c\u77e5\u3067\u304d\u307e\u3059\u3002\u3053\u3053\u306b\u306f\u3042\u306a\u305f\u306e\u30ab\u30b9\u30bf\u30e0\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +AddFileTypePanel.deleteSigButton.text=\u7f72\u540d\u3092\u524a\u9664 +AddFileTypePanel.jLabel1.text=\u7f72\u540d +AddFileTypePanel.editSigButton.text=\u7f72\u540d\u3092\u7de8\u96c6 +AddFileTypePanel.addSigButton.text=\u7f72\u540d\u3092\u8ffd\u52a0 +AddFileTypePanel.postHitCheckBox.text=\u691c\u51fa\u6642\u306b"\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb"\u3068\u3057\u3066\u30a2\u30e9\u30fc\u30c8 +AddFileTypePanel.setNameLabel.text=\u30bb\u30c3\u30c8\u540d +AddFileTypePanel.setNameTextField.text= +FileTypeIdGlobalSettingsPanel.ingestRunningWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5b9f\u884c\u4e2d\u306f\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093! +FileTypeIdGlobalSettingsPanel.jLabel3.text=Autopsy\u306f\u81ea\u52d5\u7684\u306b\u591a\u304f\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u691c\u51fa\u3067\u304d\u307e\u3059\u3002\u3053\u3053\u306b\u30ab\u30b9\u30bf\u30e0\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u8ffd\u52a0\u3057\u3066\u304f\u3060\u3055\u3044\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java index 0c885472de..46b60d9b1e 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.modules.filetypeid; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -29,6 +30,7 @@ import org.apache.tika.Tika; import org.apache.tika.io.TikaInputStream; import org.apache.tika.mime.MimeTypes; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.textextractors.TextFileExtractor; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; @@ -249,6 +251,17 @@ public class FileTypeDetector { mimeType = tikaType.replace("tika-", ""); //NON-NLS mimeType = removeOptionalParameter(mimeType); } + } else { + /* + * If the file was marked as an octet stream and the extension is .txt, try to detect a text + * encoding with Decodetect. + */ + if (file.getNameExtension().equals("txt")) { + Charset detectedCharset = TextFileExtractor.getEncoding(file); + if (detectedCharset != TextFileExtractor.UNKNOWN_CHARSET) { + mimeType = MimeTypes.PLAIN_TEXT; + } + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdModuleFactory.java index f80340303d..c8ca8b7a1c 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdModuleFactory.java @@ -41,11 +41,11 @@ public class FileTypeIdModuleFactory extends IngestModuleFactoryAdapter { * * @return The module display name. */ - static String getModuleName() { + public static String getModuleName() { return NbBundle.getMessage(FileTypeIdIngestModule.class, "FileTypeIdIngestModule.moduleName.text"); } - + @Override public String getModuleDisplayName() { return getModuleName(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddContentToHashDbAction.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddContentToHashDbAction.java index f80d791bf3..1868d3c57f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddContentToHashDbAction.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddContentToHashDbAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2018 Basis Technology Corp. + * Copyright 2013-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.modules.hashdatabase; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.logging.Level; import javax.swing.AbstractAction; @@ -42,7 +43,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Instances of this Action allow users to content to a hash database. */ -final class AddContentToHashDbAction extends AbstractAction implements Presenter.Popup { +public final class AddContentToHashDbAction extends AbstractAction implements Presenter.Popup { private static AddContentToHashDbAction instance; @@ -66,12 +67,16 @@ final class AddContentToHashDbAction extends AbstractAction implements Presenter "AddContentToHashDbAction.singleSelectionNameNoMD5"); private final static String MULTI_SELECTION_NAME_NO_MD5 = NbBundle.getMessage(AddContentToHashDbAction.class, "AddContentToHashDbAction.multipleSelectionNameNoMD5"); + private static final long serialVersionUID = 1L; /** * AddContentToHashDbAction is a singleton to support multi-selection of * nodes, since org.openide.nodes.NodeOp.findActions(Node[] nodes) will only * pick up an Action from a node if every node in the nodes array returns a * reference to the same action object from Node.getActions(boolean). + * + * @return The AddContentToHashDbAction instance which is used to provide + * the menu for adding content to a HashDb. */ public static synchronized AddContentToHashDbAction getInstance() { if (null == instance) { @@ -83,6 +88,19 @@ final class AddContentToHashDbAction extends AbstractAction implements Presenter private AddContentToHashDbAction() { } + /** + * Get the menu for adding the specified collection of Files to a HashDb. + * + * @param selectedFiles The collection of AbstractFiles the menu actions + * will be applied to. + * + * @return The menu which will allow users to add the specified files to a + * HashDb. + */ + public JMenuItem getMenuForFiles(Collection selectedFiles) { + return new AddContentToHashDbMenu(selectedFiles); + } + @Override public JMenuItem getPopupPresenter() { return new AddContentToHashDbMenu(); @@ -96,10 +114,14 @@ final class AddContentToHashDbAction extends AbstractAction implements Presenter // action. private final class AddContentToHashDbMenu extends JMenu { - AddContentToHashDbMenu() { + private static final long serialVersionUID = 1L; + + /** + * Construct an AddContentToHashDbMenu object using the specified + * collection of files as the files to be added to a HashDb. + */ + AddContentToHashDbMenu(Collection selectedFiles) { super(SINGLE_SELECTION_NAME); - // Get any AbstractFile objects from the lookup of the currently focused top component. - final Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); int numberOfFilesSelected = selectedFiles.size(); // Disable the menu if file ingest is in progress. @@ -109,16 +131,13 @@ final class AddContentToHashDbAction extends AbstractAction implements Presenter SINGLE_SELECTION_NAME_DURING_INGEST, MULTI_SELECTION_NAME_DURING_INGEST); return; - } - - if (selectedFiles.isEmpty()) { + } else if (numberOfFilesSelected == 0) { setEnabled(false); return; - } else { - setTextBasedOnNumberOfSelections(numberOfFilesSelected, - SINGLE_SELECTION_NAME, - MULTI_SELECTION_NAME); } + setTextBasedOnNumberOfSelections(numberOfFilesSelected, + SINGLE_SELECTION_NAME, + MULTI_SELECTION_NAME); // Disable the menu if md5 have not been computed or if the file size // is empty. Display the appropriate reason to the user. @@ -137,7 +156,26 @@ final class AddContentToHashDbAction extends AbstractAction implements Presenter return; } } + addExistingHashDatabases(selectedFiles); + // Add a "New Hash Set..." menu item. Selecting this item invokes a + // a hash database creation dialog and adds the selected files to the + // the new database. + addSeparator(); + JMenuItem newHashSetItem = new JMenuItem(NbBundle.getMessage(this.getClass(), + "AddContentToHashDbAction.ContentMenu.createDbItem")); + newHashSetItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + HashDb hashDb = new HashDbCreateDatabaseDialog().getHashDatabase(); + if (null != hashDb) { + addFilesToHashSet(selectedFiles, hashDb); + } + } + }); + add(newHashSetItem); + } + private void addExistingHashDatabases(Collection selectedFiles) { // Get the current set of updateable hash databases and add each // one to the menu as a separate menu item. Selecting a hash database // adds the selected files to the selected database. @@ -159,23 +197,16 @@ final class AddContentToHashDbAction extends AbstractAction implements Presenter empty.setEnabled(false); add(empty); } + } + + /** + * Construct an AddContentToHashDbMenu object using the currently + * selected files as the files to be added to a HashDb. + */ + AddContentToHashDbMenu() { + // Get any AbstractFile objects from the lookup of the currently focused top component. + this(new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class))); - // Add a "New Hash Set..." menu item. Selecting this item invokes a - // a hash database creation dialog and adds the selected files to the - // the new database. - addSeparator(); - JMenuItem newHashSetItem = new JMenuItem(NbBundle.getMessage(this.getClass(), - "AddContentToHashDbAction.ContentMenu.createDbItem")); - newHashSetItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - HashDb hashDb = new HashDbCreateDatabaseDialog().getHashDatabase(); - if (null != hashDb) { - addFilesToHashSet(selectedFiles, hashDb); - } - } - }); - add(newHashSetItem); } /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED index 0b470ce6b1..44057d0016 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED @@ -49,10 +49,7 @@ ImportCentralRepoDbProgressDialog.errorParsingFile.message=Error parsing hash se ImportCentralRepoDbProgressDialog.linesProcessed.message=\ hashes processed ImportCentralRepoDbProgressDialog.title.text=Central Repository Import Progress OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=\ - Hash Set ingest module. \n\n\ - The ingest module analyzes files in the disk image and marks them as "known" (based on NSRL hashset lookup for "known" files) and "bad / interesting" (based on one or more hash sets supplied by the user).\n\n\ - The module also contains additional non-ingest tools that are integrated in the GUI, such as file lookup by hash and hash set configuration. +OpenIDE-Module-Long-Description=Hash Set ingest module. \n\nThe ingest module analyzes files in the disk image and marks them as "known" (based on NSRL hashset lookup for "known" files) and "bad / interesting" (based on one or more hash sets supplied by the user).\n\nThe module also contains additional non-ingest tools that are integrated in the GUI, such as file lookup by hash and hash set configuration. OpenIDE-Module-Name=HashDatabases OptionsCategory_Name_HashDatabase=Hash Sets OptionsCategory_Keywords_HashDatabase=Hash Sets @@ -181,10 +178,7 @@ HashDbSearchThread.name.searching=Searching HashDbSearchThread.noMoreFilesWithMD5Msg=No other files with the same MD5 hash were found. ModalNoButtons.indexingDbsTitle=Indexing hash sets ModalNoButtons.indexingDbTitle=Indexing hash set -ModalNoButtons.exitHashDbIndexingMsg=You are about to exit out of indexing your hash sets. \n\ -The generated index will be left unusable. If you choose to continue,\n\ - please delete the corresponding -md5.idx file in the hash folder.\n\ - Exit indexing? +ModalNoButtons.exitHashDbIndexingMsg=You are about to exit out of indexing your hash sets. \nThe generated index will be left unusable. If you choose to continue,\nplease delete the corresponding -md5.idx file in the hash folder.\nExit indexing? ModalNoButtons.dlgTitle.unfinishedIndexing=Unfinished Indexing ModalNoButtons.indexThis.currentlyIndexing1Db=Currently indexing 1 hash set ModalNoButtons.indexThese.currentlyIndexing1OfNDbs=Currently indexing 1 of {0} diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle_ja.properties index 47a18afeda..3f5ea43a61 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle_ja.properties @@ -1,207 +1,297 @@ +HashDbCreateDatabaseDialog.databaseCreationError=\u65b0\u898f\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +HashDbCreateDatabaseDialog.databaseLookupError=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u30a2\u30af\u30bb\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +HashDbCreateDatabaseDialog.duplicateName=\u3053\u306e\u540d\u524d\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059 +HashDbCreateDatabaseDialog.missingOrg=\u7d44\u7e54\u3092\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059 +HashDbCreateDatabaseDialog.populateOrgsError.message=\u7d44\u7e54\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 +HashDbImportDatabaseDialog.databaseLookupError=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u30a2\u30af\u30bb\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +HashDbImportDatabaseDialog.duplicateName=\u3053\u306e\u540d\u524d\u3068\u30d0\u30fc\u30b8\u30e7\u30f3\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306f\u30d0\u30fc\u30b8\u30e7\u30f3\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059 +HashDbImportDatabaseDialog.missingOrg=\u7d44\u7e54\u3092\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059 +HashDbImportDatabaseDialog.missingVersion=\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u5165\u529b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059 +HashDbImportDatabaseDialog.mustEnterHashSetNameMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d\u3092\u5165\u529b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +HashDbImportDatabaseDialog.populateOrgsError.message=\u7d44\u7e54\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +HashDbIngestModule.dialogTitle.errorFindingArtifacts=\u6b21\u306e\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +HashDbIngestModule.errorMessage.lookingForFileArtifacts={0} \u306e\u65e2\u5b58\u306e\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +HashDbIngestModule.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u30d2\u30c3\u30c8\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=\u9855\u8457\u306a\u30d5\u30a1\u30a4\u30eb\u306e\u691c\u7d22\u306f\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002 +HashDbIngestModule.knownFileSearchWillNotExecuteWarn=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\u306e\u691c\u7d22\u306f\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002 +HashDbIngestModule.noKnownBadHashDbSetMsg=\u9855\u8457\u306a\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +HashDbIngestModule.noKnownHashDbSetMsg=\u65e2\u77e5\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +HashDbManager.CentralRepoHashDb.orgError=\u7d44\u7e54\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +HashDbManager.centralRepoLoadError.message=\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +# {0} - \u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d +HashDbManager.noDbPath.message=\u6b21\u306e\u305f\u3081\u306e\u6709\u52b9\u306a\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d1\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +HashDbSearchAction.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +HashDbSearchPanel.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +HashLookupSettingsPanel.centralRepo=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc +HashLookupSettingsPanel.editable=\u7de8\u96c6\u53ef\u80fd +HashLookupSettingsPanel.indexNsrl.text=\u3053\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306fNSRL\u306e\u3088\u3046\u306b\u898b\u3048\u308b\u305f\u3081\u3001\u30ea\u30b9\u30c8\u304b\u3089\u524a\u9664\u3055\u308c\u307e\u3059\u3002\n +HashLookupSettingsPanel.indexNsrl.title=NSRL\u306f\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u307e\u305b\u3093\u3002 +HashLookupSettingsPanel.notApplicable=N/A +HashLookupSettingsPanel.promptMessage.deleteHashDb=\u3053\u308c\u306b\u3088\u308a\u3001\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u306b\u5229\u7528\u3067\u304d\u306a\u304f\u306a\u308a\u307e\u3059\u3002\u7d9a\u884c\u3057\u307e\u3059\u304b?\n\n\u6ce8: \u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u5f8c\u304b\u3089\u3067\u3082\u518d\u30a4\u30f3\u30dd\u30fc\u30c8\u3067\u304d\u307e\u3059\u3002 +HashLookupSettingsPanel.promptTitle.deleteHashDb=\u69cb\u6210\u304b\u3089\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u524a\u9664 +HashLookupSettingsPanel.readOnly=\u8aad\u307f\u53d6\u308a\u5c02\u7528 +# {0} - \u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u540d +HashLookupSettingsPanel.removeDatabaseFailure.message=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +# {0} - nsrlUrlAddress +HashLookupSettingsPanel.removeUnindexedNsrl.text=NSRL\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3059\u308b\u4ee3\u308f\u308a\u306b\u3001\u3053\u3053\u3067\u5165\u624b\u53ef\u80fd\u306a\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u6e08\u307f\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3057\u3066\u304f\u3060\u3055\u3044:\n{0} +HashLookupSettingsPanel.removeUnindexedNsrl.title=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u3066\u3044\u306a\u3044NSRL(s)\u306f\u524a\u9664\u3055\u308c\u307e\u3059 +HashLookupSettingsPanel.saveFail.message=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +HashLookupSettingsPanel.saveFail.title=\u4fdd\u5b58\u5931\u6557 +HashLookupSettingsPanel.Title=\u30b0\u30ed\u30fc\u30d0\u30eb\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u8a2d\u5b9a +# {0} - nsrlHashSet +HashLookupSettingsPanel.unindexedNsrl.base=\u6b21\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306f\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u3066\u3044\u306a\u3044\u30d0\u30fc\u30b8\u30e7\u30f3\u306eNSRL\u306e\u3088\u3046\u306b\u898b\u3048\u308b\u305f\u3081\u3001\u30ea\u30b9\u30c8\u304b\u3089\u524a\u9664\u3055\u308c\u307e\u3059\u3002\nHash set:{0}\n +# {0} - nsrlHashSets +HashLookupSettingsPanel.unindexedNsrls.base=\u6b21\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306f\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u3066\u3044\u306a\u3044\u30d0\u30fc\u30b8\u30e7\u30f3\u306eNSRL\u306e\u3088\u3046\u306b\u898b\u3048\u308b\u305f\u3081\u3001\u30ea\u30b9\u30c8\u304b\u3089\u524a\u9664\u3055\u308c\u307e\u3059\u3002\nHash sets:{0}\n +HashLookupSettingsPanel.updateStatusError=\u30b9\u30c6\u30fc\u30bf\u30b9\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ImportCentralRepoDbProgressDialog.errorParsingFile.message=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ImportCentralRepoDbProgressDialog.linesProcessed.message=\ \u51e6\u7406\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5 +ImportCentralRepoDbProgressDialog.title.text=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306e\u30a4\u30f3\u30dd\u30fc\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059 OpenIDE-Module-Display-Category=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb OpenIDE-Module-Long-Description=\ - \u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb \n\n\ - \u30c7\u30a3\u30b9\u30af\u30a4\u30e1\u30fc\u30b8\u306b\u3042\u308b\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u3057\u3001\u300c\u65e2\u77e5\u300d\uff08NSRL\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u300c\u65e2\u77e5\u300d\u30d5\u30a1\u30a4\u30eb\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u3092\u57fa\u306b\uff09\u307e\u305f\u306f\u300c\u60aa\u8cea\uff0f\u7591\u308f\u3057\u3044\u300d\uff08\u30e6\u30fc\u30b6\u30fc\u304c\u6307\u5b9a\u3057\u305f\u5358\u6570\u307e\u305f\u306f\u8907\u6570\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u57fa\u306b\uff09\u3068\u30de\u30fc\u30af\u3057\u307e\u3059\u3002\n\n\ - \u30cf\u30c3\u30b7\u30e5\u3084\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8a2d\u5b9a\u306b\u57fa\u3065\u3044\u305f\u30d5\u30a1\u30a4\u30eb\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u306a\u3069\u3001\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306fGUI\u306b\u9023\u643a\u3057\u3066\u3044\u308b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3057\u306a\u3044\u3001\u8ffd\u52a0\u306e\u30c4\u30fc\u30eb\u304c\u542b\u307e\u308c\u307e\u3059\u3002 -OpenIDE-Module-Name=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9 + \u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3067\u3059\u3002 \n\n\ + \u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u30c7\u30a3\u30b9\u30af\u30a4\u30e1\u30fc\u30b8\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u3057\u3001\u300c\u65e2\u77e5\u300d(\u300c\u65e2\u77e5\u300d\u30d5\u30a1\u30a4\u30eb\u306eNSRL\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u306b\u57fa\u3065\u304f)\u304a\u3088\u3073\u300c\u4e0d\u6b63 / \u8208\u5473\u6df1\u3044\u300d(\u30e6\u30fc\u30b6\u30fc\u304c\u63d0\u4f9b\u3059\u308b1\u3064\u4ee5\u4e0a\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u57fa\u3065\u304f)\u3068\u30de\u30fc\u30af\u4ed8\u3051\u3057\u307e\u3059\u3002\n\n\ + \u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306b\u306f\u3001\u30cf\u30c3\u30b7\u30e5\u3084\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u69cb\u6210\u5225\u306e\u30d5\u30a1\u30a4\u30eb\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u306a\u3069\u306e\u3001GUI\u306b\u7d71\u5408\u3055\u308c\u3066\u3044\u308b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30c4\u30fc\u30eb\u4ee5\u5916\u306e\u305d\u306e\u4ed6\u306e\u30c4\u30fc\u30eb\u3082\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002 +OpenIDE-Module-Name=HashDatabases +OptionsCategory_Name_HashDatabase=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8 +OptionsCategory_Keywords_HashDatabase=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8 HashDbSearchPanel.hashTable.columnModel.title0=MD5\u30cf\u30c3\u30b7\u30e5 HashDbSearchPanel.addButton.text=\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0 -HashDbSearchPanel.hashLabel.text=MD5\u30cf\u30c3\u30b7\u30e5\uff1a +HashDbSearchPanel.hashField.text= +HashDbSearchPanel.hashLabel.text=MD5\u30cf\u30c3\u30b7\u30e5: HashDbSearchPanel.searchButton.text=\u691c\u7d22 -HashDbSearchPanel.removeButton.text=\u9078\u629e\u3057\u305f\u3082\u306e\u3092\u524a\u9664 -HashDbSearchPanel.titleLabel.text=\u6b21\u306eMD5\u30cf\u30c3\u30b7\u30e5\u4ed8\u304d\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22\uff1a -HashDbSearchPanel.errorField.text=\u30a8\u30e9\u30fc\uff1a\u5168\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u304c\u30cf\u30c3\u30b7\u30e5\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +HashDbSearchPanel.removeButton.text=\u9078\u629e\u3055\u308c\u305f\u9805\u76ee\u3092\u524a\u9664 +HashDbSearchPanel.titleLabel.text=\u6b21\u306eMD5\u30cf\u30c3\u30b7\u30e5\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22: +HashDbSearchPanel.errorField.text=\u30a8\u30e9\u30fc: \u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u304c\u30cf\u30c3\u30b7\u30e5\u3055\u308c\u3066\u3044\u308b\u308f\u3051\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 HashDbSearchPanel.saveBox.text=\u30cf\u30c3\u30b7\u30e5\u3092\u8a18\u61b6 -HashDbSearchPanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb -OpenIDE-Module-Short-Description=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u304a\u3088\u3073\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30c4\u30fc\u30eb -HashDbImportDatabaseDialog.jLabel1.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d\uff1a -HashDbImportDatabaseDialog.knownBadRadioButton.text=\u65e2\u77e5\u306e\u60aa\u8cea -HashDbImportDatabaseDialog.jLabel2.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30bf\u30a4\u30d7\uff1a +HashDbSearchPanel.cancelButton.text=\u53d6\u308a\u6d88\u3057 +OpenIDE-Module-Short-Description=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3068\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30c4\u30fc\u30eb +HashDbImportDatabaseDialog.jLabel1.text=\u540d\u524d: +HashDbImportDatabaseDialog.databasePathTextField.text= +HashDbImportDatabaseDialog.knownBadRadioButton.text=\u9855\u8457 +HashDbImportDatabaseDialog.jLabel2.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30bf\u30a4\u30d7: HashDbImportDatabaseDialog.okButton.text=OK -HashDbImportDatabaseDialog.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb -HashDbCreateDatabaseDialog.jLabel2.text=\u30bf\u30a4\u30d7\uff1a -HashDbCreateDatabaseDialog.knownBadRadioButton.text=\u65e2\u77e5\u306e\u60aa\u8cea -HashDbCreateDatabaseDialog.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb -ModalNoButtons.CURRENTLYON_LABEL.text=y\u306ex\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d -ModalNoButtons.GO_GET_COFFEE_LABEL.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d\u3067\u3059\u3002\u6642\u9593\u304c\u304b\u304b\u308b\u5834\u5408\u304c\u3042\u308a\u307e\u3059\u3002 -ModalNoButtons.CANCEL_BUTTON.text=\u30ad\u30e3\u30f3\u30bb\u30eb -HashDbImportDatabaseDialog.knownRadioButton.text=\u65e2\u77e5\uff08NSRL\u307e\u305f\u306f\u305d\u306e\u4ed6\uff09 +HashDbImportDatabaseDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +HashDbCreateDatabaseDialog.jLabel2.text=\u30bf\u30a4\u30d7: +HashDbCreateDatabaseDialog.knownBadRadioButton.text=\u9855\u8457 +HashDbCreateDatabaseDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +ModalNoButtons.CURRENTDB_LABEL.text=(CurrentDb) +ModalNoButtons.CURRENTLYON_LABEL.text=\u73fe\u5728y\u306ex\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u4e2d\u3067\u3059 +ModalNoButtons.GO_GET_COFFEE_LABEL.text=\u73fe\u5728\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u4e2d\u3067\u3059\u3002\u3053\u308c\u306b\u306f\u6642\u9593\u304c\u304b\u304b\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 +ModalNoButtons.CANCEL_BUTTON.text=\u53d6\u308a\u6d88\u3057 +HashDbImportDatabaseDialog.knownRadioButton.text=\u65e2\u77e5(NSRL\u307e\u305f\u306f\u305d\u306e\u4ed6) HashDbCreateDatabaseDialog.knownRadioButton.text=\u65e2\u77e5 -HashDbCreateDatabaseDialog.saveAsButton.text=\u540d\u524d\u3092\u3064\u3051\u3066\u4fdd\u5b58\u2026 -HashDbImportDatabaseDialog.jLabel3.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\uff1a -HashDbCreateDatabaseDialog.sendIngestMessagesCheckbox.text=\u30d2\u30c3\u30c8\u6bce\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30a4\u30f3\u30dc\u30c3\u30af\u30b9\u306b\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u308b -HashDbImportDatabaseDialog.sendIngestMessagesCheckbox.text=\u30d2\u30c3\u30c8\u6bce\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30a4\u30f3\u30dc\u30c3\u30af\u30b9\u306b\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u308b +HashDbCreateDatabaseDialog.saveAsButton.text=\u540d\u524d\u3092\u4ed8\u3051\u3066\u4fdd\u5b58... +HashDbCreateDatabaseDialog.hashSetNameTextField.text= +HashDbImportDatabaseDialog.jLabel3.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d1\u30b9: +HashDbCreateDatabaseDialog.sendIngestMessagesCheckbox.text=\u30d2\u30c3\u30c8\u3054\u3068\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u53d7\u4fe1\u7bb1\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u4fe1 +HashDbImportDatabaseDialog.sendIngestMessagesCheckbox.text=\u30d2\u30c3\u30c8\u3054\u3068\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u53d7\u4fe1\u7bb1\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u4fe1 +HashDbImportDatabaseDialog.hashSetNameTextField.text= HashDbImportDatabaseDialog.openButton.text=\u958b\u304f... -HashDbCreateDatabaseDialog.jLabel3.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d\uff1a +HashDbCreateDatabaseDialog.jLabel3.text=\u540d\u524d: HashDbCreateDatabaseDialog.okButton.text=OK -AddContentToHashDbAction.ContentMenu.noHashDbsConfigd=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 -AddContentToHashDbAction.ContentMenu.createDbItem=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f5c\u6210... -AddContentToHashDbAction.addFilesToHashSet.addToHashDbErr1.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30a8\u30e9\u30fc\u306b\u8ffd\u52a0 -AddContentToHashDbAction.addFilesToHashSet.addToHashDbErr2.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30a8\u30e9\u30fc\u306b\u8ffd\u52a0 -AddContentToHashDbAction.addFilesToHashSet.addToHashDbErr3.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30a8\u30e9\u30fc\u306b\u8ffd\u52a0 -AddContentToHashDbAction.addFilesToHashSet.unableToAddFileMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b{0}\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -AddContentToHashDbAction.addFilesToHashSet.unableToAddFileSzMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b{0}\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30cf\u30c3\u30b7\u30e5\u304c\u8a08\u7b97\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u9069\u5207\u306a\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u8a2d\u5b9a\u3057\u3001\u5b9f\u884c\u3057\u3066\u4e0b\u3055\u3044\u3002 +HashDbCreateDatabaseDialog.databasePathTextField.text= +AddContentToHashDbAction.ContentMenu.noHashDbsConfigd=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u69cb\u6210\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +AddContentToHashDbAction.ContentMenu.createDbItem=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u4f5c\u6210... +AddContentToHashDbAction.addFilesToHashSet.addToHashDbErr1.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30a8\u30e9\u30fc\u306b\u8ffd\u52a0 +AddContentToHashDbAction.addFilesToHashSet.addToHashDbErr2.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30a8\u30e9\u30fc\u306b\u8ffd\u52a0 +AddContentToHashDbAction.addFilesToHashSet.addToHashDbErr3.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30a8\u30e9\u30fc\u306b\u8ffd\u52a0 +AddContentToHashDbAction.addFilesToHashSet.unableToAddFileMsg={0} \u3092\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3002 +AddContentToHashDbAction.addFilesToHashSet.unableToAddFileEmptyMsg={0} \u3092\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3002\u30d5\u30a1\u30a4\u30eb\u306b\u30b3\u30f3\u30c6\u30f3\u30c4\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +AddContentToHashDbAction.addFilesToHashSet.unableToAddFileSzMsg={0} \u3092\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3002\u30cf\u30c3\u30b7\u30e5\u304c\u8a08\u7b97\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u9069\u5207\u306a\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u69cb\u6210\u3057\u3066\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 HashDatabaseOptionsPanelController.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -HashDatabaseOptionsPanelController.moduleErrMsg=HashDatabaseOptionsPanelController\u306e\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u5b8c\u5168\u3067\u306a\u3044\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 -HashDbConfigPanel.noSelectionText=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +HashDatabaseOptionsPanelController.moduleErrMsg=HashDatabaseOptionsPanelController\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +HashDbConfigPanel.noSelectionText= HashDbConfigPanel.errorGettingPathText=\u30d1\u30b9\u306e\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -HashDbConfigPanel.errorGettingIndexStatusText=\u30b9\u30c6\u30fc\u30bf\u30b9\u306e\u78ba\u8a8d\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -HashDbConfigPanel.indexButtonText.index=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9 -HashDbConfigPanel.indexButtonText.indexing=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d -HashDbConfigPanel.indexStatusText.indexGen=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u4f5c\u6210\u4e2d\u3067\u3059 -HashDbConfigPanel.indexStatusText.indexOnly=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u307f -HashDbConfigPanel.indexStatusText.indexed=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u6e08\u307f +HashDbConfigPanel.errorGettingIndexStatusText=\u30b9\u30c6\u30fc\u30bf\u30b9\u306e\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +HashDbConfigPanel.indexButtonText.index=\u7d22\u5f15 +HashDbConfigPanel.indexButtonText.indexing=\u7d22\u5f15\u751f\u6210 +HashDbConfigPanel.indexStatusText.indexGen=\u73fe\u5728\u7d22\u5f15\u3092\u751f\u6210\u4e2d\u3067\u3059 +HashDbConfigPanel.indexStatusText.indexOnly=\u7d22\u5f15\u306e\u307f +HashDbConfigPanel.indexButtonText.indexing=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u6e08\u307f HashDbConfigPanel.indexButtonText.reIndex=\u518d\u30a4\u30f3\u30c7\u30c3\u30af\u30b9 -HashDbConfigPanel.indexStatusText.noIndex=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u7121\u3057 -HashDbConfigPanel.dbsNotIndexedMsg=\u6b21\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3057\u307e\u3059\u304b\uff1f\n {0} -HashDbConfigPanel.unindexedDbsMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u3066\u3044\u306a\u3044\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9 -HashDbConfigPanel.allUnindexedDbsRmFromListMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u3066\u3044\u306a\u3044\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u30ea\u30b9\u30c8\u304b\u3089\u524a\u9664\u3055\u308c\u307e\u3059 +HashDbConfigPanel.indexStatusText.noIndex=\u7d22\u5f15\u304c\u3042\u308a\u307e\u305b\u3093 +HashDbConfigPanel.dbsNotIndexedMsg=\u6b21\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4eca\u3059\u3050\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3057\u307e\u3059\u304b? \n {0} +HashDbConfigPanel.dbNotIndexedMsg=\u6b21\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4eca\u3059\u3050\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3057\u307e\u3059\u304b? \n{0} +HashDbConfigPanel.unindexedDbsMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u3066\u3044\u306a\u3044\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8 +HashDbConfigPanel.allUnindexedDbsRmFromListMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u3066\u3044\u306a\u3044\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u30ea\u30b9\u30c8\u304b\u3089\u3059\u3079\u3066\u524a\u9664\u3055\u308c\u307e\u3059 HashDbConfigPanel.nameColLbl=\u540d\u524d -HashDbConfigPanel.editingCellsNotSupportedMsg=\u30bb\u30eb\u306f\u7de8\u96c6\u4e0d\u53ef\u3067\u3059 -HashDbCreateDatabaseDialog.createHashDbMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f5c\u6210 -HashDbCreateDatabaseDialog.hashDbMustHaveFileExtensionMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u306f .{0} \u306e\u62e1\u5f35\u5b50\u304c\u5fc5\u8981\u3067\u3059\u3002 +HashDbConfigPanel.editingCellsNotSupportedMsg=\u30bb\u30eb\u306e\u7de8\u96c6\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +HashDbCreateDatabaseDialog.defaultFileName=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8 +HashDbCreateDatabaseDialog.createHashDbMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u4f5c\u6210 +HashDbCreateDatabaseDialog.hashDbMustHaveFileExtensionMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d5\u30a1\u30a4\u30eb\u306b\u306f .{0} \u62e1\u5f35\u5b50\u304c\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 HashDbCreateDatabaseDialog.fileNameErr=\u30d5\u30a1\u30a4\u30eb\u540d\u30a8\u30e9\u30fc -HashDbCreateDatabaseDialog.fileNameAlreadyExistsMsg=\u540c\u540d\u306e\u30d5\u30a1\u30a4\u30eb\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u5225\u306e\u30d5\u30a1\u30a4\u30eb\u540d\u3092\u8a2d\u5b9a\u3057\u3066\u4e0b\u3055\u3044\u3002 -HashDbCreateDatabaseDialog.fileExistsErr=\u30d5\u30a1\u30a4\u30eb\u304c\u65e2\u306b\u5b58\u5728\u3057\u3066\u3044\u308b\u30a8\u30e9\u30fc -HashDbCreateDatabaseDialog.mustEnterHashSetNameMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d\u306e\u5165\u529b\u304c\u5fc5\u8981\u3067\u3059 -HashDbCreateDatabaseDialog.createHashDbErr=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u4f5c\u6210\u30a8\u30e9\u30fc -HashDbCreateDatabaseDialog.mustEnterHashDbPathMsg=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\u306e\u5165\u529b\u304c\u5fc5\u8981\u3067\u3059\u3002 -HashDbCreateDatabaseDialog.errMsg.hashDbCreationErr=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u4f5c\u6210\u30a8\u30e9\u30fc -HashDbCreateDatabaseDialog.cannotCreateFileAtLocMsg=\u6307\u5b9a\u3055\u308c\u305f\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002 -HashDbCreateDatabaseDialog.failedToCreateHashDbMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u4f5c\u6210\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 -HashDbImportDatabaseDialog.importHashDbMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 -HashDbImportDatabaseDialog.fileNameExtFilter.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb -HashDbImportDatabaseDialog.failedToGetDbPathMsg=\u9078\u629e\u3057\u305f\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d1\u30b9\u306e\u5165\u624b\u3092\u5931\u6557\u3057\u307e\u3057\u305f\u3002 -HashDbImportDatabaseDialog.importHashDbErr=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30a8\u30e9\u30fc\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 -HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u306e\u9078\u629e\u304c\u5fc5\u8981\u3067\u3059\u3002 -HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=\u9078\u629e\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 -HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg={0}\u3067\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u304f\u306e\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 +HashDbCreateDatabaseDialog.fileNameAlreadyExistsMsg=\u3053\u306e\u540d\u524d\u306e\u30d5\u30a1\u30a4\u30eb\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u65b0\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u540d\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +HashDbCreateDatabaseDialog.fileExistsErr=\u30d5\u30a1\u30a4\u30eb\u304c\u3059\u3067\u306b\u5b58\u5728\u3059\u308b\u5834\u5408\u306e\u30a8\u30e9\u30fc +HashDbCreateDatabaseDialog.mustEnterHashSetNameMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d\u3092\u5165\u529b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +HashDbCreateDatabaseDialog.createHashDbErr=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u4f5c\u6210\u30a8\u30e9\u30fc +HashDbCreateDatabaseDialog.mustEnterHashDbPathMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d1\u30b9\u3092\u5165\u529b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +HashDbCreateDatabaseDialog.errMsg.hashDbCreationErr=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u4f5c\u6210\u30a8\u30e9\u30fc +HashDbCreateDatabaseDialog.cannotCreateFileAtLocMsg=\u9078\u629e\u3057\u305f\u5834\u6240\u3067\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002 +HashDbCreateDatabaseDialog.failedToCreateHashDbMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +HashDbImportDatabaseDialog.importHashDbMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 +HashDbImportDatabaseDialog.fileNameExtFilter.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d5\u30a1\u30a4\u30eb +HashDbImportDatabaseDialog.failedToGetDbPathMsg=\u9078\u629e\u3057\u305f\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u30d1\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +HashDbImportDatabaseDialog.importHashDbErr=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30a4\u30f3\u30dd\u30fc\u30c8\u30a8\u30e9\u30fc +HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d5\u30a1\u30a4\u30eb\u306e\u30d1\u30b9\u3092\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=\u9078\u629e\u3057\u305f\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +HashDbImportDatabaseDialog.unableToCopyToUserDirMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u30e6\u30fc\u30b6\u30fc\u69cb\u6210\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc {0} \u306b\u30b3\u30d4\u30fc\u3067\u304d\u307e\u305b\u3093\u3002 +HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg={0} \u306b\u3042\u308b\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f\u3002 HashLookupModuleFactory.moduleName.text=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7 -HashLookupModuleFactory.moduleDescription.text=\u6a19\u6e96\u306eNSRL\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306a\u3069\u3001\u63d0\u4f9b\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u5229\u7528\u3057\u3066\u3001\u65e2\u77e5\u307e\u305f\u306f\u7591\u308f\u3057\u3044\u3082\u306e\u3092\u691c\u77e5\u3057\u307e\u3059\u3002 -HashDbIngestModule.noKnownHashDbSetMsg=\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 -HashDbIngestModule.knownFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u304c\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002 -HashDbIngestModule.noKnownBadHashDbSetMsg=\u65e2\u77e5\u306e\u60aa\u8cea\u306a\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30bb\u30c3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002 -HashDbConfigPanel.dbNotIndexedMsg=\u6b21\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3057\u307e\u3059\u304b\uff1f\n{0} -HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u60aa\u8cea\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u306f\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002 -HashDbIngestModule.fileReadErrorMsg=\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc\uff1a {0} -HashDbIngestModule.calcHashValueErr={0}\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8a08\u7b97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -HashDbIngestModule.hashLookupErrorMsg=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u30a8\u30e9\u30fc\uff1a {0} -HashDbIngestModule.lookingUpKnownBadHashValueErr={0}\u306e\u65e2\u77e5\u306e\u60aa\u8cea\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -HashDbIngestModule.lookingUpKnownHashValueErr={0}\u306e\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +HashLookupModuleFactory.moduleDescription.text=\u6a19\u6e96\u306eNSRL\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306a\u3069\u306e\u63d0\u4f9b\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u4f7f\u3063\u3066\u3001\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\u3068\u9855\u8457\u306a\u30d5\u30a1\u30a4\u30eb\u3092\u8b58\u5225\u3057\u307e\u3059\u3002 +HashDbIngestModule.fileReadErrorMsg=\u8aad\u307f\u53d6\u308a\u30a8\u30e9\u30fc: {0} +HashDbIngestModule.calcHashValueErr={0} ({1}) \u306e\u30cf\u30c3\u30b7\u30e5\u5024\u306e\u8a08\u7b97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +HashDbIngestModule.hashLookupErrorMsg=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u30a8\u30e9\u30fc: {0} +HashDbIngestModule.settingKnownBadStateErr={0} \u306e\u9855\u8457\u306a\u72b6\u614b\u306e\u8a2d\u5b9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +HashDbIngestModule.lookingUpKnownBadHashValueErr={0} \u306e\u9855\u8457\u306a\u30cf\u30c3\u30b7\u30e5\u5024\u306e\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +HashDbIngestModule.lookingUpKnownHashValueErr={0} \u306e\u65e2\u77e5\u30cf\u30c3\u30b7\u30e5\u5024\u306e\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 HashDbIngestModule.postToBB.fileName=\u30d5\u30a1\u30a4\u30eb\u540d HashDbIngestModule.postToBB.md5Hash=MD5\u30cf\u30c3\u30b7\u30e5 HashDbIngestModule.postToBB.hashsetName=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d -HashDbIngestModule.postToBB.knownBadMsg=\u65e2\u77e5\u306e\u60aa\u8cea\: {0} -HashDbIngestModule.complete.knownBadsFound=\u767a\u898b\u3055\u308c\u305f\u65e2\u77e5\u306e\u60aa\u8cea\uff1a -HashDbIngestModule.complete.totalCalcTime=\u8a08\u7b97\u6642\u9593\u306e\u5408\u8a08 -HashDbIngestModule.complete.totalLookupTime=\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u6642\u9593\u306e\u5408\u8a08 -HashDbIngestModule.complete.databasesUsed=\u5229\u7528\u3057\u305f\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\uff1a +HashDbIngestModule.postToBB.knownBadMsg=\u9855\u8457: {0} +HashDbIngestModule.complete.knownBadsFound=\u898b\u3064\u304b\u3063\u305f\u9855\u8457\u6027: +HashDbIngestModule.complete.totalCalcTime=\u5408\u8a08\u8a08\u7b97\u6642\u9593 +HashDbIngestModule.complete.totalLookupTime=\u5408\u8a08\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u6642\u9593 +HashDbIngestModule.complete.databasesUsed=\u4f7f\u7528\u3057\u305f\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8: HashDbIngestModule.complete.hashLookupResults=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u7d50\u679c -HashDbManager.moduleErrorListeningToUpdatesMsg=HashDbManager\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u539f\u56e0\u306a\u306e\u304b\u3092\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u5b8c\u5168\u3067\u306a\u3044\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 -HashDbManager.replacingDuplicateHashsetNameMsg=\u91cd\u8907\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d {0} \u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002\n {1}\u306b\u66f8\u304d\u63db\u3048\u307e\u3059\u3002 -HashDbManager.openHashDbErr=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u304f\u30a8\u30e9\u30fc -HashDbManager.unableToOpenHashDbMsg=\ {0} \u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u3051\u307e\u305b\u3093\u3002 -HashDbManager.savedBackupOfOldConfigMsg={0}\n\u53e4\u3044\u8a2d\u5b9a\u306e\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u30b3\u30d4\u30fc\u304c\u6b21\u306e\u901a\u308a\u4fdd\u5b58\u3055\u308c\u307e\u3057\u305f\u3002\n{1} -HashDbManager.baseMessage.updatedFormatHashDbConfig=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306e\u5f62\u5f0f\u304c\u66f4\u65b0\u3055\u308c\u307e\u3057\u305f\u3002 -HashDbManager.msgBoxTitle.confFileFmtChanged=\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u5f62\u5f0f\u306e\u5909\u66f4\u5b8c\u4e86 -HashDbManager.dlgMsg.dbNotFoundAtLoc=\ {0} \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u6b21\u306e\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u3042\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n {1}\n \u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22\u3057\u307e\u3059\u304b\uff1f -HashDbManager.dlgTitle.MissingDb=\u6b20\u843d\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9 -HashDbManager.progress.indexingHashSet=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d {0} -HashDbManager.dlgMsg.errorIndexingHashSet=\ {0} \u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d\u306e\u30a8\u30e9\u30fc -HashDbManager.hashDbIndexingErr=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d\u306e\u30a8\u30e9\u30fc -HashDbPanelSearchAction.actionName=MD5\u30cf\u30c3\u30b7\u30e5\u306b\u57fa\u3065\u304f\u30d5\u30a1\u30a4\u30eb\u691c\u7d22 -HashDbSearchAction.dlgMsg.noFilesHaveMD5Calculated=MD5\u30cf\u30c3\u30b7\u30e5\u304c\u8a08\u7b97\u3055\u308c\u3066\u3044\u308b\u30d5\u30a1\u30a4\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u307e\u305a\u306fHashDB\u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3057\u3066\u4e0b\u3055\u3044\u3002 +HashDbManager.moduleErrorListeningToUpdatesMsg=HashDbManager\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +HashDbManager.replacingDuplicateHashsetNameMsg=\u691c\u51fa\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d {0} \u3092\u8907\u88fd\u3057\u307e\u3059\u3002\n{1} \u3067\u7f6e\u304d\u63db\u3048\u4e2d\u3067\u3059\u3002 +HashDbManager.openHashDbErr=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30a4\u30f3\u30aa\u30fc\u30d7\u30f3\u30a8\u30e9\u30fc +HashDbManager.unableToOpenHashDbMsg={0} \u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u958b\u3051\u307e\u305b\u3093\u3002 +HashDbManager.savedBackupOfOldConfigMsg={0}\n\u53e4\u3044\u69cb\u6210\u306e\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u30b3\u30d4\u30fc\u304c \n{1} \u3068\u3057\u3066\u4fdd\u5b58\u3055\u308c\u307e\u3057\u305f +HashDbManager.baseMessage.updatedFormatHashDbConfig=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u69cb\u6210\u30d5\u30a1\u30a4\u30eb\u306e\u5f62\u5f0f\u304c\u66f4\u65b0\u3055\u308c\u307e\u3057\u305f\u3002 +HashDbManager.msgBoxTitle.confFileFmtChanged=\u69cb\u6210\u30d5\u30a1\u30a4\u30eb\u306e\u5f62\u5f0f\u304c\u5909\u66f4\u3055\u308c\u307e\u3057\u305f +HashDbManager.dlgMsg.dbNotFoundAtLoc=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8 {0} \u304c\n{1}\u306e\u5834\u6240\u3067\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\n\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22\u3057\u307e\u3059\u304b? +HashDbManager.dlgTitle.MissingDb=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 +HashDbManager.progress.indexingHashSet={0} \u306e\u7d22\u5f15\u751f\u6210\u4e2d\u3067\u3059 +HashDbManager.dlgMsg.errorIndexingHashSet={0} \u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u7d22\u5f15\u751f\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +HashDbManager.hashDbIndexingErr=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u30a8\u30e9\u30fc +HashDbPanelSearchAction.actionName=MD5\u30cf\u30c3\u30b7\u30e5\u306b\u3088\u308b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22 +HashDbSearchAction.dlgMsg.noFilesHaveMD5Calculated=\u73fe\u5728MD5\u30cf\u30c3\u30b7\u30e5\u304c\u8a08\u7b97\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u6700\u521d\u306bHashDB\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002 HashDbSearchManager.MD5HashSearch=MD5\u30cf\u30c3\u30b7\u30e5\u691c\u7d22 -HashDbSearchManager.noResultsFoundMsg=\u4e00\u81f4\u3059\u308b\u3082\u306e\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -HashDbSearchPanel.titleText.ingestOngoing=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\uff1b\u5b8c\u4e86\u3059\u308b\u307e\u3067\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 -HashDbSearchPanel.noFilesHaveMD5HashMsg=MD5\u30cf\u30c3\u30b7\u30e5\u4ed8\u304d\u306e\u30d5\u30a1\u30a4\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -HashDbSearchPanel.errorText.noHashesAddedMsg=\u30a8\u30e9\u30fc\uff1a\u30cf\u30c3\u30b7\u30e5\u304c\u8ffd\u52a0\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 -HashDbSearchPanel.errorText.hashAlreadyAddedMsg=\u30a8\u30e9\u30fc\uff1a\u30cf\u30c3\u30b7\u30e5\u304c\u65e2\u306b\u8ffd\u52a0\u3055\u308c\u3066\u3044\u307e\u3059\u3002 -HashDbSearchPanel.errorText.invalidMD5HashMsg=\u30a8\u30e9\u30fc\uff1a\u6709\u52b9\u306aMD5\u30cf\u30c3\u30b7\u30e5\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 -HashDbSearchThread.progress.cancellingSearch={0}\uff08\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026\uff09 -HashDbSearchThread.name.searching=\u691c\u7d22\u4e2d -HashDbSearchThread.noMoreFilesWithMD5Msg=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u4ed8\u304d\u306e\u30d5\u30a1\u30a4\u30eb\u306f\u4ed6\u306b\u3042\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -ModalNoButtons.indexingDbsTitle=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d -ModalNoButtons.indexingDbTitle=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d -ModalNoButtons.exitHashDbIndexingMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3092\u4e2d\u6b62\u3057\u307e\u3059\u3002\n\ -\u4f5c\u6210\u3055\u308c\u305f\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306f\u5229\u7528\u4e0d\u53ef\u3068\u306a\u308a\u307e\u3059\u3002\u7d9a\u884c\u3059\u308b\u5834\u5408\u306f\n\ -\u30cf\u30c3\u30b7\u30e5\u30d5\u30a9\u30eb\u30c0\u5185\u306b\u3042\u308b\u3001\u5bfe\u5fdc\u3059\u308b-md5.idx \u30d5\u30a1\u30a4\u30eb\u3092\u524a\u9664\u3057\u3066\u4e0b\u3055\u3044\u3002\n\ -\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3092\u4e2d\u6b62\u3057\u307e\u3059\u304b\uff1f -ModalNoButtons.dlgTitle.unfinishedIndexing=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u672a\u5b8c\u4e86 -ModalNoButtons.indexThis.currentlyIndexing1Db=\uff11\u3064\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d -ModalNoButtons.indexThese.currentlyIndexing1OfNDbs=\uff11\uff0f {0}\u3064\u76ee\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d -ModalNoButtons.propChg.currentlyIndexingXofN={0}\uff0f {1}\u3064\u76ee\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u4e2d -HashDbManager.duplicateHashSetNameExceptionMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d''{0}''\u306f\u65e2\u306b\u5225\u306e\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u4f7f\u308f\u308c\u3066\u3044\u307e\u3059\u3002 -HashDbManager.hashDbDoesNotExistExceptionMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u6b21\u3067\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\n{0} -HashDbManager.hashDbFileExistsExceptionMsg=\u65e2\u306b\u30d5\u30a1\u30a4\u30eb\u304c\u6b21\u306b\u5b58\u5728\u3057\u307e\u3059\n{0} -HashDbManager.hashDbAlreadyAddedExceptionMsg=\u6b21\u306e\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\n{0}\n\u306f\u65e2\u306b\u4f5c\u6210\u307e\u305f\u306f\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002 -HashDbManager.illegalHashDbFileNameExtensionMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u540d\u306f.{0}\u306e\u62e1\u5f35\u5b50\u304c\u5fc5\u8981\u3067\u3059\u3002 +HashDbSearchManager.noResultsFoundMsg=\u8a72\u5f53\u3059\u308b\u7d50\u679c\u304c\u3042\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +HashDbSearchPanel.titleText.ingestOngoing=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +HashDbSearchPanel.noFilesHaveMD5HashMsg=\u73fe\u5728MD5\u30cf\u30c3\u30b7\u30e5\u304c\u8a08\u7b97\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +HashDbSearchPanel.errorText.noHashesAddedMsg=\u30a8\u30e9\u30fc: \u30cf\u30c3\u30b7\u30e5\u304c\u8ffd\u52a0\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +HashDbSearchPanel.errorText.hashAlreadyAddedMsg=\u30a8\u30e9\u30fc: \u30cf\u30c3\u30b7\u30e5\u304c\u3059\u3067\u306b\u8ffd\u52a0\u3055\u308c\u3066\u3044\u307e\u3059\u3002 +HashDbSearchPanel.errorText.invalidMD5HashMsg=\u30a8\u30e9\u30fc: \u305d\u308c\u306f\u6709\u52b9\u306aMD5\u30cf\u30c3\u30b7\u30e5\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +HashDbSearchThread.progress.cancellingSearch={0} (\u53d6\u308a\u6d88\u3057\u4e2d\u3067\u3059...) +HashDbSearchThread.name.searching=\u691c\u7d22\u4e2d\u3067\u3059 +HashDbSearchThread.noMoreFilesWithMD5Msg=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u4ed6\u306e\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ModalNoButtons.indexingDbsTitle=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u7d22\u5f15\u751f\u6210\u4e2d\u3067\u3059 +ModalNoButtons.indexingDbTitle=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u7d22\u5f15\u751f\u6210\u4e2d\u3067\u3059 +ModalNoButtons.exitHashDbIndexingMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u7d22\u5f15\u751f\u6210\u3092\u7d42\u4e86\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307e\u3059\u3002\n\ +\u751f\u6210\u3055\u308c\u305f\u7d22\u5f15\u306f\u4f7f\u7528\u3067\u304d\u306a\u3044\u72b6\u614b\u306e\u307e\u307e\u306b\u306a\u308a\u307e\u3059\u3002\u7d9a\u884c\u3092\u9078\u629e\u3059\u308b\u5834\u5408\u3001\n\ + \u30cf\u30c3\u30b7\u30e5\u30d5\u30a9\u30eb\u30c0\u30fc\u5185\u306e\u8a72\u5f53\u3059\u308b-md5.idx\u30d5\u30a1\u30a4\u30eb\u3092\u524a\u9664\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\ + \u7d22\u5f15\u751f\u6210\u3092\u7d42\u4e86\u3057\u307e\u3059\u304b? +ModalNoButtons.dlgTitle.unfinishedIndexing=\u5b8c\u4e86\u3057\u3066\u3044\u306a\u3044\u7d22\u5f15\u751f\u6210 +ModalNoButtons.indexThis.currentlyIndexing1Db=\u73fe\u57281\u3064\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u7d22\u5f15\u751f\u6210\u4e2d\u3067\u3059 +ModalNoButtons.indexThese.currentlyIndexing1OfNDbs=\u73fe\u5728 {0} \u306e1\u3064\u3092\u7d22\u5f15\u751f\u6210\u4e2d\u3067\u3059 +ModalNoButtons.propChg.currentlyIndexingXofN=\u73fe\u5728 {1} \u306e {0} \u3092\u7d22\u5f15\u751f\u6210\u4e2d\u3067\u3059 +HashDbManager.duplicateHashSetNameExceptionMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d ''{0}'' \u306f\u3059\u3067\u306b\u4ed6\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059\u3002 +HashDbManager.hashDbDoesNotExistExceptionMsg=\n{0} \u306b\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306f\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f +HashDbManager.hashDbFileExistsExceptionMsg=at\n{0} \u306b\u30d5\u30a1\u30a4\u30eb\u304c\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059 +HashDbManager.hashDbAlreadyAddedExceptionMsg=\n{0}\n \u306b\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u3059\u3067\u306b\u4f5c\u6210\u307e\u305f\u306f\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002 +HashDbManager.illegalHashDbFileNameExtensionMsg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d5\u30a1\u30a4\u30eb\u540d\u306b\u306f .{0} \u62e1\u5f35\u5b50\u304c\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 HashDbManager.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -HashDbManager.knownBad.text=\u65e2\u77e5\u306e\u60aa\u8cea +HashDbManager.knownBad.text=\u9855\u8457 HashDbManager.known.text=\u65e2\u77e5 -HashDbManager.fileNameExtensionFilter.title=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb -HashDbSearchAction.dlgMsg.title=MD5\u30cf\u30c3\u30b7\u30e5\u306b\u57fa\u3065\u3044\u305f\u30d5\u30a1\u30a4\u30eb\u691c\u7d22 +HashDbManager.fileNameExtensionFilter.title=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d5\u30a1\u30a4\u30eb +HashDbSearchAction.dlgMsg.title=MD5\u30cf\u30c3\u30b7\u30e5\u306b\u3088\u308b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22 HashDbSearchAction.getName.text=\u30cf\u30c3\u30b7\u30e5\u691c\u7d22 -HashDbSearchPanel.dlgMsg.title=MD5\u30cf\u30c3\u30b7\u30e5\u306b\u57fa\u3065\u304f\u30d5\u30a1\u30a4\u30eb\u691c\u7d22 -AddContentToHashDbAction.singleSelectionName=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0 -AddContentToHashDbAction.multipleSelectionName=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0 -OptionsCategory_Name_HashDatabase=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9 -OptionsCategory_Keywords_HashDatabase=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9 -HashDbManager.ingestRunningExceptionMsg=\u51e6\u7406\u4e2d\uff1b\u5b8c\u4e86\u3059\u308b\u307e\u3067\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 -ModalNoButtons.CURRENTDB_LABEL.text=\uff08\u73fe\u5728\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\uff09 -HashDbCreateDatabaseDialog.defaultFileName=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8 -HashDbManager.saveErrorExceptionMsg=\u30cf\u30c3\u30b7\u30e5\u8a2d\u5b9a\u306e\u4fdd\u5b58\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +HashDbSearchPanel.dlgMsg.title=MD5\u30cf\u30c3\u30b7\u30e5\u306b\u3088\u308b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22 +AddContentToHashDbAction.singleSelectionName=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0 +AddContentToHashDbAction.multipleSelectionName=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0 +AddContentToHashDbAction.singleSelectionNameDuringIngest=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0(\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d) +AddContentToHashDbAction.multipleSelectionNameDuringIngest=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0(\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u5b9f\u884c\u4e2d) +AddContentToHashDbAction.singleSelectionNameNoMD5=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0(MD5\u30cf\u30c3\u30b7\u30e5\u306a\u3057) +AddContentToHashDbAction.multipleSelectionNameNoMD5=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0(MD5\u30cf\u30c3\u30b7\u30e5\u306a\u3057) +AddContentToHashDbAction.singleSelectionNameEmpty=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0(\u7a7a(\u672a\u8a18\u5165)\u306e\u30d5\u30a1\u30a4\u30eb) +AddContentToHashDbAction.multipleSelectionNameEmpty=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8ffd\u52a0(\u7a7a(\u672a\u8a18\u5165)\u306e\u30d5\u30a1\u30a4\u30eb) +HashDbManager.ingestRunningExceptionMsg=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +HashDbManager.saveErrorExceptionMsg=\u30cf\u30c3\u30b7\u30e5\u69cb\u6210\u306e\u4fdd\u5b58\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +HashLookupSettingsPanel.jButton3.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 +HashLookupSettingsPanel.jLabel6.text=\u30bf\u30a4\u30d7: +HashLookupSettingsPanel.jLabel4.text=\u5834\u6240: +HashLookupSettingsPanel.jLabel2.text=\u540d\u524d: +HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u3067\u3082MD5\u3092\u8a08\u7b97 +HashLookupModuleSettingsPanel.knownHashDbsLabel.text=\u4f7f\u7528\u3059\u308b\u65e2\u77e5\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u9078\u629e: +HashLookupModuleSettingsPanel.knownBadHashDbsLabel.text=\u4f7f\u7528\u3059\u308b\u9855\u8457\u306a\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u9078\u629e: AddContentToHashDbAction.addFilesToHashSet.files=\u30d5\u30a1\u30a4\u30eb AddContentToHashDbAction.addFilesToHashSet.file=\u30d5\u30a1\u30a4\u30eb -HashDbManager.errCreatingIndex.title=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -HashDbManager.errCreatingIndex.msg=\u6b21\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a {0} -HashLookupSettingsPanel.jButton3.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 -HashLookupSettingsPanel.jLabel6.text=\u30bf\u30a4\u30d7\uff1a -HashLookupSettingsPanel.jLabel4.text=\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\uff1a -HashLookupSettingsPanel.jLabel2.text=\u540d\u524d\uff1a -HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u306a\u304f\u3066\u3082\u3001MD5\u3092\u8a08\u7b97 -HashLookupModuleSettingsPanel.knownHashDbsLabel.text=\u5229\u7528\u3059\u308b\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u9078\u629e\uff1a -HashLookupModuleSettingsPanel.knownBadHashDbsLabel.text=\u51e6\u7406\u306b\u5229\u7528\u3059\u308b\u65e2\u77e5\u306e\u60aa\u8cea\u306a\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u9078\u629e\uff1a -HashLookupModuleFactory.createFileIngestModule.exception.msg=\u8a2d\u5b9a\u3092\u884c\u3046\u70ba\u306e\u60f3\u5b9a\u3055\u308c\u308b\u5f15\u6570\u306finstanceof HashLookupModuleSettings\u3067\u3059\u3002 -HashLookupModuleFactory.getIngestJobSettingsPanel.exception.msg=\u8a2d\u5b9a\u3092\u884c\u3046\u70ba\u306e\u60f3\u5b9a\u3055\u308c\u308b\u5f15\u6570\u306finstanceof HashLookupModuleSettings\u3067\u3059\u3002 -HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.toolTipText=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u306a\u304f\u3066\u3082\u3001MD5\u3092\u8a08\u7b97 +HashDbManager.errCreatingIndex.title=\u7d22\u5f15\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +HashDbManager.errCreatingIndex.msg=\u6b21\u306e\u7d22\u5f15\u3092\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +HashLookupModuleFactory.getIngestJobSettingsPanel.exception.msg=instanceof HashLookupModuleSettings\u306b\u306a\u308b\u3068\u4e88\u60f3\u3055\u308c\u308b\u8a2d\u5b9a\u5f15\u6570 +HashLookupModuleFactory.createFileIngestModule.exception.msg=instanceof HashLookupModuleSettings\u306b\u306a\u308b\u3068\u4e88\u60f3\u3055\u308c\u308b\u8a2d\u5b9a\u5f15\u6570 +HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.toolTipText=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u3067\u3082MD5\u3092\u8a08\u7b97 HashDbSearchPanel.hashTable.defaultModel.title.text=MD5\u30cf\u30c3\u30b7\u30e5 -AddContentToHashDbAction.addFilesToHashSet.unableToAddFileEmptyMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b{0}\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30d5\u30a1\u30a4\u30eb\u306b\u30b3\u30f3\u30c6\u30f3\u30c4\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -AddHashValuesToDatabaseDialog.JDialog.Title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0\ -HashLookupSettingsPanel.addHashesToDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0 -AddHashValuesToDatabaseDialog.instructionLabel.text_1=\u4e0b\u8a18\u306bMD5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8cbc\u308a\u4ed8\u3051\u308b\uff08\u30e9\u30a4\u30f3\u3054\u3068\u306b\u4e00\u3064\u305a\u3064\uff09\uff1a -AddHashValuesToDatabaseDialog.cancelButton.text_2=\u30ad\u30e3\u30f3\u30bb\u30eb -AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u304b\u3089\u8cbc\u308a\u4ed8\u3051\u308b +AddHashValuesToDatabaseDialog.JDialog.Title=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0 +AddHashValuesToDatabaseDialog.instructionLabel.text_1=\u4ee5\u4e0b\u306eMD5\u30cf\u30c3\u30b7\u30e5\u5024(1\u30e9\u30a4\u30f3\u306b\u3064\u304d1\u3064)\u3092\u8cbc\u308a\u4ed8\u3051: +AddHashValuesToDatabaseDialog.cancelButton.text_2=\u53d6\u308a\u6d88\u3057 +AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u304b\u3089\u8cbc\u308a\u4ed8\u3051 AddHashValuesToDatabaseProgressDialog.okButton.text=OK AddHashValuesToDatabaseProgressDialog.statusLabel.text=\u30b9\u30c6\u30fc\u30bf\u30b9 -AddHashValuesToDatabaseProgressDialog.title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d7\u30ed\u30b0\u30ec\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0 -AddHashValuesToDatabaseDialog.title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0 +AddHashValuesToDatabaseProgressDialog.title=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3078\u306e\u30cf\u30c3\u30b7\u30e5\u8ffd\u52a0\u306e\u9032\u6357\u72b6\u6cc1 +AddHashValuesToDatabaseDialog.title=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0 AddHashValuesToDatabaseProgressDialog.showErrorsButton.text=\u30a8\u30e9\u30fc\u3092\u8868\u793a -AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.parsing=MD5\u30cf\u30c3\u30b7\u30e5\u306b\u30c6\u30ad\u30b9\u30c8\u3092\u30d1\u30fc\u30b9\u4e2d... -AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.invalidHash=\u30a4\u30f3\u30d7\u30c3\u30c8\u306b\u7121\u52b9\u306a\u30cf\u30c3\u30b7\u30e5\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002 -AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.invaliHash.msg=\u7121\u52b9\u306a\u30cf\u30c3\u30b7\u30e5\uff1a +AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.parsing=MD5\u30cf\u30c3\u30b7\u30e5\u306e\u30c6\u30ad\u30b9\u30c8\u306e\u89e3\u6790\u4e2d\u3067\u3059... +AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.invalidHash=\u5165\u529b\u306b\u7121\u52b9\u306a\u30cf\u30c3\u30b7\u30e5\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002 +AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.invaliHash.msg=\u7121\u52b9\u306a\u30cf\u30c3\u30b7\u30e5: AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.noHashesToAdd=\u8ffd\u52a0\u3059\u308b\u30cf\u30c3\u30b7\u30e5\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.success={0}\u30cf\u30c3\u30b7\u30e5\u304c\u6b63\u3057\u304f\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002 -AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.errorAddingValidHash=\u6709\u52b9\u306a\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.errorAddingValidHash.msg=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u6709\u52b9\u306a\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -HashLookupSettingsPanel.deleteDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u524a\u9664 -HashLookupSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u3067\u3059\u3002\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 -HashLookupSettingsPanel.addHashesToDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0 -HashLookupSettingsPanel.indexPathLabelLabel.text=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u30d1\u30b9\uff1a -HashLookupSettingsPanel.createDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f5c\u6210 -HashLookupSettingsPanel.informationLabel.text=\u60c5\u5831 -HashLookupSettingsPanel.sendIngestMessagesCheckBox.text=\u30d2\u30c3\u30c8\u6bce\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30a4\u30f3\u30dc\u30c3\u30af\u30b9\u306b\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u308b -HashLookupSettingsPanel.indexButton.text=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9 -HashLookupSettingsPanel.indexLabel.text=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u30b9\u30c6\u30fc\u30bf\u30b9\uff1a -HashLookupSettingsPanel.hashDbIndexStatusLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 -HashLookupSettingsPanel.hashDbTypeLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 -HashLookupSettingsPanel.typeLabel.text=\u30bf\u30a4\u30d7\uff1a -HashLookupSettingsPanel.locationLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\uff1a -HashLookupSettingsPanel.hashDbLocationLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 -HashLookupSettingsPanel.hashDbNameLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 -HashLookupSettingsPanel.nameLabel.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d\uff1a -HashLookupSettingsPanel.hashDatabasesLabel.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\uff1a -HashLookupSettingsPanel.importDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 -HashDbCreateDatabaseDialog.databasePathLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\uff1a +AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.success={0} \u30cf\u30c3\u30b7\u30e5\u304c\u6b63\u5e38\u306b\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002 +AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.errorAddingValidHash=\u6709\u52b9\u306a\u30cf\u30c3\u30b7\u30e5\u306e\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +AddHashValuesToDatabaseProgressDialog.addHashValuesToDatabase.errorAddingValidHash.msg=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u6709\u52b9\u306a\u30cf\u30c3\u30b7\u30e5\u306e\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: +HashLookupSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +HashLookupSettingsPanel.addHashesToDatabaseButton.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0 +HashLookupSettingsPanel.indexPathLabelLabel.text=\u7d22\u5f15\u30d1\u30b9: +HashLookupSettingsPanel.createDatabaseButton.toolTipText= +HashLookupSettingsPanel.createDatabaseButton.text=\u65b0\u898f\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8 +HashLookupSettingsPanel.informationLabel.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u8a73\u7d30 +HashLookupSettingsPanel.sendIngestMessagesCheckBox.text=\u30d2\u30c3\u30c8\u3054\u3068\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u53d7\u4fe1\u7bb1\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u4fe1 +HashLookupSettingsPanel.indexButton.text=\u7d22\u5f15 +HashLookupSettingsPanel.indexLabel.text=\u7d22\u5f15\u30b9\u30c6\u30fc\u30bf\u30b9: +HashLookupSettingsPanel.hashDbIndexStatusLabel.text= +HashLookupSettingsPanel.hashDbTypeLabel.text= +HashLookupSettingsPanel.typeLabel.text=\u30bf\u30a4\u30d7: +HashLookupSettingsPanel.locationLabel.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d1\u30b9: +HashLookupSettingsPanel.hashDbLocationLabel.text= +HashLookupSettingsPanel.hashDbNameLabel.text= +HashLookupSettingsPanel.nameLabel.text=\u540d\u524d: +HashLookupSettingsPanel.hashDatabasesLabel.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8: +HashLookupSettingsPanel.importDatabaseButton.toolTipText= +HashLookupSettingsPanel.importDatabaseButton.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 +HashLookupSettingsPanel.deleteDatabaseButton.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u524a\u9664 +ImportHashDatabaseDialog.lbFilePath.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d1\u30b9: +ImportHashDatabaseDialog.tfDatabaseName.tooltip=\u3053\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u540d\u524d +ImportHashDatabaseDialog.tfDatabaseVersion.tooltip.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u756a\u53f7 +ImportHashDatabaseDialog.tfDatabaseName.tooltip=\u3053\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u540d\u524d +ImportHashDatabaseDialog.tfDatabaseVersion.tooltip.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u756a\u53f7 +ImportCentralRepoDbProgressDialog.lbProgress.text=\u30a4\u30f3\u30dd\u30fc\u30c8\u3092\u958b\u59cb\u3057\u307e\u3059... +ImportCentralRepoDbProgressDialog.bnOk.text=OK +ImportCentralRepoDbProgressDialog.bnCancel.text=\u53d6\u308a\u6d88\u3057 +HashLookupSettingsPanel.versionLabel.text_1=\u30d0\u30fc\u30b8\u30e7\u30f3: +HashLookupSettingsPanel.hashDbVersionLabel.text_1= +HashLookupSettingsPanel.orgLabel.text_1=\u7d44\u7e54: +HashLookupSettingsPanel.hashDbOrgLabel.text_1= +HashLookupSettingsPanel.readOnlyLabel.text_1=\u8aad\u307f\u53d6\u308a\u5c02\u7528: +HashLookupSettingsPanel.hashDbReadOnlyLabel.text_1= +ImportCentralRepoDbProgressDialog.jLabel1.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u306b\u30a4\u30f3\u30dd\u30fc\u30c8\u4e2d\u3067\u3059 +HashDbImportDatabaseDialog.lbVersion.text=\u30d0\u30fc\u30b8\u30e7\u30f3: +HashDbImportDatabaseDialog.lbOrg.text=\u30bd\u30fc\u30b9\u7d44\u7e54: +HashDbImportDatabaseDialog.readOnlyCheckbox.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u8aad\u307f\u53d6\u308a\u5c02\u7528\u306b\u3059\u308b +HashDbImportDatabaseDialog.orgButton.text=\u7d44\u7e54\u3092\u7ba1\u7406 +HashDbImportDatabaseDialog.versionTextField.text=1.0 +HashDbImportDatabaseDialog.fileTypeRadioButton.text=\u30ed\u30fc\u30ab\u30eb +HashDbImportDatabaseDialog.centralRepoRadioButton.text=\u30ea\u30e2\u30fc\u30c8(\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc) +HashDbImportDatabaseDialog.jLabel4.text=\u5b9b\u5148: +HashDbCreateDatabaseDialog.jLabel4.text=\u5b9b\u5148: +HashDbCreateDatabaseDialog.fileTypeRadioButton.text=\u30ed\u30fc\u30ab\u30eb +HashDbCreateDatabaseDialog.centralRepoRadioButton.text=\u30ea\u30e2\u30fc\u30c8(\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc) +HashDbCreateDatabaseDialog.lbOrg.text=\u30bd\u30fc\u30b9\u7d44\u7e54: +HashDbCreateDatabaseDialog.orgButton.text=\u7d44\u7e54\u3092\u7ba1\u7406 +HashDbCreateDatabaseDialog.databasePathLabel.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d1\u30b9: AddHashValuesToDatabaseDialog.okButton.text_2=OK -HashLookupSettingsPanel.indexPathLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u30e6\u30fc\u30b6\u30fc\u69cb\u6210\u30d5\u30a1\u30a4\u30eb\u306b\u30b3\u30d4\u30fc +HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText=\u30e9\u30a4\u30d6\u30c8\u30ea\u30a2\u30fc\u30b8\u306e\u72b6\u6cc1\u3067\u306f\u3001\u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u306b\u3088\u3063\u3066\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3078\u306e\u30d1\u30b9\u304c\u6709\u52b9\u3067\u3042\u308b\u3053\u3068\u304c\u4fdd\u8a3c\u3055\u308c\u307e\u3059\u3002 +HashLookupSettingsPanel.indexPathLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java index af27157fd8..98b2a27759 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java @@ -47,7 +47,7 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter { * * @return The module name. */ - static String getModuleName() { + public static String getModuleName() { return NbBundle.getMessage(HashLookupModuleFactory.class, "HashLookupModuleFactory.moduleName.text"); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/iOS/Bundle_ja.properties index bfccdf05e9..03cd3cc41e 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/Bundle_ja.properties @@ -1,5 +1,8 @@ -iOSModuleFactory.moduleDescription=\u30B7\u30B9\u30C6\u30E0\u304A\u3088\u3073\u7B2C\u4E09\u8005\u30A2\u30D7\u30EA\u30C7\u30FC\u30BF\u3092\u62BD\u51FA -iOSModuleFactory.moduleName=iOS\u30A2\u30CA\u30E9\u30A4\u30B6 -TextMessageAnalyzer.bbAttribute.incoming=\u53D7\u4FE1 -TextMessageAnalyzer.bbAttribute.outgoing=\u9001\u4FE1 -TextMessageAnalyzer.bbAttribute.smsMessage=SMS\u30E1\u30C3\u30BB\u30FC\u30B8 \ No newline at end of file +CallLogAnalyzer.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001\u901a\u8a71\u30ed\u30b0\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ContactAnalyzer.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001\u9023\u7d61\u5148\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +iOSModuleFactory.moduleName=iOS Analyzer +iOSModuleFactory.moduleDescription=\u30b7\u30b9\u30c6\u30e0\u3068\u30b5\u30fc\u30c9\u30d1\u30fc\u30c6\u30a3\u88fd\u30a2\u30d7\u30ea\u30c7\u30fc\u30bf\u3092\u62bd\u51fa +TextMessageAnalyzer.bbAttribute.incoming=\u53d7\u4fe1 +TextMessageAnalyzer.bbAttribute.outgoing=\u9001\u4fe1 +TextMessageAnalyzer.bbAttribute.smsMessage=SMS\u30e1\u30c3\u30bb\u30fc\u30b8 +TextMessageAnalyzer.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001\u30c6\u30ad\u30b9\u30c8\u30e1\u30c3\u30bb\u30fc\u30b8\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED index 1279d3642b..31a0690b82 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED @@ -83,8 +83,8 @@ FilesSetRulePanel.nameTextField.text= FilesSetRulePanel.ruleNameLabel.text=Rule Name (Optional): FilesSetRulePanel.messages.emptyNameCondition=You must specify a name pattern for this rule. FilesSetRulePanel.messages.invalidNameRegex=The name regular expression is not valid:\n\n{0} -FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *, ?, \", <, or > unless it is a regular expression. -FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, \", <, or > unless it is a regular expression. +FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *, ?, ", <, or > unless it is a regular expression. +FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, ", <, or > unless it is a regular expression. FilesSetRulePanel.messages.invalidPathRegex=The path regular expression is not valid:\n\n{0} FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text=Rule set with name {0} already exists. FilesSetRulePanel.pathSeparatorInfoLabel.text=Folder must be in parent path. Use '/' to give consecutive names diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle_ja.properties index 6f6d7f49cf..5a940ca1fd 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle_ja.properties @@ -1,51 +1,142 @@ -FilesIdentifierIngestJobSettingsPanel.border.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306b\u6709\u52b9\u306b\u3059\u308b\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u3092\u9078\u629e\uff1a -FilesSetPanel.descPanel.border.title=\u6982\u8981 -FilesSetPanel.descriptionPanel.border.title=\u6982\u8981 -FilesSetPanel.ignoreKnownFilesCheckbox.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\u3092\u7121\u8996 -FilesSetPanel.interesting.messages.filesSetsMustBeNamed=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u306f\u540d\u524d\u304c\u5fc5\u8981\u3067\u3059\u3002 -FilesSetPanel.interesting.nameLabel.text=\u30bb\u30c3\u30c8\u540d\uff1a -FilesSetPanel.title=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8 -FilesSetRulePanel.extensionRadioButton.text=\u62e1\u5f35\u5b50\u306e\u307f -FilesSetRulePanel.fullNameRadioButton.text=\u30d5\u30eb\u30cd\u30fc\u30e0 -FilesSetRulePanel.jLabel1.text=\u30bf\u30a4\u30d7*\uff1a -FilesSetRulePanel.messages.emptyNameCondition=\u3053\u306e\u30eb\u30fc\u30eb\u306f\u30cd\u30fc\u30e0\u30d1\u30bf\u30fc\u30f3\u3092\u7279\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 -FilesSetRulePanel.messages.invalidCharInName=\u6b63\u898f\u8868\u73fe\u4ee5\u5916\u306f\\\u3001/\u3001\:\u3001*\u3001?\u3001"\u3001<\u3001>\u3092\u540d\u524d\u306b\u542b\u3081\u307e\u305b\u3093\u3002 -FilesSetRulePanel.messages.invalidCharInPath=\u6b63\u898f\u8868\u73fe\u4ee5\u5916\u306f\\\u3001\:\u3001*\u3001?\u3001"\u3001<\u3001>\u3092\u30d1\u30b9\u306b\u542b\u3081\u307e\u305b\u3093\u3002 -FilesSetRulePanel.messages.invalidNameRegex=\u6b63\u898f\u8868\u73fe\u306f\u6709\u52b9\u306a\u540d\u524d\u3067\u306f\u3042\u308a\u307e\u305b\u3093\uff1a\n\n{0} -FilesSetRulePanel.messages.invalidPathRegex=\u6b63\u898f\u8868\u73fe\u306f\u6709\u52b9\u306a\u30d1\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\uff1a\n\n{0} -FilesSetRulePanel.nameRegexCheckbox.text=\u6b63\u898f\u8868\u73fe -FilesSetRulePanel.pathRegexCheckBox.text=\u6b63\u898f\u8868\u73fe -FilesSetRulePanel.pathSeparatorInfoLabel.text=/\u3092\u30d1\u30b9\u533a\u5207\u308a\u6587\u5b57\u3068\u3057\u3066\u5229\u7528 -FilesSetRulePanel.ruleNameLabel.text=\u30eb\u30fc\u30eb\u540d\uff1a -FilesSetRulePanel.title=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u30eb\u30fc\u30eb -InterestingItemsIdentifierIngestModule.moduleDescription=\u7591\u308f\u3057\u3044\u30a2\u30a4\u30c6\u30e0\u30eb\u30fc\u30eb\u30bb\u30c3\u30c8\u306e\u5b9a\u7fa9\u3092\u3082\u3068\u306b\u7591\u308f\u3057\u3044\u30a2\u30a4\u30c6\u30e0\u3092\u691c\u77e5\u3057\u307e\u3059\u3002 -InterestingItemsIdentifierIngestModule.moduleName=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u691c\u77e5 +FilesIdentifierIngestJobSettingsPanel.getError=\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +FilesIdentifierIngestJobSettingsPanel.updateError=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u306e\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3092\u66f4\u65b0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +FilesIdentifierIngestModule.getFilesError=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +FilesIdentifierIngestModule.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u306e\u30d2\u30c3\u30c8\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +# {0} - daysIncluded +FilesSet.rule.dateRule.toString=({0} \u65e5\u4ee5\u5185\u306b\u4fee\u6b63) +FilesSetDefsPanel.bytes=\u30d0\u30a4\u30c8 +FilesSetDefsPanel.cancelImportMsg=\u30a4\u30f3\u30dd\u30fc\u30c8\u3092\u53d6\u308a\u6d88\u3059 +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +FilesSetDefsPanel.exportButtonActionPerformed.fileExistPrompt=\u30d5\u30a1\u30a4\u30eb {0} \u304c\u5b58\u5728\u3057\u307e\u3059\u3002\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b? +FilesSetDefsPanel.gigaBytes=\u30ae\u30ac\u30d0\u30a4\u30c8 +# {0} - \u30d5\u30a3\u30eb\u30bf\u30fc\u540d +# {1} - \u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u540d +FilesSetDefsPanel.ingest.fileFilterInUseError=\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u30d5\u30a3\u30eb\u30bf\u30fc {0} \u3092\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb {1} \u304c\u4f7f\u7528\u4e2d\u3067\u3059\u3002\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u304c\u4f7f\u7528\u3057\u3066\u3044\u308b\u9593\u306f\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3002 +FilesSetDefsPanel.Ingest.Title=\u30d5\u30a1\u30a4\u30eb\u30d5\u30a3\u30eb\u30bf\u30fc\u8a2d\u5b9a +FilesSetDefsPanel.interesting.copySetButton.text=\u30bb\u30c3\u30c8\u3092\u30b3\u30d4\u30fc +FilesSetDefsPanel.interesting.exportButtonAction.featureName=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u306e\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +FilesSetDefsPanel.interesting.ExportedMsg=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u304c\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u307e\u3057\u305f +FilesSetDefsPanel.interesting.exportSetButton.text=\u30bb\u30c3\u30c8\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +FilesSetDefsPanel.interesting.failExportMsg=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u306e\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u306b\u5931\u6557\u3057\u307e\u3057\u305f +FilesSetDefsPanel.interesting.failImportMsg=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u304c\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +FilesSetDefsPanel.interesting.fileExtensionFilterLbl=Autopsy\u306e\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u30d5\u30a1\u30a4\u30eb(xml) +FilesSetDefsPanel.interesting.importButtonAction.featureName=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u306e\u30a4\u30f3\u30dd\u30fc\u30c8 +FilesSetDefsPanel.interesting.importOwConflict=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u306e\u30b3\u30f3\u30d5\u30ea\u30af\u30c8\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 +FilesSetDefsPanel.interesting.importSetButton.text=\u30bb\u30c3\u30c8\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 +# {0} - \u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u540d +FilesSetDefsPanel.interesting.overwriteSetPrompt=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8 <{0}> \u304c\u3059\u3067\u306b\u30ed\u30fc\u30ab\u30eb\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b? +FilesSetDefsPanel.Interesting.Title=\u8208\u5473\u6df1\u3044\u9805\u76ee\u306e\u30b0\u30ed\u30fc\u30d0\u30eb\u8a2d\u5b9a +FilesSetDefsPanel.kiloBytes=\u30ad\u30ed\u30d0\u30a4\u30c8 +FilesSetDefsPanel.loadError=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +FilesSetDefsPanel.megaBytes=\u30e1\u30ac\u30d0\u30a4\u30c8 +FilesSetDefsPanel.noSkipMsg=\u3044\u3044\u3048\u3001\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059 +FilesSetDefsPanel.saveError=\u30d5\u30a1\u30a4\u30eb\u306b\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u3092\u4fdd\u5b58\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +FilesSetDefsPanel.yesOwMsg=\u306f\u3044\u3001\u4e0a\u66f8\u304d\u3057\u307e\u3059 +FilesSetPanel.filter.title=\u30d5\u30a1\u30a4\u30eb\u30d5\u30a3\u30eb\u30bf\u30fc +FilesSetPanel.ingest.createNewFilter=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u4f5c\u6210/\u7de8\u96c6... +FilesSetPanel.ingest.messages.filtersMustBeNamed=\u30d5\u30a1\u30a4\u30eb\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d5\u30a3\u30eb\u30bf\u30fc\u306b\u540d\u524d\u3092\u4ed8\u3051\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +FilesSetPanel.rule.title=\u30d5\u30a1\u30a4\u30eb\u30d5\u30a3\u30eb\u30bf\u30fc\u30eb\u30fc\u30eb +FilesSetRulePanel.bytes=\u30d0\u30a4\u30c8 +# {0} - \u6b63\u898f\u8868\u73fe +FilesSetRulePanel.CommaInRegexWarning=\u8b66\u544a: \u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u30ab\u30f3\u30de\u306f\u6b63\u898f\u8868\u73fe\u306e\u4e00\u90e8\u3068\u3057\u3066\u89e3\u91c8\u3055\u308c\u3001\u8907\u6570\u306e\u62e1\u5f35\u5b50 (\u5165\u529b\u6570: "{0}") \u306b\u5165\u529b\u3092\u5206\u5272\u3057\u307e\u305b\u3093\u3002 +FilesSetRulePanel.DaysIncludedEmptyError=\u542b\u307e\u308c\u308b\u65e5\u6570\u306f\u7a7a(\u672a\u5165\u529b)\u306b\u3067\u304d\u307e\u305b\u3093\u3002 +FilesSetRulePanel.DaysIncludedEmptyError=\u542b\u307e\u308c\u308b\u65e5\u6570\u306f\u6b63\u306e\u6574\u6570\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +FilesSetRulePanel.gigaBytes=\u30ae\u30ac\u30d0\u30a4\u30c8 +FilesSetRulePanel.kiloBytes=\u30ad\u30ed\u30d0\u30a4\u30c8 +FilesSetRulePanel.megaBytes=\u30e1\u30ac\u30d0\u30a4\u30c8 +FilesSetRulePanel.nameTextField.extensionExample=\u4f8b: "jpg" \u307e\u305f\u306f "jpg,jpeg,gif" +FilesSetRulePanel.nameTextField.fullNameExample=\u4f8b: "file.exe" +FilesSetRulePanel.NoConditionError=\u30eb\u30fc\u30eb\u3092\u4f5c\u308b\u305f\u3081\u306e\u6761\u4ef6\u304c\u5c11\u306a\u304f\u3068\u30821\u3064\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +FilesSetRulePanel.NoMimeTypeError=\u6709\u52b9\u306aMIME\u30bf\u30a4\u30d7\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +FilesSetRulePanel.NoNameError=\u540d\u524d\u306f\u7a7a(\u672a\u5165\u529b)\u306b\u3067\u304d\u307e\u305b\u3093 +FilesSetRulePanel.NoPathError=\u30d1\u30b9\u306f\u7a7a(\u672a\u5165\u529b)\u306b\u3067\u304d\u307e\u305b\u3093 +FilesSetRulePanel.ZeroFileSizeError=\u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba\u306e\u6761\u4ef6\u5024\u306f0\u306b\u3067\u304d\u307e\u305b\u3093(= \u3092\u9078\u629e\u3059\u308b\u5834\u5408\u3092\u9664\u304f)\u3002 +FilesSetsManager.allFilesAndDirectories=\u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u3068\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc(\u672a\u4f7f\u7528\u9818\u57df\u4ee5\u5916) +FilesSetsManager.allFilesDirectoriesAndUnallocated=\u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u3001\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3001\u672a\u4f7f\u7528\u9818\u57df +InterestingItemsIngestModuleFactory.defaultSettingsError=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 OpenIDE-Module-Display-Category=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb -OpenIDE-Module-Long-Description=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u691c\u77e5\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3002\n\n\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u30eb\u30fc\u30eb\u30bb\u30c3\u30c8\u306e\u5b9a\u7fa9\u3092\u3082\u3068\u306b\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u77e5\u3057\u307e\u3059\u3002 -OpenIDE-Module-Name=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u691c\u77e5 -OpenIDE-Module-Short-Description=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u691c\u77e5\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb -OptionsCategory_Name_InterestingItemDefinitions=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb -OptionsCategory_Keywords_InterestingItemDefinitions=\u7591\u308f\u3057\u3044\u30a2\u30a4\u30c6\u30e0\u5b9a\u7fa9 -FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text=\u540d\u524d\u304c{0}\u306e\u30eb\u30fc\u30eb\u30bb\u30c3\u30c8\u306f\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 -FilesSetRulePanel.interesting.jLabel5.text=\u898b\u3064\u3051\u305f\u3044\u30d5\u30a1\u30a4\u30eb\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -FilesSetDefsPanel.interesting.setsListLabel.text=\u30eb\u30fc\u30eb\u30bb\u30c3\u30c8 +OpenIDE-Module-Long-Description=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u8b58\u5225\u5b50\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3067\u3059\u3002\n\n \u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u306e\u30eb\u30fc\u30eb\u30bb\u30c3\u30c8\u3067\u5b9a\u3081\u308b\u3068\u304a\u308a\u306b\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u3092\u8b58\u5225\u3057\u307e\u3059\u3002 +OpenIDE-Module-Short-Description=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u8b58\u5225\u5b50\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3067\u3059\u3002 +OpenIDE-Module-Name=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u8b58\u5225\u5b50 +OptionsCategory_Name_InterestingItemDefinitions=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb +OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions +InterestingItemsIdentifierIngestModule.moduleName=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u8b58\u5225\u5b50 +InterestingItemsIdentifierIngestModule.moduleDescription=\u8208\u5473\u6df1\u3044\u9805\u76ee\u306e\u30eb\u30fc\u30eb\u30bb\u30c3\u30c8\u3067\u5b9a\u3081\u308b\u3068\u304a\u308a\u306b\u8208\u5473\u6df1\u3044\u9805\u76ee\u3092\u8b58\u5225\u3057\u307e\u3059\u3002 +FilesSetPanel.interesting.title=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u306e\u30eb\u30fc\u30eb +FilesSetPanel.interesting.messages.filesSetsMustBeNamed=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u306e\u540d\u524d\u3092\u4ed8\u3051\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +FilesSetPanel.messages.filesSetsReservedName=\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u304c\u30ea\u30b6\u30fc\u30d6\u3059\u308b\u540d\u524d\u3092\u9078\u629e\u3057\u307e\u3057\u305f\u3002\u5225\u306e\u540d\u524d\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +FilesSetPanel.ignoreKnownFilesCheckbox.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\u3092\u7121\u8996 +FilesSetPanel.descriptionPanel.border.title=\u8aac\u660e +FilesSetPanel.interesting.nameLabel.text=\u30bb\u30c3\u30c8\u540d: +FilesSetPanel.ingest.nameLabel.text=\u30d5\u30a3\u30eb\u30bf\u30fc\u540d: +FilesSetPanel.descPanel.border.title=\u8aac\u660e +FilesSetPanel.ignoreUnallocCheckbox.toolTipText=\u524a\u9664\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306a\u3069\u3001\u672a\u4f7f\u7528\u9818\u57df\u3092\u7121\u8996\u3057\u307e\u3059\u3002\u5b9f\u884c\u901f\u5ea6\u304c\u901f\u304f\u306a\u308a\u3001\u3088\u308a\u5b8c\u5168\u3067\u306f\u306a\u3044\u7d50\u679c\u304c\u751f\u6210\u3055\u308c\u307e\u3059\u3002 +FilesSetPanel.ignoreUnallocCheckbox.text=\u672a\u4f7f\u7528\u9818\u57df\u3092\u7121\u8996 +FilesSetRulePanel.title=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u306e\u30eb\u30fc\u30eb +FilesSetRulePanel.extensionRadioButton.text=\u62e1\u5f35\u5b50\u306e\u307f +FilesSetRulePanel.pathRegexCheckBox.text=\u6b63\u898f\u8868\u73fe +FilesSetRulePanel.pathTextField.text= +FilesSetRulePanel.fullNameRadioButton.text=\u5b8c\u5168\u306a\u540d\u524d +FilesSetRulePanel.nameRegexCheckbox.text=\u5f93\u5c5e\u6587\u5b57\u5217 / \u6b63\u898f\u8868\u73fe +FilesSetRulePanel.ruleNameTextField.text= +FilesSetRulePanel.nameTextField.text= +FilesSetRulePanel.ruleNameLabel.text=\u30eb\u30fc\u30eb\u540d(\u4efb\u610f): +FilesSetRulePanel.messages.emptyNameCondition=\u3053\u306e\u30eb\u30fc\u30eb\u306e\u540d\u524d\u30d1\u30bf\u30fc\u30f3\u3092\u6307\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +FilesSetRulePanel.messages.invalidNameRegex=\u540d\u524d\u306e\u6b63\u898f\u8868\u73fe\u304c\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093:\n\n{0} +FilesSetRulePanel.messages.invalidCharInName=\u540d\u524d\u306b\u306f \\\u3001/\u3001 :\u3001*\u3001?\u3001\"\u3001 <\u3001> \u3092\u542b\u3081\u3089\u308c\u307e\u305b\u3093(\u6b63\u898f\u8868\u73fe\u306e\u5834\u5408\u3092\u9664\u304f)\u3002 +FilesSetRulePanel.messages.invalidCharInPath=\u30d1\u30b9\u306b\u306f \\\u3001/\u3001 :\u3001*\u3001?\u3001\"\u3001 <\u3001> \u3092\u542b\u3081\u3089\u308c\u307e\u305b\u3093(\u6b63\u898f\u8868\u73fe\u306e\u5834\u5408\u3092\u9664\u304f)\u3002 +FilesSetRulePanel.messages.invalidPathRegex=\u30d1\u30b9\u306e\u6b63\u898f\u8868\u73fe\u304c\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093:\n\n{0} +FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text= {0} \u306e\u540d\u524d\u306e\u30eb\u30fc\u30eb\u30bb\u30c3\u30c8\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 +FilesSetRulePanel.pathSeparatorInfoLabel.text=\u30d5\u30a9\u30eb\u30c0\u30fc\u304c\u89aa\u30d1\u30b9\u306b\u542b\u307e\u308c\u3066\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002'/\u2019 \u3092\u4f7f\u7528\u3057\u3066\u9023\u7d9a\u3059\u308b\u540d\u524d\u3092\u53d6\u5f97 +FilesIdentifierIngestJobSettingsPanel.border.title=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306b\u6709\u52b9\u5316\u3059\u308b\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u3092\u9078\u629e: +FilesSetRulePanel.jLabel1.text=\u30bf\u30a4\u30d7: +FilesSetRulePanel.interesting.jLabel5.text=\u691c\u7d22\u3057\u305f\u3044\u30d5\u30a1\u30a4\u30eb\u306b\u95a2\u3059\u308b\u60c5\u5831\u3092\u5165\u529b\u3057\u307e\u3059\u3002 +FilesSetRulePanel.ingest.jLabel5.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u3057\u305f\u3044\u30d5\u30a1\u30a4\u30eb\u306b\u95a2\u3059\u308b\u60c5\u5831\u3092\u5165\u529b\u3057\u307e\u3059\u3002 +FilesSetRulePanel.nameCheck.text=\u540d\u524d: +FilesSetRulePanel.pathCheck.text=\u30d5\u30a9\u30eb\u30c0\u30fc\u540d: +FilesSetRulePanel.filesRadioButton.text=\u30d5\u30a1\u30a4\u30eb +FilesSetRulePanel.dirsRadioButton.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc +FilesSetDefsPanel.interesting.setsListLabel.text=\u30eb\u30fc\u30eb\u30bb\u30c3\u30c8: +FilesSetDefsPanel.ingest.setsListLabel.text=\u30d5\u30a1\u30a4\u30eb\u30d5\u30a3\u30eb\u30bf\u30fc: +FilesSetDefsPanel.interesting.jTextArea1.text=\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u6307\u5b9a\u3057\u305f\u6761\u4ef6\u3068\u4e00\u81f4\u3059\u308b\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22\u3067\u304d\u307e\u3059\u3002\u5404\u30bb\u30c3\u30c8\u306b\u3001\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u306e\u7279\u5fb4\u3068\u4e00\u81f4\u3059\u308b\u30eb\u30fc\u30eb\u30ea\u30b9\u30c8\u304c\u7528\u610f\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u30d5\u30a1\u30a4\u30eb\u306f\u691c\u7d22\u5bfe\u8c61\u3068\u306a\u308b1\u3064\u306e\u30eb\u30fc\u30eb\u3068\u4e00\u81f4\u3059\u308b\u3060\u3051\u3067\u5341\u5206\u3067\u3059\u3002 +FilesSetDefsPanel.ingest.jTextArea1.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u306e\u30b5\u30d6\u30bb\u30c3\u30c8\u306e\u307f\u3092\u5206\u6790\u3059\u308b\u3088\u3046\u30eb\u30fc\u30eb\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002\u30eb\u30fc\u30eb\u3092\u30bb\u30c3\u30c8\u306b\u69cb\u9020\u5316\u3057\u307e\u3059\u3002\u4e00\u5ea6\u306b1\u3064\u306e\u30bb\u30c3\u30c8\u306e\u307f\u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002\u30d5\u30a1\u30a4\u30eb\u306f\u5206\u6790\u5bfe\u8c61\u3068\u306a\u308b1\u3064\u306e\u30eb\u30fc\u30eb\u3068\u4e00\u81f4\u3059\u308b\u3060\u3051\u3067\u5341\u5206\u3067\u3059\u3002 FilesSetDefsPanel.interesting.editSetButton.text=\u30bb\u30c3\u30c8\u3092\u7de8\u96c6 +FilesSetDefsPanel.ingest.editSetButton.text=\u7de8\u96c6\u30d5\u30a3\u30eb\u30bf\u30fc FilesSetDefsPanel.interesting.newSetButton.text=\u65b0\u898f\u30bb\u30c3\u30c8 +FilesSetDefsPanel.ingest.newSetButton.text=\u65b0\u898f\u30d5\u30a3\u30eb\u30bf\u30fc FilesSetDefsPanel.interesting.deleteSetButton.text=\u30bb\u30c3\u30c8\u3092\u524a\u9664 +FilesSetDefsPanel.ingest.deleteSetButton.text=\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u524a\u9664 +FilesSetDefsPanel.interesting.jLabel6.text=\u8a73\u7d30\u3092\u8a2d\u5b9a +FilesSetDefsPanel.ingest.jLabel6.text=\u30d5\u30a3\u30eb\u30bf\u30fc\u8a73\u7d30 FilesSetDefsPanel.newRuleButton.text=\u65b0\u898f\u30eb\u30fc\u30eb +FilesSetDefsPanel.jLabel8.text=\u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba: +FilesSetDefsPanel.jLabel7.text=MIME\u30bf\u30a4\u30d7: FilesSetDefsPanel.rulePathConditionRegexCheckBox.text=\u6b63\u898f\u8868\u73fe -FilesSetDefsPanel.jLabel4.text=\u30d1\u30b9\u30d1\u30bf\u30fc\u30f3\uff1a +FilesSetDefsPanel.jLabel4.text=\u30d1\u30b9\u306e\u5f93\u5c5e\u6587\u5b57\u5217: FilesSetDefsPanel.jLabel1.text=\u30eb\u30fc\u30eb\u8a73\u7d30 -FilesSetDefsPanel.dirsRadioButton.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea -FilesSetDefsPanel.jLabel2.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\uff1a +FilesSetDefsPanel.dirsRadioButton.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc +FilesSetDefsPanel.jLabel2.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7: FilesSetDefsPanel.deleteRuleButton.text=\u30eb\u30fc\u30eb\u3092\u524a\u9664 -FilesSetDefsPanel.fileNameRegexCheckbox.text=\u6b63\u898f\u8868\u73fe +FilesSetDefsPanel.fileNameRegexCheckbox.text=\u5f93\u5c5e\u6587\u5b57\u5217 / \u6b63\u898f\u8868\u73fe FilesSetDefsPanel.ignoreKnownFilesCheckbox.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\u3092\u7121\u8996 -FilesSetDefsPanel.fileNameRadioButton.text=\u30d5\u30a1\u30a4\u30eb\u540d -FilesSetDefsPanel.jLabel5.text=\u6982\u8981\uff1a -FilesSetDefsPanel.jLabel3.text=\u30cd\u30fc\u30e0\u30d1\u30bf\u30fc\u30f3 +FilesSetDefsPanel.rulePathConditionTextField.text= +FilesSetDefsPanel.fileNameRadioButton.text=\u5b8c\u5168\u306a\u540d\u524d +FilesSetDefsPanel.jLabel5.text=\u8aac\u660e: +FilesSetDefsPanel.fileNameTextField.text= +FilesSetDefsPanel.jLabel3.text=\u540d\u524d: FilesSetDefsPanel.fileNameExtensionRadioButton.text=\u62e1\u5f35\u5b50\u306e\u307f -FilesSetDefsPanel.rulesListLabel.text=\u30eb\u30fc\u30eb\uff1a +FilesSetDefsPanel.rulesListLabel.text=\u30eb\u30fc\u30eb: FilesSetDefsPanel.editRuleButton.text=\u30eb\u30fc\u30eb\u3092\u7de8\u96c6 FilesSetDefsPanel.filesRadioButton.text=\u30d5\u30a1\u30a4\u30eb -FilesSetDefsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u3067\u3059\u3002\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +FilesSetRulePanel.allRadioButton.text=\u3059\u3079\u3066 +FilesSetDefsPanel.ingoreUnallocCheckbox.text=\u672a\u4f7f\u7528\u9818\u57df\u3092\u7121\u8996 +FilesSetDefsPanel.ingoreUnallocCheckbox.toolTipText=\u524a\u9664\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306a\u3069\u3001\u672a\u4f7f\u7528\u9818\u57df\u3092\u7121\u8996\u3057\u307e\u3059\u3002\u5b9f\u884c\u901f\u5ea6\u304c\u901f\u304f\u306a\u308a\u3001\u3088\u308a\u5b8c\u5168\u3067\u306f\u306a\u3044\u7d50\u679c\u304c\u751f\u6210\u3055\u308c\u307e\u3059\u3002 +FilesSetDefsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +FilesSetDefsPanel.allRadioButton.text=\u3059\u3079\u3066 +FilesSetRulePanel.dateCheck.text=\u6b21\u306e\u4e2d\u3067\u4fee\u6b63\u6e08\u307f: +FilesSetRulePanel.fileSizeCheck.text=\u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba: +FilesSetRulePanel.mimeCheck.text=MIME\u30bf\u30a4\u30d7: +FilesSetDefsPanel.modifiedDateLabel.text=\u6b21\u306e\u4e2d\u3067\u4fee\u6b63\u6e08\u307f: +FilesSetDefsPanel.daysIncludedTextField.text= +FilesSetDefsPanel.daysIncludedLabel.text=\u65e5 +FilesSetRulePanel.daysIncludedLabel.text=\u65e5 diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED index 87dacfc16c..2dc971a40d 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED @@ -21,7 +21,7 @@ PhotoRecIngestModule.complete.totalParsetime=Total Parsing Time: PhotoRecIngestModule.complete.photoRecResults=PhotoRec Results PhotoRecIngestModule.NotEnoughDiskSpace.detail.msg=PhotoRec error processing {0} with {1} Not enough space on primary disk to save unallocated space. PhotoRecIngestModule.cancelledByUser=PhotoRec cancelled by user. -PhotoRecIngestModule.error.exitValue=PhotoRec carver returned error exit value \= {0} when scanning {1} +PhotoRecIngestModule.error.exitValue=PhotoRec carver returned error exit value = {0} when scanning {1} PhotoRecIngestModule.error.msg=Error processing {0} with PhotoRec carver. PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving: PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settings diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle_ja.properties index d97b6ae613..864c9c27f4 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle_ja.properties @@ -1,25 +1,30 @@ -moduleDescription.text=\u30b7\u30b9\u30c6\u30e0\u306e\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u306b\u5bfe\u3057\u3066PhotoRec\u30ab\u30fc\u30d0\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002 -moduleDisplayName.text=PhotoRec\u30ab\u30fc\u30d0 +# {0} - \u51fa\u529b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u540d +cannotCreateOutputDir.message=\u51fa\u529b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093: {0}\u3002 +cannotRunExecutable.message=PhotoRec\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 +missingExecutable.message=\u5b9f\u884c\u53ef\u80fd\u306aPhotoRec\u3092\u898b\u3064\u3051\u3089\u308c\u307e\u305b\u3093\u3002 +OpenIDE-Module-Name=PhotoRec Carver\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb OpenIDE-Module-Display-Category=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb -OpenIDE-Module-Long-Description=PhotoRec\u30ab\u30fc\u30d0\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3002\n\n\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u3092\u5207\u308a\u51fa\u3057\u3001\u5207\u308a\u51fa\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u3059\u308b\u3081\u306b\u30b7\u30b9\u30c6\u30e0\u3078\u30d5\u30a3\u30fc\u30c9\u3057\u307e\u3059\u3002 -OpenIDE-Module-Name=PhotoRec\u30ab\u30fc\u30d0\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb -OpenIDE-Module-Short-Description=\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u3092\u5207\u308a\u51fa\u3057\u3001\u5207\u308a\u51fa\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u306e\u305f\u3081\u306b\u30b7\u30b9\u30c6\u30e0\u3078\u30d5\u30a3\u30fc\u30c9\u3057\u307e\u3059\u3002 -unallocatedSpaceProcessingSettingsError.message=\u300c\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u3092\u51e6\u7406\u300d\u304c\u30c1\u30a7\u30c3\u30af\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002PhotoRec\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u3092\u30ab\u30fc\u30d6\u3059\u308b\u3088\u3046\u306b\u8a2d\u8a08\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u306e\u51e6\u7406\u3092\u6709\u52b9\u306b\u3059\u308b\u304b\u3001\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u7121\u52b9\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -missingExecutable.message=PhotoRec\u306e\u5b9f\u884c\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -cannotRunExecutable.message=PhotoRec\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -cannotCreateOutputDir.message=\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea{0}\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -PhotoRecIngestModule.processTerminated=\u30b9\u30ad\u30e3\u30f3\u306b\u304b\u3051\u308c\u308b\u6700\u5927\u306e\u6642\u9593\u304c\u904e\u304e\u305f\u306e\u3067\u3001PhotoRec\u30ab\u30fc\u30d0\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u505c\u6b62\u3057\u307e\u3057\u305f -PhotoRecIngestModule.moduleError=PhotoRec\u30ab\u30fc\u30d0\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -PhotoRecIngestModule.UnableToCarve=\u6b21\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u30ab\u30fc\u30d6\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} -PhotoRecIngestModule.NotEnoughDiskSpace=\u672a\u5272\u308a\u5f53\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u4fdd\u5b58\u3059\u308b\u306e\u306b\u30c7\u30a3\u30b9\u30af\u30b9\u30da\u30fc\u30b9\u304c\u8db3\u308a\u307e\u305b\u3093\u3002\u30ab\u30fc\u30d3\u30f3\u30b0\u304c\u30b9\u30ad\u30c3\u30d7\u3055\u308c\u307e\u3059\u3002 -PhotoRecIngestModule.complete.numberOfCarved=\u30ab\u30fc\u30d6\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u6570\uff1a -PhotoRecIngestModule.complete.totalWritetime=\u30c7\u30a3\u30b9\u30af\u3078\u306e\u66f8\u304d\u8fbc\u307f\u6642\u9593\u306e\u5408\u8a08 -PhotoRecIngestModule.complete.totalParsetime=\u30d1\u30fc\u30b7\u30f3\u30b0\u6642\u9593\u306e\u5408\u8a08\uff1a +OpenIDE-Module-Long-Description=PhotoRec Carver\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3067\u3059\u3002\n\n \u672a\u4f7f\u7528\u9818\u57df\u3092\u30ab\u30fc\u30d3\u30f3\u30b0\u3057\u3001\u7d50\u679c\u3068\u3057\u3066\u30ab\u30fc\u30d3\u30f3\u30b0\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u30b7\u30b9\u30c6\u30e0\u306b\u623b\u3057\u3066\u51e6\u7406\u3057\u307e\u3059\u3002 +OpenIDE-Module-Short-Description=\u672a\u4f7f\u7528\u9818\u57df\u3092\u30ab\u30fc\u30d3\u30f3\u30b0\u3057\u3068\u3057\u3066\u30ab\u30fc\u30d3\u30f3\u30b0\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u30b7\u30b9\u30c6\u30e0\u306b\u623b\u3057\u3066\u51e6\u7406\u3057\u307e\u3059\u3002 +moduleDisplayName.text=PhotoRec Carver +moduleDescription.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5185\u306e\u672a\u4f7f\u7528\u9818\u57df\u306b\u5bfe\u3057\u3066PhotoRec Carver\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002 +PhotoRecIngestModule.nonHostnameUNCPathUsed=PhotoRec\u306f\u3001IP\u30a2\u30c9\u30ec\u30b9\u3092\u542b\u3080UNC\u30d1\u30b9\u3092\u4f7f\u3063\u3066\u64cd\u4f5c\u3067\u304d\u307e\u305b\u3093\u3002 +PhotoRecIngestModule.PermissionsNotSufficient=\u30a2\u30af\u30bb\u30b9\u3059\u308b\u305f\u3081\u306b\u5341\u5206\u306a\u6a29\u9650\u304c\u3042\u308a\u307e\u305b\u3093 +PhotoRecIngestModule.PermissionsNotSufficientSeeReference=Autopsy\u30d8\u30eb\u30d7\u306e\u300c\u5171\u6709\u30c9\u30e9\u30a4\u30d6\u8a8d\u8a3c\u300d\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +PhotoRecIngestModule.processTerminated=\u30b9\u30ad\u30e3\u30f3\u6642\u306e\u6700\u5927\u8a31\u5bb9\u5b9f\u884c\u56de\u6570\u3092\u8d85\u3048\u305f\u305f\u3081\u3001PhotoRec Carver\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u7d42\u4e86\u3057\u307e\u3057\u305f +PhotoRecIngestModule.moduleError=PhotoRec Carver\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc +PhotoRecIngestModule.UnableToCarve=\u6b21\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u30ab\u30fc\u30d3\u30f3\u30b0\u3067\u304d\u307e\u305b\u3093: {0} +PhotoRecIngestModule.NotEnoughDiskSpace=\u672a\u4f7f\u7528\u30d5\u30a1\u30a4\u30eb\u3092\u4fdd\u5b58\u3059\u308b\u305f\u3081\u306e\u5341\u5206\u306a\u30c7\u30a3\u30b9\u30af\u9818\u57df\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u30ab\u30fc\u30d3\u30f3\u30b0\u304c\u30b9\u30ad\u30c3\u30d7\u3055\u308c\u307e\u3057\u305f\u3002 +PhotoRecIngestModule.complete.numberOfCarved=\u30ab\u30fc\u30d3\u30f3\u30b0\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u6570: +PhotoRecIngestModule.complete.totalWritetime=\u30c7\u30a3\u30b9\u30af\u3078\u306e\u66f8\u304d\u8fbc\u307f\u306b\u8981\u3059\u308b\u5408\u8a08\u6642\u9593: +PhotoRecIngestModule.complete.totalParsetime=\u5408\u8a08\u89e3\u6790\u6642\u9593: PhotoRecIngestModule.complete.photoRecResults=PhotoRec\u7d50\u679c -PhotoRecIngestModule.NotEnoughDiskSpace.detail.msg={0}\u3092{1}\u3067\u51e6\u7406\u3059\u308b\u969b\u306bPhotoRec\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u672a\u5272\u308a\u5f53\u3066\u30b9\u30da\u30fc\u30b9\u3092\u30d7\u30e9\u30a4\u30de\u30ea\u30fc\u30c7\u30a3\u30b9\u30af\u306b\u4fdd\u5b58\u3059\u308b\u306e\u306b\u30b9\u30da\u30fc\u30b9\u304c\u8db3\u308a\u307e\u305b\u3093\u3002 -PhotoRecIngestModule.cancelledByUser=PhotoRec\u304c\u30e6\u30fc\u30b6\u30fc\u306b\u3088\u308a\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u307e\u3057\u305f\u3002 -PhotoRecIngestModule.error.exitValue=PhotoRec\u304c\u30a8\u30e9\u30fc\u3092\u51fa\u3057\u307e\u3057\u305f\u3002Exit\u30d0\u30ea\u30e5\u30fc\uff1d{1}\u3092\u30b9\u30ad\u30e3\u30f3\u4e2d\u306b{0} -PhotoRecIngestModule.error.msg=PhotoRec\u30ab\u30fc\u30d0\u3067{0}\u3092\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -PhotoRecIngestModule.complete.numberOfErrors=\u30ab\u30fc\u30d3\u30f3\u30b0\u4e2d\u306e\u30a8\u30e9\u30fc\u6570\uff1a -PhotoRecIngestModule.PermissionsNotSufficient=\u30d1\u30fc\u30df\u30c3\u30b7\u30e7\u30f3\u306e\u30a2\u30af\u30bb\u30b9\u304c\u4e0d\u5341\u5206\u3067\u3059 -PhotoRecIngestModule.PermissionsNotSufficientSeeReference=Autopsy\u30d8\u30eb\u30d7\u3067\u300c\u5171\u6709\u30c9\u30e9\u30a4\u30d6\u8a8d\u8a3c\u300d\u3092\u3054\u89a7\u304f\u3060\u3055\u3044\u3002 \ No newline at end of file +PhotoRecIngestModule.NotEnoughDiskSpace.detail.msg={1} \u3092\u4f7f\u3063\u3066 {0} \u3092\u51e6\u7406\u4e2d\u306bPhotoRec\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u30021\u6b21\u30c7\u30a3\u30b9\u30af\u306b\u672a\u4f7f\u7528\u30d5\u30a1\u30a4\u30eb\u3092\u4fdd\u5b58\u3059\u308b\u305f\u3081\u306e\u5341\u5206\u306a\u9818\u57df\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +PhotoRecIngestModule.cancelledByUser=PhotoRec\u304c\u30e6\u30fc\u30b6\u30fc\u306b\u3088\u3063\u3066\u53d6\u308a\u6d88\u3055\u308c\u307e\u3057\u305f\u3002 +PhotoRecIngestModule.error.exitValue={1} \u3092\u30b9\u30ad\u30e3\u30f3\u4e2d\u306bPhotoRec Carver\u304c \u9593\u9055\u3063\u305f\u7d42\u4e86\u5024 \= {0} \u3092\u8fd4\u3057\u307e\u3057\u305f +PhotoRecIngestModule.error.msg=PhotoRec Carver\u3067 {0} \u3092\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +PhotoRecIngestModule.complete.numberOfErrors=\u30ab\u30fc\u30d3\u30f3\u30b0\u4e2d\u306e\u30a8\u30e9\u30fc\u6570: +PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec\u8a2d\u5b9a +PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text=\u7834\u640d\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u7dad\u6301 +unallocatedSpaceProcessingSettingsError.message=\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d5\u30a3\u30eb\u30bf\u30fc\u304c\u672a\u4f7f\u7528\u9818\u57df\u3092\u7121\u8996\u3057\u307e\u3059\u3002\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u672a\u4f7f\u7528\u9818\u57df\u3092\u30ab\u30fc\u30d3\u30f3\u30b0\u3057\u307e\u3059\u3002\u672a\u4f7f\u7528\u9818\u57df\u3092\u7121\u8996\u3057\u306a\u3044\u304b\u3001\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u7121\u52b9\u5316\u3057\u306a\u3044\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +unsupportedOS.message=PhotoRec\u30e2\u30b8\u30e5\u30fc\u30eb\u306fWindows\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u306e\u307f\u3067\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java index c9fad6c1bc..d359332f84 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java @@ -85,7 +85,7 @@ public class PlasoIngestModule implements DataSourceIngestModule { private static final int LOG2TIMELINE_WORKERS = 2; private static final long TERMINATION_CHECK_INTERVAL = 5; private static final TimeUnit TERMINATION_CHECK_INTERVAL_UNITS = TimeUnit.SECONDS; - + private File log2TimeLineExecutable; private File psortExecutable; @@ -103,8 +103,7 @@ public class PlasoIngestModule implements DataSourceIngestModule { @NbBundle.Messages({ "PlasoIngestModule.executable.not.found=Plaso Executable Not Found.", - "PlasoIngestModule.requires.windows=Plaso module requires windows.", - "PlasoIngestModule.dataSource.not.an.image=Datasource is not an Image."}) + "PlasoIngestModule.requires.windows=Plaso module requires windows."}) @Override public void startUp(IngestJobContext context) throws IngestModuleException { this.context = context; @@ -121,11 +120,6 @@ public class PlasoIngestModule implements DataSourceIngestModule { throw new IngestModuleException(Bundle.PlasoIngestModule_executable_not_found(), exception); } - Content dataSource = context.getDataSource(); - if (!(dataSource instanceof Image)) { - throw new IngestModuleException(Bundle.PlasoIngestModule_dataSource_not_an_image()); - } - image = (Image) dataSource; } @NbBundle.Messages({ @@ -138,80 +132,90 @@ public class PlasoIngestModule implements DataSourceIngestModule { "PlasoIngestModule.psort.cancelled=psort run was canceled", "PlasoIngestModule.bad.imageFile=Cannot find image file name and path", "PlasoIngestModule.completed=Plaso Processing Completed", - "PlasoIngestModule.has.run=Plaso Plugin has been run.", - "PlasoIngestModule.psort.fail=Plaso returned an error when sorting events. Results are not complete."}) + "PlasoIngestModule.has.run=Plaso", + "PlasoIngestModule.psort.fail=Plaso returned an error when sorting events. Results are not complete.", + "PlasoIngestModule.dataSource.not.an.image=Skipping non-disk image datasource"}) @Override public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) { - assert dataSource.equals(image); - statusHelper.switchToDeterminate(100); - currentCase = Case.getCurrentCase(); - fileManager = currentCase.getServices().getFileManager(); + if (!(dataSource instanceof Image)) { + IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, + Bundle.PlasoIngestModule_has_run(), + Bundle.PlasoIngestModule_dataSource_not_an_image()); + IngestServices.getInstance().postMessage(message); + return ProcessResult.OK; + } else { + image = (Image) dataSource; - String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS - Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), PLASO, currentTime); - try { - Files.createDirectories(moduleOutputPath); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error creating Plaso module output directory.", ex); //NON-NLS - return ProcessResult.ERROR; - } + statusHelper.switchToDeterminate(100); + currentCase = Case.getCurrentCase(); + fileManager = currentCase.getServices().getFileManager(); - // Run log2timeline - logger.log(Level.INFO, "Starting Plaso Run.");//NON-NLS - statusHelper.progress(Bundle.PlasoIngestModule_starting_log2timeline(), 0); - ProcessBuilder log2TimeLineCommand = buildLog2TimeLineCommand(moduleOutputPath, image); - try { - Process log2TimeLineProcess = log2TimeLineCommand.start(); - try (BufferedReader log2TimeLineOutpout = new BufferedReader(new InputStreamReader(log2TimeLineProcess.getInputStream()))) { - L2TStatusProcessor statusReader = new L2TStatusProcessor(log2TimeLineOutpout, statusHelper, moduleOutputPath); - new Thread(statusReader, "log2timeline status reader").start(); //NON-NLS - ExecUtil.waitForTermination(LOG2TIMELINE_EXECUTABLE, log2TimeLineProcess, TERMINATION_CHECK_INTERVAL, TERMINATION_CHECK_INTERVAL_UNITS, new DataSourceIngestModuleProcessTerminator(context)); - statusReader.cancel(); - } - - if (context.dataSourceIngestIsCancelled()) { - logger.log(Level.INFO, "Log2timeline run was canceled"); //NON-NLS - return ProcessResult.OK; - } - if (Files.notExists(moduleOutputPath.resolve(PLASO))) { - logger.log(Level.WARNING, "Error running log2timeline: there was no storage file."); //NON-NLS + String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS + Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), PLASO, currentTime); + try { + Files.createDirectories(moduleOutputPath); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Error creating Plaso module output directory.", ex); //NON-NLS return ProcessResult.ERROR; } - // sort the output - statusHelper.progress(Bundle.PlasoIngestModule_running_psort(), 33); - ProcessBuilder psortCommand = buildPsortCommand(moduleOutputPath); - int result = ExecUtil.execute(psortCommand, new DataSourceIngestModuleProcessTerminator(context)); - if (result != 0) { - logger.log(Level.SEVERE, String.format("Error running Psort, error code returned %d", result)); //NON-NLS - MessageNotifyUtil.Notify.error(MODULE_NAME, Bundle.PlasoIngestModule_psort_fail()); - return ProcessResult.ERROR; - } + // Run log2timeline + logger.log(Level.INFO, "Starting Plaso Run.");//NON-NLS + statusHelper.progress(Bundle.PlasoIngestModule_starting_log2timeline(), 0); + ProcessBuilder log2TimeLineCommand = buildLog2TimeLineCommand(moduleOutputPath, image); + try { + Process log2TimeLineProcess = log2TimeLineCommand.start(); + try (BufferedReader log2TimeLineOutpout = new BufferedReader(new InputStreamReader(log2TimeLineProcess.getInputStream()))) { + L2TStatusProcessor statusReader = new L2TStatusProcessor(log2TimeLineOutpout, statusHelper, moduleOutputPath); + new Thread(statusReader, "log2timeline status reader").start(); //NON-NLS + ExecUtil.waitForTermination(LOG2TIMELINE_EXECUTABLE, log2TimeLineProcess, TERMINATION_CHECK_INTERVAL, TERMINATION_CHECK_INTERVAL_UNITS, new DataSourceIngestModuleProcessTerminator(context)); + statusReader.cancel(); + } - if (context.dataSourceIngestIsCancelled()) { - logger.log(Level.INFO, "psort run was canceled"); //NON-NLS - return ProcessResult.OK; - } - Path plasoFile = moduleOutputPath.resolve("plasodb.db3"); //NON-NLS - if (Files.notExists(plasoFile)) { - logger.log(Level.SEVERE, "Error running Psort: there was no sqlite db file."); //NON-NLS + if (context.dataSourceIngestIsCancelled()) { + logger.log(Level.INFO, "Log2timeline run was canceled"); //NON-NLS + return ProcessResult.OK; + } + if (Files.notExists(moduleOutputPath.resolve(PLASO))) { + logger.log(Level.WARNING, "Error running log2timeline: there was no storage file."); //NON-NLS + return ProcessResult.ERROR; + } + + // sort the output + statusHelper.progress(Bundle.PlasoIngestModule_running_psort(), 33); + ProcessBuilder psortCommand = buildPsortCommand(moduleOutputPath); + int result = ExecUtil.execute(psortCommand, new DataSourceIngestModuleProcessTerminator(context)); + if (result != 0) { + logger.log(Level.SEVERE, String.format("Error running Psort, error code returned %d", result)); //NON-NLS + MessageNotifyUtil.Notify.error(MODULE_NAME, Bundle.PlasoIngestModule_psort_fail()); + return ProcessResult.ERROR; + } + + if (context.dataSourceIngestIsCancelled()) { + logger.log(Level.INFO, "psort run was canceled"); //NON-NLS + return ProcessResult.OK; + } + Path plasoFile = moduleOutputPath.resolve("plasodb.db3"); //NON-NLS + if (Files.notExists(plasoFile)) { + logger.log(Level.SEVERE, "Error running Psort: there was no sqlite db file."); //NON-NLS + return ProcessResult.ERROR; + } + + // parse the output and make artifacts + createPlasoArtifacts(plasoFile.toString(), statusHelper); + + } catch (IOException ex) { + logger.log(Level.SEVERE, "Error running Plaso.", ex);//NON-NLS return ProcessResult.ERROR; } - // parse the output and make artifacts - createPlasoArtifacts(plasoFile.toString(), statusHelper); - - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error running Plaso.", ex);//NON-NLS - return ProcessResult.ERROR; + IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, + Bundle.PlasoIngestModule_has_run(), + Bundle.PlasoIngestModule_completed()); + IngestServices.getInstance().postMessage(message); + return ProcessResult.OK; } - - IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, - Bundle.PlasoIngestModule_has_run(), - Bundle.PlasoIngestModule_completed()); - IngestServices.getInstance().postMessage(message); - return ProcessResult.OK; } private ProcessBuilder buildLog2TimeLineCommand(Path moduleOutputPath, Image image) { @@ -240,8 +244,10 @@ public class PlasoIngestModule implements DataSourceIngestModule { static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) { ProcessBuilder processBuilder = new ProcessBuilder(commandLine); - /* Add an environment variable to force log2timeline/psort to run with - * the same permissions Autopsy uses. */ + /* + * Add an environment variable to force log2timeline/psort to run with + * the same permissions Autopsy uses. + */ processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS return processBuilder; } @@ -277,31 +283,30 @@ public class PlasoIngestModule implements DataSourceIngestModule { "PlasoIngestModule.create.artifacts.cancelled=Cancelled Plaso Artifact Creation ", "# {0} - file that events are from", "PlasoIngestModule.artifact.progress=Adding events to case: {0}", - "PlasoIngestModule.info.empty.database=Plaso database was empty.", - }) + "PlasoIngestModule.info.empty.database=Plaso database was empty.",}) private void createPlasoArtifacts(String plasoDb, DataSourceIngestModuleProgress statusHelper) { Blackboard blackboard = currentCase.getSleuthkitCase().getBlackboard(); String sqlStatement = "SELECT substr(filename,1) AS filename, " - + " strftime('%s', datetime) AS epoch_date, " - + " description, " - + " source, " - + " type, " - + " sourcetype " - + " FROM log2timeline " - + " WHERE source NOT IN ('FILE', " - + " 'WEBHIST') " // bad dates and duplicates with what we have. - + " AND sourcetype NOT IN ('UNKNOWN', " - + " 'PE Import Time');"; // lots of bad dates //NON-NLS + + " strftime('%s', datetime) AS epoch_date, " + + " description, " + + " source, " + + " type, " + + " sourcetype " + + " FROM log2timeline " + + " WHERE source NOT IN ('FILE', " + + " 'WEBHIST') " // bad dates and duplicates with what we have. + + " AND sourcetype NOT IN ('UNKNOWN', " + + " 'PE Import Time');"; // lots of bad dates //NON-NLS try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + plasoDb); //NON-NLS ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) { - + boolean dbHasData = false; - + while (resultSet.next()) { dbHasData = true; - + if (context.dataSourceIngestIsCancelled()) { logger.log(Level.INFO, "Cancelled Plaso Artifact Creation."); //NON-NLS return; @@ -314,20 +319,20 @@ public class PlasoIngestModule implements DataSourceIngestModule { logger.log(Level.INFO, "File {0} from Plaso output not found in case. Associating it with the data source instead.", currentFileName);//NON-NLS resolvedFile = image; } - + String description = resultSet.getString("description"); TimelineEventType eventType = findEventSubtype(currentFileName, resultSet); - + // If the description is empty use the event type display name // as the description. - if ( description == null || description.isEmpty() ) { + if (description == null || description.isEmpty()) { if (eventType != TimelineEventType.OTHER) { description = eventType.getDisplayName(); } else { continue; } } - + Collection bbattributes = Arrays.asList( new BlackboardAttribute( TSK_DATETIME, MODULE_NAME, @@ -338,14 +343,16 @@ public class PlasoIngestModule implements DataSourceIngestModule { new BlackboardAttribute( TSK_TL_EVENT_TYPE, MODULE_NAME, eventType.getTypeID())); - + try { BlackboardArtifact bbart = resolvedFile.newArtifact(TSK_TL_EVENT); bbart.addAttributes(bbattributes); try { - /* Post the artifact which will index the artifact for + /* + * Post the artifact which will index the artifact for * keyword search, and fire an event to notify UI of - * this new artifact */ + * this new artifact + */ blackboard.postArtifact(bbart, MODULE_NAME); } catch (BlackboardException ex) { logger.log(Level.SEVERE, "Error Posting Artifact.", ex);//NON-NLS @@ -354,12 +361,12 @@ public class PlasoIngestModule implements DataSourceIngestModule { logger.log(Level.SEVERE, "Exception Adding Artifact.", ex);//NON-NLS } } - + // Check if there is data the db - if( !dbHasData ) { + if (!dbHasData) { logger.log(Level.INFO, String.format("PlasoDB was empty: %s", plasoDb)); MessageNotifyUtil.Notify.info(MODULE_NAME, Bundle.PlasoIngestModule_info_empty_database()); - } + } } catch (SQLException ex) { logger.log(Level.SEVERE, "Error while trying to read into a sqlite db.", ex);//NON-NLS } @@ -377,8 +384,8 @@ public class PlasoIngestModule implements DataSourceIngestModule { // check the cached file //TODO: would we reduce 'cache misses' if we retrieved the events sorted by file? Is that overhead worth it? if (previousFile != null - && previousFile.getName().equalsIgnoreCase(fileName) - && previousFile.getParentPath().equalsIgnoreCase(filePath)) { + && previousFile.getName().equalsIgnoreCase(fileName) + && previousFile.getParentPath().equalsIgnoreCase(filePath)) { return previousFile; } @@ -416,7 +423,7 @@ public class PlasoIngestModule implements DataSourceIngestModule { switch (row.getString("source")) { case "WEBHIST": //These shouldn't actually be present, but keeping the logic just in case... if (fileName.toLowerCase().contains(COOKIE) - || row.getString("type").toLowerCase().contains(COOKIE)) {//NON-NLS + || row.getString("type").toLowerCase().contains(COOKIE)) {//NON-NLS return TimelineEventType.WEB_COOKIE; } else { diff --git a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle_ja.properties index 17ff042186..5b6cba0d5f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle_ja.properties @@ -1,11 +1,15 @@ -VMExtractorIngestModuleFactory.moduleDisplayName=\u4EEE\u60F3\u30DE\u30B7\u30F3\u30A8\u30AF\u30B9\u30C8\u30E9\u30AF\u30BF\u30FC -VMExtractorIngestModuleFactory.moduleDescription=\u4EEE\u60F3\u30DE\u30B7\u30F3\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u62BD\u51FA\u3057\u3001\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u3068\u3057\u3066\u30B1\u30FC\u30B9\u306B\u8FFD\u52A0\u3057\u307E\u3059\u3002 -VMExtractorIngestModule.cannotCreateOutputDir.message=\u30A2\u30A6\u30C8\u30D7\u30C3\u30C8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\uFF1A{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -VMExtractorIngestModule.addedVirtualMachineImage.message=\u4EEE\u60F3\u30DE\u30B7\u30F3\u30A4\u30E1\u30FC\u30B8{0}\u3092\u8FFD\u52A0 -VMExtractorIngestModule.searchingImage.message=\u30A4\u30E1\u30FC\u30B8\u306B\u4EEE\u60F3\u30DE\u30B7\u30F3\u30D5\u30A1\u30A4\u30EB\u304C\u306A\u3044\u304B\u691C\u7D22\u4E2D -VMExtractorIngestModule.exportingToDisk.message=\u4EEE\u60F3\u30DE\u30B7\u30F3\u30D5\u30A1\u30A4\u30EB\u3092\u30C7\u30A3\u30B9\u30AF\u306B\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u4E2D -VMExtractorIngestModule.queuingIngestJobs.message=\u62BD\u51FA\u3055\u308C\u305F\u4EEE\u60F3\u30DE\u30B7\u30F3\u306E\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30B8\u30E7\u30D6\u3092\u30AD\u30E5\u30FC\u30A4\u30F3\u30B0 -VMExtractorIngestModule.msgNotify.failedExtractVM.title.txt=\u4EEE\u60F3\u30DE\u30B7\u30F3\u30D5\u30A1\u30A4\u30EB\u3092\u62BD\u51FA\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -VMExtractorIngestModule.msgNotify.failedExtractVM.msg.txt=\u4EEE\u60F3\u30DE\u30B7\u30F3\u30D5\u30A1\u30A4\u30EB{0}\u3092\u30C7\u30A3\u30B9\u30AF\u306B\u66F8\u304D\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -VMExtractorIngestModule.msgNotify.failedIngestVM.title.txt=\u4EEE\u60F3\u30DE\u30B7\u30F3\u3092\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -VMExtractorIngestModule.msgNotify.failedIngestVM.msg.txt=\u4EEE\u60F3\u30DE\u30B7\u30F3\u30D5\u30A1\u30A4\u30EB{0}\u3092\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 \ No newline at end of file +# {0} - \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d +deviceIdQueryErrMsg=\u30c7\u30d0\u30a4\u30b9ID\u306e\u306a\u3044\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9 {0} +# {0} - \u51fa\u529b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u540d +VMExtractorIngestModule.cannotCreateOutputDir.message=\u51fa\u529b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093: {0}\u3002 +VMExtractorIngestModule.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +VMExtractorIngestModuleFactory.moduleDisplayName=\u4eee\u60f3\u30de\u30b7\u30f3\u62bd\u51fa\u30c4\u30fc\u30eb +VMExtractorIngestModuleFactory.moduleDescription=\u4eee\u60f3\u30de\u30b7\u30f3\u30d5\u30a1\u30a4\u30eb\u3092\u62bd\u51fa\u3057\u3001\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3068\u3057\u3066\u30b1\u30fc\u30b9\u306b\u8ffd\u52a0\u3057\u307e\u3059\u3002 +VMExtractorIngestModule.addedVirtualMachineImage.message=\u8ffd\u52a0\u3055\u308c\u305f\u4eee\u60f3\u30de\u30b7\u30f3\u30a4\u30e1\u30fc\u30b8 {0} +VMExtractorIngestModule.searchingImage.message=\u4eee\u60f3\u30de\u30b7\u30f3\u30d5\u30a1\u30a4\u30eb\u306e\u30a4\u30e1\u30fc\u30b8\u3092\u691c\u7d22\u4e2d\u3067\u3059 +VMExtractorIngestModule.exportingToDisk.message=\u4eee\u60f3\u30de\u30b7\u30f3\u30d5\u30a1\u30a4\u30eb\u3092\u30c7\u30a3\u30b9\u30af\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u4e2d\u3067\u3059 +VMExtractorIngestModule.queuingIngestJobs.message=\u62bd\u51fa\u3055\u308c\u305f\u4eee\u60f3\u30de\u30b7\u30f3\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b8\u30e7\u30d6\u3092\u30ad\u30e5\u30fc\u30a4\u30f3\u30b0\u4e2d\u3067\u3059 +VMExtractorIngestModule.msgNotify.failedExtractVM.title.txt=\u4eee\u60f3\u30de\u30b7\u30f3\u30d5\u30a1\u30a4\u30eb\u3092\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +VMExtractorIngestModule.msgNotify.failedExtractVM.msg.txt=\u4eee\u60f3\u30de\u30b7\u30f3\u30d5\u30a1\u30a4\u30eb {0} \u3092\u30c7\u30a3\u30b9\u30af\u306b\u66f8\u304d\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +VMExtractorIngestModule.msgNotify.failedIngestVM.title.txt=\u4eee\u60f3\u30de\u30b7\u30f3\u30d5\u30a1\u30a4\u30eb\u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +VMExtractorIngestModule.msgNotify.failedIngestVM.msg.txt=\u4eee\u60f3\u30de\u30b7\u30f3\u30d5\u30a1\u30a4\u30eb {0} \u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f diff --git a/Core/src/org/sleuthkit/autopsy/progress/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/progress/Bundle_ja.properties new file mode 100644 index 0000000000..d56b88e754 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/progress/Bundle_ja.properties @@ -0,0 +1,6 @@ +# \u3053\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u30d8\u30c3\u30c0\u30fc\u3092\u5909\u66f4\u3059\u308b\u306b\u306f\u3001[\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u30d7\u30ed\u30d1\u30c6\u30a3] \u3067 [\u30e9\u30a4\u30bb\u30f3\u30b9\u30d8\u30c3\u30c0\u30fc] \u3092\u9078\u629e\u3057\u307e\u3059\u3002 +# \u3053\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u5909\u66f4\u3059\u308b\u306b\u306f\u3001[\u30c4\u30fc\u30eb | \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8] \u3092\u9078\u629e\u3057\u3001 +# \u30a8\u30c7\u30a3\u30bf\u30fc\u3067\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u958b\u304d\u307e\u3059\u3002 + +ProgressPanel.progressMessage.text=\u30e1\u30c3\u30bb\u30fc\u30b8 +TaskCanceller.progress.cancellingMessage=\u53d6\u308a\u6d88\u3057\u4e2d\u3067\u3059... diff --git a/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java b/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java index 8d92e40d70..31fb900c56 100644 --- a/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java +++ b/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; import javax.swing.JDialog; import javax.swing.SwingUtilities; +import org.apache.commons.lang3.SerializationUtils; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.util.HelpCtx; @@ -65,7 +66,7 @@ public final class ModalDialogProgressIndicator implements ProgressIndicator { this.title = title; progressPanel = new ProgressPanel(); progressPanel.setIndeterminate(true); - this.buttonLabels = buttonLabels; + this.buttonLabels = SerializationUtils.clone(buttonLabels); this.focusedButtonLabel = focusedButtonLabel; this.buttonListener = buttonListener; } diff --git a/Core/src/org/sleuthkit/autopsy/python/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/python/Bundle_ja.properties index c407b6cf35..fdbd43efd7 100644 --- a/Core/src/org/sleuthkit/autopsy/python/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/python/Bundle_ja.properties @@ -1,2 +1,2 @@ -JythonModuleLoader.errorMessages.failedToOpenModule={0}\u304C\u958B\u3051\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u8A73\u7D30\u306F\u30ED\u30B0\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 -JythonModuleLoader.errorMessages.failedToLoadModule={0}. {1}. \u304C\u958B\u3051\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u8A73\u7D30\u306F\u30ED\u30B0\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 \ No newline at end of file +JythonModuleLoader.errorMessages.failedToOpenModule={0}\u304c\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u8a73\u7d30\u306f\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +JythonModuleLoader.errorMessages.failedToLoadModule={0}. {1}. \u304c\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u8a73\u7d30\u306f\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties index f666513e92..6dde31add8 100644 --- a/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties @@ -1,8 +1,334 @@ OpenIDE-Module-Name=\u30ec\u30dd\u30fc\u30c8 -DefaultReportConfigurationPanel.infoLabel.text=\u3053\u306e\u30ec\u30dd\u30fc\u30c8\u306f\u6b21\u306e\u30b9\u30af\u30ea\u30fc\u30f3\u3067\u8a2d\u5b9a\u3055\u308c\u307e\u3059\u3002 -ReportBranding.defaultReportTitle.text=Autopsy\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30ec\u30dd\u30fc\u30c8 -ReportBranding.defaultReportFooter.text=Autopsy\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u30fb\u30c7\u30b8\u30bf\u30eb\u30fb\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30fb\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u306b\u3088\u308a\u63d0\u4f9b - www.sleuthkit.org -ReportProgressPanel.pathLabel.text=\u30d1\u30b9\u30e9\u30d9\u30eb -ReportProgressPanel.reportLabel.text=\u30ec\u30dd\u30fc\u30c8\u30e9\u30d9\u30eb -ReportProgressPanel.statusMessageLabel.text=\u30d7\u30ed\u30bb\u30b7\u30f3\u30b0\u30e9\u30d9\u30eb +CTL_ReportWizardAction=\u30ec\u30dd\u30fc\u30c8\u3092\u5b9f\u884c +ArtifactSelectionDialog.titleLabel.text=\u30ec\u30dd\u30fc\u30c8\u4f5c\u6210\u5bfe\u8c61\u306e\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u9078\u629e: +ArtifactSelectionDialog.okButton.text=OK +PortableCaseInterestingItemsListPanel.error.errorLoadingTags=\u8208\u5473\u6df1\u3044\u9805\u76ee\u30bb\u30c3\u30c8\u540d\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseInterestingItemsListPanel.error.errorTitle=\u30b1\u30fc\u30b9\u306e\u8208\u5473\u6df1\u3044\u9805\u76ee\u30bb\u30c3\u30c8\u540d\u306e\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseInterestingItemsListPanel.error.noOpenCase=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093 +PortableCaseReportModule.compressCase.canceled=\u30e6\u30fc\u30b6\u30fc\u306b\u3088\u3063\u3066\u5727\u7e2e\u304c\u53d6\u308a\u6d88\u3055\u308c\u307e\u3057\u305f +PortableCaseReportModule.compressCase.errorCompressingCase=\u30b1\u30fc\u30b9\u306e\u5727\u7e2e\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +# {0} - \u4e00\u6642\u30d5\u30a9\u30eb\u30c0\u30fc\u30d1\u30b9 +PortableCaseReportModule.compressCase.errorCreatingTempFolder=\u4e00\u6642\u30d5\u30a9\u30eb\u30c0\u30fc {0} \u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +PortableCaseReportModule.compressCase.errorFinding7zip=7-Zip\u5b9f\u884c\u53ef\u80fd\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +PortableCaseReportModule.copyContentToPortableCase.copyingFile={0} \u30d5\u30a1\u30a4\u30eb\u306e\u30b3\u30d4\u30fc\u4e2d\u3067\u3059 +# {0} - \u30b1\u30fc\u30b9\u30d5\u30a9\u30eb\u30c0\u30fc +PortableCaseReportModule.createCase.caseDirExists=\u30b1\u30fc\u30b9\u30d5\u30a9\u30eb\u30c0\u30fc {0} \u304c\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059 +PortableCaseReportModule.createCase.errorCreatingCase=\u30b1\u30fc\u30b9\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +# {0} - \u30d5\u30a9\u30eb\u30c0\u30fc +PortableCaseReportModule.createCase.errorCreatingFolder=Error creating \u30d5\u30a9\u30eb\u30c0\u30fc {0} \u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseReportModule.createCase.errorStoringMaxIds=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9ID\u306e\u6700\u5927\u6570\u3092\u4fdd\u5b58\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseReportModule.generateReport.caseClosed=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u304c\u9589\u3058\u3089\u308c\u307e\u3057\u305f\u3002 +PortableCaseReportModule.generateReport.compressingCase=\u30b1\u30fc\u30b9\u3092\u5727\u7e2e\u4e2d\u3067\u3059... +# {0} - \u30bf\u30b0\u540d +PortableCaseReportModule.generateReport.copyingArtifacts={0} \u3068\u3057\u3066\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30b3\u30d4\u30fc\u4e2d\u3067\u3059... +# {0} - \u30bf\u30b0\u540d +PortableCaseReportModule.generateReport.copyingFiles={0} \u3068\u3057\u3066\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u30b3\u30d4\u30fc\u4e2d\u3067\u3059... +PortableCaseReportModule.generateReport.copyingTags=\u30bf\u30b0\u3092\u30b3\u30d4\u30fc\u4e2d\u3067\u3059... +PortableCaseReportModule.generateReport.creatingCase=\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f5c\u6210\u4e2d\u3067\u3059... +PortableCaseReportModule.generateReport.errorCopyingArtifacts=\u30bf\u30b0\u4ed8\u304d\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u306e\u30b3\u30d4\u30fc\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseReportModule.generateReport.errorCopyingFiles=\u30bf\u30b0\u4ed8\u304d\u30d5\u30a1\u30a4\u30eb\u306e\u30b3\u30d4\u30fc\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseReportModule.generateReport.errorCopyingInterestingFiles=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u306e\u30b3\u30d4\u30fc\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseReportModule.generateReport.errorCopyingInterestingResults=\u8208\u5473\u6df1\u3044\u7d50\u679c\u306e\u30b3\u30d4\u30fc\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseReportModule.generateReport.errorCopyingTags=\u30bf\u30b0\u306e\u30b3\u30d4\u30fc\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +# {0} - \u5c5e\u6027\u30bf\u30a4\u30d7\u540d +PortableCaseReportModule.generateReport.errorLookingUpAttrType=\u5c5e\u6027\u30bf\u30a4\u30d7 {0} \u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseReportModule.generateReport.interestingItemError=\u8208\u5473\u6df1\u3044\u9805\u76ee\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseReportModule.generateReport.noContentToCopy=\u30b3\u30d4\u30fc\u3059\u308b\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u3001\u7d50\u679c\u3001\u307e\u305f\u306f\u30bf\u30b0\u4ed8\u304d\u9805\u76ee\u304c\u3042\u308a\u307e\u305b\u3093 +# {0} - \u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc +PortableCaseReportModule.generateReport.outputDirDoesNotExist=\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc {0} \u304c\u5b58\u5728\u3057\u307e\u305b\u3093 +# {0} - \u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc +PortableCaseReportModule.generateReport.outputDirIsNotDir=\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc {0} \u306f\u30d5\u30a9\u30eb\u30c0\u30fc\u3067\u306f\u3042\u308a\u307e\u305b\u3093 +PortableCaseReportModule.generateReport.verifying=\u9078\u629e\u3057\u305f\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u3092\u691c\u8a3c\u4e2d\u3067\u3059... +PortableCaseReportModule.getDescription.description=\u9078\u629e\u3057\u305f\u9805\u76ee\u3092\u7c21\u5358\u306b\u5171\u6709\u53ef\u80fd\u306a\u65b0\u898f\u30b7\u30f3\u30b0\u30eb\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u306b\u30b3\u30d4\u30fc\u3057\u307e\u3059 +PortableCaseReportModule.getName.name=\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9 +PortableCaseTagsListPanel.error.errorLoadingTags=\u30bf\u30b0\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseTagsListPanel.error.errorTitle=\u30b1\u30fc\u30b9\u306e\u30bf\u30b0\u540d\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +PortableCaseTagsListPanel.error.noOpenCase=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093 +ReportExcel.exceptionMessage.dataTooLarge=\u5024\u304c\u9577\u3059\u304e\u3066Excel\u306e\u30bb\u30eb\u5185\u306b\u53ce\u307e\u308a\u307e\u305b\u3093\u3002 +ReportExcel.exceptionMessage.errorText=Excel\u306e\u30bb\u30eb\u5185\u3067\u30c7\u30fc\u30bf\u3092\u8868\u793a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ReportExcel.writeSummary.caseName=\u30b1\u30fc\u30b9\u540d: +ReportExcel.writeSummary.caseNotes=\u30b1\u30fc\u30b9\u5099\u8003: +ReportExcel.writeSummary.caseNum=\u30b1\u30fc\u30b9\u756a\u53f7: +ReportExcel.writeSummary.examiner=\u8abf\u67fb\u54e1: +ReportExcel.writeSummary.numImages=\u30a4\u30e1\u30fc\u30b8\u6570: +ReportExcel.writeSummary.sheetName=\u30b5\u30de\u30ea\u30fc +ReportExcel.writeSummary.summary=\u30b5\u30de\u30ea\u30fc +ReportGenerator.artTableColHdr.comment=\u30b3\u30e1\u30f3\u30c8 +ReportGenerator.errList.noOpenCase=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +ReportGenerator.tagTable.header.userName=\u30e6\u30fc\u30b6\u30fc\u540d +ReportHTML.writeSum.case=\u30b1\u30fc\u30b9: +ReportHTML.writeSum.caseNotes=\u5099\u8003: +ReportHTML.writeSum.caseNumber=\u30b1\u30fc\u30b9\u756a\u53f7: +ReportHTML.writeSum.caseNumImages=\u30a4\u30e1\u30fc\u30b8\u6570: +ReportHTML.writeSum.examiner=\u8abf\u67fb\u54e1: +ReportVisualPanel1.reportModulesLabel.text=\u30ec\u30dd\u30fc\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb: +ReportVisualPanel1.invalidModuleWarning=\u7121\u52b9\u306a\u30ec\u30dd\u30fc\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb ({0}) \u306b\u906d\u9047\u3057\u307e\u3057\u305f +DefaultReportConfigurationPanel.infoLabel.text=\u3053\u306e\u30ec\u30dd\u30fc\u30c8\u306f\u6b21\u306e\u753b\u9762\u3067\u69cb\u6210\u3055\u308c\u307e\u3059\u3002 +ReportVisualPanel2.dataLabel.text=\u30ec\u30dd\u30fc\u30c8\u4f5c\u6210\u5bfe\u8c61\u306e\u30c7\u30fc\u30bf\u3092\u9078\u629e: +ReportVisualPanel2.deselectAllButton.text=\u3059\u3079\u3066\u9078\u629e\u89e3\u9664 +ReportVisualPanel2.selectAllButton.text=\u3059\u3079\u3066\u9078\u629e +ReportVisualPanel2.advancedButton.text=\u30c7\u30fc\u30bf\u30bf\u30a4\u30d7 +ArtifactSelectionDialog.deselectAllButton.text=\u3059\u3079\u3066\u9078\u629e\u89e3\u9664 +ArtifactSelectionDialog.selectAllButton.text=\u3059\u3079\u3066\u9078\u629e +ReportGenerationPanel.closeButton.text=\u9589\u3058\u308b +ReportProgressPanel.reportLabel.text=reportLabel +ReportProgressPanel.pathLabel.text=pathLabel ReportProgressPanel.separationLabel.text=: +ReportProgressPanel.statusMessageLabel.text=processingLabel +ReportGenerationPanel.titleLabel.text=\u30ec\u30dd\u30fc\u30c8\u751f\u6210\u9032\u6357\u72b6\u6cc1 +ReportVisualPanel2.taggedResultsRadioButton.text=\u30bf\u30b0\u4ed8\u304d\u7d50\u679c +ReportVisualPanel2.allResultsRadioButton.text=\u3059\u3079\u3066\u306e\u7d50\u679c +ReportWizardFileOptionsVisualPanel.selectAllButton.text=\u3059\u3079\u3066\u9078\u629e +ReportWizardFileOptionsVisualPanel.deselectAllButton.text=\u3059\u3079\u3066\u9078\u629e\u89e3\u9664 +ReportWizardFileOptionsVisualPanel.jLabel1.text=\u6b21\u306e\u30d5\u30a1\u30a4\u30eb\u30ec\u30dd\u30fc\u30c8\u306b\u542b\u3081\u308b\u9805\u76ee\u3092\u9078\u629e: +ArtifactSelectionDialog.dlgTitle.text=\u9ad8\u5ea6\u306a\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u9078\u629e +FileReportDataTypes.filename.text=\u540d\u524d +FileReportDataTypes.fileExt.text=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50 +FileReportDataTypes.fileType.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 +FileReportDataTypes.isDel.text=\u524a\u9664\u3055\u308c\u3066\u3044\u307e\u3059 +FileReportDataTypes.aTime.text=\u6700\u7d42\u30a2\u30af\u30bb\u30b9\u65e5 +FileReportDataTypes.crTime.text=\u4f5c\u6210\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb +FileReportDataTypes.mTime.text=\u6700\u7d42\u66f4\u65b0\u65e5 +FileReportDataTypes.size.text=\u30b5\u30a4\u30ba +FileReportDataTypes.address.text=\u30a2\u30c9\u30ec\u30b9 +FileReportDataTypes.hash.text=\u30cf\u30c3\u30b7\u30e5\u5024 +FileReportDataTypes.knownStatus.text=\u65e2\u77e5\u306e\u30b9\u30c6\u30fc\u30bf\u30b9 +FileReportDataTypes.perms.text=\u30a2\u30af\u30bb\u30b9\u6a29 +FileReportDataTypes.path.text=\u5b8c\u5168\u306a\u30d1\u30b9 +FileReportText.getName.text=\u30d5\u30a1\u30a4\u30eb - \u30c6\u30ad\u30b9\u30c8 +FileReportText.getDesc.text=\u30b1\u30fc\u30b9\u306e\u500b\u3005\u306e\u30d5\u30a1\u30a4\u30eb\u306b\u95a2\u3059\u308b\u60c5\u5831\u3092\u542b\u3080\u533a\u5207\u308a\u30c6\u30ad\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb\u3067\u3059\u3002 +ReportBodyFile.progress.querying=\u30d5\u30a1\u30a4\u30eb\u3092\u30af\u30a8\u30ea\u4e2d\u3067\u3059... +ReportBodyFile.ingestWarning.text=\u8b66\u544a: \u3053\u306e\u30ec\u30dd\u30fc\u30c8\u306f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b5\u30fc\u30d3\u30b9\u306e\u5b8c\u4e86\u524d\u306b\u5b9f\u884c\u3055\u308c\u307e\u3057\u305f\\uff01 +ReportBodyFile.progress.loading=\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u3067\u3059... +ReportBodyFile.progress.processing={0} \u3092\u73fe\u5728\u51e6\u7406\u4e2d\u3067\u3059... +ReportBodyFile.getName.text=TSK Body\u30d5\u30a1\u30a4\u30eb +ReportBodyFile.getDesc.text=\u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u306b\u5bfe\u5fdc\u3057\u305f\u3001MAC\u56de\u6570\u3092\u6301\u3064Body\u30d5\u30a1\u30a4\u30eb\u5f62\u5f0f\u3067\u3059\u3002\u3053\u306e\u5f62\u5f0f\u306f\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30d3\u30e5\u30fc\u306b\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002 +ReportBodyFile.getFilePath.text=BodyFile.txt +ReportKML.progress.querying=\u30d5\u30a1\u30a4\u30eb\u3092\u30af\u30a8\u30ea\u4e2d\u3067\u3059... +ReportKML.progress.loading=\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u3067\u3059... +ReportKML.getName.text=Google Earth KML +ReportKML.getDesc.text=\u95a2\u9023\u30d5\u30a1\u30a4\u30eb\u306e\u5ea7\u6a19\u3092\u6301\u3064KML\u5f62\u5f0f\u30ec\u30dd\u30fc\u30c8\u3067\u3059\u3002\u3053\u306e\u5f62\u5f0f\u306fGoogle Earth\u30d3\u30e5\u30fc\u306b\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002 +ReportKML.getFilePath.text=ReportKML.kml +ReportBranding.defaultReportTitle.text=Autopsy\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30ec\u30dd\u30fc\u30c8 +ReportBranding.defaultReportFooter.text=Autopsy\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u30c7\u30b8\u30bf\u30eb\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u4f7f\u7528 - www.sleuthkit.org +ReportExcel.numAartifacts.text=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u6570: +ReportExcel.getName.text=Excel\u30ec\u30dd\u30fc\u30c8 +ReportExcel.getDesc.text=\u7d50\u679c\u3068\u30bf\u30b0\u4ed8\u304d\u9805\u76ee\u306b\u95a2\u3059\u308bExcel (XLS)\u5f62\u5f0f\u306e\u30ec\u30dd\u30fc\u30c8\u3067\u3059\u3002 +ReportExcel.sheetName.text=\u30b5\u30de\u30ea\u30fc +ReportExcel.cellVal.summary=\u30b5\u30de\u30ea\u30fc +ReportExcel.cellVal.caseName=\u30b1\u30fc\u30b9\u540d: +ReportExcel.cellVal.caseNum=\u30b1\u30fc\u30b9\u756a\u53f7: +ReportExcel.cellVal.examiner=\u8abf\u67fb\u54e1: +ReportExcel.cellVal.numImages=\u30a4\u30e1\u30fc\u30b8\u6570: +ReportGenerationPanel.confDlg.sureToClose.msg=\u30c0\u30a4\u30a2\u30ed\u30b0\u3092\u9589\u3058\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b?\n\u3059\u3079\u3066\u306e\u30ec\u30dd\u30fc\u30c8\u304c\u53d6\u308a\u6d88\u3055\u308c\u307e\u3059\u3002 +ReportGenerationPanel.confDlg.title.closing=\u9589\u3058\u3066\u3044\u307e\u3059 +ReportGenerationPanel.confDlg.cancelReport.msg=\u30ec\u30dd\u30fc\u30c8\u3092\u53d6\u308a\u6d88\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +ReportGenerator.displayProgress.title.text=\u30ec\u30dd\u30fc\u30c8\u751f\u6210\u9032\u6357\u72b6\u6cc1... +ReportGenerator.progress.queryingDb.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30af\u30a8\u30ea\u4e2d\u3067\u3059... +ReportGenerator.progress.processingFile.text={0} \u3092\u73fe\u5728\u51e6\u7406\u4e2d\u3067\u3059... +ReportGenerator.artifactTable.taggedResults.text=\u6b21\u306e\u3044\u305a\u308c\u304b\u3067\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u7d50\u679c\u3092\u542b\u307f\u307e\u3059: +ReportGenerator.progress.processing={0} \u3092\u73fe\u5728\u51e6\u7406\u4e2d\u3067\u3059...\u3002 +ReportGenerator.msgShow.skippingArtType.title=\u30ec\u30dd\u30fc\u30c8\u5185\u306e\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30bf\u30a4\u30d7 {0} \u306e\u30b9\u30ad\u30c3\u30d7\u4e2d\u3067\u3059 +ReportGenerator.msgShow.skippingArtType.msg=\u4e0d\u660e\u306a\u30ec\u30dd\u30fc\u30c8\u4f5c\u6210\u5bfe\u8c61\u306e\u5217 +ReportGenerator.makeContTagTab.taggedFiles.msg=\u6b21\u306e\u3044\u305a\u308c\u304b\u3067\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u542b\u307f\u307e\u3059: +ReportGenerator.makeBbArtTagTab.taggedRes.msg=\u3053\u306e\u30ec\u30dd\u30fc\u30c8\u306f\u6b21\u3067\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u7d50\u679c\u306e\u307f\u3092\u542b\u307f\u307e\u3059: +ReportGenerator.tagTable.header.resultType=\u7d50\u679c\u30bf\u30a4\u30d7 +ReportGenerator.tagTable.header.tag=\u30bf\u30b0 +ReportGenerator.tagTable.header.comment=\u30b3\u30e1\u30f3\u30c8 +ReportGenerator.tagTable.header.srcFile=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb +ReportGenerator.progress.createdThumb.text=\u30b5\u30e0\u30cd\u30a4\u30eb\u306e\u4f5c\u6210\u4e2d\u3067\u3059... +ReportGenerator.htmlOutput.header.file=\u30d5\u30a1\u30a4\u30eb +ReportGenerator.htmlOutput.header.tag=\u30bf\u30b0 +ReportGenerator.htmlOutput.header.comment=\u30b3\u30e1\u30f3\u30c8 +ReportGenerator.htmlOutput.header.timeModified=MFT\u5909\u66f4\u6642\u523b +ReportGenerator.htmlOutput.header.timeChanged=\u5909\u66f4\u3055\u308c\u305f\u523b\u523b +ReportGenerator.htmlOutput.header.timeAccessed=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u6642\u523b +ReportGenerator.htmlOutput.header.timeCreated=\u4f5c\u6210\u3055\u308c\u305f\u6642\u523b +ReportGenerator.htmlOutput.header.size=\u30b5\u30a4\u30ba(\u30d0\u30a4\u30c8) +ReportGenerator.htmlOutput.header.hash=\u30cf\u30c3\u30b7\u30e5 +ReportGenerator.thumbnailTable.name=\u30bf\u30b0\u4ed8\u304d\u30a4\u30e1\u30fc\u30b8 +ReportGenerator.thumbnailTable.desc=\u30bf\u30b0\u4ed8\u304d\u306e\u30d5\u30a1\u30a4\u30eb\u3068\u7d50\u679c\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30a4\u30e1\u30fc\u30b8\u306e\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u542b\u307f\u307e\u3059\u3002 +ReportGenerator.writeKwHits.userSrchs=\u30e6\u30fc\u30b6\u30fc\u691c\u7d22 +ReportGenerator.progress.processing={0} ({1}) \u3092\u73fe\u5728\u51e6\u7406\u4e2d\u3067\u3059... +ReportGenerator.artTableColHdr.url=URL +ReportGenerator.artTableColHdr.title=\u30bf\u30a4\u30c8\u30eb +ReportGenerator.artTableColHdr.dateCreated=\u4f5c\u6210\u3055\u308c\u305f\u30c7\u30fc\u30bf +ReportGenerator.artTableColHdr.program=\u30d7\u30ed\u30b0\u30e9\u30e0 +ReportGenerator.artTableColHdr.urlDomainDecoded=URL\u30c9\u30e1\u30a4\u30f3 +ReportGenerator.artTableColHdr.srcFile=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb +ReportGenerator.artTableColHdr.dateTime=\u65e5\u6642 +ReportGenerator.artTableColHdr.name=\u540d\u524d +ReportGenerator.artTableColHdr.value=\u5024 +ReportGenerator.artTableColHdr.dateAccessed=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u30c7\u30fc\u30bf +ReportGenerator.artTableColHdr.referrer=\u30ea\u30d5\u30a1\u30e9\u30fc +ReportGenerator.artTableColHdr.dest=\u5b9b\u5148 +ReportGenerator.artTableColHdr.sourceUrl=\u30bd\u30fc\u30b9URL +ReportGenerator.artTableColHdr.path=\u30d1\u30b9 +ReportGenerator.artTableColHdr.progName=\u30d7\u30ed\u30b0\u30e9\u30e0\u540d +ReportGenerator.artTableColHdr.dateTime=\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u65e5\u6642 +ReportGenerator.artTableColHdr.preview=\u30d7\u30ec\u30d3\u30e5\u30fc +ReportGenerator.artTableColHdr.file=\u30d5\u30a1\u30a4\u30eb +ReportGenerator.artTableColHdr.size=\u30b5\u30a4\u30ba +ReportGenerator.artTableColHdr.deviceId=\u30c7\u30d0\u30a4\u30b9ID +ReportGenerator.artTableColHdr.text=\u30c6\u30ad\u30b9\u30c8 +ReportGenerator.artTableColHdr.domain=\u30c9\u30e1\u30a4\u30f3 +ReportGenerator.artTableColHdr.dateTaken=\u53d6\u5f97\u65e5 +ReportGenerator.artTableColHdr.devManufacturer=\u30c7\u30d0\u30a4\u30b9\u30e1\u30fc\u30ab\u30fc +ReportGenerator.artTableColHdr.devMake=\u30c7\u30d0\u30a4\u30b9\u30e1\u30fc\u30ab\u30fc +ReportGenerator.artTableColHdr.devModel=\u30c7\u30d0\u30a4\u30b9\u578b\u5f0f +ReportGenerator.artTableColHdr.latitude=\u7def\u5ea6 +ReportGenerator.artTableColHdr.longitude=\u7d4c\u5ea6 +ReportGenerator.artTableColHdr.latitudeStart=\u958b\u59cb\u7def\u5ea6 +ReportGenerator.artTableColHdr.longitudeStart=\u958b\u59cb\u7d4c\u5ea6 +ReportGenerator.artTableColHdr.latitudeEnd=\u7d42\u4e86\u7def\u5ea6 +ReportGenerator.artTableColHdr.longitudeEnd=\u7d42\u4e86\u7d4c\u5ea6 +ReportGenerator.artTableColHdr.associatedArtifact=\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8 +ReportGenerator.artTableColHdr.count=\u30ab\u30a6\u30f3\u30c8 +ReportGenerator.artTableColHdr.personName=\u4eba\u540d +ReportGenerator.artTableColHdr.phoneNumber=\u96fb\u8a71\u756a\u53f7 +ReportGenerator.artTableColHdr.phoneNumHome=\u96fb\u8a71\u756a\u53f7(\u81ea\u5b85) +ReportGenerator.artTableColHdr.phoneNumOffice=\u96fb\u8a71\u756a\u53f7(\u4f1a\u793e) +ReportGenerator.artTableColHdr.phoneNumMobile=\u96fb\u8a71\u756a\u53f7(\u643a\u5e2f) +ReportGenerator.artTableColHdr.email=\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9 +ReportGenerator.artTableColHdr.msgType=\u30e1\u30c3\u30bb\u30fc\u30b8\u30bf\u30a4\u30d7 +ReportGenerator.artTableColHdr.direction=\u65b9\u5411 +ReportGenerator.artTableColHdr.readStatus=\u8aad\u307f\u53d6\u308a\u30b9\u30c6\u30fc\u30bf\u30b9 +ReportGenerator.artTableColHdr.fromPhoneNum=\u767a\u4fe1\u5143\u96fb\u8a71\u756a\u53f7 +ReportGenerator.artTableColHdr.fromEmail=\u9001\u4fe1\u5143\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9 +ReportGenerator.artTableColHdr.toPhoneNum=\u76f8\u624b\u5148\u96fb\u8a71\u756a\u53f7 +ReportGenerator.artTableColHdr.toEmail=\u9001\u4fe1\u5148\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9 +ReportGenerator.artTableColHdr.subject=\u4ef6\u540d +ReportGenerator.artTableColHdr.tskEmailTo=To\u306e\u5b9b\u5148\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9 +ReportGenerator.artTableColHdr.tskEmailTo=CC\u306e\u5b9b\u5148\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9 +ReportGenerator.artTableColHdr.tskEmailTo=BCC\u306e\u5b9b\u5148\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9 +ReportGenerator.artTableColHdr.tskEmailTo=\u5dee\u51fa\u4eba\u306e\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9 +ReportGenerator.artTableColHdr.tskMsgId=\u30e1\u30c3\u30bb\u30fc\u30b8ID +ReportGenerator.artTableColHdr.tskDateTimeRcvd=\u53d7\u4fe1\u65e5 +ReportGenerator.artTableColHdr.tskDateTimeSent=\u9001\u4fe1\u65e5 +ReportGenerator.artTableColHdr.tskSubject=\u4ef6\u540d +ReportGenerator.artTableColHdr.tskSetName=\u30bb\u30c3\u30c8\u540d +ReportGenerator.artTableColHdr.tskInterestingFilesCategory=\u30eb\u30fc\u30eb +ReportGenerator.artTableColHdr.tskGpsRouteCategory=\u30ab\u30c6\u30b4\u30ea\u30fc +ReportGenerator.artTableColHdr.tskPath=\u30d1\u30b9 +ReportGenerator.artTableColHdr.calendarEntryType=\u30ab\u30ec\u30f3\u30c0\u30fc\u5165\u529b\u30bf\u30a4\u30d7 +ReportGenerator.artTableColHdr.description=\u8aac\u660e +ReportGenerator.artTableColHdr.startDateTime=\u958b\u59cb\u65e5\u6642 +ReportGenerator.artTableColHdr.endDateTime=\u7d42\u4e86\u65e5\u6642 +ReportGenerator.artTableColHdr.location=\u5834\u6240 +ReportGenerator.artTableColHdr.shortCut=\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8 +ReportGenerator.artTableColHdr.deviceName=\u30c7\u30d0\u30a4\u30b9\u540d +ReportGenerator.artTableColHdr.deviceAddress=\u30c7\u30d0\u30a4\u30b9\u30a2\u30c9\u30ec\u30b9 +ReportGenerator.artTableColHdr.altitude=\u6a19\u9ad8 +ReportGenerator.artTableColHdr.locationAddress=\u6240\u5728\u5730\u30a2\u30c9\u30ec\u30b9 +ReportGenerator.artTableColHdr.category=\u30ab\u30c6\u30b4\u30ea\u30fc +ReportGenerator.artTableColHdr.userId=\u30e6\u30fc\u30b6\u30fcID +ReportGenerator.artTableColHdr.userName=\u30e6\u30fc\u30b6\u30fc\u540d +ReportGenerator.artTableColHdr.password=\u30d1\u30b9\u30ef\u30fc\u30c9 +ReportGenerator.artTableColHdr.appName=\u30a2\u30d7\u30ea\u540d +ReportGenerator.artTableColHdr.appPath=\u30a2\u30d7\u30ea\u30d1\u30b9 +ReportGenerator.artTableColHdr.replytoAddress=\u8fd4\u4fe1\u5148\u30a2\u30c9\u30ec\u30b9 +ReportGenerator.artTableColHdr.mailServer=\u30e1\u30fc\u30eb\u30b5\u30fc\u30d0\u30fc +ReportGenerator.artTableColHdr.tags=\u30bf\u30b0 +ReportGenerator.artTableColHdr.localPath=\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9 +ReportGenerator.artTableColHdr.remotePath=\u30ea\u30e2\u30fc\u30c8\u30d1\u30b9 +ReportGenerator.errors.reportErrorTitle=\u30ec\u30dd\u30fc\u30c8\u751f\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ReportGenerator.errors.reportErrorText=\u30ec\u30dd\u30fc\u30c8\u751f\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: +ReportHTML.addThumbRows.dataType.title=\u30bf\u30b0\u4ed8\u3051\u30a4\u30e1\u30fc\u30b8 - {0} +ReportHTML.addThumbRows.dataType.msg=\u30a4\u30e1\u30fc\u30b8\u3092\u542b\u3080\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u7d50\u679c\u3068\u30b3\u30f3\u30c6\u30f3\u30c4 +ReportHTML.thumbLink.tags=\u30bf\u30b0: +ReportHTML.getName.text=HTML\u30ec\u30dd\u30fc\u30c8 +ReportHTML.getDesc.text=\u7d50\u679c\u3068\u30bf\u30b0\u4ed8\u304d\u9805\u76ee\u306b\u95a2\u3059\u308bHTML\u5f62\u5f0f\u306e\u30ec\u30dd\u30fc\u30c8\u3067\u3059\u3002 +ReportHTML.writeIndex.title=\u30b1\u30fc\u30b9 {0} \u306e +ReportHTML.writeIndex.noFrames.msg=\u304a\u4f7f\u3044\u306e\u30d6\u30e9\u30a6\u30b6\u30fc\u306f\u5f53\u793e\u306e\u30d5\u30ec\u30fc\u30e0\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3068\u4e92\u63db\u6027\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +ReportHTML.writeIndex.noFrames.seeNav=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u306e\u30ea\u30f3\u30af\u306f\u3001the navigation page \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +ReportHTML.writeIndex.seeSum=and \u30b1\u30fc\u30b9\u30b5\u30de\u30ea\u30fc\u306e \u30b5\u30de\u30ea\u30fc\u30da\u30fc\u30b8\u3067\u3059\u3002 +ReportHTML.writeNav.title=\u30ec\u30dd\u30fc\u30c8\u30ca\u30d3\u30b2\u30fc\u30b7\u30e7\u30f3 +ReportHTML.writeNav.h1=\u30ec\u30dd\u30fc\u30c8\u30ca\u30d3\u30b2\u30fc\u30b7\u30e7\u30f3 +ReportHTML.writeNav.summary=\u30b1\u30fc\u30b9\u30b5\u30de\u30ea\u30fc +ReportHTML.writeSum.title=\u30b1\u30fc\u30b9\u30b5\u30de\u30ea\u30fc +ReportHTML.writeSum.warningMsg=\u8b66\u544a: \u3053\u306e\u30ec\u30dd\u30fc\u30c8\u306f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30b5\u30fc\u30d3\u30b9\u306e\u5b8c\u4e86\u524d\u306b\u5b9f\u884c\u3055\u308c\u307e\u3057\u305f\! +# +# autopsy/test/scripts/regression.py._html_report_diff()\u306f reportGenOn.text\u3001caseName\u3001caseNum\u3001 +# examiner\u3092\u6b63\u898f\u8868\u73fe\u30b7\u30b0\u30cd\u30c1\u30e3\u30fc\u3068\u3057\u3066\u4f7f\u7528\u3057\u3001report.html\u3068summary.html\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002 +# +ReportHTML.writeSum.reportGenOn.text={0} \u3067\u751f\u6210\u3055\u308c\u305fHTML\u30ec\u30dd\u30fc\u30c8 +ReportHTML.writeSum.imageInfoHeading=

    \u30a4\u30e1\u30fc\u30b8\u60c5\u5831:

    +ReportHTML.writeSum.softwareInfoHeading=

    \u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u60c5\u5831:

    +ReportHTML.writeSum.ingestHistoryHeading=

    \u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5c65\u6b74:

    +ReportHTML.writeSum.modulesEnabledHeading=\u6709\u52b9\u5316\u3055\u308c\u305f\u30e2\u30b8\u30e5\u30fc\u30eb: +ReportHTML.writeSum.autopsyVersion=Autopsy\u30d0\u30fc\u30b8\u30e7\u30f3: +ReportHTML.writeSum.timezone=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3: +ReportHTML.writeSum.path=\u30d1\u30b9: +ReportProgressPanel.progress.queuing=\u30ad\u30e5\u30fc\u30a4\u30f3\u30b0\u4e2d\u3067\u3059... +ReportProgressPanel.initPathLabel.noFile=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u304c\u3042\u308a\u307e\u305b\u3093 +ReportProgressPanel.start.cancelButton.text=\u53d6\u308a\u6d88\u3057 +ReportProgressPanel.start.progress.text=\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u59cb\u4e2d\u3067\u3059... +ReportProgressPanel.complete.processLbl.text=\u5b8c\u4e86 +ReportProgressPanel.complete.processLb2.text=\u30a8\u30e9\u30fc\u3067\u5b8c\u4e86\u3057\u307e\u3057\u305f +ReportProgressPanel.complete.cancelButton.text=\u5b8c\u4e86 +ReportProgressPanel.cancel.cancelButton.toolTipText=\u53d6\u308a\u6d88\u3057\u6e08\u307f +ReportProgressPanel.cancel.procLbl.text=\u53d6\u308a\u6d88\u3057\u6e08\u307f +ReportVisualPanel1.getName.text=\u30ec\u30dd\u30fc\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u9078\u629e\u3057\u3066\u69cb\u6210 +ReportVisualPanel2.getName.text=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30ec\u30dd\u30fc\u30c8\u3092\u69cb\u6210 +ReportWizardAction.actionName.text=\u30ec\u30dd\u30fc\u30c8\u751f\u6210 +ReportWizardAction.reportWiz.title=\u30ec\u30dd\u30fc\u30c8\u751f\u6210 +ReportWizardAction.toolBarButton.text=\u30ec\u30dd\u30fc\u30c8\u751f\u6210 +ReportWizardFileOptionsPanel.finishButton.text=\u5b8c\u4e86 +ReportWizardFileOptionsVisualPanel.getName.text=\u30d5\u30a1\u30a4\u30eb\u30ec\u30dd\u30fc\u30c8\u3092\u69cb\u6210 +ReportWizardPanel1.nextButton.text=\u6b21\u3078 > +ReportWizardPanel1.finishButton.text=\u5b8c\u4e86 +ReportWizardPanel2.finishButton.text=\u5b8c\u4e86 +ReportWizardPanel2.nextButton.text=\u6b21\u3078 > +ReportBodyFile.generateReport.srcModuleName.text=TSK Body\u30d5\u30a1\u30a4\u30eb +ReportExcel.endReport.srcModuleName.text=Excel\u30ec\u30dd\u30fc\u30c8 +ReportHTML.writeIndex.srcModuleName.text=HTML\u30ec\u30dd\u30fc\u30c8 +ReportKML.genReport.srcModuleName.text=\u5730\u7406\u7a7a\u9593\u30c7\u30fc\u30bf +ReportKML.genReport.reportName=KML\u30ec\u30dd\u30fc\u30c8 +ReportGenerator.artTableColHdr.extension.text=\u62e1\u5f35\u5b50 +ReportGenerator.artTableColHdr.mimeType.text=MIME\u30bf\u30a4\u30d7 +ReportGenerator.artTableColHdr.processorArchitecture.text=\u30d7\u30ed\u30bb\u30c3\u30b5\u30fc\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3 +ReportGenerator.artTableColHdr.osName.text=\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0\u540d +ReportGenerator.artTableColHdr.osInstallDate.text=\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u65e5 +ReportGenerator.errList.failedMakeRptFolder=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30ec\u30dd\u30fc\u30c8\u3092\u751f\u6210\u3067\u304d\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +ReportGenerator.notifyErr.errsDuringRptGen=\u30ec\u30dd\u30fc\u30c8\u751f\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: +ReportGenerator.errList.failedGetContentTags=\u30b3\u30f3\u30c6\u30f3\u30c4\u30bf\u30b0\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ReportGenerator.errList.failedGetBBArtifactTags=\u30d6\u30e9\u30c3\u30af\u30dc\u30fc\u30c9\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30bf\u30b0\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ReportGenerator.errList.errGetContentFromBBArtifact=\u30ec\u30dd\u30fc\u30c8\u4f5c\u6210\u5bfe\u8c61\u306e\u30d6\u30e9\u30c3\u30af\u30dc\u30fc\u30c9\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ReportGenerator.errList.failedGetBBAttribs=\u30ec\u30dd\u30fc\u30c8\u751f\u6210\u4e2d\u306b\u30d6\u30e9\u30c3\u30af\u30dc\u30fc\u30c9\u5c5e\u6027\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ReportGenerator.errList.failedGetBBArtifacts=\u30ec\u30dd\u30fc\u30c8\u751f\u6210\u4e2d\u306b\u30d6\u30e9\u30c3\u30af\u30dc\u30fc\u30c9\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ReportGenerator.errList.failedQueryKWLists=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u30af\u30a8\u30ea\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ReportGenerator.errList.failedGetAbstractFileByID=ID\u5225\u306b\u62bd\u8c61\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ReportGenerator.errList.failedQueryKWs=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u30af\u30a8\u30ea\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ReportGenerator.errList.failedQueryHashsetLists=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30ea\u30b9\u30c8\u3092\u30af\u30a8\u30ea\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ReportGenerator.errList.failedGetAbstractFileFromID=ID\u304b\u3089\u62bd\u8c61\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ReportGenerator.errList.failedQueryHashsetHits=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d2\u30c3\u30c8\u3092\u30af\u30a8\u30ea\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ReportGenerator.errList.coreExceptionWhileGenRptRow=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30ec\u30dd\u30fc\u30c8\u306e\u5217\u30c7\u30fc\u30bf\u306e\u751f\u6210\u4e2d\u306b\u30b3\u30a2\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ReportKML.latLongStartPoint={0};{1};;{2} (\u958b\u59cb)\n +ReportKML.latLongEndPoint={0};{1};;{2} (\u7d42\u4e86)\n +ReportGenerationPanel.cancelButton.actionCommand=\u53d6\u308a\u6d88\u3057 +ReportGenerationPanel.cancelButton.text=\u53d6\u308a\u6d88\u3057 +ReportHTMLConfigurationPanel.headerTextField.text= +ReportHTMLConfigurationPanel.footerTextField.text= +ReportHTMLConfigurationPanel.headerLabel.text=\u30d8\u30c3\u30c0\u30fc: +ReportHTMLConfigurationPanel.footerLabel.text=\u30d5\u30c3\u30bf\u30fc: +CreatePortableCasePanel.selectAllButton.text=\u3059\u3079\u3066\u9078\u629e +CreatePortableCasePanel.deselectAllButton.text=\u3059\u3079\u3066\u9078\u629e\u89e3\u9664 +CreatePortableCasePanel.outputFolderTextField.text=jTextField1 +CreatePortableCasePanel.chooseOutputFolderButton.text=\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u9078\u629e +CreatePortableCasePanel.jLabel1.text=\u6b21\u3068\u3057\u3066\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8: +CreatePortableCasePanel.jLabel2.text=\u51fa\u529b\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u9078\u629e: +CreatePortableCasePanel.errorLabel.text_1=Windows\u306e\u307f +ReportWizardPortableCaseOptionsVisualPanel.errorLabel.text=Windows\u306e\u307f +ReportWizardPortableCaseOptionsVisualPanel.compressCheckbox.text=\u30b1\u30fc\u30b9\u3092\u30a2\u30fc\u30ab\u30a4\u30d6\u5185\u306b\u30d1\u30c3\u30b1\u30fc\u30b8\u5316: +PortableCaseTagsListPanel.deselectButton.text=\u3059\u3079\u3066\u9078\u629e\u89e3\u9664 +PortableCaseTagsListPanel.selectButton.text=\u3059\u3079\u3066\u9078\u629e +PortableCaseInterestingItemsListPanel.selectButton.text=\u3059\u3079\u3066\u9078\u629e +PortableCaseInterestingItemsListPanel.deselectButton.text=\u3059\u3079\u3066\u9078\u629e\u89e3\u9664 +ReportFileTextConfigurationPanel.tabDelimitedButton.text=\u533a\u5207\u308a\u30bf\u30d6 +ReportFileTextConfigurationPanel.commaDelimitedButton.text=\u533a\u5207\u308a\u30ab\u30f3\u30de +PortableCaseTagsListPanel.descLabel.text=\u6b21\u306e\u30bf\u30b0\u3092\u542b\u3080: +PortableCaseInterestingItemsListPanel.descLabel.text=\u3053\u308c\u3089\u306e\u30bb\u30c3\u30c8\u306e\u8208\u5473\u6df1\u3044\u9805\u76ee\u3092\u542b\u3080: +ReportWizardPortableCaseOptionsVisualPanel.compressCheckbox.toolTipText= +ReportWizardPortableCaseOptionsVisualPanel.getName.title=\u30dd\u30fc\u30bf\u30d6\u30eb\u30b1\u30fc\u30b9\u8a2d\u5b9a\u3092\u9078\u629e +TableReportGenerator.StatusColumn.Header=\u30ec\u30d3\u30e5\u30fc\u30b9\u30c6\u30fc\u30bf\u30b9 diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ArtifactSelectionDialog.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ArtifactSelectionDialog.java index 8fef4083ef..78ae4479a5 100644 --- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ArtifactSelectionDialog.java +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ArtifactSelectionDialog.java @@ -77,8 +77,16 @@ class ArtifactSelectionDialog extends javax.swing.JDialog { BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO.getDisplayName())); doNotReport.add(new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getLabel(), - BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getDisplayName())); // output is too unstructured for table review - + BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getDisplayName())); // output is too unstructured for table review + doNotReport.add(new BlackboardArtifact.Type( + BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getLabel(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getDisplayName())); + doNotReport.add(new BlackboardArtifact.Type( + BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT.getLabel(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT.getDisplayName())); + artifactTypes = Case.getCurrentCaseThrows().getSleuthkitCase().getArtifactTypesInUse(); artifactTypes.removeAll(doNotReport); Collections.sort(artifactTypes, new Comparator() { diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/report/infrastructure/Bundle_ja.properties old mode 100755 new mode 100644 index c911dd381d..5077b78fd1 --- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/Bundle_ja.properties @@ -183,4 +183,4 @@ FileReportDataTypes.address.text=\u30a2\u30c9\u30ec\u30b9 FileReportDataTypes.hash.text=\u30cf\u30c3\u30b7\u30e5\u5024 FileReportDataTypes.knownStatus.text=\u65e2\u77e5\u30b9\u30c6\u30fc\u30bf\u30b9 FileReportDataTypes.perms.text=\u30d1\u30fc\u30df\u30c3\u30b7\u30e7\u30f3 -FileReportDataTypes.path.text=\u30d5\u30eb\u30d1\u30b9 \ No newline at end of file +FileReportDataTypes.path.text=\u30d5\u30eb\u30d1\u30b9 diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportVisualPanel2.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportVisualPanel2.java index 7a69681322..e804f0384f 100644 --- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportVisualPanel2.java +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportVisualPanel2.java @@ -206,7 +206,14 @@ final class ReportVisualPanel2 extends JPanel { doNotReport.add(new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getLabel(), BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getDisplayName())); // output is too unstructured for table review - + doNotReport.add(new BlackboardArtifact.Type( + BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getLabel(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getDisplayName())); + doNotReport.add(new BlackboardArtifact.Type( + BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT.getLabel(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT.getDisplayName())); // get artifact types that exist in the current case artifacts = openCase.getSleuthkitCase().getArtifactTypesInUse(); diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java index e336538d4d..634f779988 100644 --- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java @@ -58,7 +58,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; @ActionReferences(value = { @ActionReference(path = "Menu/Tools", position = 301, separatorAfter = 399) , - @ActionReference(path = "Toolbars/Case", position = 105)}) + @ActionReference(path = "Toolbars/Case", position = 106)}) public final class ReportWizardAction extends CallableSystemAction implements Presenter.Toolbar, ActionListener { private static final Logger logger = Logger.getLogger(ReportWizardAction.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java index 264ed461c4..3d4355f233 100644 --- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java @@ -108,6 +108,14 @@ class TableReportGenerator { doNotReport.add(new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getLabel(), BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getDisplayName())); // output is too unstructured for table review + doNotReport.add(new BlackboardArtifact.Type( + BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getLabel(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getDisplayName())); + doNotReport.add(new BlackboardArtifact.Type( + BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT.getLabel(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT.getDisplayName())); Case.getCurrentCaseThrows().getSleuthkitCase().getArtifactTypes().forEach(artifactTypes::add); artifactTypes.removeAll(doNotReport); diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/bodyfile/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/report/modules/bodyfile/Bundle_ja.properties old mode 100755 new mode 100644 diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/excel/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/report/modules/excel/Bundle_ja.properties old mode 100755 new mode 100644 index 0c990dc024..fd9a57fb2a --- a/Core/src/org/sleuthkit/autopsy/report/modules/excel/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/report/modules/excel/Bundle_ja.properties @@ -7,4 +7,4 @@ ReportExcel.cellVal.caseName=\u30b1\u30fc\u30b9\u540d\uff1a ReportExcel.cellVal.caseNum=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a ReportExcel.cellVal.examiner=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a ReportExcel.cellVal.numImages=\u30a4\u30e1\u30fc\u30b8\u6570\uff1a -ReportExcel.endReport.srcModuleName.text=Excel\u30ec\u30dd\u30fc\u30c8 \ No newline at end of file +ReportExcel.endReport.srcModuleName.text=Excel\u30ec\u30dd\u30fc\u30c8 diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/file/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/report/modules/file/Bundle_ja.properties old mode 100755 new mode 100644 diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle.properties-MERGED index 32f6867f0c..0be7595111 100755 --- a/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle.properties-MERGED @@ -5,8 +5,8 @@ ReportHTML.getName.text=HTML Report ReportHTML.getDesc.text=A report about results and tagged items in HTML format. ReportHTML.writeIndex.title=for case {0} ReportHTML.writeIndex.noFrames.msg=Your browser is not compatible with our frame setup. -ReportHTML.writeIndex.noFrames.seeNav=Please see the navigation page for artifact links, -ReportHTML.writeIndex.seeSum=and the summary page for a case summary. +ReportHTML.writeIndex.noFrames.seeNav=Please see the navigation page for artifact links, +ReportHTML.writeIndex.seeSum=and the summary page for a case summary. ReportHTML.writeNav.title=Report Navigation ReportHTML.writeNav.h1=Report Navigation ReportHTML.writeNav.summary=Case Summary @@ -16,7 +16,7 @@ ReportHTML.writeSum.caseNumber=Case Number: ReportHTML.writeSum.caseNumImages=Number of Images: ReportHTML.writeSum.examiner=Examiner: ReportHTML.writeSum.title=Case Summary -ReportHTML.writeSum.warningMsg=Warning, this report was run before ingest services completed\! +ReportHTML.writeSum.warningMsg=Warning, this report was run before ingest services completed! # # autopsy/test/scripts/regression.py._html_report_diff() uses reportGenOn.text, caseName, caseNum, # examiner as a regex signature to skip report.html and summary.html diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle_ja.properties old mode 100755 new mode 100644 index 50dfb93246..f05aa0e273 --- a/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle_ja.properties @@ -22,4 +22,4 @@ ReportHTML.writeSum.imageInfoHeading=

    \u30a4\u30e1\u30fc\u30b8\u60c5\u5831\uf ReportHTML.writeSum.timezone=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\uff1a ReportHTML.writeSum.reportGenOn.text={0}\u306bHTML\u30ec\u30dd\u30fc\u30c8\u306f\u751f\u6210\u3055\u308c\u307e\u3057\u305f ReportHTML.writeSum.noCaseNum=\u30b1\u30fc\u30b9\u756a\u53f7\u304c\u3042\u308a\u307e\u305b\u3093 -ReportHTML.writeIndex.srcModuleName.text=HTML\u30ec\u30dd\u30fc\u30c8 \ No newline at end of file +ReportHTML.writeIndex.srcModuleName.text=HTML\u30ec\u30dd\u30fc\u30c8 diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle.properties-MERGED index 0823cd171b..3cbb7f6506 100755 --- a/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle.properties-MERGED @@ -23,3 +23,10 @@ ReportKML.genReport.srcModuleName.text=Geospatial Data ReportKML.genReport.reportName=KML Report ReportKML.latLongStartPoint={0};{1};;{2} (Start)\n ReportKML.latLongEndPoint={0};{1};;{2} (End)\n +Route_Details_Header=GPS Route +Waypoint_Bookmark_Display_String=GPS Bookmark +Waypoint_EXIF_Display_String=EXIF Metadata With Location +Waypoint_Last_Known_Display_String=GPS Last Known Location +Waypoint_Route_Point_Display_String=GPS Individual Route Point +Waypoint_Search_Display_String=GPS Search +Waypoint_Trackpoint_Display_String=GPS Trackpoint diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle_ja.properties old mode 100755 new mode 100644 index 27048c26c1..4593a4b4b6 --- a/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle_ja.properties @@ -3,4 +3,4 @@ ReportKML.getName.text=Google Earth/KML ReportKML.progress.loading=\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u2026 ReportKML.progress.querying=\u30d5\u30a1\u30a4\u30eb\u306e\u30af\u30a8\u30ea\u3092\u5b9f\u884c\u4e2d\u2026 ReportKML.genReport.reportName=KML\u30ec\u30dd\u30fc\u30c8 -ReportKML.getFilePath.text=\u30ec\u30dd\u30fc\u30c8KML.kml \ No newline at end of file +ReportKML.getFilePath.text=\u30ec\u30dd\u30fc\u30c8KML.kml diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java b/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java index c1a8ec4046..a002a0279d 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java @@ -21,13 +21,10 @@ package org.sleuthkit.autopsy.report.modules.kml; import org.sleuthkit.autopsy.report.GeneralReportModule; import javax.swing.JPanel; - import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.*; import org.sleuthkit.autopsy.ingest.IngestManager; -import org.sleuthkit.datamodel.BlackboardArtifact; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -36,6 +33,8 @@ import java.io.OutputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; import java.util.logging.Level; import org.jdom2.Document; import org.jdom2.Element; @@ -44,15 +43,24 @@ import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; import org.jdom2.CDATA; import org.openide.filesystems.FileUtil; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; +import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint; +import org.sleuthkit.autopsy.geolocation.datamodel.Route; +import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder; import org.sleuthkit.autopsy.report.ReportBranding; import org.sleuthkit.autopsy.report.ReportProgressPanel; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; /** * Generates a KML file based on geospatial information from the BlackBoard. */ -class KMLReport implements GeneralReportModule { +public final class KMLReport implements GeneralReportModule { private static final Logger logger = Logger.getLogger(KMLReport.class.getName()); private static final String KML_STYLE_FILE = "style.kml"; @@ -63,7 +71,16 @@ class KMLReport implements GeneralReportModule { private SleuthkitCase skCase; private final SimpleDateFormat kmlDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"); private Namespace ns; - private final String SEP = "
    "; + private final static String HTML_PROP_FORMAT = "%s: %s
    "; + + private Element gpsExifMetadataFolder; + private Element gpsBookmarksFolder; + private Element gpsLastKnownLocationFolder; + private Element gpsRouteFolder; + private Element gpsSearchesFolder; + private Element gpsTrackpointsFolder; + + private List waypointList = null; private enum FeatureColor { RED("style.kml#redFeature"), @@ -101,7 +118,7 @@ class KMLReport implements GeneralReportModule { * @param baseReportDir path to save the report * @param progressPanel panel to update the report's progress */ - @NbBundle.Messages({ + @Messages({ "KMLReport.unableToExtractPhotos=Could not extract photo information.", "KMLReport.exifPhotoError=Could not extract photos with EXIF metadata.", "KMLReport.bookmarkError=Could not extract Bookmark information.", @@ -111,14 +128,27 @@ class KMLReport implements GeneralReportModule { "KMLReport.gpsRouteError=Could not extract GPS Route information.", "KMLReport.gpsRouteDatabaseError=Could not get GPS Routes from database.", "KMLReport.gpsSearchDatabaseError=Could not get GPS Searches from database.", - "KMLReport.trackpointError=Could not extract Trackpoint information.", - "KMLReport.trackpointDatabaseError=Could not get GPS Trackpoints from database.", + "KMLReport.trackpointError=Could not extract Trackpoint information.", + "KMLReport.trackpointDatabaseError=Could not get GPS Trackpoints from database.", "KMLReport.stylesheetError=Error placing KML stylesheet. The .KML file will not function properly.", "KMLReport.kmlFileWriteError=Could not write the KML file.", "# {0} - filePath", "KMLReport.errorGeneratingReport=Error adding {0} to case as a report.", - "KMLReport.unableToOpenCase=Exception while getting open case." + "KMLReport.unableToOpenCase=Exception while getting open case.", + "Waypoint_Bookmark_Display_String=GPS Bookmark", + "Waypoint_Last_Known_Display_String=GPS Last Known Location", + "Waypoint_EXIF_Display_String=EXIF Metadata With Location", + "Waypoint_Route_Point_Display_String=GPS Individual Route Point", + "Waypoint_Search_Display_String=GPS Search", + "Waypoint_Trackpoint_Display_String=GPS Trackpoint", + "Route_Details_Header=GPS Route" }) + + public void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List waypointList) { + this.waypointList = waypointList; + generateReport(baseReportDir, progressPanel); + } + @Override public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) { try { @@ -133,272 +163,20 @@ class KMLReport implements GeneralReportModule { progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.querying")); String kmlFileFullPath = baseReportDir + REPORT_KML; //NON-NLS String errorMessage = ""; - + skCase = currentCase.getSleuthkitCase(); progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.loading")); - ns = Namespace.getNamespace("", "http://www.opengis.net/kml/2.2"); //NON-NLS - - Element kml = new Element("kml", ns); //NON-NLS - kml.addNamespaceDeclaration(Namespace.getNamespace("gx", "http://www.google.com/kml/ext/2.2")); //NON-NLS - kml.addNamespaceDeclaration(Namespace.getNamespace("kml", "http://www.opengis.net/kml/2.2")); //NON-NLS - kml.addNamespaceDeclaration(Namespace.getNamespace("atom", "http://www.w3.org/2005/Atom")); //NON-NLS - Document kmlDocument = new Document(kml); - - Element document = new Element("Document", ns); //NON-NLS - kml.addContent(document); - - Element name = new Element("name", ns); //NON-NLS - ReportBranding rb = new ReportBranding(); - name.setText(rb.getReportTitle() + " KML"); //NON-NLS - document.addContent(name); - - // Check if ingest has finished - if (IngestManager.getInstance().isIngestRunning()) { - Element ingestwarning = new Element("snippet", ns); //NON-NLS - ingestwarning.addContent(NbBundle.getMessage(this.getClass(), "ReportBodyFile.ingestWarning.text")); //NON-NLS - document.addContent(ingestwarning); - } - - // Create folder structure - Element gpsExifMetadataFolder = new Element("Folder", ns); //NON-NLS - CDATA cdataExifMetadataFolder = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/camera-icon-16.png"); //NON-NLS - Element hrefExifMetadata = new Element("href", ns).addContent(cdataExifMetadataFolder); //NON-NLS - gpsExifMetadataFolder.addContent(new Element("Icon", ns).addContent(hrefExifMetadata)); //NON-NLS - - Element gpsBookmarksFolder = new Element("Folder", ns); //NON-NLS - CDATA cdataBookmarks = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gpsfav.png"); //NON-NLS - Element hrefBookmarks = new Element("href", ns).addContent(cdataBookmarks); //NON-NLS - gpsBookmarksFolder.addContent(new Element("Icon", ns).addContent(hrefBookmarks)); //NON-NLS - - Element gpsLastKnownLocationFolder = new Element("Folder", ns); //NON-NLS - CDATA cdataLastKnownLocation = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-lastlocation.png"); //NON-NLS - Element hrefLastKnownLocation = new Element("href", ns).addContent(cdataLastKnownLocation); //NON-NLS - gpsLastKnownLocationFolder.addContent(new Element("Icon", ns).addContent(hrefLastKnownLocation)); //NON-NLS - - Element gpsRouteFolder = new Element("Folder", ns); //NON-NLS - CDATA cdataRoute = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS - Element hrefRoute = new Element("href", ns).addContent(cdataRoute); //NON-NLS - gpsRouteFolder.addContent(new Element("Icon", ns).addContent(hrefRoute)); //NON-NLS - - Element gpsSearchesFolder = new Element("Folder", ns); //NON-NLS - CDATA cdataSearches = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-search.png"); //NON-NLS - Element hrefSearches = new Element("href", ns).addContent(cdataSearches); //NON-NLS - gpsSearchesFolder.addContent(new Element("Icon", ns).addContent(hrefSearches)); //NON-NLS - - Element gpsTrackpointsFolder = new Element("Folder", ns); //NON-NLS - CDATA cdataTrackpoints = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS - Element hrefTrackpoints = new Element("href", ns).addContent(cdataTrackpoints); //NON-NLS - gpsTrackpointsFolder.addContent(new Element("Icon", ns).addContent(hrefTrackpoints)); //NON-NLS - - gpsExifMetadataFolder.addContent(new Element("name", ns).addContent("EXIF Metadata")); //NON-NLS - gpsBookmarksFolder.addContent(new Element("name", ns).addContent("GPS Bookmarks")); //NON-NLS - gpsLastKnownLocationFolder.addContent(new Element("name", ns).addContent("GPS Last Known Location")); //NON-NLS - gpsRouteFolder.addContent(new Element("name", ns).addContent("GPS Routes")); //NON-NLS - gpsSearchesFolder.addContent(new Element("name", ns).addContent("GPS Searches")); //NON-NLS - gpsTrackpointsFolder.addContent(new Element("name", ns).addContent("GPS Trackpoints")); //NON-NLS - - document.addContent(gpsExifMetadataFolder); - document.addContent(gpsBookmarksFolder); - document.addContent(gpsLastKnownLocationFolder); - document.addContent(gpsRouteFolder); - document.addContent(gpsSearchesFolder); - document.addContent(gpsTrackpointsFolder); + Document kmlDocument = setupReportDocument(); ReportProgressPanel.ReportStatus result = ReportProgressPanel.ReportStatus.COMPLETE; - /** - * In the following code, nulls are okay, and are handled when we go to - * write out the KML feature. Nulls are expected to be returned from any - * method where the artifact is not found and is handled in the - * individual feature creation methods. This is done because we don't - * know beforehand which attributes will be included for which artifact, - * as anyone could write a module that adds additional attributes to an - * artifact. - * - * If there are any issues reading the database getting artifacts and - * attributes, or any exceptions thrown during this process, a severe - * error is logged, the report is marked as "Incomplete KML Report", and - * we use a best-effort method to generate KML information on everything - * we can successfully pull out of the database. - */ try { - for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF)) { - String fileName = ""; - long fileId = 0; - try { - Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED); - String desc = getDescriptionFromArtifact(artifact, "EXIF Metadata With Locations"); //NON-NLS - Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE); - Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE); - Element point = makePoint(lat, lon, getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE)); - - if (lat != null && lat != 0.0 && lon != null && lon != 0.0) { - AbstractFile abstractFile = artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID()); - fileName = abstractFile.getName(); - fileId = abstractFile.getId(); - Path path; - copyFileUsingStream(abstractFile, Paths.get(baseReportDir, abstractFile.getName()).toFile()); - try { - path = Paths.get(removeLeadingImgAndVol(abstractFile.getUniquePath())); - } catch (TskCoreException ex) { - path = Paths.get(abstractFile.getParentPath(), abstractFile.getName()); - } - String formattedCoordinates = String.format("%.2f, %.2f", lat, lon); - if (path == null) { - path = Paths.get(abstractFile.getName()); - } - gpsExifMetadataFolder.addContent(makePlacemarkWithPicture(abstractFile.getName(), FeatureColor.RED, desc, timestamp, point, path, formattedCoordinates)); - } - } catch (ReadContentInputStreamException ex) { - logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d).", fileName, fileId), ex); - } catch (Exception ex) { - errorMessage = Bundle.KMLReport_unableToExtractPhotos(); - logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS - result = ReportProgressPanel.ReportStatus.ERROR; - } - } - } catch (TskCoreException ex) { - errorMessage = Bundle.KMLReport_exifPhotoError(); - logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS - result = ReportProgressPanel.ReportStatus.ERROR; - } - - try { - for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK)) { - try { - Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); - String desc = getDescriptionFromArtifact(artifact, "GPS Bookmark"); //NON-NLS - Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE); - Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE); - Element point = makePoint(lat, lon, getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE)); - String bookmarkName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); - String formattedCoordinates = String.format("%.2f, %.2f", lat, lon); - gpsBookmarksFolder.addContent(makePlacemark(bookmarkName, FeatureColor.BLUE, desc, timestamp, point, formattedCoordinates)); - } catch (Exception ex) { - errorMessage = Bundle.KMLReport_bookmarkError(); - logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS - result = ReportProgressPanel.ReportStatus.ERROR; - } - } - } catch (TskCoreException ex) { - errorMessage = Bundle.KMLReport_gpsBookmarkError(); - logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS - result = ReportProgressPanel.ReportStatus.ERROR; - } - - try { - for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION)) { - try { - Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); - String desc = getDescriptionFromArtifact(artifact, "GPS Last Known Location"); //NON-NLS - Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE); - Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE); - Double alt = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); - Element point = makePoint(lat, lon, alt); - String formattedCoordinates = String.format("%.2f, %.2f", lat, lon); - gpsLastKnownLocationFolder.addContent(makePlacemark("Last Known Location", FeatureColor.PURPLE, desc, timestamp, point, formattedCoordinates)); //NON-NLS - } catch (Exception ex) { - errorMessage = Bundle.KMLReport_locationError(); - logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS - result = ReportProgressPanel.ReportStatus.ERROR; - } - } - } catch (TskCoreException ex) { - errorMessage = Bundle.KMLReport_locationDatabaseError(); - logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS - result = ReportProgressPanel.ReportStatus.ERROR; - } - - try { - for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE)) { - try { - Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); - String desc = getDescriptionFromArtifact(artifact, "GPS Route"); - Double latitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START); - Double longitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START); - Double latitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END); - Double longitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END); - Double altitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); - - Element route = makeLineString(latitudeStart, longitudeStart, altitude, latitudeEnd, longitudeEnd, altitude); - Element startingPoint = makePoint(latitudeStart, longitudeStart, altitude); - Element endingPoint = makePoint(latitudeEnd, longitudeEnd, altitude); - - String formattedCoordinates = String.format("%.2f, %.2f to %.2f, %.2f", latitudeStart, longitudeStart, latitudeEnd, longitudeEnd); - gpsRouteFolder.addContent(makePlacemark("As-the-crow-flies Route", FeatureColor.GREEN, desc, timestamp, route, formattedCoordinates)); //NON-NLS - - formattedCoordinates = String.format("%.2f, %.2f", latitudeStart, longitudeStart); - gpsRouteFolder.addContent(makePlacemark("Start", FeatureColor.GREEN, desc, timestamp, startingPoint, formattedCoordinates)); //NON-NLS - - formattedCoordinates = String.format("%.2f, %.2f", latitudeEnd, longitudeEnd); - gpsRouteFolder.addContent(makePlacemark("End", FeatureColor.GREEN, desc, timestamp, endingPoint, formattedCoordinates)); //NON-NLS - } catch (Exception ex) { - errorMessage = Bundle.KMLReport_gpsRouteError(); - logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS - result = ReportProgressPanel.ReportStatus.ERROR; - } - } - } catch (TskCoreException ex) { - errorMessage = Bundle.KMLReport_gpsRouteDatabaseError(); - logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS - result = ReportProgressPanel.ReportStatus.ERROR; - } - - try { - for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH)) { - Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); - String desc = getDescriptionFromArtifact(artifact, "GPS Search"); //NON-NLS - Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE); - Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE); - Double alt = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); - Element point = makePoint(lat, lon, alt); - String formattedCoordinates = String.format("%.2f, %.2f", lat, lon); - String searchName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); - if (searchName == null || searchName.isEmpty()) { - searchName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION); - } - if (searchName == null || searchName.isEmpty()) { - searchName = "GPS Search"; - } - gpsSearchesFolder.addContent(makePlacemark(searchName, FeatureColor.WHITE, desc, timestamp, point, formattedCoordinates)); //NON-NLS - } - } catch (TskCoreException ex) { - errorMessage = Bundle.KMLReport_gpsSearchDatabaseError(); - logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS - result = ReportProgressPanel.ReportStatus.ERROR; - } - - try { - for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT)) { - try { - Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); - String desc = getDescriptionFromArtifact(artifact, "GPS Trackpoint"); //NON-NLS - Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE); - Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE); - Double alt = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); - Element point = makePoint(lat, lon, alt); - String formattedCoordinates = String.format("%.2f, %.2f, %.2f", lat, lon, alt); - String trackName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); - if (trackName == null || trackName.isEmpty()) { - trackName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME); - } - if (trackName == null || trackName.isEmpty()) { - trackName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG); - } - if (trackName == null || trackName.isEmpty()) { - trackName = "GPS Trackpoint"; - } - gpsTrackpointsFolder.addContent(makePlacemark(trackName, FeatureColor.YELLOW, desc, timestamp, point, formattedCoordinates)); - } catch (Exception ex) { - errorMessage = Bundle.KMLReport_trackpointError(); - logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS - result = ReportProgressPanel.ReportStatus.ERROR; - } - } - } catch (TskCoreException ex) { - errorMessage = Bundle.KMLReport_trackpointDatabaseError(); + makeRoutes(skCase); + addLocationsToReport(skCase, baseReportDir); + } catch (GeoLocationDataException | IOException ex) { + errorMessage = "Failed to complete report."; logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS result = ReportProgressPanel.ReportStatus.ERROR; } @@ -442,247 +220,266 @@ class KMLReport implements GeneralReportModule { } /** - * Get a Double from an artifact if it exists, return null otherwise. + * Do all of the setting up of elements needed for the report. * - * @param artifact The artifact to query - * @param type The attribute type we're looking for - * - * @return The Double if it exists, or null if not + * @return The report document object. */ - private Double getDouble(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) { - Double returnValue = null; - try { - BlackboardAttribute bba = artifact.getAttribute(new BlackboardAttribute.Type(type)); - if (bba != null) { - Double value = bba.getValueDouble(); - returnValue = value; - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting Double value: " + type.toString(), ex); //NON-NLS + private Document setupReportDocument() { + ns = Namespace.getNamespace("", "http://www.opengis.net/kml/2.2"); //NON-NLS + + Element kml = new Element("kml", ns); //NON-NLS + kml.addNamespaceDeclaration(Namespace.getNamespace("gx", "http://www.google.com/kml/ext/2.2")); //NON-NLS + kml.addNamespaceDeclaration(Namespace.getNamespace("kml", "http://www.opengis.net/kml/2.2")); //NON-NLS + kml.addNamespaceDeclaration(Namespace.getNamespace("atom", "http://www.w3.org/2005/Atom")); //NON-NLS + Document kmlDocument = new Document(kml); + + Element document = new Element("Document", ns); //NON-NLS + kml.addContent(document); + + Element name = new Element("name", ns); //NON-NLS + ReportBranding rb = new ReportBranding(); + name.setText(rb.getReportTitle() + " KML"); //NON-NLS + document.addContent(name); + + // Check if ingest has finished + if (IngestManager.getInstance().isIngestRunning()) { + Element ingestwarning = new Element("snippet", ns); //NON-NLS + ingestwarning.addContent(NbBundle.getMessage(this.getClass(), "ReportBodyFile.ingestWarning.text")); //NON-NLS + document.addContent(ingestwarning); } - return returnValue; + + // Create folder structure + gpsExifMetadataFolder = new Element("Folder", ns); //NON-NLS + CDATA cdataExifMetadataFolder = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/camera-icon-16.png"); //NON-NLS + Element hrefExifMetadata = new Element("href", ns).addContent(cdataExifMetadataFolder); //NON-NLS + gpsExifMetadataFolder.addContent(new Element("Icon", ns).addContent(hrefExifMetadata)); //NON-NLS + + gpsBookmarksFolder = new Element("Folder", ns); //NON-NLS + CDATA cdataBookmarks = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gpsfav.png"); //NON-NLS + Element hrefBookmarks = new Element("href", ns).addContent(cdataBookmarks); //NON-NLS + gpsBookmarksFolder.addContent(new Element("Icon", ns).addContent(hrefBookmarks)); //NON-NLS + + gpsLastKnownLocationFolder = new Element("Folder", ns); //NON-NLS + CDATA cdataLastKnownLocation = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-lastlocation.png"); //NON-NLS + Element hrefLastKnownLocation = new Element("href", ns).addContent(cdataLastKnownLocation); //NON-NLS + gpsLastKnownLocationFolder.addContent(new Element("Icon", ns).addContent(hrefLastKnownLocation)); //NON-NLS + + gpsRouteFolder = new Element("Folder", ns); //NON-NLS + CDATA cdataRoute = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS + Element hrefRoute = new Element("href", ns).addContent(cdataRoute); //NON-NLS + gpsRouteFolder.addContent(new Element("Icon", ns).addContent(hrefRoute)); //NON-NLS + + gpsSearchesFolder = new Element("Folder", ns); //NON-NLS + CDATA cdataSearches = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-search.png"); //NON-NLS + Element hrefSearches = new Element("href", ns).addContent(cdataSearches); //NON-NLS + gpsSearchesFolder.addContent(new Element("Icon", ns).addContent(hrefSearches)); //NON-NLS + + gpsTrackpointsFolder = new Element("Folder", ns); //NON-NLS + CDATA cdataTrackpoints = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS + Element hrefTrackpoints = new Element("href", ns).addContent(cdataTrackpoints); //NON-NLS + gpsTrackpointsFolder.addContent(new Element("Icon", ns).addContent(hrefTrackpoints)); //NON-NLS + + gpsExifMetadataFolder.addContent(new Element("name", ns).addContent("EXIF Metadata")); //NON-NLS + gpsBookmarksFolder.addContent(new Element("name", ns).addContent("GPS Bookmarks")); //NON-NLS + gpsLastKnownLocationFolder.addContent(new Element("name", ns).addContent("GPS Last Known Location")); //NON-NLS + gpsRouteFolder.addContent(new Element("name", ns).addContent("GPS Routes")); //NON-NLS + gpsSearchesFolder.addContent(new Element("name", ns).addContent("GPS Searches")); //NON-NLS + gpsTrackpointsFolder.addContent(new Element("name", ns).addContent("GPS Trackpoints")); //NON-NLS + + document.addContent(gpsExifMetadataFolder); + document.addContent(gpsBookmarksFolder); + document.addContent(gpsLastKnownLocationFolder); + document.addContent(gpsRouteFolder); + document.addContent(gpsSearchesFolder); + document.addContent(gpsTrackpointsFolder); + + return kmlDocument; } /** - * Get a Long from an artifact if it exists, return null otherwise. + * For the given point, create the data needed for the EXIF_METADATA * - * @param artifact The artifact to query - * @param type The attribute type we're looking for + * @param location The geolocation of the data + * @param baseReportDirectory The report directory where the image will be + * created. * - * @return The Long if it exists, or null if not + * @throws IOException */ - private Long getLong(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) { - Long returnValue = null; - try { - BlackboardAttribute bba = artifact.getAttribute(new BlackboardAttribute.Type(type)); - if (bba != null) { - Long value = bba.getValueLong(); - returnValue = value; + void addExifMetadataContent(List points, String baseReportDirectory) throws IOException { + for (Waypoint point : points) { + Element mapPoint = makePoint(point); + if (mapPoint == null) { + return; } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting Long value: " + type.toString(), ex); //NON-NLS + + AbstractFile abstractFile = point.getImage(); + String details = getFormattedDetails(point, Bundle.Waypoint_EXIF_Display_String()); + + Path path; + copyFileUsingStream(abstractFile, Paths.get(baseReportDirectory, abstractFile.getName()).toFile()); + try { + path = Paths.get(removeLeadingImgAndVol(abstractFile.getUniquePath())); + } catch (TskCoreException ex) { + path = Paths.get(abstractFile.getParentPath(), abstractFile.getName()); + } + if (path == null) { + path = Paths.get(abstractFile.getName()); + } + + gpsExifMetadataFolder.addContent(makePlacemarkWithPicture(abstractFile.getName(), FeatureColor.RED, details, point.getTimestamp(), mapPoint, path, formattedCoordinates(point.getLatitude(), point.getLongitude()))); } - return returnValue; } /** - * Get an Integer from an artifact if it exists, return null otherwise. + * Add the new location to the correct folder based on artifact type. * - * @param artifact The artifact to query - * @param type The attribute type we're looking for + * @param skCase Currently open case + * @param baseReportDir Output directory for the report. * - * @return The Integer if it exists, or null if not + * @throws TskCoreException + * @throws IOException */ - private Integer getInteger(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) { - Integer returnValue = null; - try { - BlackboardAttribute bba = artifact.getAttribute(new BlackboardAttribute.Type(type)); - if (bba != null) { - Integer value = bba.getValueInt(); - returnValue = value; - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting Integer value: " + type.toString(), ex); //NON-NLS + void addLocationsToReport(SleuthkitCase skCase, String baseReportDir) throws GeoLocationDataException, IOException { + if (waypointList == null) { + addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(skCase), baseReportDir); + addWaypoints(WaypointBuilder.getBookmarkWaypoints(skCase), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String()); + addWaypoints(WaypointBuilder.getLastKnownWaypoints(skCase), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String()); + addWaypoints(WaypointBuilder.getSearchWaypoints(skCase), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String()); + addWaypoints(WaypointBuilder.getTrackpointWaypoints(skCase), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String()); + } else { + addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(waypointList), baseReportDir); + addWaypoints(WaypointBuilder.getBookmarkWaypoints(waypointList), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String()); + addWaypoints(WaypointBuilder.getLastKnownWaypoints(waypointList), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String()); + addWaypoints(WaypointBuilder.getSearchWaypoints(waypointList), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String()); + addWaypoints(WaypointBuilder.getTrackpointWaypoints(waypointList), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String()); } - return returnValue; } /** - * Get a String from an artifact if it exists, return null otherwise. + * For each point in the waypoint list an Element to represent the given + * waypoint is created and added it to the given Element folder. * - * @param artifact The artifact to query - * @param type The attribute type we're looking for - * - * @return The String if it exists, or null if not + * @param points List of waypoints to add to the report + * @param folder The Element folder to add the points to + * @param waypointColor The color the waypoint should appear in the report */ - private String getString(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) { - String returnValue = null; - try { - BlackboardAttribute bba = artifact.getAttribute(new BlackboardAttribute.Type(type)); - if (bba != null) { - String value = bba.getValueString(); - if (value != null && !value.isEmpty()) { - returnValue = value; - } - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting String value: " + type.toString(), ex); //NON-NLS + void addWaypoints(List points, Element folder, FeatureColor waypointColor, String headerLabel) { + for (Waypoint point : points) { + addContent(folder, point.getLabel(), waypointColor, getFormattedDetails(point, headerLabel), point.getTimestamp(), makePoint(point), point.getLatitude(), point.getLongitude()); } - return returnValue; } /** - * This method creates a text description for a map feature using all the - * geospatial and time data we can for the Artifact. It queries the - * following attributes: + * Adds the waypoint Element with details to the report in the given folder. * - * TSK_GEO_LATITUDE 54; TSK_GEO_LONGITUDE 55; TSK_GEO_LATITUDE_START 98; - * TSK_GEO_LATITUDE_END 99; TSK_GEO_LONGITUDE_START 100; - * TSK_GEO_LONGITUDE_END 101; TSK_GEO_VELOCITY 56; TSK_GEO_ALTITUDE 57; - * TSK_GEO_BEARING 58; TSK_GEO_HPRECISION 59; TSK_GEO_VPRECISION 60; - * TSK_GEO_MAPDATUM 61; TSK_DATETIME_START 83; TSK_DATETIME_END 84; - * TSK_LOCATION 86; TSK_PATH_SOURCE 94; - * - * @param artifact the artifact to query. - * @param featureType the type of Artifact we're working on. - * - * @return a String with the information we have available + * @param folder Element folder to add the waypoint to + * @param waypointLabel String waypoint Label + * @param waypointColor FeatureColor for the waypoint + * @param formattedDetails String HTML formatted waypoint details + * @param timestamp Long timestamp (unix\jave epoch seconds) + * @param point Element point object + * @param latitude Double latitude value + * @param longitude Double longitude value */ - private String getDescriptionFromArtifact(BlackboardArtifact artifact, String featureType) { - StringBuilder result = new StringBuilder("

    " + featureType + "

    "); //NON-NLS - - String name = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); - if (name != null && !name.isEmpty()) { - result.append("Name: ").append(name).append(SEP); //NON-NLS + void addContent(Element folder, String waypointLabel, FeatureColor waypointColor, String formattedDetails, Long timestamp, Element point, Double latitude, Double longitude) { + if (folder != null && point != null) { + String formattedCords = formattedCoordinates(latitude, longitude); + folder.addContent(makePlacemark(waypointLabel, waypointColor, formattedDetails, timestamp, point, formattedCords)); } - - String location = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION); - if (location != null && !location.isEmpty()) { - result.append("Location: ").append(location).append(SEP); //NON-NLS - } - - Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); - if (timestamp != null) { - result.append("Timestamp: ").append(getTimeStamp(timestamp)).append(SEP); //NON-NLS - result.append("Unix timestamp: ").append(timestamp).append(SEP); //NON-NLS - } - - Long startingTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START); - if (startingTimestamp != null) { - result.append("Starting Timestamp: ").append(getTimeStamp(startingTimestamp)).append(SEP); //NON-NLS - result.append("Starting Unix timestamp: ").append(startingTimestamp).append(SEP); //NON-NLS - } - - Long endingTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END); - if (endingTimestamp != null) { - result.append("Ending Timestamp: ").append(getTimeStamp(endingTimestamp)).append(SEP); //NON-NLS - result.append("Ending Unix timestamp: ").append(endingTimestamp).append(SEP); //NON-NLS - } - - Long createdTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED); - if (createdTimestamp != null) { - result.append("Created Timestamp: ").append(getTimeStamp(createdTimestamp)).append(SEP); //NON-NLS - result.append("Created Unix timestamp: ").append(createdTimestamp).append(SEP); //NON-NLS - } - - Double latitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE); - if (latitude != null) { - result.append("Latitude: ").append(latitude).append(SEP); //NON-NLS - } - - Double longitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE); - if (longitude != null) { - result.append("Longitude: ").append(longitude).append(SEP); //NON-NLS - } - - Double latitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START); - if (latitudeStart != null) { - result.append("Latitude Start: ").append(latitudeStart).append(SEP); //NON-NLS - } - - Double longitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START); - if (longitudeStart != null) { - result.append("Longitude Start: ").append(longitudeStart).append(SEP); //NON-NLS - } - - Double latitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END); - if (latitudeEnd != null) { - result.append("Latitude End: ").append(latitudeEnd).append(SEP); //NON-NLS - } - - Double longitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END); - if (longitudeEnd != null) { - result.append("Longitude End: ").append(longitudeEnd).append(SEP); //NON-NLS - } - - Double velocity = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_VELOCITY); - if (velocity != null) { - result.append("Velocity: ").append(velocity).append(SEP); //NON-NLS - } - - Double altitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); - if (altitude != null) { - result.append("Altitude: ").append(altitude).append(SEP); //NON-NLS - } - - Double bearing = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_BEARING); - if (bearing != null) { - result.append("Bearing: ").append(bearing).append(SEP); //NON-NLS - } - - Integer hPrecision = getInteger(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_HPRECISION); - if (hPrecision != null) { - result.append("Horizontal Precision Figure of Merit: ").append(hPrecision).append(SEP); //NON-NLS - } - - Integer vPrecision = getInteger(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_VPRECISION); - if (vPrecision != null) { - result.append("Vertical Precision Figure of Merit: ").append(vPrecision).append(SEP); //NON-NLS - } - - String mapDatum = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_MAPDATUM); - if (mapDatum != null && !mapDatum.isEmpty()) { - result.append("Map Datum: ").append(mapDatum).append(SEP); //NON-NLS - } - - String programName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME); - if (programName != null && !programName.isEmpty()) { - result.append("Reported by: ").append(programName).append(SEP); //NON-NLS - } - - String flag = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG); - if (flag != null && !flag.isEmpty()) { - result.append("Flag: ").append(flag).append(SEP); //NON-NLS - } - - String pathSource = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_SOURCE); - if (pathSource != null && !pathSource.isEmpty()) { - result.append("Source: ").append(pathSource).append(SEP); //NON-NLS - } - - String deviceMake = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE); - if (deviceMake != null && !deviceMake.isEmpty()) { - result.append("Device Make: ").append(deviceMake).append(SEP); //NON-NLS - } - - String deviceModel = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MODEL); - if (deviceModel != null && !deviceModel.isEmpty()) { - result.append("Device Model: ").append(deviceModel).append(SEP); //NON-NLS - } - - return result.toString(); } + /** + * Add the route to the route folder in the document. + * + * @param skCase Currently open case. + * + * @throws TskCoreException + */ + void makeRoutes(SleuthkitCase skCase) throws GeoLocationDataException { + List routes = null; + + if (waypointList == null) { + routes = Route.getRoutes(skCase); + } else { + routes = new ArrayList<>(); + } + + for (Route route : routes) { + addRouteToReport(route); + } + } + + void addRouteToReport(Route route) { + List routePoints = route.getRoute(); + Waypoint start = null; + Waypoint end = null; + // This is hardcoded knowledge that there is only two points + // a start and end. In the long run it would be nice to + // support the idea of a route with multiple points. The Route + // class supports that idea. Would be nice to figure out how to support + // for report. + if (routePoints != null && routePoints.size() > 1) { + start = routePoints.get(0); + end = routePoints.get(1); + } + + if (start == null || end == null) { + return; + } + + Element reportRoute = makeLineString(start.getLatitude(), start.getLongitude(), end.getLatitude(), end.getLongitude()); + Element startingPoint = makePoint(start.getLatitude(), start.getLongitude(), start.getAltitude()); + Element endingPoint = makePoint(end.getLatitude(), end.getLongitude(), end.getAltitude()); + + String formattedEnd = formattedCoordinates(end.getLatitude(), end.getLongitude()); + String formattedStart = formattedCoordinates(start.getLatitude(), start.getLongitude()); + + String formattedCoordinates = String.format("%s to %s", formattedStart, formattedEnd); + + if (reportRoute != null) { + gpsRouteFolder.addContent(makePlacemark(route.getLabel(), FeatureColor.GREEN, getFormattedDetails(route), route.getTimestamp(), reportRoute, formattedCoordinates)); //NON-NLS + } + + if (startingPoint != null) { + gpsRouteFolder.addContent(makePlacemark(start.getLabel(), + FeatureColor.GREEN, getFormattedDetails(start, Bundle.Waypoint_Route_Point_Display_String()), + start.getTimestamp(), startingPoint, formattedStart)); //NON-NLS + } + + if (endingPoint != null) { + gpsRouteFolder.addContent(makePlacemark(end.getLabel(), + FeatureColor.GREEN, + getFormattedDetails(end, Bundle.Waypoint_Route_Point_Display_String()), + end.getTimestamp(), endingPoint, formattedEnd)); //NON-NLS + } + } + + /** + * Format a point time stamp (in seconds) to the report format. + * + * @param timeStamp The timestamp in epoch seconds. + * + * @return The formatted timestamp + */ private String getTimeStamp(long timeStamp) { return kmlDateFormat.format(new java.util.Date(timeStamp * 1000)); } + /** + * Create the point for the given artifact. + * + * @param point Artifact point. + * + * @return point element. + */ + private Element makePoint(Waypoint point) { + return makePoint(point.getLatitude(), point.getLongitude(), point.getAltitude()); + } + /** * Create a Point for use in a Placemark. Note in this method altitude is * ignored, as Google Earth apparently has trouble using altitudes for - * LineStrings, though the parameters are still in the call. Also note that - * any null value passed in will be set to 0.0, under the idea that it is - * better to show some data with gaps, than to show nothing at all. + * LineStrings, though the parameters are still in the call. * * @param latitude point latitude * @param longitude point longitude @@ -691,21 +488,16 @@ class KMLReport implements GeneralReportModule { * @return the Point as an Element */ private Element makePoint(Double latitude, Double longitude, Double altitude) { - if (latitude == null) { - latitude = 0.0; - } - if (longitude == null) { - longitude = 0.0; - } - if (altitude == null) { - altitude = 0.0; + if (latitude == null || longitude == null) { + return null; } + Element point = new Element("Point", ns); //NON-NLS - // KML uses lon, lat. Deliberately reversed. - Element coordinates = new Element("coordinates", ns).addContent(longitude + "," + latitude + "," + altitude); //NON-NLS + // KML uses lon, lat. Deliberately reversed.1 + Element coordinates = new Element("coordinates", ns).addContent(longitude + "," + latitude + "," + (altitude != null ? altitude : 0.0)); //NON-NLS - if (altitude != 0) { + if (altitude != null && altitude != 0) { /* * Though we are including a non-zero altitude, clamp it to the * ground because inaccuracies from the GPS data can cause the @@ -725,9 +517,10 @@ class KMLReport implements GeneralReportModule { * Create a LineString for use in a Placemark. Note in this method, start * and stop altitudes get ignored, as Google Earth apparently has trouble * using altitudes for LineStrings, though the parameters are still in the - * call. Also note that any null value passed in will be set to 0.0, under - * the idea that it is better to show some data with gaps, than to show - * nothing at all. + * call. + * + * If null values are pass for the latitudes or longitudes a line will not + * be drawn. * * @param startLatitude Starting latitude * @param startLongitude Starting longitude @@ -738,24 +531,9 @@ class KMLReport implements GeneralReportModule { * * @return the Line as an Element */ - private Element makeLineString(Double startLatitude, Double startLongitude, Double startAltitude, Double stopLatitude, Double stopLongitude, Double stopAltitude) { - if (startLatitude == null) { - startLatitude = 0.0; - } - if (startLongitude == null) { - startLongitude = 0.0; - } - if (startAltitude == null) { - startAltitude = 0.0; - } - if (stopLatitude == null) { - stopLatitude = 0.0; - } - if (stopLongitude == null) { - stopLongitude = 0.0; - } - if (stopAltitude == null) { - stopAltitude = 0.0; + private Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude) { + if (startLatitude == null || startLongitude == null || stopLatitude == null || stopLongitude == null) { + return null; } Element lineString = new Element("LineString", ns); //NON-NLS @@ -925,4 +703,111 @@ class KMLReport implements GeneralReportModule { } return strbuf.toString(); } + + /** + * Get the nicely formatted details for the given waypoint. + * + * @param point Waypoint object + * @param header String details header + * + * @return HTML formatted String of details for given waypoint + */ + private String getFormattedDetails(Waypoint point, String header) { + StringBuilder result = new StringBuilder(); //NON-NLS + result.append(String.format("

    %s

    ", header)) + .append(formatAttribute("Name", point.getLabel())); + + Long timestamp = point.getTimestamp(); + if (timestamp != null) { + result.append(formatAttribute("Timestamp", getTimeStamp(timestamp))); + } + + result.append(formatAttribute("Latitude", point.getLatitude().toString())) + .append(formatAttribute("Longitude", point.getLongitude().toString())); + + if (point.getAltitude() != null) { + result.append(formatAttribute("Altitude", point.getAltitude().toString())); + } + + List list = point.getOtherProperties(); + for (Waypoint.Property prop : list) { + String value = prop.getValue(); + if (value != null && !value.isEmpty()) { + result.append(formatAttribute(prop.getDisplayName(), value)); + } + } + + return result.toString(); + } + + private String formatAttribute(String title, String value) { + return String.format(HTML_PROP_FORMAT, title, value); + } + + /** + * Returns an HTML formatted string of all the + * + * @param route + * + * @return A HTML formatted list of the Route attributes + */ + private String getFormattedDetails(Route route) { + List points = route.getRoute(); + StringBuilder result = new StringBuilder(); //NON-NLS + + result.append(String.format("

    %s

    ", Bundle.Route_Details_Header())) + .append(formatAttribute("Name", route.getLabel())); + + Long timestamp = route.getTimestamp(); + if (timestamp != null) { + result.append(formatAttribute("Timestamp", getTimeStamp(timestamp))); + } + + if (points.size() > 1) { + Waypoint start = points.get(0); + Waypoint end = points.get(1); + + result.append(formatAttribute("Start Latitude", start.getLatitude().toString())) + .append(formatAttribute("Start Longitude", start.getLongitude().toString())); + + Double altitude = start.getAltitude(); + if (altitude != null) { + result.append(formatAttribute("Start Altitude", altitude.toString())); + } + + result.append(formatAttribute("End Latitude", end.getLatitude().toString())) + .append(formatAttribute("End Longitude", end.getLongitude().toString())); + + altitude = end.getAltitude(); + if (altitude != null) { + result.append(formatAttribute("End Altitude", altitude.toString())); + } + } + + List list = route.getOtherProperties(); + for (Waypoint.Property prop : list) { + String value = prop.getValue(); + if (value != null && !value.isEmpty()) { + result.append(formatAttribute(prop.getDisplayName(), value)); + } + } + + return result.toString(); + } + + /** + * Helper functions for consistently formatting longitude and latitude. + * + * @param latitude Double latitude value + * @param longitude Double longitude value + * + * @return String Nicely formatted double values separated by a comma + */ + private String formattedCoordinates(Double latitude, Double longitude) { + if (latitude == null || longitude == null) { + return ""; + } + + return String.format("%.2f, %.2f", latitude, longitude); + } } diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java b/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java index a9c30cce92..b9fb79f00d 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java @@ -59,6 +59,7 @@ import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.FileSystem; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.LocalFilesDataSource; +import org.sleuthkit.datamodel.Pool; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; import org.sleuthkit.datamodel.TagName; @@ -894,6 +895,7 @@ public class PortableCaseReportModule implements ReportModule { oldAttr.getValueLong())); break; case STRING: + case JSON: newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()), oldAttr.getValueString())); break; @@ -1016,6 +1018,9 @@ public class PortableCaseReportModule implements ReportModule { Volume vs = (Volume)content; newContent = portableSkCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(), vs.getDescription(), vs.getFlags(), trans); + } else if (content instanceof Pool) { + Pool pool = (Pool)content; + newContent = portableSkCase.addPool(parentId, pool.getType(), trans); } else if (content instanceof FileSystem) { FileSystem fs = (FileSystem)content; newContent = portableSkCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(), diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/stix/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/report/modules/stix/Bundle_ja.properties index 5dfc25469b..b57761ac09 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/stix/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/report/modules/stix/Bundle_ja.properties @@ -10,4 +10,4 @@ STIXReportModuleConfigPanel.jButton1.text=\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u STIXReportModuleConfigPanel.jCheckBox1.text=\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30d5\u30a1\u30a4\u30eb\u306e\u8aa4\u3063\u305f\u30a4\u30f3\u30b8\u30b1\u30fc\u30bf\u30fc\u306e\u7d50\u679c\u3082\u542b\u3080 STIXReportModuleConfigPanel.jLabel2.text=STIX\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306fSTIX\u30d5\u30a1\u30a4\u30eb\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u9078\u629e STIXReportModule.notifyErr.noFildDirProvided=STIX\u30d5\u30a1\u30a4\u30eb\uff0f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304c\u63d0\u4f9b\u3055\u308c\u3066\u3044\u307e\u305b\u3093 -STIXReportModule.progress.noFildDirProvided=STIX\u30d5\u30a1\u30a4\u30eb\uff0f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304c\u63d0\u4f9b\u3055\u308c\u3066\u3044\u307e\u305b\u3093 \ No newline at end of file +STIXReportModule.progress.noFildDirProvided=STIX\u30d5\u30a1\u30a4\u30eb\uff0f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304c\u63d0\u4f9b\u3055\u308c\u3066\u3044\u307e\u305b\u3093 diff --git a/Core/src/org/sleuthkit/autopsy/test/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/test/Bundle_ja.properties new file mode 100644 index 0000000000..5e2cc58164 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/test/Bundle_ja.properties @@ -0,0 +1,3 @@ +CustomArtifactsCreatorDataSourceIngestModule.exceptionMessage.errorCreatingCustomType=\u30ab\u30b9\u30bf\u30e0\u306e\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30bf\u30a4\u30d7\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +CustomArtifactsCreatorFileIngestModule.exceptionMessage.errorCreatingCustomType=\u30ab\u30b9\u30bf\u30e0\u306e\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30bf\u30a4\u30d7\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +InterestingArtifactCreatorIngestModule.exceptionMessage.errorCreatingCustomType=\u30ab\u30b9\u30bf\u30e0\u306e\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30bf\u30a4\u30d7\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java index b27fa29dd6..e584c6bdfc 100644 --- a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java +++ b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java @@ -50,6 +50,8 @@ final class CustomArtifactType { private static final String BYTES_ATTR_DISPLAY_NAME = "Custom Bytes"; private static final String STRING_ATTR_TYPE_NAME = "CUSTOM_STRING_ATTRIBUTE"; private static final String STRING_ATTR_DISPLAY_NAME = "Custom String"; + private static final String JSON_ATTR_TYPE_NAME = "CUSTOM_JSON_ATTRIBUTE"; + private static final String JSON_ATTR_DISPLAY_NAME = "Custom Json"; private static BlackboardArtifact.Type artifactType; private static BlackboardAttribute.Type intAttrType; private static BlackboardAttribute.Type doubleAttrType; @@ -57,6 +59,7 @@ final class CustomArtifactType { private static BlackboardAttribute.Type dateTimeAttrType; private static BlackboardAttribute.Type bytesAttrType; private static BlackboardAttribute.Type stringAttrType; + private static BlackboardAttribute.Type jsonAttrType; /** * Adds the custom artifact type, with its associated custom attribute @@ -73,6 +76,7 @@ final class CustomArtifactType { dateTimeAttrType = blackboard.getOrAddAttributeType(DATETIME_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME, DATETIME_ATTR_DISPLAY_NAME); bytesAttrType = blackboard.getOrAddAttributeType(BYTES_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE, BYTES_ATTR_DISPLAY_NAME); stringAttrType = blackboard.getOrAddAttributeType(STRING_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, STRING_ATTR_DISPLAY_NAME); + jsonAttrType = blackboard.getOrAddAttributeType(JSON_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON, JSON_ATTR_DISPLAY_NAME); } /** @@ -93,6 +97,7 @@ final class CustomArtifactType { attributes.add(new BlackboardAttribute(dateTimeAttrType, MODULE_NAME, 60L)); attributes.add(new BlackboardAttribute(bytesAttrType, MODULE_NAME, DatatypeConverter.parseHexBinary("ABCD"))); attributes.add(new BlackboardAttribute(stringAttrType, MODULE_NAME, "Zero")); + attributes.add(new BlackboardAttribute(jsonAttrType, MODULE_NAME, "{\"fruit\": \"Apple\",\"size\": \"Large\",\"color\": \"Red\"}")); artifact.addAttributes(attributes); /* diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/textextractors/Bundle_ja.properties old mode 100755 new mode 100644 index 5d243eba03..8966e582e2 --- a/Core/src/org/sleuthkit/autopsy/textextractors/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/textextractors/Bundle_ja.properties @@ -1,2 +1,2 @@ -AbstractFileTikaTextExtract.index.exception.tikaParse.msg=\u4f8b\u5916\uff1a\u30d5\u30a1\u30a4\u30eb\uff1a{0}, {1}\u306eApache Tika\u30d1\u30fc\u30b9\u30bf\u30b9\u30af\u5b9f\u884c\u4e2d\u306e\u4e88\u671f\u305b\u306c\u4f8b\u5916 -AbstractFileTikaTextExtract.index.tikaParseTimeout.text=\u4f8b\u5916\uff1a\u30b3\u30f3\u30c6\u30f3\u30c4\uff1a{0}, {1}\u306eApache Tika\u30d1\u30fc\u30b9\u306e\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8 \ No newline at end of file +AbstractFileTikaTextExtract.index.tikaParseTimeout.text=\u4f8b\u5916: \u6b21\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u306eTika\u89e3\u6790\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8: {0}\u3001{1} +AbstractFileTikaTextExtract.index.exception.tikaParse.msg=\u4f8b\u5916: \u6b21\u306e\u30d5\u30a1\u30a4\u30eb\u306eTika\u89e3\u6790\u30bf\u30b9\u30af\u5b9f\u884c\u6642\u306e\u4e88\u671f\u305b\u306c\u4f8b\u5916: {0}\u3001{1} diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/SqliteTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/SqliteTextExtractor.java index 889062189c..50151c9da9 100755 --- a/Core/src/org/sleuthkit/autopsy/textextractors/SqliteTextExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/SqliteTextExtractor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2018-2018 Basis Technology Corp. + * Copyright 2018-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,7 +44,7 @@ final class SqliteTextExtractor implements TextExtractor { private static final Logger logger = Logger.getLogger(SqliteTextExtractor.class.getName()); private final AbstractFile file; - public SqliteTextExtractor(AbstractFile file) { + SqliteTextExtractor(AbstractFile file) { this.file = file; } /** @@ -101,7 +101,7 @@ final class SqliteTextExtractor implements TextExtractor { * * @param file Sqlite file */ - public SQLiteStreamReader(AbstractFile file) { + SQLiteStreamReader(AbstractFile file) { this.file = file; reader = new SQLiteTableReader.Builder(file) .forAllColumnNames(getColumnNameStrategy()) @@ -140,7 +140,7 @@ final class SqliteTextExtractor implements TextExtractor { } fillBuffer(objectStr); - columnIndex = columnIndex % totalColumns; + columnIndex %= totalColumns; } }; } @@ -171,7 +171,7 @@ final class SqliteTextExtractor implements TextExtractor { fillBuffer(columnName + ((columnIndex == totalColumns) ? "\n" : " ")); //Reset the columnCount to 0 for next table read - columnIndex = columnIndex % totalColumns; + columnIndex %= totalColumns; } }; } @@ -204,7 +204,7 @@ final class SqliteTextExtractor implements TextExtractor { */ @Override public int read(char[] cbuf, int off, int len) throws IOException { - buf = cbuf; + buf = cbuf; //needs to be the same memory address and not a copy of the contents since we are filling it in bufIndex = off; @@ -277,12 +277,12 @@ final class SqliteTextExtractor implements TextExtractor { private final String entity; private Integer pointer; - public ExcessBytes(String entity, Integer pointer) { + ExcessBytes(String entity, Integer pointer) { this.entity = entity; this.pointer = pointer; } - public boolean isFinished() { + boolean isFinished() { return entity.length() == pointer; } @@ -296,7 +296,7 @@ final class SqliteTextExtractor implements TextExtractor { * * @return number of characters read into the buffer */ - public int read(char[] buf, int off, int len) { + int read(char[] buf, int off, int len) { for (int i = off; i < len; i++) { if (isFinished()) { return i - off; diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/TextExtractorFactory.java b/Core/src/org/sleuthkit/autopsy/textextractors/TextExtractorFactory.java index 2c8316ba60..ff0ba51dd1 100755 --- a/Core/src/org/sleuthkit/autopsy/textextractors/TextExtractorFactory.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/TextExtractorFactory.java @@ -89,6 +89,7 @@ public class TextExtractorFactory { */ private static List getFileExtractors(AbstractFile content, Lookup context) { List fileExtractors = Arrays.asList( + new TextFileExtractor(content), new HtmlTextExtractor(content), new SqliteTextExtractor(content), new TikaTextExtractor(content)); diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/TextFileExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/TextFileExtractor.java new file mode 100644 index 0000000000..3efb6b1aed --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/textextractors/TextFileExtractor.java @@ -0,0 +1,135 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018-2019 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.textextractors; + +import com.ethteck.decodetect.core.Decodetect; +import com.ethteck.decodetect.core.DecodetectResult; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; +import java.util.List; +import org.apache.tika.parser.txt.CharsetDetector; +import org.apache.tika.parser.txt.CharsetMatch; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ReadContentInputStream; + +/** + * Extract text from text files + */ +public final class TextFileExtractor implements TextExtractor { + public static Charset UNKNOWN_CHARSET = new Charset("unknown", null) { + @Override + public boolean contains(Charset cs) { + return false; + } + + @Override + public CharsetDecoder newDecoder() { + return null; + } + + @Override + public CharsetEncoder newEncoder() { + return null; + } + }; + + // This value will be used as a threshold for determining which encoding + // detection library to use. If Tika's own confidence is at least + // MIN_MATCH_CONFIDENCE, Tika's result will be used for decoding. + // Otherwise, Decodetect will be used. + static final private int MIN_TIKA_MATCH_CONFIDENCE = 35; + + // This value determines whether we will consider Decodetect's top-scoring + // result a legitimate match or if we will disregard its findings + // + // Possible values are 0 to 1, inclusive + static final private double MIN_DECODETECT_MATCH_CONFIDENCE = 0.4; + + private final AbstractFile file; + + public TextFileExtractor(AbstractFile file) { + this.file = file; + } + + @Override + public Reader getReader() { + Charset encoding = getEncoding(file); + if (encoding.equals(UNKNOWN_CHARSET)) { + encoding = StandardCharsets.UTF_8; + } + return getReader(encoding); + } + + public Reader getReader(Charset encoding) { + return new InputStreamReader(new BufferedInputStream(new ReadContentInputStream(file)), encoding); + } + + @Override + public boolean isSupported() { + return file.getMIMEType().equals("text/plain"); + } + + public class TextFileExtractorException extends Exception { + public TextFileExtractorException(String msg, Throwable ex) { + super(msg, ex); + } + public TextFileExtractorException(String msg) { + super(msg); + } + } + + public static Charset getEncoding(Content content) { + try (InputStream stream = new BufferedInputStream(new ReadContentInputStream(content))) { + // Tika first + CharsetDetector detector = new CharsetDetector(); + detector.setText(stream); + CharsetMatch tikaResult = detector.detect(); + if (tikaResult != null && tikaResult.getConfidence() >= MIN_TIKA_MATCH_CONFIDENCE) { + try { + return Charset.forName(tikaResult.getName()); + } catch (UnsupportedCharsetException ignored) { + } + } + + // Decodetect if Tika fails or falls below confidence threshold + int maxBytes = 100000; + int numBytes = Math.min(stream.available(), maxBytes); + byte[] targetArray = new byte[numBytes]; + stream.read(targetArray); + List results = Decodetect.DECODETECT.getResults(targetArray); + if (!results.isEmpty()) { + DecodetectResult topResult = results.get(0); + if (topResult.getConfidence() >= MIN_DECODETECT_MATCH_CONFIDENCE) { + return topResult.getEncoding(); + } + } + } catch (IOException ignored) { + } + return UNKNOWN_CHARSET; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/Bundle_ja.properties new file mode 100644 index 0000000000..f09a84b18c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/Bundle_ja.properties @@ -0,0 +1,12 @@ +OptionsCategory_Name_Machine_Translation=\u6a5f\u68b0\u7ffb\u8a33 +OptionsCategory_Keywords_Machine_Translation_Settings=\u6a5f\u68b0\u7ffb\u8a33\u8a2d\u5b9a +TranslationContentPanel.ocrLabel.text=OCR: +TranslationOptionsPanel.noTextTranslators.text=\u30c6\u30ad\u30b9\u30c8\u7ffb\u8a33\u30c4\u30fc\u30eb\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\u3002\u7ffb\u8a33\u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u3059\u3002 +TranslationOptionsPanel.noTextTranslators.text=\u30c6\u30ad\u30b9\u30c8\u7ffb\u8a33\u30c4\u30fc\u30eb\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u7ffb\u8a33\u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u3059\u3002 +TranslationOptionsPanel.textTranslatorsUnavailable.text=\u9078\u629e\u3057\u305f\u30c6\u30ad\u30b9\u30c8\u7ffb\u8a33\u30c4\u30fc\u30eb\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002\u7ffb\u8a33\u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u3059\u3002 +TranslationOptionsPanel.translationDisabled.text=\u7ffb\u8a33\u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u3059 +TranslationOptionsPanelController.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc +TranslationOptionsPanelController.moduleErr.msg=TranslationSettingsPanelController\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +TranslationContentPanel.showLabel.text=\u8868\u793a: +TranslationOptionsPanel.translationServiceLabel.text=\u30c6\u30ad\u30b9\u30c8\u7ffb\u8a33\u30c4\u30fc\u30eb: +TranslationOptionsPanel.translationOptionsDescription.text=\u30b5\u30fc\u30c9\u30d1\u30fc\u30c6\u30a3\u88fd\u30c6\u30ad\u30b9\u30c8\u7ffb\u8a33\u30b5\u30fc\u30d3\u30b9\u3092\u69cb\u6210\u3057\u3001\u30c6\u30ad\u30b9\u30c8\u3068\u30d5\u30a1\u30a4\u30eb\u540d\u306e\u7ffb\u8a33\u3092\u6709\u52b9\u5316\u3057\u307e\u3059\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle_ja.properties new file mode 100644 index 0000000000..885df0f635 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle_ja.properties @@ -0,0 +1,30 @@ +BingTranslator.name.text=Bing\u3067\u7ffb\u8a33\u8005\u3092\u691c\u7d22 +BingTranslatorSettingsPanel.warning.invalidKey=\u7121\u52b9\u306a\u7ffb\u8a33\u8a8d\u8a3c\u30ad\u30fc\u3067\u3059 +BingTranslatorSettingsPanel.warning.targetLanguageFailure=\u5bfe\u8c61\u8a00\u8a9e\u306e\u30ea\u30b9\u30c8\u3092\u53d6\u5f97\u3067\u304d\u306a\u3044\u304b\u3001\u53d7\u4fe1\u3057\u305f\u7d50\u679c\u3092\u89e3\u6790\u3067\u304d\u307e\u305b\u3093 +GoogleTranslator.name.text=Google\u7ffb\u8a33 +GoogleTranslatorSettingsPanel.browseButton.text=\u53c2\u7167 +GoogleTranslatorSettingsPanel.credentialsLabel.text=\u8cc7\u683c\u60c5\u5831\u306e\u30d1\u30b9: +GoogleTranslatorSettingsPanel.errorMessage.fileNotFound=\u8cc7\u683c\u60c5\u5831\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002\u6709\u52b9\u306aJSON\u8cc7\u683c\u60c5\u5831\u30d5\u30a1\u30a4\u30eb\u306e\u3042\u308b\u5834\u6240\u3092\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +GoogleTranslatorSettingsPanel.errorMessage.noFileSelected=Google\u7ffb\u8a33\u306b\u8cc7\u683c\u60c5\u5831\u3092\u63d0\u4f9b\u3059\u308b\u305f\u3081\u306b\u30011\u3064\u306eJSON\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +GoogleTranslatorSettingsPanel.errorMessage.translationFailure=\u6307\u5b9a\u3057\u305f\u8cc7\u683c\u60c5\u5831\u3067\u306f\u7ffb\u8a33\u3067\u304d\u307e\u305b\u3093 +GoogleTranslatorSettingsPanel.errorMessage.unableToMakeCredentials=\u8cc7\u683c\u60c5\u5831\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u8cc7\u683c\u60c5\u5831\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u69cb\u7bc9\u3067\u304d\u307e\u305b\u3093\u3002\u6709\u52b9\u306aJSON\u8cc7\u683c\u60c5\u5831\u30d5\u30a1\u30a4\u30eb\u306e\u3042\u308b\u5834\u6240\u3092\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +GoogleTranslatorSettingsPanel.errorMessage.unableToReadCredentials=\u8cc7\u683c\u60c5\u5831\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u8cc7\u683c\u60c5\u5831\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3002\u6709\u52b9\u306aJSON\u8cc7\u683c\u60c5\u5831\u30d5\u30a1\u30a4\u30eb\u306e\u3042\u308b\u5834\u6240\u3092\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +GoogleTranslatorSettingsPanel.errorMessage.unknownFailureGetting=\u73fe\u5728\u306e\u8cc7\u683c\u60c5\u5831\u30d5\u30a1\u30a4\u30eb\u3067\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u8a00\u8a9e\u306e\u30ea\u30b9\u30c8\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002 +GoogleTranslatorSettingsPanel.errorMessage.unknownFailurePopulating=\u73fe\u5728\u306e\u8cc7\u683c\u60c5\u5831\u30d5\u30a1\u30a4\u30eb\u3067\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u8a00\u8a9e\u306e\u30ea\u30b9\u30c8\u3092\u5165\u529b\u3067\u304d\u307e\u305b\u3093\u3002 +GoogleTranslatorSettingsPanel.fileChooser.confirmButton=\u9078\u629e +GoogleTranslatorSettingsPanel.json.description=JSON\u30d5\u30a1\u30a4\u30eb +GoogleTranslatorSettingsPanel.warningLabel.text= +GoogleTranslatorSettingsPanel.targetLanguageLabel.text=\u5bfe\u8c61\u8a00\u8a9e: +BingTranslatorSettingsPanel.testButton.text=\u30c6\u30b9\u30c8 +BingTranslatorSettingsPanel.testResultValueLabel.text= +BingTranslatorSettingsPanel.resultLabel.text=\u7d50\u679c: +BingTranslatorSettingsPanel.untranslatedLabel.text=\u672a\u7ffb\u8a33: +BingTranslatorSettingsPanel.targetLanguageLabel.text=\u5bfe\u8c61\u8a00\u8a9e: +BingTranslatorSettingsPanel.authenticationKeyField.toolTipText=\u6b21\u306e\u30cf\u30c3\u30b7\u30e5\u3092\u5165\u529b\u3059\u308b +GoogleTranslatorSettingsPanel.testButton.text=\u30c6\u30b9\u30c8 +GoogleTranslatorSettingsPanel.untranslatedLabel.text=\u672a\u7ffb\u8a33: +GoogleTranslatorSettingsPanel.resultLabel.text=\u7d50\u679c: +GoogleTranslatorSettingsPanel.testResultValueLabel.text= +BingTranslatorSettingsPanel.authenticationKeyLabel.text=\u8a8d\u8a3c\u30ad\u30fc: +BingTranslatorSettingsPanel.instructionsTextArea.text=\u304a\u4f7f\u3044\u306eMicrosoft Translator\u30a2\u30ab\u30a6\u30f3\u30c8\u306bMicrosoft Translator\u8a8d\u8a3c\u30ad\u30fc\u3092\u63d0\u4f9b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u30ad\u30fc\u306e\u53d6\u5f97\u65b9\u6cd5\u306b\u95a2\u3059\u308b\u624b\u9806\u306f\u3053\u3061\u3089\u306b\u3042\u308a\u307e\u3059: https://docs.microsoft.com/en-us/azure/cognitive-services/translator/translator-text-how-to-signup +GoogleTranslatorSettingsPanel.instructionsTextArea.text=\u304a\u4f7f\u3044\u306eGoogle\u7ffb\u8a33\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u30b5\u30fc\u30d3\u30b9\u30a2\u30ab\u30a6\u30f3\u30c8\u30ad\u30fc\u306b\u542b\u307e\u308c\u308bJSON\u8cc7\u683c\u60c5\u5831\u30d5\u30a1\u30a4\u30eb\u304c\u5fc5\u8981\u306b\u306a\u308a\u307e\u3059\u3002\u30b5\u30fc\u30d3\u30b9\u30a2\u30ab\u30a6\u30f3\u30c8\u30ad\u30fc\u306e\u4f5c\u6210\u65b9\u6cd5\u306b\u95a2\u3059\u308b\u60c5\u5831\u306f\u3053\u3061\u3089\u306b\u3042\u308a\u3042\u307e\u3059: https://cloud.google.com/iam/docs/creating-managing-service-account-keys diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties-MERGED index 141ca2f7ef..fa1a250e44 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties-MERGED @@ -2,16 +2,15 @@ OptionsCategory_Name_Machine_Translation=Machine Translation OptionsCategory_Keywords_Machine_Translation_Settings=Machine Translation Settings TranslatedContentPanel.comboBoxOption.originalText=Original Text (Up to 25KB) TranslatedContentPanel.comboBoxOption.translatedText=Translated Text -TranslatedContentViewer.emptyTranslation=The resulting translation was empty. -TranslatedContentViewer.errorExtractingText=Could not extract text from file. -TranslatedContentViewer.errorMsg=Error encountered while getting file text. -TranslatedContentViewer.extractingFileText=Extracting text from file, please wait... -TranslatedContentViewer.extractingImageText=Extracting text from image, please wait... -TranslatedContentViewer.noIndexedTextMsg=Run the Keyword Search Ingest Module to get text for translation. -TranslatedContentViewer.noServiceProvider=Machine Translation software was not found. -TranslatedContentViewer.textAlreadyIndexed=Please view the original text in the Indexed Text viewer. +TranslatedContentViewer.emptyTranslation=The machine translation software did not return any text. +# {0} - exception message +TranslatedContentViewer.errorExtractingText=An error occurred while extracting the text ({0}). +TranslatedContentViewer.extractingText=Extracting text, please wait... +TranslatedContentViewer.fileHasNoText=File has no text. +TranslatedContentViewer.noServiceProvider=The machine translation software was not found. TranslatedContentViewer.translatingText=Translating text, please wait... -TranslatedContentViewer.translationException=Error encountered while attempting translation. +# {0} - exception message +TranslatedContentViewer.translationException=An error occurred while translating the text ({0}). TranslatedTextViewer.maxPayloadSize=Up to the first %dKB of text will be translated TranslatedTextViewer.title=Translation TranslatedTextViewer.toolTip=Displays translated file text. diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle_ja.properties new file mode 100644 index 0000000000..b9442d6a12 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle_ja.properties @@ -0,0 +1,22 @@ +OptionsCategory_Name_Machine_Translation=\u6a5f\u68b0\u7ffb\u8a33 +OptionsCategory_Keywords_Machine_Translation_Settings=\u6a5f\u68b0\u7ffb\u8a33\u8a2d\u5b9a +TranslatedContentPanel.comboBoxOption.originalText=\u30aa\u30ea\u30b8\u30ca\u30eb\u30c6\u30ad\u30b9\u30c8(\u6700\u592725KB) +TranslatedContentPanel.comboBoxOption.translatedText=\u7ffb\u8a33\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8 +TranslatedContentViewer.emptyTranslation=\u751f\u6210\u3055\u308c\u305f\u7ffb\u8a33\u7d50\u679c\u306f\u7a7a(\u672a\u5165\u529b)\u3067\u3057\u305f\u3002 +TranslatedContentViewer.errorExtractingText=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30c6\u30ad\u30b9\u30c8\u3092\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +TranslatedContentViewer.errorMsg=\u30d5\u30a1\u30a4\u30eb\u306e\u30c6\u30ad\u30b9\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +TranslatedContentViewer.extractingFileText=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30c6\u30ad\u30b9\u30c8\u3092\u62bd\u51fa\u4e2d\u3067\u3059\u3002\u304a\u5f85\u3061\u304f\u3060\u3055\u3044... +TranslatedContentViewer.extractingImageText=\u30a4\u30e1\u30fc\u30b8\u304b\u3089\u30c6\u30ad\u30b9\u30c8\u3092\u62bd\u51fa\u4e2d\u3067\u3059\u3002\u304a\u5f85\u3061\u304f\u3060\u3055\u3044... +TranslatedContentViewer.noIndexedTextMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c\u3057\u3001\u7ffb\u8a33\u30c6\u30ad\u30b9\u30c8\u3092\u53d6\u5f97\u3057\u307e\u3059\u3002 +TranslatedContentViewer.noServiceProvider=\u6a5f\u68b0\u7ffb\u8a33\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +TranslatedContentViewer.textAlreadyIndexed=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8\u306e\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u30aa\u30ea\u30b8\u30ca\u30eb\u30c6\u30ad\u30b9\u30c8\u3092\u8868\u793a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +TranslatedContentViewer.translatingText=\u30c6\u30ad\u30b9\u30c8\u3092\u7ffb\u8a33\u4e2d\u3067\u3059\u3002T\u304a\u5f85\u3061\u304f\u3060\u3055\u3044... +TranslatedContentViewer.translationException=\u7ffb\u8a33\u3092\u8a66\u307f\u3066\u3044\u308b\u9593\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +TranslatedTextViewer.maxPayloadSize=\u30c6\u30ad\u30b9\u30c8\u306e\u6700\u521d\u306e %dKB \u307e\u3067\u7ffb\u8a33\u3055\u308c\u307e\u3059 +TranslatedTextViewer.title=\u7ffb\u8a33 +TranslatedTextViewer.toolTip=\u7ffb\u8a33\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306e\u30c6\u30ad\u30b9\u30c8\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +TranslationContentPanel.autoDetectOCR=\u81ea\u52d5\u691c\u51fa\u3055\u308c\u305f\u8a00\u8a9e +TranslationContentPanel.ocrLabel.text=OCR: +TranslationOptionsPanelController.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc +TranslationOptionsPanelController.moduleErr.msg=TranslationSettingsPanelController\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +TranslationContentPanel.showLabel.text=\u8868\u793a: diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java index 8c49c23ed7..7e07545b49 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java @@ -53,6 +53,7 @@ import org.sleuthkit.autopsy.texttranslation.TranslationException; import org.sleuthkit.datamodel.Content; import java.util.List; import java.util.logging.Level; +import javax.swing.SwingUtilities; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.texttranslation.ui.TranslationContentPanel.DisplayDropdownOptions; @@ -63,7 +64,7 @@ import org.sleuthkit.autopsy.texttranslation.ui.TranslationContentPanel.DisplayD @ServiceProvider(service = TextViewer.class, position = 4) public final class TranslatedTextViewer implements TextViewer { - private static final Logger logger = Logger.getLogger(TranslatedTextViewer.class.getName()); + private static final Logger logger = Logger.getLogger(TranslatedTextViewer.class.getName()); private static final boolean OCR_ENABLED = true; private static final boolean OCR_DISABLED = false; @@ -72,7 +73,7 @@ public final class TranslatedTextViewer implements TextViewer { private final TranslationContentPanel panel = new TranslationContentPanel(); private volatile Node node; - private volatile BackgroundTranslationTask updateTask; + private volatile ExtractAndTranslateTextTask backgroundTask; private final ThreadFactory translationThreadFactory = new ThreadFactoryBuilder().setNameFormat("translation-content-viewer-%d").build(); private final ExecutorService executorService = Executors.newSingleThreadExecutor(translationThreadFactory); @@ -95,7 +96,7 @@ public final class TranslatedTextViewer implements TextViewer { panel.addLanguagePackNames(INSTALLED_LANGUAGE_PACKS); } } - + int payloadMaxInKB = TextTranslationService.getInstance().getMaxTextChars() / 1000; panel.setWarningLabelMsg(String.format(Bundle.TranslatedTextViewer_maxPayloadSize(), payloadMaxInKB)); @@ -129,10 +130,10 @@ public final class TranslatedTextViewer implements TextViewer { public void resetComponent() { panel.reset(); this.node = null; - if (updateTask != null) { - updateTask.cancel(true); + if (backgroundTask != null) { + backgroundTask.cancel(true); } - updateTask = null; + backgroundTask = null; } @Override @@ -157,62 +158,74 @@ public final class TranslatedTextViewer implements TextViewer { } /** - * Fetches file text and performs translation. + * Extracts text from a file and optionally translates it. */ - private class BackgroundTranslationTask extends SwingWorker { + private class ExtractAndTranslateTextTask extends SwingWorker { + + private final AbstractFile file; + private final boolean translateText; + + private ExtractAndTranslateTextTask(AbstractFile file, boolean translateText) { + this.file = file; + this.translateText = translateText; + } @NbBundle.Messages({ - "TranslatedContentViewer.noIndexedTextMsg=Run the Keyword Search Ingest Module to get text for translation.", - "TranslatedContentViewer.textAlreadyIndexed=Please view the original text in the Indexed Text viewer.", - "TranslatedContentViewer.errorMsg=Error encountered while getting file text.", - "TranslatedContentViewer.errorExtractingText=Could not extract text from file.", - "TranslatedContentViewer.translatingText=Translating text, please wait..." + "TranslatedContentViewer.extractingText=Extracting text, please wait...", + "TranslatedContentViewer.translatingText=Translating text, please wait...", + "# {0} - exception message", "TranslatedContentViewer.errorExtractingText=An error occurred while extracting the text ({0}).", + "TranslatedContentViewer.fileHasNoText=File has no text.", + "TranslatedContentViewer.noServiceProvider=The machine translation software was not found.", + "# {0} - exception message", "TranslatedContentViewer.translationException=An error occurred while translating the text ({0})." }) @Override public String doInBackground() throws InterruptedException { if (this.isCancelled()) { throw new InterruptedException(); } - String dropdownSelection = panel.getDisplayDropDownSelection(); - if (dropdownSelection.equals(DisplayDropdownOptions.ORIGINAL_TEXT.toString())) { - try { - return getFileText(node); - } catch (IOException ex) { - logger.log(Level.WARNING, "Error getting text", ex); - return Bundle.TranslatedContentViewer_errorMsg(); - } catch (TextExtractor.InitReaderException ex) { - logger.log(Level.WARNING, "Error getting text", ex); - return Bundle.TranslatedContentViewer_errorExtractingText(); - } - } else { - try { - return translate(getFileText(node)); - } catch (IOException ex) { - logger.log(Level.WARNING, "Error translating text", ex); - return Bundle.TranslatedContentViewer_errorMsg(); - } catch (TextExtractor.InitReaderException ex) { - logger.log(Level.WARNING, "Error translating text", ex); - return Bundle.TranslatedContentViewer_errorExtractingText(); - } + SwingUtilities.invokeLater(() -> { + panel.display(Bundle.TranslatedContentViewer_extractingText(), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC); + }); + String fileText; + try { + fileText = getFileText(file); + } catch (IOException | TextExtractor.InitReaderException ex) { + logger.log(Level.WARNING, String.format("Error extracting text for file %s (objId=%d)", file.getName(), file.getId()), ex); + return Bundle.TranslatedContentViewer_errorExtractingText(ex.getMessage()); } - } - /** - * Update the extraction loading message depending on the file type. - * - * @param isImage Boolean indicating if the selecting node is an image - */ - @NbBundle.Messages({"TranslatedContentViewer.extractingImageText=Extracting text from image, please wait...", - "TranslatedContentViewer.extractingFileText=Extracting text from file, please wait...",}) - private void updateExtractionLoadingMessage(boolean isImage) { - if (isImage) { - panel.display(Bundle.TranslatedContentViewer_extractingImageText(), - ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC); - } else { - panel.display(Bundle.TranslatedContentViewer_extractingFileText(), - ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC); + if (this.isCancelled()) { + throw new InterruptedException(); } + + if (fileText == null || fileText.isEmpty()) { + return Bundle.TranslatedContentViewer_fileHasNoText(); + } + + if (!this.translateText) { + return fileText; + } + + SwingUtilities.invokeLater(() -> { + panel.display(Bundle.TranslatedContentViewer_translatingText(), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC); + }); + String translation; + try { + translation = translate(fileText); + } catch (NoServiceProviderException ex) { + logger.log(Level.WARNING, String.format("Error translating text for file %s (objId=%d)", file.getName(), file.getId()), ex); + translation = Bundle.TranslatedContentViewer_noServiceProvider(); + } catch (TranslationException ex) { + logger.log(Level.WARNING, String.format("Error translating text for file %s (objId=%d)", file.getName(), file.getId()), ex); + translation = Bundle.TranslatedContentViewer_translationException(ex.getMessage()); + } + + if (this.isCancelled()) { + throw new InterruptedException(); + } + + return translation; } @Override @@ -227,8 +240,12 @@ public final class TranslatedTextViewer implements TextViewer { String orientDetectSubstring = result.substring(0, maxOrientChars); ComponentOrientation orientation = TextUtil.getTextDirection(orientDetectSubstring); panel.display(result, orientation, Font.PLAIN); - } catch (InterruptedException | ExecutionException | CancellationException ignored) { - //InterruptedException & CancellationException - User cancelled, no error. + + } catch (InterruptedException | CancellationException ignored) { + // Task cancelled, no error. + } catch (ExecutionException ex) { + logger.log(Level.WARNING, String.format("Error occurred during background task execution for file %s (objId=%d)", file.getName(), file.getId()), ex); + panel.display(Bundle.TranslatedContentViewer_translationException(ex.getMessage()), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC); } } @@ -240,36 +257,21 @@ public final class TranslatedTextViewer implements TextViewer { * @return Translated text or error message */ @NbBundle.Messages({ - "TranslatedContentViewer.emptyTranslation=The resulting translation was empty.", - "TranslatedContentViewer.noServiceProvider=Machine Translation software was not found.", - "TranslatedContentViewer.translationException=Error encountered while attempting translation."}) - private String translate(String input) throws InterruptedException { - if (this.isCancelled()) { - throw new InterruptedException(); - } - - panel.display(Bundle.TranslatedContentViewer_translatingText(), - ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC); - - try { - TextTranslationService translatorInstance = TextTranslationService.getInstance(); - String translatedResult = translatorInstance.translate(input); - if (translatedResult.isEmpty()) { - return Bundle.TranslatedContentViewer_emptyTranslation(); - } - return translatedResult; - } catch (NoServiceProviderException ex) { - return Bundle.TranslatedContentViewer_noServiceProvider(); - } catch (TranslationException ex) { - logger.log(Level.WARNING, "Error translating text", ex); - return Bundle.TranslatedContentViewer_translationException() + " (" + ex.getMessage() + ")"; + "TranslatedContentViewer.emptyTranslation=The machine translation software did not return any text." + }) + private String translate(String input) throws NoServiceProviderException, TranslationException { + TextTranslationService translatorInstance = TextTranslationService.getInstance(); + String translatedResult = translatorInstance.translate(input); + if (translatedResult.isEmpty()) { + return Bundle.TranslatedContentViewer_emptyTranslation(); } + return translatedResult; } /** * Extracts text from the given node * - * @param node Selected node in UI + * @param file Selected node in UI * * @return Extracted text * @@ -277,33 +279,22 @@ public final class TranslatedTextViewer implements TextViewer { * @throws InterruptedException * @throws * org.sleuthkit.autopsy.textextractors.TextExtractor.InitReaderException - * @throws NoOpenCoreException - * @throws KeywordSearchModuleException */ - private String getFileText(Node node) throws IOException, + private String getFileText(AbstractFile file) throws IOException, InterruptedException, TextExtractor.InitReaderException { - AbstractFile source = (AbstractFile) DataContentViewerUtility.getDefaultContent(node); - boolean isImage = false; - - if (source != null) { - isImage = source.getMIMEType().toLowerCase().startsWith("image/"); - } - - updateExtractionLoadingMessage(isImage); - + final boolean isImage = file.getMIMEType().toLowerCase().startsWith("image/"); // NON-NLS String result; - if (isImage) { - result = extractText(source, OCR_ENABLED); + result = extractText(file, OCR_ENABLED); } else { - result = extractText(source, OCR_DISABLED); + result = extractText(file, OCR_DISABLED); } //Correct for UTF-8 byte[] resultInUTF8Bytes = result.getBytes("UTF8"); - byte[] trimToArraySize = Arrays.copyOfRange(resultInUTF8Bytes, 0, - Math.min(resultInUTF8Bytes.length, MAX_EXTRACT_SIZE_BYTES) ); + byte[] trimToArraySize = Arrays.copyOfRange(resultInUTF8Bytes, 0, + Math.min(resultInUTF8Bytes.length, MAX_EXTRACT_SIZE_BYTES)); return new String(trimToArraySize, "UTF-8"); } @@ -348,7 +339,7 @@ public final class TranslatedTextViewer implements TextViewer { textBuilder.append(cbuf, 0, read); bytesRead += read; } - + return textBuilder.toString(); } @@ -399,23 +390,28 @@ public final class TranslatedTextViewer implements TextViewer { */ private abstract class SelectionChangeListener implements ActionListener { - public String currentSelection = null; + private String currentSelection; - public abstract String getSelection(); + abstract String getSelection(); @Override public final void actionPerformed(ActionEvent e) { String selection = getSelection(); if (!selection.equals(currentSelection)) { currentSelection = selection; - if (updateTask != null && !updateTask.isDone()) { - updateTask.cancel(true); + + if (backgroundTask != null && !backgroundTask.isDone()) { + backgroundTask.cancel(true); } - updateTask = new BackgroundTranslationTask(); + + AbstractFile file = node.getLookup().lookup(AbstractFile.class); + String textDisplaySelection = panel.getDisplayDropDownSelection(); + boolean translateText = !textDisplaySelection.equals(DisplayDropdownOptions.ORIGINAL_TEXT.toString()); + backgroundTask = new ExtractAndTranslateTextTask(file, translateText); //Pass the background task to a single threaded pool to keep //the number of jobs running to one. - executorService.execute(updateTask); + executorService.execute(backgroundTask); } } } @@ -426,7 +422,7 @@ public final class TranslatedTextViewer implements TextViewer { private class DisplayDropDownChangeListener extends SelectionChangeListener { @Override - public String getSelection() { + String getSelection() { return panel.getDisplayDropDownSelection(); } } @@ -437,7 +433,7 @@ public final class TranslatedTextViewer implements TextViewer { private class OCRDropdownChangeListener extends SelectionChangeListener { @Override - public String getSelection() { + String getSelection() { return panel.getSelectedOcrLanguagePack(); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties old mode 100755 new mode 100644 index c631cee729..918ab67c66 --- a/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties @@ -1,47 +1,66 @@ -CTL_MakeTimeline=\u300c\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u300d -CTL_TimeLineTopComponent=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30a6\u30a3\u30f3\u30c9\u30a6 -CTL_TimeLineTopComponentAction=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c8\u30c3\u30d7\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8 -OpenTimelineAction.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 +CTL_MakeTimeline=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 +CTL_TimeLineTopComponentAction=TimeLineTopComponent +CTL_TimeLineTopComponent=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 + +OpenTimelineAction.displayName=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 +OpenTimeLineAction.msgdlg.text=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +OpenTimelineAction.settingsErrorMessage=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u8a2d\u5b9a\u3092\u521d\u671f\u5316\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +PrompDialogManager.buttonType.continueNoUpdate=\u66f4\u65b0\u305b\u305a\u306b\u7d9a\u884c +PrompDialogManager.buttonType.showTimeline=\u7d9a\u884c +PrompDialogManager.buttonType.update=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u66f4\u65b0 +PromptDialogManager.confirmDuringIngest.contentText=\u7d9a\u884c\u3057\u307e\u3059\u304b? +PromptDialogManager.confirmDuringIngest.headerText=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3048\u306b\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u66f4\u65b0\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307e\u3059\u3002 \u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u5b8c\u5168\u3067\u306f\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +PromptDialogManager.progressDialog.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u3092\u5165\u529b\u4e2d\u3067\u3059 +PromptDialogManager.rebuildPrompt.details=\u8a73\u7d30 +PromptDialogManager.rebuildPrompt.headerText=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u304c\u5b8c\u5168\u304a\u3088\u3073/\u307e\u305f\u306f\u6700\u65b0\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 \u4e00\u90e8\u306e\u30a4\u30d9\u30f3\u30c8\u304c\u898b\u3064\u304b\u3089\u306a\u3044\u304b\u3001\u6b63\u78ba\u3067\u306f\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u307e\u305f\u3001\u4e00\u90e8\u306e\u6a5f\u80fd\u304c\u5229\u7528\u3067\u304d\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +PromptDialogManager.showTimeLineDisabledMessage.contentText=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u6a5f\u80fd\u306f\u307e\u3060\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 \u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u304c\u7121\u52b9\u306b\u306a\u308a\u307e\u3059\u3002 +PromptDialogManager.showTimeLineDisabledMessage.headerText= +PromptDialogManager.showTooManyFiles.contentText=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u6570\u304c\u591a\u3059\u304e\u3066\u3001\u76f8\u5fdc\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u4fdd\u8a3c\u3067\u304d\u307e\u305b\u3093\u3002 \u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u304c\u7121\u52b9\u306b\u306a\u308a\u307e\u3059\u3002 +PromptDialogManager.showTooManyFiles.headerText= +ShowInTimelineDialog.amountValidator.message=\u5165\u529b\u3059\u308b\u91cf\u306b\u306f\u6570\u5b57\u306e\u307f\u304c\u542b\u307e\u308c\u3066\u3044\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002 +ShowInTimelineDialog.artifactTitle=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306b\u7d50\u679c\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +ShowInTimelineDialog.eventSelectionValidator.message=\u30a4\u30d9\u30f3\u30c8\u3092\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +# {0} - \u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 +ShowInTimelineDialog.fileTitle=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306b {0} \u3092\u8868\u793a\u3057\u307e\u3059\u3002 +ShowInTimelineDialog.showTimelineButtonType.text=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3092\u8868\u793a +Timeline.dialogs.title=\ \u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 Timeline.frameName.text={0} - Autopsy\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 -Timeline.goToButton.text=\u4e0b\u8a18\u3078\u79fb\u52d5\uff1a -Timeline.pushDescrLOD.confdlg.msg={0}\u30a4\u30d9\u30f3\u30c8\u306e\u8a73\u7d30\u304c\u8868\u793a\u53ef\u80fd\u3067\u3059\u3002\u3053\u306e\u51e6\u7406\u306f\u9577\u6642\u9593\u304b\u304b\u308b\u3082\u3057\u304f\u306fAutopsy\u3092\u30af\u30e9\u30c3\u30b7\u30e5\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u5b9f\u884c\u3057\u307e\u3059\u304b\uff1f -Timeline.resultPanel.loading=\u30ed\u30fc\u30c9\u4e2d\u30fb\u30fb\u30fb Timeline.resultsPanel.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u7d50\u679c -Timeline.runJavaFxThread.progress.creating=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u4f5c\u6210\u4e2d\u30fb\u30fb\u30fb +Timeline.runJavaFxThread.progress.creating=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3092\u4f5c\u6210\u4e2d\u3067\u3059 ... Timeline.zoomOutButton.text=\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8 +Timeline.goToButton.text=\u79fb\u52d5: +Timeline.yearBarChart.x.years=\u5e74 +Timeline.resultPanel.loading=\u8aad\u307f\u8fbc\u307f\u4e2d... + +TimeLineController.errorTitle=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30a8\u30e9\u30fc\u3067\u3059\u3002 +TimeLineController.outOfDate.errorMessage=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u304c\u6700\u65b0\u306e\u60c5\u5831\u3067\u306f\u306a\u3044\u304b\u3069\u3046\u304b\u3092\u5224\u65ad\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 \u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3092\u66f4\u65b0\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3068\u4eee\u5b9a\u3057\u307e\u3059\u3002 \u30ed\u30b0\u3092\u8868\u793a\u3057\u3066\u8a73\u7d30\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +TimeLineController.rebuildReasons.incompleteOldSchema=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u4ee5\u524d\u306b\u4e0d\u5b8c\u5168\u306a\u60c5\u5831\u3067\u5165\u529b\u3055\u308c\u307e\u3057\u305f: \u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u66f4\u65b0\u3057\u306a\u3044\u9650\u308a\u3001\u4e00\u90e8\u306e\u6a5f\u80fd\u304c\u5229\u7528\u3067\u304d\u306a\u3044\u304b\u6a5f\u80fd\u3057\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +TimeLineController.rebuildReasons.ingestWasRunning=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u4ee5\u524d\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5b9f\u884c\u4e2d\u306b\u5165\u529b\u3055\u308c\u307e\u3057\u305f: \u4e00\u90e8\u306e\u30a4\u30d9\u30f3\u30c8\u304c\u898b\u3064\u304b\u3089\u306a\u3044\u304b\u3001\u4e0d\u5b8c\u5168\u304b\u3001\u6b63\u78ba\u3067\u306f\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +TimeLineController.rebuildReasons.outOfDate=\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u306f\u6700\u65b0\u3067\u306f\u3042\u308a\u307e\u305b\u3093: \u3059\u3079\u3066\u306e\u30c7\u30fc\u30bf\u3092\u8868\u793a\u3067\u304d\u307e\u305b\u3093\u3002 +TimeLineController.rebuildReasons.outOfDateError=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u304c\u6700\u65b0\u306e\u60c5\u5831\u3067\u306f\u306a\u3044\u304b\u3069\u3046\u304b\u3092\u5224\u65ad\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +TimeLineController.setEventsDBStale.errMsgNotStale=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u53e4\u3044\u3082\u306e\u3068\u3057\u3066\u30de\u30fc\u30af\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u4e00\u90e8\u306e\u7d50\u679c\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u304b\u898b\u3064\u304b\u3089\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +TimeLineController.setEventsDBStale.errMsgStale=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u53e4\u3044\u3082\u306e\u3068\u3057\u3066\u30de\u30fc\u30af\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u4e00\u90e8\u306e\u7d50\u679c\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u304b\u898b\u3064\u304b\u3089\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +TimeLinecontroller.setIngestRunning.errMsgNotRunning=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b9f\u884c\u3057\u3066\u3044\u306a\u3044\u9593\u306b\u3001\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u5165\u529b\u6e08\u307f\u3068\u3057\u3066\u30de\u30fc\u30af\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u4e00\u90e8\u306e\u7d50\u679c\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u304b\u898b\u3064\u304b\u3089\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +TimeLineController.setIngestRunning.errMsgRunning=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5b9f\u884c\u4e2d\u306b\u3001\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u5165\u529b\u6e08\u307f\u3068\u3057\u3066\u30de\u30fc\u30af\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u4e00\u90e8\u306e\u7d50\u679c\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u304b\u898b\u3064\u304b\u3089\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +TimeLinecontroller.updateNowQuestion=\u4eca\u3059\u3050\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u66f4\u65b0\u3057\u307e\u3059\u304b? TimelineFrame.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 +TimelinePanel.jButton1.text=6m +TimelinePanel.jButton13.text=\u3059\u3079\u3066 +TimelinePanel.jButton10.text=1h +TimelinePanel.jButton9.text=12h +TimelinePanel.jButton11.text=5y +TimelinePanel.jButton12.text=10y +TimelinePanel.jButton6.text=1w +TimelinePanel.jButton5.text=1y +TimelinePanel.jButton8.text=1d +TimelinePanel.jButton7.text=3d +TimelinePanel.jButton2.text=1m +TimelinePanel.jButton3.text=3m +TimelinePanel.jButton4.text=2w +ProgressWindow.progressHeader.text=\ +# {0} - \u65e5\u4ed8\u7bc4\u56f2\u306e\u958b\u59cb +# {1} - \u65e5\u4ed8\u7bc4\u56f2\u306e\u7d42\u4e86 +TimeLineResultView.startDateToEndDate.text={0} ~ {1} TimeLineTopComponent.eventsTab.name=\u30a4\u30d9\u30f3\u30c8 TimeLineTopComponent.filterTab.name=\u30d5\u30a3\u30eb\u30bf\u30fc -OpenTimeLineAction.msgdlg.text=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -PrompDialogManager.buttonType.continueNoUpdate=\u66f4\u65b0\u305b\u305a\u6b21\u3078 -PrompDialogManager.buttonType.showTimeline=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3092\u8868\u793a -PrompDialogManager.buttonType.update=\u66f4\u65b0 -PromptDialogManager.confirmDuringIngest.contentText=\u6b21\u3078\u9032\u307f\u307e\u3059\u304b\uff1f -PromptDialogManager.confirmDuringIngest.headerText=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u524d\u306b\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3092\u8868\u793a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307e\u3059\u3002\n\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u304c\u5b8c\u6210\u3057\u3066\u3044\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -PromptDialogManager.progressDialog.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u3092\u5165\u529b\u4e2d -PromptDialogManager.rebuildPrompt.details=\u8a73\u7d30\uff1a -PromptDialogManager.rebuildPrompt.headerText=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u4e0d\u5b8c\u5168\u307e\u305f\u306f\u6700\u65b0\u3067\u306f\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002\n \u6b20\u843d\u3057\u3066\u3044\u308b\u307e\u305f\u306f\u4e0d\u6b63\u78ba\u306a\u30a4\u30d9\u30f3\u30c8\u304c\u4e00\u90e8\u3042\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002\u4e00\u90e8\u306e\u6a5f\u80fd\u304c\u5229\u7528\u3067\u304d\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -Timeline.confirmation.dialogs.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u66f4\u65b0\u3057\u307e\u3059\u304b\uff1f -Timeline.pushDescrLOD.confdlg.title=\u8aac\u660e\u306e\u8a18\u8ff0\u30ec\u30d9\u30eb\u3092\u5909\u66f4\u3057\u307e\u3059\u304b\uff1f -TimeLineController.errorTitle=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30a8\u30e9\u30fc -TimeLineController.outOfDate.errorMessage=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u304c\u6700\u65b0\u304b\u78ba\u8a8d\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u66f4\u65b0\u304c\u5fc5\u8981\u3060\u3068\u60f3\u5b9a\u3057\u307e\u3059\u3002 -TimeLineController.rebuildReasons.incompleteOldSchema=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30a4\u30d9\u30f3\u30c8\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u4e0d\u5b8c\u5168\u306a\u60c5\u5831\u304c\u4ee5\u524d\u5165\u529b\u3055\u308c\u3066\u3044\u307e\u3057\u305f\uff1a\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u66f4\u65b0\u3057\u306a\u3044\u3068\u3001\u4e00\u90e8\u306e\u6a5f\u80fd\u304c\u5229\u7528\u3067\u304d\u306a\u3044\u3001\u307e\u305f\u306f\u6a5f\u80fd\u3057\u306a\u3044\u304b\u3082\u3057\u308c\u306a\u3044\u3067\u3059\u3002 -TimeLineController.rebuildReasons.ingestWasRunning=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b9f\u884c\u4e2d\u306b\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30a4\u30d9\u30f3\u30c8\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u60c5\u5831\u304c\u5165\u529b\u3055\u308c\u3066\u3044\u307e\u3057\u305f\uff1a\u30a4\u30d9\u30f3\u30c8\u304c\u6b20\u3051\u3066\u3044\u308b\u3001\u4e0d\u5b8c\u5168\u3001\u307e\u305f\u306f\u4e0d\u6b63\u78ba\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -TimeLineController.rebuildReasons.outOfDate=\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u304c\u6700\u65b0\u3067\u306f\u3042\u308a\u307e\u305b\u3093\uff1a\u898b\u308c\u306a\u3044\u30a4\u30d9\u30f3\u30c8\u304c\u3042\u308a\u307e\u3059\u3002 -TimeLineController.rebuildReasons.outOfDateError=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u304c\u6700\u65b0\u304b\u78ba\u8a8d\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -TimeLinecontroller.updateNowQuestion=\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4eca\u66f4\u65b0\u3057\u307e\u3059\u304b\uff1f -TimelinePanel.jButton13.text=\u5168\u3066 -Timeline.yearBarChart.x.years=\u5e74 -TimelinePanel.jButton1.text=6\u30f6\u6708 -TimelinePanel.jButton10.text=1\u6642\u9593 -TimelinePanel.jButton9.text=12\u6642\u9593 -TimelinePanel.jButton11.text=5\u5e74 -TimelinePanel.jButton12.text=10\u5e74 -TimelinePanel.jButton6.text=1\u9031\u9593 -TimelinePanel.jButton5.text=1\u5e74 -TimelinePanel.jButton8.text=1\u65e5 -TimelinePanel.jButton7.text=3\u65e5 -TimelinePanel.jButton2.text=1\u30f6\u6708 -TimelinePanel.jButton3.text=3\u30f6\u6708 -TimelinePanel.jButton4.text=2\u9031\u9593 -TimeLineResultView.startDateToEndDate.text={0}\u304b\u3089{1} \ No newline at end of file +TimelineTopComponent.selectedEventListener.errorMsg=\u9078\u629e\u3057\u305f\u30a4\u30d9\u30f3\u30c8\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u53d6\u5f97\u4e2d\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3057\u305f\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/EventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/EventsModel.java new file mode 100755 index 0000000000..146bc156ed --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/EventsModel.java @@ -0,0 +1,803 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014-2019 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.timeline; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import com.google.common.eventbus.EventBus; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import javafx.beans.InvalidationListener; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.property.ReadOnlyObjectWrapper; +import javafx.collections.FXCollections; +import javafx.collections.ObservableMap; +import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; +import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; +import org.joda.time.DateTimeZone; +import org.joda.time.Interval; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent.DeletedBlackboardArtifactTagInfo; +import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent.DeletedContentTagInfo; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; +import org.sleuthkit.autopsy.timeline.events.TagsAddedEvent; +import org.sleuthkit.autopsy.timeline.events.TagsDeletedEvent; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; +import org.sleuthkit.autopsy.timeline.utils.CacheLoaderImpl; +import org.sleuthkit.autopsy.timeline.utils.FilterUtils; +import org.sleuthkit.autopsy.timeline.zooming.EventsModelParams; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TimelineManager; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TimelineEvent; +import org.sleuthkit.datamodel.TimelineEventType; +import org.sleuthkit.datamodel.TimelineFilter; +import org.sleuthkit.datamodel.TimelineFilter.DataSourceFilter; +import org.sleuthkit.datamodel.TimelineFilter.DataSourcesFilter; +import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter; +import org.sleuthkit.datamodel.TimelineFilter.FileTypesFilter; +import org.sleuthkit.datamodel.TimelineFilter.HashHitsFilter; +import org.sleuthkit.datamodel.TimelineFilter.HideKnownFilter; +import org.sleuthkit.datamodel.TimelineFilter.RootFilter; +import org.sleuthkit.datamodel.TimelineFilter.TagsFilter; +import org.sleuthkit.datamodel.TimelineFilter.TextFilter; +import org.sleuthkit.datamodel.TimelineLevelOfDetail; + +/** + * In the timeline implementation of the MVC pattern, this class acts as the + * model. The views are the event counts view, the event details view and the + * events list view. + * + * Concurrency Policy: TimelineManager is internally synchronized, so methods + * that only access the TimelineManager atomically do not need further + * synchronization. All other member state variables should only be accessed + * with intrinsic lock of the containing FilteredEventsModel held. + * + */ +public final class EventsModel { + + private static final Logger logger = Logger.getLogger(EventsModel.class.getName()); + private final EventBus eventbus = new EventBus("EventsModel_EventBus"); //NON-NLS + private final Case currentCase; + private final TimelineManager caseDbEventManager; + + /* + * User-specified parameters for the model exposed as JFX properties. These + * parameters apply across all of the views of the model and are set using + * GUI elements such the event filters panel. + * + * IMPORTANT: Note that the parameters are exposed both as a set and + * individually. + */ + private final ReadOnlyObjectWrapper modelParamsProperty = new ReadOnlyObjectWrapper<>(); + private final ReadOnlyObjectWrapper filterStateProperty = new ReadOnlyObjectWrapper<>(); + private final ReadOnlyObjectWrapper timeRangeProperty = new ReadOnlyObjectWrapper<>(); + private final ReadOnlyObjectWrapper eventTypesHierarchyLevelProperty = new ReadOnlyObjectWrapper<>(TimelineEventType.HierarchyLevel.CATEGORY); + private final ReadOnlyObjectWrapper timelineLODProperty = new ReadOnlyObjectWrapper<>(TimelineLevelOfDetail.LOW); + + /* + * Caches of model data from the case database. + */ + private final ObservableMap datasourceIDsToNamesMap = FXCollections.observableHashMap(); + private final LoadingCache maxEventTimeCache; + private final LoadingCache minEventTimeCache; + private final LoadingCache idsToEventsCache; + private final LoadingCache> eventCountsCache; + + /** + * Makes a new data source filter from a given entry in the cache of data + * source object IDs to data source names. + * + * @param dataSourceEntry The cache entry. + * + * @return A new DataSourceFilter. + */ + private static DataSourceFilter newDataSourceFilter(Map.Entry dataSourceEntry) { + return new DataSourceFilter(dataSourceEntry.getValue(), dataSourceEntry.getKey()); + } + + /** + * Constructs the model in the timeline implementation of the MVC pattern. + * + * @param currentCase The current case. + * @param modelParams The initial state of the model parameters. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public EventsModel(Case currentCase, ReadOnlyObjectProperty modelParams) throws TskCoreException { + this.currentCase = currentCase; + this.caseDbEventManager = currentCase.getSleuthkitCase().getTimelineManager(); + + /* + * Set up the caches of model data from the case database. Note that the + * build() method calls specify the methods used to create default cache + * entries when a call to get() would otherwise return a cache miss. + */ + populateDataSourcesCache(); + idsToEventsCache = CacheBuilder.newBuilder() + .maximumSize(5000L) + .expireAfterAccess(10, TimeUnit.MINUTES) + .build(new CacheLoaderImpl<>(caseDbEventManager::getEventById)); + eventCountsCache = CacheBuilder.newBuilder() + .maximumSize(1000L) + .expireAfterAccess(10, TimeUnit.MINUTES) + .build(new CacheLoaderImpl<>(this::countEventsByType)); + maxEventTimeCache = CacheBuilder.newBuilder() + .build(new CacheLoaderImpl<>(ignored -> caseDbEventManager.getMaxEventTime())); + minEventTimeCache = CacheBuilder.newBuilder() + .build(new CacheLoaderImpl<>(ignored -> caseDbEventManager.getMinEventTime())); + + /* + * Add a listener to the data sources cache that adds a data source + * filter to the event filter state model parameter when a data source + * is added to the cache. + */ + InvalidationListener dataSourcesMapListener = observable -> { + RootFilterState rootFilter = filterStateProperty.getReadOnlyProperty().get(); + addDataSourceFilters(rootFilter); + filterStateProperty.set(rootFilter.copyOf()); + }; + datasourceIDsToNamesMap.addListener(dataSourcesMapListener); + + /* + * Initialize the events filter state model parameter with the default + * events filter. + */ + filterStateProperty.set(getDefaultEventFilterState()); + + /* + * Add a listener to the model parameters property that updates the + * properties that expose the individual model parameters when they are + * changed through the model parameters property. + */ + modelParamsProperty.addListener(observable -> { + final EventsModelParams params = modelParamsProperty.get(); + if (params != null) { + synchronized (EventsModel.this) { + eventTypesHierarchyLevelProperty.set(params.getEventTypesHierarchyLevel()); + filterStateProperty.set(params.getEventFilterState()); + timeRangeProperty.set(params.getTimeRange()); + timelineLODProperty.set(params.getTimelineLOD()); + } + } + }); + + modelParamsProperty.bind(modelParams); + } + + /** + * Populates the map of data source object IDs to data source names from the + * data source data in the case database. + */ + synchronized private void populateDataSourcesCache() throws TskCoreException { + SleuthkitCase skCase = currentCase.getSleuthkitCase(); + for (DataSource ds : skCase.getDataSources()) { + datasourceIDsToNamesMap.putIfAbsent(ds.getId(), ds.getName()); + } + } + + /** + * Adds a data source filter for each data source in the data sources cache + * to a given root filter state object. + * + * @param rootFilterState A root filter state object. + */ + synchronized void addDataSourceFilters(RootFilterState rootFilterState) { + DataSourcesFilter dataSourcesFilter = rootFilterState.getDataSourcesFilterState().getFilter(); + datasourceIDsToNamesMap.entrySet().forEach(entry -> dataSourcesFilter.addSubFilter(newDataSourceFilter(entry))); + } + + /** + * Gets the count of all events that fit the given model parameters. The + * counts are organized by event type for the given event types hierarchy + * level. + * + * @param modelParams The model parameters. + * + * @return A mapping of event types to event counts at the given event types + * hierarchy level. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + private Map countEventsByType(EventsModelParams modelParams) throws TskCoreException { + if (modelParams.getTimeRange() == null) { + return Collections.emptyMap(); + } else { + return caseDbEventManager.countEventsByType(modelParams.getTimeRange().getStartMillis() / 1000, + modelParams.getTimeRange().getEndMillis() / 1000, + modelParams.getEventFilterState().getActiveFilter(), + modelParams.getEventTypesHierarchyLevel()); + } + } + + /** + * Gets the case database events manager. + * + * @return The case database events manager. + */ + public TimelineManager getEventManager() { + return caseDbEventManager; + } + + /** + * Gets the case database. + * + * @return The case database. + */ + public SleuthkitCase getSleuthkitCase() { + return currentCase.getSleuthkitCase(); + } + + /** + * Gets the model parameters property. + * + * @return A read only, observable property for the current model + * parameters. + */ + synchronized public ReadOnlyObjectProperty modelParamsProperty() { + return modelParamsProperty.getReadOnlyProperty(); + } + + /** + * Gets a read only, observable property for the time range model parameter. + * + * @return The time range model parameter property. + */ + @NbBundle.Messages({ + "FilteredEventsModel.timeRangeProperty.errorTitle=Timeline", + "FilteredEventsModel.timeRangeProperty.errorMessage=Error getting spanning interval."}) + synchronized public ReadOnlyObjectProperty timeRangeProperty() { + if (timeRangeProperty.get() == null) { + try { + timeRangeProperty.set(EventsModel.this.getSpanningInterval()); + } catch (TskCoreException timelineCacheException) { + MessageNotifyUtil.Notify.error(Bundle.FilteredEventsModel_timeRangeProperty_errorTitle(), + Bundle.FilteredEventsModel_timeRangeProperty_errorMessage()); + logger.log(Level.SEVERE, "Error getting spanning interval.", timelineCacheException); + } + } + return timeRangeProperty.getReadOnlyProperty(); + } + + /** + * Gets a read only, observable property for the timeline level of detail + * model parameter. + * + * @return The timeline level of detail model parameter property. + */ + synchronized public ReadOnlyObjectProperty descriptionLODProperty() { + return timelineLODProperty.getReadOnlyProperty(); + } + + /** + * Gets a read only, observable property for the event filter model + * parameter. + * + * @return The event filter model parameter property. + */ + synchronized public ReadOnlyObjectProperty eventFilterProperty() { + return filterStateProperty.getReadOnlyProperty(); + } + + /** + * Gets a read only, observable property for the event types hierarchy level + * model parameter. + * + * @return The event types hierarchy level model parameter property. + */ + synchronized public ReadOnlyObjectProperty eventTypesHierarchyLevelProperty() { + return eventTypesHierarchyLevelProperty.getReadOnlyProperty(); + } + + /** + * Gets the current model parameters. + * + * @return The current model parameters. + */ + synchronized public EventsModelParams getModelParams() { + return modelParamsProperty.get(); + } + + /** + * Gets the time range model parameter. + * + * @return The time range model parameter. + */ + synchronized public Interval getTimeRange() { + return getModelParams().getTimeRange(); + } + + /** + * Gets the time range model parameter. + * + * @return The time range model parameter. + */ + synchronized public TimelineLevelOfDetail getDescriptionLOD() { + return getModelParams().getTimelineLOD(); + } + + /** + * Gets the event filter model parameter. + * + * @return The event filter model parameter. + */ + synchronized public RootFilterState getEventFilterState() { + return getModelParams().getEventFilterState(); + } + + /** + * Gets the event types hierarchy level model model parameter. + * + * @return The event types hierarchy level model model parameter. + */ + synchronized public TimelineEventType.HierarchyLevel getEventTypeZoom() { + return getModelParams().getEventTypesHierarchyLevel(); + } + + /** + * Gets a new instance of the default event filter state model parameter, + * with data source filters for every data source currently in the data + * sopurces cache. + * + * @return An instance of the default filter state model parameter. + */ + public synchronized RootFilterState getDefaultEventFilterState() { + /* + * Construct data source filters for all of the data sources in the data + * sources cache. + */ + DataSourcesFilter dataSourcesFilter = new DataSourcesFilter(); + datasourceIDsToNamesMap.entrySet().forEach(dataSourceEntry + -> dataSourcesFilter.addSubFilter(newDataSourceFilter(dataSourceEntry))); + + /* + * Make the rest of the event filters and wrap all of the filters with + * filter state objects for the GUI. + */ + RootFilterState rootFilterState = new RootFilterState(new RootFilter( + new HideKnownFilter(), + new TagsFilter(), + new HashHitsFilter(), + new TextFilter(), + new EventTypeFilter(TimelineEventType.ROOT_EVENT_TYPE), + dataSourcesFilter, + FilterUtils.createDefaultFileTypesFilter(), + Collections.emptySet())); + + return rootFilterState; + } + + /** + * Gets an event given its event ID. + * + * @param eventID The event ID. + * + * @return The event. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public TimelineEvent getEventById(Long eventID) throws TskCoreException { + try { + return idsToEventsCache.get(eventID); + } catch (ExecutionException ex) { + throw new TskCoreException("Error getting cached event from ID", ex); + } + } + + /** + * Gets a set of events given their event IDs. + * + * @param eventIDs The event IDs. + * + * @return THe events. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public Set getEventsById(Collection eventIDs) throws TskCoreException { + Set events = new HashSet<>(); + for (Long id : eventIDs) { + events.add(getEventById(id)); + } + return events; + } + + /** + * Gets a list of event IDs for a given time range and a given events + * filter. + * + * @param timeRange The time range. + * @param filterState A filter state object for the events filter. + * + * @return The events. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public List getEventIDs(Interval timeRange, FilterState filterState) throws TskCoreException { + final Interval overlap; + RootFilter intersection; + synchronized (this) { + overlap = EventsModel.this.getSpanningInterval().overlap(timeRange); + intersection = getEventFilterState().intersect(filterState).getActiveFilter(); + } + return caseDbEventManager.getEventIDs(overlap, intersection); + } + + /** + * Gets a set of event IDs associated with a given file. + * + * @param file The file. + * @param includeDerivedArtifacts If true, also gets the event IDs of events + * associated with artifacts for which the + * file is the source file. + * + * @return The event IDs. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public Set getEventIDsForFile(AbstractFile file, boolean includeDerivedArtifacts) throws TskCoreException { + return caseDbEventManager.getEventIDsForContent(file, includeDerivedArtifacts); + } + + /** + * Gets a set of event IDs associated with a given artifact. + * + * @param artifact The artifact. + * + * @return The event IDs. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public List getEventIDsForArtifact(BlackboardArtifact artifact) throws TskCoreException { + return caseDbEventManager.getEventIDsForArtifact(artifact); + } + + /** + * Gets counts by event type of the events within a given time range. + * + * @param timeRange The time range. + * + * @return The event counts by type. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public Map getEventCounts(Interval timeRange) throws TskCoreException { + final RootFilterState filter; + final TimelineEventType.HierarchyLevel typeZoom; + synchronized (this) { + filter = getEventFilterState(); + typeZoom = getEventTypeZoom(); + } + try { + return eventCountsCache.get(new EventsModelParams(timeRange, typeZoom, filter, null)); + } catch (ExecutionException executionException) { + throw new TskCoreException("Error getting cached event counts.`1", executionException); + } + } + + /** + * Gets the spanning interval for the events that fall within the time range + * and event filter model parameters, in terms of a given time zone. + * + * @param timeZone The time zone. + * + * @return The spanning interval. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public Interval getSpanningInterval(DateTimeZone timeZone) throws TskCoreException { + return caseDbEventManager.getSpanningInterval(modelParamsProperty().get().getTimeRange(), getEventFilterState().getActiveFilter(), timeZone); + } + + /** + * Gets the spanning interval for all of the events in the case database. + * + * @return The spanning interval. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public Interval getSpanningInterval() throws TskCoreException { + return new Interval(getMinEventTime() * 1000, 1000 + getMaxEventTime() * 1000); + } + + /** + * Gets the spanning interval for a collection of events. + * + * @param eventIDs The event IDs of the events. + * + * @return The spanning interval. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public Interval getSpanningInterval(Collection eventIDs) throws TskCoreException { + return caseDbEventManager.getSpanningInterval(eventIDs); + } + + /** + * Gets the minimum event time in the case database, in seconds since the + * UNIX epoch. + * + * @return The minimum event time. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public Long getMinEventTime() throws TskCoreException { + try { + return minEventTimeCache.get("min"); // NON-NLS + } catch (ExecutionException ex) { + throw new TskCoreException("Error getting cached min time.", ex); + } + } + + /** + * Gets the maximum event time in the case database, in seconds since the + * UNIX epoch. + * + * @return The maximum event time. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + public Long getMaxEventTime() throws TskCoreException { + try { + return maxEventTimeCache.get("max"); // NON-NLS + } catch (ExecutionException ex) { + throw new TskCoreException("Error getting cached max time.", ex); + } + } + + /** + * Updates the events model for a content tag added event and publishes a + * tag added event via the model's event bus. + * + * @param evt The event. + * + * @return If a tags added event was published via the model's event bus. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + synchronized public boolean handleContentTagAdded(ContentTagAddedEvent evt) throws TskCoreException { + ContentTag contentTag = evt.getAddedTag(); + Content content = contentTag.getContent(); + Set updatedEventIDs = caseDbEventManager.updateEventsForContentTagAdded(content); + if (isNotEmpty(updatedEventIDs)) { + invalidateCaches(updatedEventIDs); + } + return postTagsAdded(updatedEventIDs); + } + + /** + * Updates the events model for an artifact tag added event and publishes a + * tag added event via the model's event bus. + * + * @param evt The event. + * + * @return If a tags added event was published via the model's event bus. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + synchronized public boolean handleArtifactTagAdded(BlackBoardArtifactTagAddedEvent evt) throws TskCoreException { + BlackboardArtifactTag artifactTag = evt.getAddedTag(); + BlackboardArtifact artifact = artifactTag.getArtifact(); + Set updatedEventIDs = caseDbEventManager.updateEventsForArtifactTagAdded(artifact); + if (isNotEmpty(updatedEventIDs)) { + invalidateCaches(updatedEventIDs); + } + return postTagsAdded(updatedEventIDs); + } + + /** + * Updates the events model for a content tag deleted event and publishes a + * tag deleted event via the model's event bus. + * + * @param evt The event. + * + * @return If a tags deleted event was published via the model's event bus. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + synchronized public boolean handleContentTagDeleted(ContentTagDeletedEvent evt) throws TskCoreException { + DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo(); + Content content = currentCase.getSleuthkitCase().getContentById(deletedTagInfo.getContentID()); + Set updatedEventIDs = caseDbEventManager.updateEventsForContentTagDeleted(content); + if (isNotEmpty(updatedEventIDs)) { + invalidateCaches(updatedEventIDs); + } + return postTagsDeleted(updatedEventIDs); + } + + /** + * Updates the events model for an artifact tag deleted event and publishes + * a tag deleted event via the model's event bus. + * + * @param evt The event. + * + * @return If a tags deleted event was published via the model's event bus. + * + * @throws TskCoreException If there is an error reading model data from the + * case database. + */ + synchronized public boolean handleArtifactTagDeleted(BlackBoardArtifactTagDeletedEvent evt) throws TskCoreException { + DeletedBlackboardArtifactTagInfo deletedTagInfo = evt.getDeletedTagInfo(); + BlackboardArtifact artifact = currentCase.getSleuthkitCase().getBlackboardArtifact(deletedTagInfo.getArtifactID()); + Set updatedEventIDs = caseDbEventManager.updateEventsForArtifactTagDeleted(artifact); + if (isNotEmpty(updatedEventIDs)) { + invalidateCaches(updatedEventIDs); + } + return postTagsDeleted(updatedEventIDs); + } + + /** + * Post a TagsAddedEvent to all registered subscribers, if the given set of + * updated event IDs is not empty. + * + * @param updatedEventIDs The set of event ids to be included in the + * TagsAddedEvent. + * + * @return True if an event was posted. + */ + private boolean postTagsAdded(Set updatedEventIDs) { + boolean tagsUpdated = !updatedEventIDs.isEmpty(); + if (tagsUpdated) { + eventbus.post(new TagsAddedEvent(updatedEventIDs)); + } + return tagsUpdated; + } + + /** + * Post a TagsDeletedEvent to all registered subscribers, if the given set + * of updated event IDs is not empty. + * + * @param updatedEventIDs The set of event ids to be included in the + * TagsDeletedEvent. + * + * @return True if an event was posted. + */ + private boolean postTagsDeleted(Set updatedEventIDs) { + boolean tagsUpdated = !updatedEventIDs.isEmpty(); + if (tagsUpdated) { + eventbus.post(new TagsDeletedEvent(updatedEventIDs)); + } + return tagsUpdated; + } + + /** + * Register the given object to receive events. + * + * @param subscriber The object to register. Must implement public methods + * annotated with Subscribe. + */ + synchronized public void registerForEvents(Object subscriber) { + eventbus.register(subscriber); + } + + /** + * Un-register the given object, so it no longer receives events. + * + * @param subscriber The object to un-register. + */ + synchronized public void unRegisterForEvents(Object subscriber) { + eventbus.unregister(subscriber); + } + + /** + * Posts a refresh requested event to all registered subscribers. + */ + public void postRefreshRequest() { + eventbus.post(new RefreshRequestedEvent()); + } + + /** + * Gets a list of the event types from the case database. + * + * @return The list of event types. + */ + public ImmutableList getEventTypes() { + return caseDbEventManager.getEventTypes(); + } + + /** + * Sets the hash set hits flag for the events associated with the source + * files for a collection of hash set hit artifacts. + * + * @param hashSetHitArtifacts The hash set hit artifacts. + * + * @return The event IDs of the updated events. + * + * @throws TskCoreException If there is an error reading model data from or + * writing model data to the case database. + */ + synchronized public Set updateEventsForHashSetHits(Collection hashSetHitArtifacts) throws TskCoreException { + Set updatedEventIDs = new HashSet<>(); + for (BlackboardArtifact artifact : hashSetHitArtifacts) { + Content content = currentCase.getSleuthkitCase().getContentById(artifact.getObjectID()); + updatedEventIDs.addAll(caseDbEventManager.updateEventsForHashSetHit(content)); + } + if (isNotEmpty(updatedEventIDs)) { + invalidateCaches(updatedEventIDs); + } + return updatedEventIDs; + } + + /** + * Invalidates all of the the model caches and publishes a caches + * invalidated event. Optionally, a collection of event IDs may be supplied, + * in which case only the corresponding entries in the event IDs cache are + * invalidated. + * + * @param updatedEventIDs Either null or a collection of the event IDs. + * + * @throws TskCoreException + */ + public synchronized void invalidateCaches(Collection updatedEventIDs) throws TskCoreException { + populateDataSourcesCache(); + minEventTimeCache.invalidateAll(); + maxEventTimeCache.invalidateAll(); + idsToEventsCache.invalidateAll(emptyIfNull(updatedEventIDs)); + eventCountsCache.invalidateAll(); + eventbus.post(new CacheInvalidatedEvent()); + } + + /** + * Event fired when a cache has been invalidated and the views need to be + * refreshed + */ + public static class CacheInvalidatedEvent { + + private CacheInvalidatedEvent() { + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java deleted file mode 100755 index 483172844f..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ /dev/null @@ -1,651 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2019 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.timeline; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableList; -import com.google.common.eventbus.EventBus; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import javafx.beans.InvalidationListener; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.ReadOnlyObjectWrapper; -import javafx.collections.FXCollections; -import javafx.collections.ObservableMap; -import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; -import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; -import org.joda.time.DateTimeZone; -import org.joda.time.Interval; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; -import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; -import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent.DeletedBlackboardArtifactTagInfo; -import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; -import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; -import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent.DeletedContentTagInfo; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.events.AutopsyEvent; -import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; -import org.sleuthkit.autopsy.timeline.events.TagsAddedEvent; -import org.sleuthkit.autopsy.timeline.events.TagsDeletedEvent; -import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; -import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; -import org.sleuthkit.autopsy.timeline.utils.CacheLoaderImpl; -import org.sleuthkit.autopsy.timeline.utils.FilterUtils; -import org.sleuthkit.autopsy.timeline.zooming.ZoomState; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardArtifactTag; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentTag; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TimelineManager; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TimelineEvent; -import org.sleuthkit.datamodel.TimelineEventType; -import org.sleuthkit.datamodel.TimelineFilter; -import org.sleuthkit.datamodel.TimelineFilter.DataSourceFilter; -import org.sleuthkit.datamodel.TimelineFilter.DataSourcesFilter; -import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter; -import org.sleuthkit.datamodel.TimelineFilter.FileTypesFilter; -import org.sleuthkit.datamodel.TimelineFilter.HashHitsFilter; -import org.sleuthkit.datamodel.TimelineFilter.HideKnownFilter; -import org.sleuthkit.datamodel.TimelineFilter.RootFilter; -import org.sleuthkit.datamodel.TimelineFilter.TagsFilter; -import org.sleuthkit.datamodel.TimelineFilter.TextFilter; -import org.sleuthkit.datamodel.TimelineLevelOfDetail; - -/** - * This class acts as the model for a TimelineView - * - * Views can register listeners on properties returned by methods. - * - * This class is implemented as a filtered view into an underlying - * TimelineManager. - * - * Maintainers, NOTE: as many methods as possible should cache their results so - * as to avoid unnecessary db calls through the TimelineManager -jm - * - * Concurrency Policy: TimelineManager is internally synchronized, so methods - * that only access the TimelineManager atomically do not need further - * synchronization. All other member state variables should only be accessed - * with intrinsic lock of containing FilteredEventsModel held. - * - */ -public final class FilteredEventsModel { - - private static final Logger logger = Logger.getLogger(FilteredEventsModel.class.getName()); - - private final TimelineManager eventManager; - - private final Case autoCase; - private final EventBus eventbus = new EventBus("FilteredEventsModel_EventBus"); //NON-NLS - - //Filter and zoome state - private final ReadOnlyObjectWrapper requestedFilter = new ReadOnlyObjectWrapper<>(); - private final ReadOnlyObjectWrapper requestedTimeRange = new ReadOnlyObjectWrapper<>(); - private final ReadOnlyObjectWrapper requestedZoomState = new ReadOnlyObjectWrapper<>(); - private final ReadOnlyObjectWrapper requestedTypeZoom = new ReadOnlyObjectWrapper<>(TimelineEventType.HierarchyLevel.CATEGORY); - private final ReadOnlyObjectWrapper requestedLOD = new ReadOnlyObjectWrapper<>(TimelineLevelOfDetail.LOW); - // end Filter and zoome state - - //caches - private final LoadingCache maxCache; - private final LoadingCache minCache; - private final LoadingCache idToEventCache; - private final LoadingCache> eventCountsCache; - /** - * Map from datasource id to datasource name. - */ - private final ObservableMap datasourcesMap = FXCollections.observableHashMap(); - // end caches - - /** - * Make a DataSourceFilter from an entry from the datasourcesMap. - * - * @param dataSourceEntry A map entry from datasource id to datasource name. - * - * @return A new DataSourceFilter for the given datsourcesMap entry. - */ - private static DataSourceFilter newDataSourceFromMapEntry(Map.Entry dataSourceEntry) { - return new DataSourceFilter(dataSourceEntry.getValue(), dataSourceEntry.getKey()); - } - - public FilteredEventsModel(Case autoCase, ReadOnlyObjectProperty currentStateProperty) throws TskCoreException { - this.autoCase = autoCase; - this.eventManager = autoCase.getSleuthkitCase().getTimelineManager(); - populateFilterData(); - - //caches - idToEventCache = CacheBuilder.newBuilder() - .maximumSize(5000L) - .expireAfterAccess(10, TimeUnit.MINUTES) - .build(new CacheLoaderImpl<>(eventManager::getEventById)); - eventCountsCache = CacheBuilder.newBuilder() - .maximumSize(1000L) - .expireAfterAccess(10, TimeUnit.MINUTES) - .build(new CacheLoaderImpl<>(this::countEventsByType)); - - maxCache = CacheBuilder.newBuilder() - .build(new CacheLoaderImpl<>(ignored -> eventManager.getMaxEventTime())); - minCache = CacheBuilder.newBuilder() - .build(new CacheLoaderImpl<>(ignored -> eventManager.getMinEventTime())); - - InvalidationListener filterSyncListener = observable -> { - RootFilterState rootFilter = filterProperty().get(); - syncFilters(rootFilter); - requestedFilter.set(rootFilter.copyOf()); - }; - - datasourcesMap.addListener(filterSyncListener); - - requestedFilter.set(getDefaultFilter()); - - requestedZoomState.addListener(observable -> { - final ZoomState zoomState = requestedZoomState.get(); - - if (zoomState != null) { - synchronized (FilteredEventsModel.this) { - requestedTypeZoom.set(zoomState.getTypeZoomLevel()); - requestedFilter.set(zoomState.getFilterState()); - requestedTimeRange.set(zoomState.getTimeRange()); - requestedLOD.set(zoomState.getDescriptionLOD()); - } - } - }); - - requestedZoomState.bind(currentStateProperty); - } - - /** - * get the count of all events that fit the given zoom params organized by - * the EvenType of the level specified in the zoomState - * - * @param zoomState The params that control what events to count and how to - * organize the returned map - * - * @return a map from event type( of the requested level) to event counts - * - * @throws org.sleuthkit.datamodel.TskCoreException - */ - private Map countEventsByType(ZoomState zoomState) throws TskCoreException { - if (zoomState.getTimeRange() == null) { - return Collections.emptyMap(); - } else { - return eventManager.countEventsByType(zoomState.getTimeRange().getStartMillis() / 1000, - zoomState.getTimeRange().getEndMillis() / 1000, - zoomState.getFilterState().getActiveFilter(), zoomState.getTypeZoomLevel()); - } - } - - public TimelineManager getEventManager() { - return eventManager; - } - - public SleuthkitCase getSleuthkitCase() { - return autoCase.getSleuthkitCase(); - } - - public Interval getBoundingEventsInterval(Interval timeRange, RootFilter filter, DateTimeZone timeZone) throws TskCoreException { - return eventManager.getSpanningInterval(timeRange, filter, timeZone); - } - - /** - * Readonly observable property for the current ZoomState - * - * @return A readonly observable property for the current ZoomState. - */ - synchronized public ReadOnlyObjectProperty zoomStateProperty() { - return requestedZoomState.getReadOnlyProperty(); - } - - /** - * Get the current ZoomState - * - * @return The current ZoomState - */ - synchronized public ZoomState getZoomState() { - return requestedZoomState.get(); - } - - /** - * Update the data used to determine the available filters. - */ - synchronized private void populateFilterData() throws TskCoreException { - SleuthkitCase skCase = autoCase.getSleuthkitCase(); - - //because there is no way to remove a datasource we only add to this map. - for (DataSource ds : skCase.getDataSources()) { - datasourcesMap.putIfAbsent(ds.getId(), ds.getName()); - } - } - - /** - * "sync" the given root filter with the state of the casee: Disable filters - * for tags that are not in use in the case, and add new filters for tags, - * hashsets, and datasources, that don't have them. New filters are selected - * by default. - * - * @param rootFilterState the filter state to modify so it is consistent - * with the tags in use in the case - */ - public void syncFilters(RootFilterState rootFilterState) { - DataSourcesFilter dataSourcesFilter = rootFilterState.getDataSourcesFilterState().getFilter(); - datasourcesMap.entrySet().forEach(entry -> dataSourcesFilter.addSubFilter(newDataSourceFromMapEntry(entry))); - } - - /** - * Get a read only view of the time range currently in view. - * - * @return A read only view of the time range currently in view. - */ - @NbBundle.Messages({ - "FilteredEventsModel.timeRangeProperty.errorTitle=Timeline", - "FilteredEventsModel.timeRangeProperty.errorMessage=Error getting spanning interval."}) - synchronized public ReadOnlyObjectProperty timeRangeProperty() { - if (requestedTimeRange.get() == null) { - try { - requestedTimeRange.set(getSpanningInterval()); - } catch (TskCoreException timelineCacheException) { - MessageNotifyUtil.Notify.error(Bundle.FilteredEventsModel_timeRangeProperty_errorTitle(), - Bundle.FilteredEventsModel_timeRangeProperty_errorMessage()); - logger.log(Level.SEVERE, "Error getting spanning interval.", timelineCacheException); - } - } - return requestedTimeRange.getReadOnlyProperty(); - } - - synchronized public ReadOnlyObjectProperty descriptionLODProperty() { - return requestedLOD.getReadOnlyProperty(); - } - - synchronized public ReadOnlyObjectProperty filterProperty() { - return requestedFilter.getReadOnlyProperty(); - } - - synchronized public ReadOnlyObjectProperty eventTypeZoomProperty() { - return requestedTypeZoom.getReadOnlyProperty(); - } - - /** - * The time range currently in view. - * - * @return The time range currently in view. - */ - synchronized public Interval getTimeRange() { - return getZoomState().getTimeRange(); - } - - synchronized public TimelineLevelOfDetail getDescriptionLOD() { - return getZoomState().getDescriptionLOD(); - } - - synchronized public RootFilterState getFilterState() { - return getZoomState().getFilterState(); - } - - synchronized public TimelineEventType.HierarchyLevel getEventTypeZoom() { - return getZoomState().getTypeZoomLevel(); - } - - /** - * Get the default filter used at startup. - * - * @return the default filter used at startup - */ - public synchronized RootFilterState getDefaultFilter() { - DataSourcesFilter dataSourcesFilter = new DataSourcesFilter(); - datasourcesMap.entrySet().forEach(dataSourceEntry - -> dataSourcesFilter.addSubFilter(newDataSourceFromMapEntry(dataSourceEntry))); - - HashHitsFilter hashHitsFilter = new HashHitsFilter(); - - TagsFilter tagsFilter = new TagsFilter(); - - FileTypesFilter fileTypesFilter = FilterUtils.createDefaultFileTypesFilter(); - - return new RootFilterState(new RootFilter(new HideKnownFilter(), - tagsFilter, - hashHitsFilter, - new TextFilter(), - new EventTypeFilter(TimelineEventType.ROOT_EVENT_TYPE), - dataSourcesFilter, - fileTypesFilter, - Collections.emptySet())); - } - - public Interval getBoundingEventsInterval(DateTimeZone timeZone) throws TskCoreException { - return eventManager.getSpanningInterval(zoomStateProperty().get().getTimeRange(), getFilterState().getActiveFilter(), timeZone); - } - - public TimelineEvent getEventById(Long eventID) throws TskCoreException { - try { - return idToEventCache.get(eventID); - } catch (ExecutionException ex) { - throw new TskCoreException("Error getting cached event from ID", ex); - } - } - - public Set getEventsById(Collection eventIDs) throws TskCoreException { - Set events = new HashSet<>(); - for (Long id : eventIDs) { - events.add(getEventById(id)); - } - return events; - } - - public List getEventIDs(Interval timeRange, FilterState filter) throws TskCoreException { - - final Interval overlap; - RootFilter intersection; - synchronized (this) { - overlap = getSpanningInterval().overlap(timeRange); - intersection = getFilterState().intersect(filter).getActiveFilter(); - } - - return eventManager.getEventIDs(overlap, intersection); - } - - /** - * Return the number of events that pass the requested filter and are within - * the given time range. - * - * NOTE: this method does not change the requested time range - * - * @param timeRange - * - * @return - * - * @throws org.sleuthkit.datamodel.TskCoreException - */ - public Map getEventCounts(Interval timeRange) throws TskCoreException { - - final RootFilterState filter; - final TimelineEventType.HierarchyLevel typeZoom; - synchronized (this) { - filter = getFilterState(); - typeZoom = getEventTypeZoom(); - } - try { - return eventCountsCache.get(new ZoomState(timeRange, typeZoom, filter, null)); - } catch (ExecutionException executionException) { - throw new TskCoreException("Error getting cached event counts.`1", executionException); - } - } - - /** - * @return The smallest interval spanning all the events from the case, - * ignoring any filters or requested ranges. - * - * @throws org.sleuthkit.datamodel.TskCoreException - */ - public Interval getSpanningInterval() throws TskCoreException { - return new Interval(getMinTime() * 1000, 1000 + getMaxTime() * 1000); - } - - /** - * Get the smallest interval spanning all the given events. - * - * @param eventIDs The IDs of the events to get a spanning interval arround. - * - * @return the smallest interval spanning all the given events - * - * @throws org.sleuthkit.datamodel.TskCoreException - */ - public Interval getSpanningInterval(Collection eventIDs) throws TskCoreException { - return eventManager.getSpanningInterval(eventIDs); - } - - /** - * @return the time (in seconds from unix epoch) of the absolutely first - * event available from the repository, ignoring any filters or - * requested ranges - * - * @throws org.sleuthkit.datamodel.TskCoreException - */ - public Long getMinTime() throws TskCoreException { - try { - return minCache.get("min"); // NON-NLS - } catch (ExecutionException ex) { - throw new TskCoreException("Error getting cached min time.", ex); - } - } - - /** - * @return the time (in seconds from unix epoch) of the absolutely last - * event available from the repository, ignoring any filters or - * requested ranges - * - * @throws org.sleuthkit.datamodel.TskCoreException - */ - public Long getMaxTime() throws TskCoreException { - try { - return maxCache.get("max"); // NON-NLS - } catch (ExecutionException ex) { - throw new TskCoreException("Error getting cached max time.", ex); - } - } - - synchronized public boolean handleContentTagAdded(ContentTagAddedEvent evt) throws TskCoreException { - ContentTag contentTag = evt.getAddedTag(); - Content content = contentTag.getContent(); - Set updatedEventIDs = eventManager.updateEventsForContentTagAdded(content); - if (isNotEmpty(updatedEventIDs)) { - invalidateCaches(updatedEventIDs); - } - return postTagsAdded(updatedEventIDs); - } - - synchronized public boolean handleArtifactTagAdded(BlackBoardArtifactTagAddedEvent evt) throws TskCoreException { - BlackboardArtifactTag artifactTag = evt.getAddedTag(); - BlackboardArtifact artifact = artifactTag.getArtifact(); - Set updatedEventIDs = eventManager.updateEventsForArtifactTagAdded(artifact); - if (isNotEmpty(updatedEventIDs)) { - invalidateCaches(updatedEventIDs); - } - return postTagsAdded(updatedEventIDs); - } - - synchronized public boolean handleContentTagDeleted(ContentTagDeletedEvent evt) throws TskCoreException { - DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo(); - Content content = autoCase.getSleuthkitCase().getContentById(deletedTagInfo.getContentID()); - Set updatedEventIDs = eventManager.updateEventsForContentTagDeleted(content); - if (isNotEmpty(updatedEventIDs)) { - invalidateCaches(updatedEventIDs); - } - return postTagsDeleted(updatedEventIDs); - } - - synchronized public boolean handleArtifactTagDeleted(BlackBoardArtifactTagDeletedEvent evt) throws TskCoreException { - DeletedBlackboardArtifactTagInfo deletedTagInfo = evt.getDeletedTagInfo(); - BlackboardArtifact artifact = autoCase.getSleuthkitCase().getBlackboardArtifact(deletedTagInfo.getArtifactID()); - Set updatedEventIDs = eventManager.updateEventsForArtifactTagDeleted(artifact); - if (isNotEmpty(updatedEventIDs)) { - invalidateCaches(updatedEventIDs); - } - return postTagsDeleted(updatedEventIDs); - } - - /** - * Get a Set of event IDs for the events that are derived from the given - * file. - * - * @param file The AbstractFile to get derived event IDs - * for. - * @param includeDerivedArtifacts If true, also get event IDs for events - * derived from artifacts derived form this - * file. If false, only gets events derived - * directly from this file (file system - * timestamps). - * - * @return A Set of event IDs for the events that are derived from the given - * file. - * - * @throws org.sleuthkit.datamodel.TskCoreException - */ - public Set getEventIDsForFile(AbstractFile file, boolean includeDerivedArtifacts) throws TskCoreException { - return eventManager.getEventIDsForContent(file, includeDerivedArtifacts); - } - - /** - * Get a List of event IDs for the events that are derived from the given - * artifact. - * - * @param artifact The BlackboardArtifact to get derived event IDs for. - * - * @return A List of event IDs for the events that are derived from the - * given artifact. - * - * @throws org.sleuthkit.datamodel.TskCoreException - */ - public List getEventIDsForArtifact(BlackboardArtifact artifact) throws TskCoreException { - return eventManager.getEventIDsForArtifact(artifact); - } - - /** - * Post a TagsAddedEvent to all registered subscribers, if the given set of - * updated event IDs is not empty. - * - * @param updatedEventIDs The set of event ids to be included in the - * TagsAddedEvent. - * - * @return True if an event was posted. - */ - private boolean postTagsAdded(Set updatedEventIDs) { - boolean tagsUpdated = !updatedEventIDs.isEmpty(); - if (tagsUpdated) { - eventbus.post(new TagsAddedEvent(updatedEventIDs)); - } - return tagsUpdated; - } - - /** - * Post a TagsDeletedEvent to all registered subscribers, if the given set - * of updated event IDs is not empty. - * - * @param updatedEventIDs The set of event ids to be included in the - * TagsDeletedEvent. - * - * @return True if an event was posted. - */ - private boolean postTagsDeleted(Set updatedEventIDs) { - boolean tagsUpdated = !updatedEventIDs.isEmpty(); - if (tagsUpdated) { - eventbus.post(new TagsDeletedEvent(updatedEventIDs)); - } - return tagsUpdated; - } - - /** - * Register the given object to receive events. - * - * @param subscriber The object to register. Must implement public methods - * annotated with Subscribe. - */ - synchronized public void registerForEvents(Object subscriber) { - eventbus.register(subscriber); - } - - /** - * Un-register the given object, so it no longer receives events. - * - * @param subscriber The object to un-register. - */ - synchronized public void unRegisterForEvents(Object subscriber) { - eventbus.unregister(subscriber); - } - - /** - * Post a RefreshRequestedEvent to all registered subscribers. - */ - public void postRefreshRequest() { - eventbus.post(new RefreshRequestedEvent()); - } - - /** - * (Re)Post an AutopsyEvent received from another event distribution system - * locally to all registered subscribers. - * - * @param event The event to re-post. - */ - public void postAutopsyEventLocally(AutopsyEvent event) { - eventbus.post(event); - } - - public ImmutableList getEventTypes() { - return eventManager.getEventTypes(); - } - - synchronized public Set setHashHit(Collection artifacts) throws TskCoreException { - Set updatedEventIDs = new HashSet<>(); - for (BlackboardArtifact artifact : artifacts) { - Content content = autoCase.getSleuthkitCase().getContentById(artifact.getObjectID()); - updatedEventIDs.addAll(eventManager.updateEventsForHashSetHit(content)); - } - if (isNotEmpty(updatedEventIDs)) { - invalidateCaches(updatedEventIDs); - } - return updatedEventIDs; - } - - /** - * Invalidate the timeline caches for the given event IDs. Also forces the - * filter values to be updated with any new values from the case data.( data - * sources, tags, etc) - * - * @param updatedEventIDs A collection of the event IDs whose cached event - * objects should be invalidated. Can be null or an - * empty sett to invalidate the general caches, such - * as min/max time, or the counts per event type. - * - * @throws TskCoreException - */ - public synchronized void invalidateCaches(Collection updatedEventIDs) throws TskCoreException { - minCache.invalidateAll(); - maxCache.invalidateAll(); - idToEventCache.invalidateAll(emptyIfNull(updatedEventIDs)); - eventCountsCache.invalidateAll(); - - populateFilterData(); - - eventbus.post(new CacheInvalidatedEvent()); - } - - /** - * Event fired when a cache has been invalidated. The UI should make it - * clear that the view is potentially out of date and present an action to - * refresh the view. - */ - public static class CacheInvalidatedEvent { - - private CacheInvalidatedEvent() { - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java index 1c1dae4165..abad95b2ff 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java @@ -195,7 +195,7 @@ final class ShowInTimelineDialog extends Dialog { //add events to table Set events = new HashSet<>(); - FilteredEventsModel eventsModel = controller.getEventsModel(); + EventsModel eventsModel = controller.getEventsModel(); for (Long eventID : eventIDS) { try { events.add(eventsModel.getEventById(eventID)); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 8235fb79f3..adf4509e76 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014-2018 Basis Technology Corp. + * Copyright 2014-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -85,11 +85,10 @@ import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.DescriptionFilterSt import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; import org.sleuthkit.autopsy.timeline.utils.IntervalUtils; import org.sleuthkit.autopsy.timeline.zooming.TimeUnits; -import org.sleuthkit.autopsy.timeline.zooming.ZoomState; +import org.sleuthkit.autopsy.timeline.zooming.EventsModelParams; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT; -import org.sleuthkit.datamodel.TimelineEvent; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TimelineEventType; import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter; @@ -200,16 +199,16 @@ public class TimeLineController { private final ReadOnlyObjectWrapper viewMode = new ReadOnlyObjectWrapper<>(ViewMode.COUNTS); @GuardedBy("filteredEvents") - private final FilteredEventsModel filteredEvents; + private final EventsModel filteredEvents; @GuardedBy("this") - private final ZoomState InitialZoomState; + private final EventsModelParams InitialZoomState; @GuardedBy("this") - private final History historyManager = new History<>(); + private final History historyManager = new History<>(); @GuardedBy("this") - private final ReadOnlyObjectWrapper currentParams = new ReadOnlyObjectWrapper<>(); + private final ReadOnlyObjectWrapper currentParams = new ReadOnlyObjectWrapper<>(); //selected events (ie shown in the result viewer) @GuardedBy("this") @@ -281,7 +280,7 @@ public class TimeLineController { TimeLineController(Case autoCase) throws TskCoreException { this.autoCase = autoCase; - filteredEvents = new FilteredEventsModel(autoCase, currentParams.getReadOnlyProperty()); + filteredEvents = new EventsModel(autoCase, currentParams.getReadOnlyProperty()); /* * as the history manager's current state changes, modify the tags * filter to be in sync, and expose that as propery from @@ -289,16 +288,16 @@ public class TimeLineController { * filters? */ historyManager.currentState().addListener((observable, oldState, newState) -> { - ZoomState historyManagerState = newState; - filteredEvents.syncFilters(historyManagerState.getFilterState()); + EventsModelParams historyManagerState = newState; + filteredEvents.addDataSourceFilters(historyManagerState.getEventFilterState()); currentParams.set(historyManagerState); }); try { - InitialZoomState = new ZoomState(filteredEvents.getSpanningInterval(), + InitialZoomState = new EventsModelParams(filteredEvents.getSpanningInterval(), TimelineEventType.HierarchyLevel.CATEGORY, - filteredEvents.filterProperty().get(), + filteredEvents.eventFilterProperty().get(), TimelineLevelOfDetail.LOW); } catch (TskCoreException ex) { throw new TskCoreException("Error getting spanning interval.", ex); @@ -318,17 +317,17 @@ public class TimeLineController { /** * @return a shared events model */ - public FilteredEventsModel getEventsModel() { + public EventsModel getEventsModel() { return filteredEvents; } public void applyDefaultFilters() { - pushFilters(filteredEvents.getDefaultFilter()); + pushFilters(filteredEvents.getDefaultEventFilterState()); } public void zoomOutToActivity() throws TskCoreException { - Interval boundingEventsInterval = filteredEvents.getBoundingEventsInterval(getJodaTimeZone()); - advance(filteredEvents.zoomStateProperty().get().withTimeRange(boundingEventsInterval)); + Interval boundingEventsInterval = filteredEvents.getSpanningInterval(getJodaTimeZone()); + advance(filteredEvents.modelParamsProperty().get().withTimeRange(boundingEventsInterval)); } private final ObservableSet pinnedEvents = FXCollections.observableSet(); @@ -494,7 +493,7 @@ public class TimeLineController { } synchronized public void pushEventTypeZoom(TimelineEventType.HierarchyLevel typeZoomeLevel) { - ZoomState currentZoom = filteredEvents.zoomStateProperty().get(); + EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get(); if (currentZoom == null) { advance(InitialZoomState.withTypeZoomLevel(typeZoomeLevel)); } else if (currentZoom.hasTypeZoomLevel(typeZoomeLevel) == false) { @@ -525,7 +524,7 @@ public class TimeLineController { } } - ZoomState currentZoom = filteredEvents.zoomStateProperty().get(); + EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get(); if (currentZoom == null) { advance(InitialZoomState.withTimeRange(clampedTimeRange)); return true; @@ -556,7 +555,7 @@ public class TimeLineController { } synchronized public void pushDescrLOD(TimelineLevelOfDetail newLOD) { - ZoomState currentZoom = filteredEvents.zoomStateProperty().get(); + EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get(); if (currentZoom == null) { advance(InitialZoomState.withDescrLOD(newLOD)); } else if (currentZoom.hasDescrLOD(newLOD) == false) { @@ -567,7 +566,7 @@ public class TimeLineController { @SuppressWarnings("AssignmentToMethodParameter") //clamp timerange to case synchronized public void pushTimeAndType(Interval timeRange, TimelineEventType.HierarchyLevel typeZoom) throws TskCoreException { Interval overlappingTimeRange = this.filteredEvents.getSpanningInterval().overlap(timeRange); - ZoomState currentZoom = filteredEvents.zoomStateProperty().get(); + EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get(); if (currentZoom == null) { advance(InitialZoomState.withTimeAndType(overlappingTimeRange, typeZoom)); } else if (currentZoom.hasTimeRange(overlappingTimeRange) == false && currentZoom.hasTypeZoomLevel(typeZoom) == false) { @@ -580,7 +579,7 @@ public class TimeLineController { } synchronized public void pushFilters(RootFilterState filter) { - ZoomState currentZoom = filteredEvents.zoomStateProperty().get(); + EventsModelParams currentZoom = filteredEvents.modelParamsProperty().get(); if (currentZoom == null) { advance(InitialZoomState.withFilterState(filter)); } else if (currentZoom.hasFilterState(filter) == false) { @@ -596,7 +595,7 @@ public class TimeLineController { historyManager.retreat(); } - synchronized private void advance(ZoomState newState) { + synchronized private void advance(EventsModelParams newState) { historyManager.advance(newState); } @@ -746,7 +745,7 @@ public class TimeLineController { case DATA_ADDED: ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == TSK_HASHSET_HIT.getTypeID()) { - logFutureException(executor.submit(() -> filteredEvents.setHashHit(eventData.getArtifacts())), + logFutureException(executor.submit(() -> filteredEvents.updateEventsForHashSetHits(eventData.getArtifacts())), "Error executing task in response to DATA_ADDED event.", "Error executing response to new data."); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/actions/Bundle_ja.properties old mode 100755 new mode 100644 index af71283ca2..ae83b8c4ac --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/Bundle_ja.properties @@ -1,26 +1,39 @@ -Back.longText=\u6700\u5F8C\u306E\u30D3\u30E5\u30FC\u8A2D\u5B9A\u306B\u623B\u308B\u3002 -Back.text=\u623B\u308B -Forward.text=\u6B21\u3078 -OpenReportAction.DisplayName=\u30EC\u30DD\u30FC\u30C8\u3092\u958B\u304F -OpenReportAction.MessageBoxTitle=\u30EC\u30DD\u30FC\u30C8\u3092\u958B\u304F\u306E\u3092\u5931\u6557\u3057\u307E\u3057\u305F -OpenReportAction.MissingReportFileMessage=\u30EC\u30DD\u30FC\u30C8\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u305B\u3093\u3002 -OpenReportAction.NoAssociatedEditorMessage=\u3053\u306E\u30BF\u30A4\u30D7\u306E\u30EC\u30DD\u30FC\u30C8\u306E\u95A2\u9023\u30A8\u30C7\u30A3\u30BF\u30FC\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u307E\u305F\u306F\u95A2\u9023\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u304C\u8D77\u52D5\u3057\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -OpenReportAction.NoOpenInEditorSupportMessage=\u3053\u306E\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\uFF08\u30AA\u30DA\u30EC\u30FC\u30C6\u30A3\u30F3\u30B0\u30B7\u30B9\u30C6\u30E0\uFF09\u306F\u3053\u306E\u3088\u3046\u306B\u30D5\u30A1\u30A4\u30EB\u3092\u30A8\u30C7\u30A3\u30BF\u30FC\u3067\u958B\u304F\u306E\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u307E\u305B\u3093\u3002 -OpenReportAction.ReportFileOpenPermissionDeniedMessage=\u30EC\u30DD\u30FC\u30C8\u30D5\u30A1\u30A4\u30EB\u3092\u958B\u304F\u8A31\u53EF\u304C\u5374\u4E0B\u3055\u308C\u307E\u3057\u305F\u3002 -ResetFilters.text=\u5168\u3066\u306E\u30D5\u30A3\u30EB\u30BF\u30FC\u3092\u30EA\u30BB\u30C3\u30C8 -RestFilters.longText=\u5168\u3066\u306E\u30D5\u30A3\u30EB\u30BF\u30FC\u3092\u30C7\u30D5\u30A9\u30EB\u30C8\u72B6\u614B\u306B\u30EA\u30BB\u30C3\u30C8\u3057\u307E\u3059\u3002 -SaveSnapshot.action.longText=\u30EC\u30DD\u30FC\u30C8\u3068\u3057\u3066\u30D3\u30B8\u30E5\u30A2\u30E9\u30A4\u30BC\u30FC\u30B7\u30E7\u30F3\uFF08\u53EF\u8996\u5316\uFF09\u306E\u30B9\u30AF\u30EA\u30FC\u30F3\u30AD\u30E3\u30D7\u30C1\u30E3\u3092\u4FDD\u5B58\u3057\u307E\u3059\u3002 -SaveSnapshot.action.name.text=\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u30EC\u30DD\u30FC\u30C8 -SaveSnapshot.fileChoose.title.text=\u6B21\u3078\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u3092\u4FDD\u5B58 -SaveSnapShotAsReport.ErrorWritingReport=\u30EC\u30DD\u30FC\u30C8{0}\u3092\u30C7\u30A3\u30B9\u30AF\u306B\u66F8\u304D\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u8A73\u7D30\u306F\u30ED\u30B0\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 -SaveSnapShotAsReport.FailedToAddReport=\u30EC\u30DD\u30FC\u30C8\u3068\u3057\u3066\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u3092\u8FFD\u52A0\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u8A73\u7D30\u306F\u30ED\u30B0\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 -SaveSnapsHotAsReport.ReportName=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30FC\u30EC\u30DD\u30FC\u30C8\u30FC{0} -SaveSnapShotAsReport.ReportSavedAt=\u30EC\u30DD\u30FC\u30C8\u306F\u6B21\u3078\u4FDD\u5B58\u3055\u308C\u307E\u3057\u305F[{0}] -SaveSnapShotAsReport.Success=\u6210\u529F -Timeline.ModuleName=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3 -ZoomIn.action.text=\u62E1\u5927 -ZoomIn.longText=\u304A\u3088\u305D\u534A\u5206\u306E\u6642\u9593\u3092\u8868\u793A\u3059\u308B\u3088\u3046\u306B\u62E1\u5927\u3057\u307E\u3059\u3002 -ZoomOut.action.text=\u7E2E\u5C0F -ZoomOut.longText=\u304A\u3088\u305D50%\u591A\u304F\u8868\u793A\u3059\u308B\u3088\u3046\u306B\u7E2E\u5C0F\u3057\u307E\u3059\u3002 -ZoomToEvents.action.text=\u30A4\u30D9\u30F3\u30C8\u306B\u30BA\u30FC\u30E0 -ZoomToEvents.longText=\u4E00\u756A\u8FD1\u304F\u306E\u30A4\u30D9\u30F3\u30C8\u304C\u8868\u793A\u3055\u308C\u308B\u3088\u3046\u306B\u7E2E\u5C0F\u3057\u307E\u3059\u3002 +# {0} - \u30a2\u30af\u30b7\u30e7\u30f3\u30fb\u30a2\u30af\u30bb\u30e9\u30ec\u30fc\u30bf\u30fb\u30ad\u30fc +Back.longText=\u623b\u308b: {0}\n\u524d\u306e\u30d3\u30e5\u30fc\u8a2d\u5b9a\u306b\u623b\u308a\u307e\u3059\u3002 +Back.text=\u623b\u308b +# {0} - \u30a2\u30af\u30b7\u30e7\u30f3\u30fb\u30a2\u30af\u30bb\u30e9\u30ec\u30fc\u30bf\u30fb\u30ad\u30fc +Forward.longText=\u9032\u3080: {0}\n\u6b21\u306e\u30d3\u30e5\u30fc\u8a2d\u5b9a\u306b\u9032\u307f\u307e\u3059\u3002 +Forward.text=\u9032\u3080 +OpenReportAction.DisplayName=\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u304f +OpenReportAction.MessageBoxTitle=\u30ec\u30dd\u30fc\u30c8\u30aa\u30fc\u30d7\u30f3\u5931\u6557 +OpenReportAction.MissingReportFileMessage=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +OpenReportAction.NoAssociatedEditorMessage=\u3053\u306e\u30bf\u30a4\u30d7\u306e\u30ec\u30dd\u30fc\u30c8\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30a8\u30c7\u30a3\u30bf\u30fc\u304c\u306a\u3044\u304b\u3001\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +OpenReportAction.NoOpenInEditorSupportMessage=\u3053\u306e\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0(\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0)\u306f\u3053\u306e\u65b9\u6cd5\u3067\u30a8\u30c7\u30a3\u30bf\u30fc\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u304f\u3053\u3068\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u305b\u3093\u3002 +OpenReportAction.ReportFileOpenPermissionDeniedMessage=\u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u304f\u8a31\u53ef\u304c\u62d2\u5426\u3055\u308c\u307e\u3057\u305f\u3002 +RebuildDataBase.longText=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u66f4\u65b0\u3057\u3066\u65b0\u3057\u3044\u30a4\u30d9\u30f3\u30c8\u3092\u542b\u3081\u307e\u3059\u3002 +RebuildDataBase.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u66f4\u65b0 +ResetFilters.text=\u3059\u3079\u3066\u306e\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u30ea\u30bb\u30c3\u30c8 +RestFilters.longText=\u3059\u3079\u3066\u306e\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u72b6\u614b\u306b\u30ea\u30bb\u30c3\u30c8\u3057\u307e\u3059\u3002 +SaveSnapShotAsReport.action.dialogs.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 +SaveSnapShotAsReport.action.longText=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u73fe\u5728\u306e\u30d3\u30e5\u30fc\u306e\u30b9\u30af\u30ea\u30fc\u30f3\u30ad\u30e3\u30d7\u30c1\u30e3\u30fc\u3092\u30ec\u30dd\u30fc\u30c8\u3068\u3057\u3066\u4fdd\u5b58\u3057\u307e\u3059\u3002 +SaveSnapShotAsReport.action.name.text=\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u30fb\u30ec\u30dd\u30fc\u30c8 +SaveSnapShotAsReport.duplicateReportNameError.text=\u305d\u306e\u540d\u524d\u306e\u30ec\u30dd\u30fc\u30c8\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 +# {0} - \u30ec\u30dd\u30fc\u30c8\u30d1\u30b9 +SaveSnapShotAsReport.ErrorWritingReport={0} \u306b\u3042\u308b\u30c7\u30a3\u30b9\u30af\u306b\u30ec\u30dd\u30fc\u30c8\u3092\u66f8\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +SaveSnapShotAsReport.FailedToAddReport=\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u3092\u30b1\u30fc\u30b9\u3068\u3057\u3066\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +SaveSnapShotAsReport.reportName.header=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u30fb\u30ec\u30dd\u30fc\u30c8\u306e\u30ec\u30dd\u30fc\u30c8\u540d\u3092\u5165\u529b\u3057\u307e\u3059\u3002 +# {0} - \u751f\u6210\u3055\u308c\u305f\u30c7\u30d5\u30a9\u30eb\u30c8\u30ec\u30dd\u30fc\u30c8\u540d +SaveSnapShotAsReport.reportName.prompt=\u6b21\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30ec\u30dd\u30fc\u30c8\u540d\u3092\u7a7a(\u672a\u5165\u529b)\u306e\u307e\u307e\u306b\u3059\u308b: {0}\u3002 +# {0} - \u30ec\u30dd\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 +SaveSnapShotAsReport.ReportSavedAt=[{0}] \u306b\u30ec\u30dd\u30fc\u30c8\u304c\u4fdd\u5b58\u3055\u308c\u307e\u3057\u305f +SaveSnapShotAsReport.Success=\u6210\u529f +Timeline.ModuleName=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 +ViewArtifactInTimelineAction.displayName=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306b\u7d50\u679c\u3092\u8868\u793a... +ViewFileInTimelineAction.viewFile.displayName=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a... +DataResultFilterNode.action.viewSrcFileInDir.text=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306b\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a... +ZoomIn.action.text=\u30ba\u30fc\u30e0\u30a4\u30f3 +ZoomIn.longText=\u30ba\u30fc\u30e0\u30a4\u30f3\u3057\u3066\u7d04\u534a\u5206\u306e\u6642\u9593\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +ZoomOut.action.text=\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8 +ZoomOut.longText=\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8\u3057\u3066\u7d0450%\u4ee5\u4e0a\u306e\u6642\u9593\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +ZoomToEvents.action.text=\u30a4\u30d9\u30f3\u30c8\u306b\u30ba\u30fc\u30e0 +ZoomToEvents.longText=\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8\u3057\u3066\u6700\u3082\u6700\u8fd1\u306e\u30a4\u30d9\u30f3\u30c8\u3092\u8868\u793a\u3057\u307e\u3059\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ResetFilters.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ResetFilters.java index 30ed0ebb81..57f0ed61e5 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/ResetFilters.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ResetFilters.java @@ -22,7 +22,7 @@ import javafx.beans.binding.BooleanBinding; import javafx.event.ActionEvent; import org.controlsfx.control.action.Action; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; /** @@ -32,7 +32,7 @@ import org.sleuthkit.autopsy.timeline.TimeLineController; "RestFilters.longText=Reset all filters to their default state."}) public class ResetFilters extends Action { - private FilteredEventsModel eventsModel; + private EventsModel eventsModel; public ResetFilters(final TimeLineController controller) { this(Bundle.ResetFilters_text(), controller); @@ -44,12 +44,12 @@ public class ResetFilters extends Action { eventsModel = controller.getEventsModel(); disabledProperty().bind(new BooleanBinding() { { - bind(eventsModel.zoomStateProperty()); + bind(eventsModel.modelParamsProperty()); } @Override protected boolean computeValue() { - return eventsModel.zoomStateProperty().getValue().getFilterState().equals(eventsModel.getDefaultFilter()); + return eventsModel.modelParamsProperty().getValue().getEventFilterState().equals(eventsModel.getDefaultEventFilterState()); } }); setEventHandler((ActionEvent t) -> { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java index 6208714e06..b5949b7f01 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java @@ -137,7 +137,7 @@ public class SaveSnapshotAsReport extends Action { reportMainFilePath = new SnapShotReportWriter(currentCase, reportFolderPath, reportName, - controller.getEventsModel().getZoomState(), + controller.getEventsModel().getModelParams(), generationDate, snapshot).writeReport(); } catch (IOException ex) { LOGGER.log(Level.SEVERE, "Error writing report to disk at " + reportFolderPath, ex); //NON_NLS diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewArtifactInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewArtifactInTimelineAction.java index ed36bff868..69f998dc9b 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewArtifactInTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewArtifactInTimelineAction.java @@ -25,6 +25,7 @@ import org.openide.util.NbBundle; import org.openide.util.actions.SystemAction; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.timeline.OpenTimelineAction; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -43,6 +44,10 @@ public final class ViewArtifactInTimelineAction extends AbstractAction { public ViewArtifactInTimelineAction(BlackboardArtifact artifact) { super(Bundle.ViewArtifactInTimelineAction_displayName()); this.artifact = artifact; + // If timeline functionality is not available this action is disabled. + if ("false".equals(ModuleSettings.getConfigSetting("timeline", "enable_timeline"))) { + setEnabled(false); + } } @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewFileInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewFileInTimelineAction.java index e763f4c428..8ef6bce42c 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewFileInTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewFileInTimelineAction.java @@ -25,6 +25,7 @@ import org.openide.util.NbBundle; import org.openide.util.actions.SystemAction; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.timeline.OpenTimelineAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; @@ -54,6 +55,10 @@ public final class ViewFileInTimelineAction extends AbstractAction { && file.getAtime() <= 0)) { this.setEnabled(false); } + // If timeline functionality is not available this action is disabled. + if ("false".equals(ModuleSettings.getConfigSetting("timeline", "enable_timeline"))) { + setEnabled(false); + } } @NbBundle.Messages({"ViewFileInTimelineAction.viewFile.displayName=View File in Timeline... "}) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ZoomOut.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ZoomOut.java index 9550004e1c..5022d3e8da 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/ZoomOut.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ZoomOut.java @@ -26,7 +26,7 @@ import javafx.scene.image.ImageView; import org.controlsfx.control.action.Action; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.datamodel.TskCoreException; @@ -58,10 +58,10 @@ public class ZoomOut extends Action { //disable action when the current time range already encompases the entire case. disabledProperty().bind(new BooleanBinding() { - private final FilteredEventsModel eventsModel = controller.getEventsModel(); + private final EventsModel eventsModel = controller.getEventsModel(); { - bind(eventsModel.zoomStateProperty(), eventsModel.timeRangeProperty()); + bind(eventsModel.modelParamsProperty(), eventsModel.timeRangeProperty()); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ZoomToEvents.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ZoomToEvents.java index e85de25b5f..799208bf8e 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/ZoomToEvents.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ZoomToEvents.java @@ -27,7 +27,7 @@ import org.controlsfx.control.action.Action; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.datamodel.TskCoreException; /** @@ -56,10 +56,10 @@ public class ZoomToEvents extends Action { //disable action when the current time range already encompases the entire case. disabledProperty().bind(new BooleanBinding() { - private final FilteredEventsModel eventsModel = controller.getEventsModel(); + private final EventsModel eventsModel = controller.getEventsModel(); { - bind(eventsModel.zoomStateProperty()); + bind(eventsModel.modelParamsProperty()); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/Bundle_ja.properties new file mode 100644 index 0000000000..56871db82c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/Bundle_ja.properties @@ -0,0 +1,22 @@ +BaseTypes.fileSystem.name=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0 +BaseTypes.webActivity.name=Web\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3\u30fc +BaseTypes.miscTypes.name=\u3055\u307e\u3056\u307e\u306a\u30bf\u30a4\u30d7 +FileSystemTypes.fileModified.name=\u4fee\u6b63\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb +FileSystemTypes.fileAccessed.name=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb +FileSystemTypes.fileCreated.name=\u4f5c\u6210\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb +FileSystemTypes.fileChanged.name=\u5909\u66f4\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb +MiscTypes.message.name=\u30e1\u30c3\u30bb\u30fc\u30b8 +MiscTypes.GPSRoutes.name=GPS\u30eb\u30fc\u30c8 +MiscTypes.GPSTrackpoint.name=\u5834\u6240\u5c65\u6b74 +MiscTypes.Calls.name=\u547c\u3073\u51fa\u3057 +MiscTypes.Email.name=\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9 +MiscTypes.recentDocuments.name=\u6700\u8fd1\u4f7f\u3063\u305f\u6587\u66f8 +MiscTypes.installedPrograms.name=\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3055\u308c\u3066\u3044\u308b\u30d7\u30ed\u30b0\u30e9\u30e0 +MiscTypes.exif.name=\u7d42\u4e86 +MiscTypes.devicesAttached.name=\u88c5\u7740\u3055\u308c\u3066\u3044\u308b\u30c7\u30d0\u30a4\u30b9 +RootEventType.eventTypes.name=\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7 +WebTypes.webDownloads.name=Web\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9 +WebTypes.webCookies.name=Web\u30af\u30c3\u30ad\u30fc +WebTypes.webBookmarks.name=Web\u30d6\u30c3\u30af\u30de\u30fc\u30af +WebTypes.webHistory.name=Web\u5c65\u6b74 +WebTypes.webSearch.name=Web\u691c\u7d22 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/Bundle_ja.properties old mode 100755 new mode 100644 index 7096881555..8e1199be05 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/Bundle_ja.properties @@ -1 +1,11 @@ -EventRoodNode.tooManyNode.displayName=\u8868\u793a\u3059\u308b\u30a4\u30d9\u30f3\u30c8\u6570\u304c\u591a\u3059\u304e\u307e\u3059\u3002\u6700\u5927 \= {0}\u3002\u8868\u793a\u3059\u308b\u30a4\u30d9\u30f3\u30c8\u306f{1}\u3042\u308a\u307e\u3059\u3002 \ No newline at end of file +EventNode.getAction.errorTitle=\u30a2\u30af\u30b7\u30e7\u30f3\u306e\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +EventNode.getAction.linkedFileMessage=\u9078\u629e\u3057\u305f\u7d50\u679c\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u53d6\u5f97\u4e2d\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3057\u305f\u3002 [\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a] \u30a2\u30af\u30b7\u30e7\u30f3\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +# {0} - \u8868\u793a\u53ef\u80fd\u306a\u6700\u5927\u30a4\u30d9\u30f3\u30c8\u6570 +# {1} - \u591a\u3059\u304e\u308b\u30a4\u30d9\u30f3\u30c8\u6570 +EventRoodNode.tooManyNode.displayName=\u30a4\u30d9\u30f3\u30c8\u304c\u591a\u3059\u304e\u3066\u8868\u793a\u3067\u304d\u307e\u305b\u3093\u3002 \u6700\u5927 = {0} \u3067\u3059\u3002\u305f\u3060\u3057\u3001{1} \u3092\u8868\u793a\u3067\u304d\u307e\u3059\u3002 +NodeProperty.displayName.baseType=\u57fa\u672c\u30bf\u30a4\u30d7 +NodeProperty.displayName.dateTime=\u65e5/\u6642\u9593 +NodeProperty.displayName.description=\u8a18\u8ff0 +NodeProperty.displayName.icon=\u30a2\u30a4\u30b3\u30f3 +NodeProperty.displayName.known=\u65e2\u77e5 +NodeProperty.displayName.subType=\u30b5\u30d6\u30bf\u30a4\u30d7 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java index 56dc02daed..018adf1018 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java @@ -45,7 +45,7 @@ import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.NodeProperty; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.autopsy.timeline.ui.EventTypeUtils; @@ -262,7 +262,7 @@ public class EventNode extends DisplayableItemNode { * @return An EventNode with the content (and possible artifact) backing * this event in its lookup. */ - public static EventNode createEventNode(final Long eventID, FilteredEventsModel eventsModel) throws TskCoreException { + public static EventNode createEventNode(final Long eventID, EventsModel eventsModel) throws TskCoreException { SleuthkitCase sleuthkitCase = eventsModel.getSleuthkitCase(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java index e7c14d87f9..833d6ccc41 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java @@ -32,7 +32,7 @@ import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.datamodel.TskCoreException; /** @@ -49,7 +49,7 @@ public class EventRootNode extends DisplayableItemNode { */ public static final int MAX_EVENTS_TO_DISPLAY = 5000; - public EventRootNode(Collection eventIds, FilteredEventsModel filteredEvents) { + public EventRootNode(Collection eventIds, EventsModel filteredEvents) { super(Children.create(new EventNodeChildFactory(eventIds, filteredEvents), true), Lookups.singleton(eventIds)); } @@ -83,10 +83,10 @@ public class EventRootNode extends DisplayableItemNode { /** * filteredEvents is used to lookup the events from their IDs */ - private final FilteredEventsModel filteredEvents; + private final EventsModel filteredEvents; private final Map nodesMap = new HashMap<>(); - EventNodeChildFactory(Collection eventIds, FilteredEventsModel filteredEvents) { + EventNodeChildFactory(Collection eventIds, EventsModel filteredEvents) { this.eventIDs = eventIds; this.filteredEvents = filteredEvents; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java index c7734dbeea..994b23d845 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java @@ -27,14 +27,14 @@ import javax.imageio.ImageIO; import org.joda.time.format.DateTimeFormat; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.report.uisnapshot.UiSnapShotReportWriter; -import org.sleuthkit.autopsy.timeline.zooming.ZoomState; +import org.sleuthkit.autopsy.timeline.zooming.EventsModelParams; /** * Generate and write the Timeline snapshot report to disk. */ public class SnapShotReportWriter extends UiSnapShotReportWriter{ - private final ZoomState zoomState; + private final EventsModelParams zoomState; private final BufferedImage image; /** @@ -49,7 +49,7 @@ public class SnapShotReportWriter extends UiSnapShotReportWriter{ * @param generationDate The generation Date of the report. * @param snapshot A snapshot of the view to include in the report. */ - public SnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, ZoomState zoomState, Date generationDate, BufferedImage snapshot) { + public SnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, EventsModelParams zoomState, Date generationDate, BufferedImage snapshot) { super(currentCase, reportFolderPath, reportName, generationDate); this.zoomState = zoomState; this.image = snapshot; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java index d9a51f2023..7a4fc4db85 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java @@ -37,7 +37,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; @@ -75,7 +75,7 @@ public abstract class AbstractTimeLineView extends BorderPane { private Task updateTask; private final TimeLineController controller; - private final FilteredEventsModel filteredEvents; + private final EventsModel filteredEvents; /** * Constructor @@ -86,7 +86,7 @@ public abstract class AbstractTimeLineView extends BorderPane { this.controller = controller; this.filteredEvents = controller.getEventsModel(); this.filteredEvents.registerForEvents(this); - this.filteredEvents.zoomStateProperty().addListener(updateListener); + this.filteredEvents.modelParamsProperty().addListener(updateListener); TimeLineController.timeZoneProperty().addListener(updateListener); } @@ -170,7 +170,7 @@ public abstract class AbstractTimeLineView extends BorderPane { * * @return The FilteredEventsModel for this view. */ - protected FilteredEventsModel getEventsModel() { + protected EventsModel getEventsModel() { return filteredEvents; } @@ -224,7 +224,7 @@ public abstract class AbstractTimeLineView extends BorderPane { updateTask = null; } //remvoe and gc updateListener - this.filteredEvents.zoomStateProperty().removeListener(updateListener); + this.filteredEvents.modelParamsProperty().removeListener(updateListener); TimeLineController.timeZoneProperty().removeListener(updateListener); updateListener = null; filteredEvents.unRegisterForEvents(this); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle_ja.properties old mode 100755 new mode 100644 index 5c8dacfed2..2be73fcb0d --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle_ja.properties @@ -1,27 +1,31 @@ -Timeline.node.root=\u30eb\u30fc\u30c8 -Timeline.ui.TimeLineChart.tooltip.text=\u30c0\u30d6\u30eb\u30af\u30ea\u30c3\u30af\u3067\u4e0b\u8a18\u306e\u7bc4\u56f2\u3078\u30ba\u30fc\u30e0\uff1a\n{0}\u301c{1}\n\u53f3\u30af\u30ea\u30c3\u30af\u3067\u5143\u306b\u623b\u308a\u307e\u3059\u3002 -Timeline.ui.ZoomRanges.all.text=\u5168\u3066 +/* + * Autopsy\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30fb\u30d6\u30e9\u30a6\u30b6\u30fc + * + * Copyright 2013-15 Basis Technology Corp. + * \u9023\u7d61\u5148: carrier sleuthkit org + * + * Apache License, Version 2.0 (\u4ee5\u4e0b\u300c\u30e9\u30a4\u30bb\u30f3\u30b9\u300d\u3068\u3044\u3044\u307e\u3059)\u306b\u304a\u3044\u3066\u4f7f\u7528\u8a31\u8afe\u3055\u308c\u3066\u3044\u307e\u3059\u3002 + * \u672c\u30e9\u30a4\u30bb\u30f3\u30b9\u3092\u9075\u5b88\u3059\u308b\u3053\u3068\u306a\u304f\u3057\u3066\u3001\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\u3002 + * \u672c\u30e9\u30a4\u30bb\u30f3\u30b9\u306e\u30b3\u30d4\u30fc\u306f\u6b21\u306e\u30b5\u30a4\u30c8\u3067\u53d6\u5f97\u3067\u304d\u307e\u3059\u3002 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * \u9069\u7528\u6cd5\u4ee4\u306b\u3088\u3063\u3066\u7fa9\u52d9\u4ed8\u3051\u3089\u308c\u308b\u5834\u5408\u307e\u305f\u306f\u66f8\u9762\u306b\u3066\u5408\u610f\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u3092\u9664\u304d\u3001\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u306f + * \u660e\u793a\u7684\u3067\u3042\u308c\u9ed9\u793a\u7684\u3067\u3042\u308c\u3001\u3044\u304b\u306a\u308b\u985e\u306e\u4fdd\u8a3c\u307e\u305f\u306f\u6761\u4ef6\u306a\u3057\u306b\u3001 + * \u672c\u30e9\u30a4\u30bb\u30f3\u30b9\u306b\u304a\u3044\u3066\u300c\u73fe\u72b6\u300d\u306e\u307e\u307e\u3067\u914d\u5e03\u3055\u308c\u307e\u3059\u3002 + * \u672c\u30e9\u30a4\u30bb\u30f3\u30b9\u306b\u304a\u3051\u308b\u30a2\u30af\u30bb\u30b9\u6a29\u304a\u3088\u3073\u5236\u9650\u4e8b\u9805\u3092\u898f\u5b9a\u3059\u308b + * \u7279\u5b9a\u306e\u8a00\u8a9e\u306b\u3064\u3044\u3066\u306f\u3001\u300c\u30e9\u30a4\u30bb\u30f3\u30b9\u300d\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002 + */ -ViewFrame.histogramTask.preparing=\u6e96\u5099\u4e2d -ViewFrame.histogramTask.queryDb=DB\u3092\u30af\u30a8\u30ea\u4e2d -ViewFrame.histogramTask.resetUI=ui\u3092\u518d\u8a2d\u5b9a\u4e2d -ViewFrame.histogramTask.title=\u30d2\u30b9\u30c8\u30b0\u30e9\u30e0\u3092\u518d\u30d3\u30eb\u30c9 -ViewFrame.histogramTask.updateUI2=ui\u3092\u66f4\u65b0\u4e2d -TimeZonePanel.localRadio.text=\u30ed\u30fc\u30ab\u30eb\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 -ViewFrame.countsToggle.text=\u30ab\u30a6\u30f3\u30c8 -ViewFrame.detailsToggle.text=\u8a73\u7d30 -ViewFrame.endLabel.text=\u30a8\u30f3\u30c9\uff1a -ViewFrame.noEventsDialogLabel.text=\u73fe\u5728\u306e\u30ba\u30fc\u30e0\uff0f\u30d5\u30a3\u30eb\u30bf\u30fc\u8a2d\u5b9a\u3067\u306f\u898b\u3048\u308b\u30a4\u30d9\u30f3\u30c8\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -ViewFrame.resetFiltersButton.text=\u5168\u3066\u306e\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u30ea\u30bb\u30c3\u30c8 -ViewFrame.startLabel.text=\u30b9\u30bf\u30fc\u30c8\uff1a -ViewFrame.viewModeLabel.text=\u30d3\u30b8\u30e5\u30a2\u30e9\u30a4\u30bc\u30fc\u30b7\u30e7\u30f3\u30e2\u30fc\u30c9\uff1a -ViewFrame.zoomButton.text=\u30a4\u30d9\u30f3\u30c8\u3078\u30ba\u30fc\u30e0 -ViewFrame.zoomMenuButton.text=\u4e0b\u8a18\u3078\u30ba\u30fc\u30e0\u30a4\u30f3\uff0f\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8 -*=Autopsy\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30d6\u30e9\u30a6\u30b6 -AbstractTimelineChart.defaultTooltip.text=\u30de\u30a6\u30b9\u3092\u30c9\u30e9\u30c3\u30b0\u3057\u3066\u30ba\u30fc\u30e0\u3059\u308b\u30bf\u30a4\u30e0\u9593\u9694\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\u305d\u306e\u4ed6\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u306f\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044 -IntervalSelector.ClearSelectedIntervalAction.tooltTipText=\u9078\u629e\u3057\u305f\u9593\u9694\u3092\u30af\u30ea\u30a2\u3059\u308b +AbstractTimelineChart.defaultTooltip.text=\u30de\u30a6\u30b9\u3092\u30c9\u30e9\u30c3\u30b0\u3057\u3001\u30ba\u30fc\u30e0\u30a4\u30f3\u3059\u308b\u6642\u9593\u9593\u9694\u3092\u9078\u629e\u3057\u307e\u3059\u3002\n\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3001\u3055\u3089\u306a\u308b\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +HistoryToolBar.historyLabel.text=\u5c65\u6b74 +IntervalSelector.ClearSelectedIntervalAction.tooltTipText=\u9078\u629e\u3057\u305f\u9593\u9694\u3092\u6d88\u53bb IntervalSelector.ZoomAction.name=\u30ba\u30fc\u30e0 -NoEventsDialog.titledPane.text=\u898b\u308c\u308b\u30a4\u30d9\u30f3\u30c8\u304c\u3042\u308a\u307e\u305b\u3093 +NoEventsDialog.titledPane.text=\u8868\u793a\u53ef\u80fd\u306a\u30a4\u30d9\u30f3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093 +Timeline.node.root=\u30eb\u30fc\u30c8 +# {0} - \u958b\u59cb\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7 +# {1} - \u7d42\u4e86\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7 +Timeline.ui.TimeLineChart.tooltip.text=\u30c0\u30d6\u30eb\u30af\u30ea\u30c3\u30af\u3057\u3066\u7bc4\u56f2\u306b\u30ba\u30fc\u30e0\u30a4\u30f3:\n{0} to {1}.\n\n\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3066\u9589\u3058\u307e\u3059\u3002 Timeline.ui.ZoomRanges.onemin.text=1\u5206 Timeline.ui.ZoomRanges.fifteenmin.text=15\u5206 Timeline.ui.ZoomRanges.onehour.text=1\u6642\u9593 @@ -31,15 +35,38 @@ Timeline.ui.ZoomRanges.oneday.text=1\u65e5 Timeline.ui.ZoomRanges.threedays.text=3\u65e5 Timeline.ui.ZoomRanges.oneweek.text=1\u9031\u9593 Timeline.ui.ZoomRanges.twoweeks.text=2\u9031\u9593 -Timeline.ui.ZoomRanges.onemonth.text=1\u30f6\u6708 -Timeline.ui.ZoomRanges.threemonths.text=3\u30f6\u6708 -Timeline.ui.ZoomRanges.sixmonths.text=6\u30f6\u6708 +Timeline.ui.ZoomRanges.onemonth.text=1\u30f5\u6708 +Timeline.ui.ZoomRanges.threemonths.text=3\u30f5\u6708 +Timeline.ui.ZoomRanges.sixmonths.text=6\u30f5\u6708 Timeline.ui.ZoomRanges.oneyear.text=1\u5e74 Timeline.ui.ZoomRanges.threeyears.text=3\u5e74 Timeline.ui.ZoomRanges.fiveyears.text=5\u5e74 Timeline.ui.ZoomRanges.tenyears.text=10\u5e74 +Timeline.ui.ZoomRanges.all.text=\u3059\u3079\u3066 TimeLineChart.zoomHistoryActionGroup.name=\u30ba\u30fc\u30e0\u5c65\u6b74 -TimeZonePanel.title=\u6642\u9593\u3092\u6b21\u3067\u8868\u793a\uff1a -ViewFrame.refresh=\u30ea\u30d5\u30ec\u30c3\u30b7\u30e5 -ViewFrame.tagsAddedOrDeleted=\u30bf\u30b0\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f\u304a\u3088\u3073\u307e\u305f\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002\u30d3\u30b8\u30e5\u30a2\u30e9\u30a4\u30bc\u30fc\u30b7\u30e7\u30f3\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -ViewRefreshTask.preparing=\u30ba\u30fc\u30e0\u304a\u3088\u3073\u30d5\u30a3\u30eb\u30bf\u30fc\u8a2d\u5b9a\u3092\u89e3\u6790\u4e2d +TimeZonePanel.title=\u6b21\u306b\u6642\u9593\u3092\u8868\u793a: +ViewFrame.countsToggle.text=\u30ab\u30a6\u30f3\u30c8 +ViewFrame.detailsToggle.text=\u8a73\u7d30 +ViewFrame.endLabel.text=\u7d42\u4e86: +ViewFrame.histogramTask.preparing=\u6e96\u5099\u4e2d +ViewFrame.histogramTask.queryDb=FB\u3092\u30af\u30a8\u30ea\u4e2d\u3067\u3059 +ViewFrame.histogramTask.resetUI=UI\u3092\u30ea\u30bb\u30c3\u30c8\u4e2d\u3067\u3059 +ViewFrame.histogramTask.title=\u30d2\u30b9\u30c8\u30b0\u30e9\u30e0\u306e\u518d\u69cb\u7bc9\u4e2d\u3067\u3059 +ViewFrame.histogramTask.updateUI2=UI\u3092\u66f4\u65b0\u4e2d\u3067\u3059 +ViewFrame.listToggle.text=\u30ea\u30b9\u30c8 +ViewFrame.noEventsDialogLabel.text=\u73fe\u5728\u306e\u30ba\u30fc\u30e0 / \u30d5\u30a3\u30eb\u30bf\u30fc\u8a2d\u5b9a\u3067\u8868\u793a\u53ef\u80fd\u306a\u30a4\u30d9\u30f3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +# {0} - \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d +ViewFrame.notification.analysisComplete={0} \u306e\u5206\u6790\u306f\u5b8c\u4e86\u3057\u307e\u3057\u305f\u3002 \u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +# {0} - \u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u540d +ViewFrame.notification.newDataSource={0} \u304c\u65b0\u3057\u3044\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3068\u3057\u3066\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002 \u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +ViewFrame.refresh.longText=\u30d3\u30e5\u30fc\u3092\u66f4\u65b0\u3057\u3001\u65b0\u305f\u306b\u66f4\u65b0\u3055\u308c\u305f\u30bf\u30b0\u306a\u3069\u3001\u8868\u793a\u3055\u308c\u3066\u3044\u306a\u3044\u3082\u306e\u306e\u3001\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u5185\u306b\u3042\u308b\u60c5\u5831\u3092\u542b\u3081\u307e\u3059\u3002 +ViewFrame.refresh.text=\u30d3\u30e5\u30fc\u3092\u66f4\u65b0 +ViewFrame.startLabel.text=\u958b\u59cb: +ViewFrame.tagsAddedOrDeleted=\u30bf\u30b0\u304c\u4f5c\u6210\u304a\u3088\u3073/\u307e\u305f\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002 \u30d3\u30e5\u30fc\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +ViewFrame.viewModeLabel.text=\u30d3\u30e5\u30fc\u30e2\u30fc\u30c9: +ViewFrame.zoomButton.text=\u30a4\u30d9\u30f3\u30c8\u306b\u30ba\u30fc\u30e0 +TimeZonePanel.localRadio.text=\u30ed\u30fc\u30ab\u30eb\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 +TimeZonePanel.otherRadio.text=GMT / UTC +ViewFrame.resetFiltersButton.text=\u3059\u3079\u3066\u306e\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u30ea\u30bb\u30c3\u30c8 +ViewFrame.zoomMenuButton.text=\u30ba\u30fc\u30e0\u30a4\u30f3/\u30a2\u30a6\u30c8 +ViewRefreshTask.preparing=\u30ba\u30fc\u30e0\u8a2d\u5b9a\u3068\u30d5\u30a3\u30eb\u30bf\u30fc\u8a2d\u5b9a\u3092\u5206\u6790\u4e2d\u3067\u3059 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java index 9e780277f3..39506554ac 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java @@ -77,7 +77,7 @@ import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.actions.AddManualEvent; @@ -236,7 +236,7 @@ final public class ViewFrame extends BorderPane { private final NotificationPane notificationPane = new NotificationPane(); private final TimeLineController controller; - private final FilteredEventsModel filteredEvents; + private final EventsModel filteredEvents; /** * Listen to changes in the range slider selection and forward to the @@ -426,7 +426,7 @@ final public class ViewFrame extends BorderPane { //listen for changes in the time range / zoom params TimeLineController.timeZoneProperty().addListener(timeZoneProp -> refreshTimeUI()); filteredEvents.timeRangeProperty().addListener(timeRangeProp -> refreshTimeUI()); - filteredEvents.zoomStateProperty().addListener(zoomListener); + filteredEvents.modelParamsProperty().addListener(zoomListener); refreshTimeUI(); //populate the view refreshHistorgram(); @@ -474,7 +474,7 @@ final public class ViewFrame extends BorderPane { @Subscribe @NbBundle.Messages({ "ViewFrame.notification.cacheInvalidated=The event data has been updated, the visualization may be out of date."}) - public void handleCacheInvalidated(FilteredEventsModel.CacheInvalidatedEvent event) { + public void handleCacheInvalidated(EventsModel.CacheInvalidatedEvent event) { Platform.runLater(() -> { if (hostedView.needsRefresh() == false) { hostedView.setNeedsRefresh(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/Bundle_ja.properties old mode 100755 new mode 100644 index e60d46f934..138bb42bfe --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/Bundle_ja.properties @@ -1,14 +1,25 @@ -CountsViewPane.numberOfEvents=\u30a4\u30d9\u30f3\u30c8\u6570 -CountsViewPane.detailSwitchMessage=\u79d2\u3088\u308a\u5c0f\u3055\u3044\u5358\u4f4d\u306f\u3042\u308a\u307e\u305b\u3093\u3002\n\u8a73\u7d30\u30d3\u30e5\u30fc\u306b\u5909\u66f4\u3057\u307e\u3059\u304b\uff1f -CountsViewPane.detailSwitchTitle=\u8a73\u7d30\u30d3\u30e5\u30fc\u306b\u5909\u66f4\u3057\u307e\u3059\u304b\uff1f +CountsViewPane.detailSwitchMessage=\u79d2\u3088\u308a\u3082\u5c0f\u3055\u3044\u6642\u9593\u5206\u89e3\u80fd\u306f\u3042\u308a\u307e\u305b\u3093\u3002\n\u4ee3\u308f\u308a\u306b\u8a73\u7d30\u30d3\u30e5\u30fc\u306b\u5207\u308a\u66ff\u3048\u307e\u3059\u304b? +CountsViewPane.detailSwitchTitle=\u8a73\u7d30\u30d3\u30e5\u30fc\u306b\u5207\u308a\u66ff\u3048\u307e\u3059\u304b? +CountsViewPane.linearRadio.text=\u7dda\u5f62 +CountsViewPane.loggedTask.name=\u30ab\u30a6\u30f3\u30c8\u30d3\u30e5\u30fc\u306e\u66f4\u65b0\u4e2d\u3067\u3059 +CountsViewPane.loggedTask.updatingCounts=\u30d3\u30e5\u30fc\u306e\u5165\u529b\u4e2d\u3067\u3059 +CountsViewPane.logRadio.text=\u5bfe\u6570 +# {0} - \u76ee\u76db\u540d +CountsViewPane.numberOfEvents=\u30a4\u30d9\u30f3\u30c8\u6570 ({0}) +CountsViewPane.scaleHelp.label.text=\u76ee\u76db: +CountsViewPane.scaleHelpLinear=\u7dda\u5f62\u76ee\u76db\u306f\u591a\u304f\u306e\u4f7f\u7528\u4e8b\u4f8b\u306b\u9069\u3057\u3066\u3044\u307e\u3059\u3002 \u3053\u306e\u76ee\u76db\u3092\u9078\u629e\u3059\u308b\u3068\u3001\u30d0\u30fc\u306e\u9ad8\u3055\u304c1\u5bfe1\u306e\u7dda\u5f62\u3067\u30ab\u30a6\u30f3\u30c8\u3092\u8868\u3057\u3001y\u8ef8\u306b\u5024\u30e9\u30d9\u30eb\u304c\u4ed8\u304d\u307e\u3059\u3002\u5024\u306e\u7bc4\u56f2\u304c\u975e\u5e38\u306b\u5927\u304d\u3044\u3068\u3001\u30ab\u30a6\u30f3\u30c8\u306e\u5c11\u306a\u3044\u6642\u9593\u5468\u671f\u306b\u5c0f\u3055\u3059\u304e\u3066\u8868\u793a\u3067\u304d\u306a\u3044\u30d0\u30fc\u304c\u3042\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 \u30e6\u30fc\u30b6\u30fc\u304c\u3053\u308c\u3092\u5224\u5225\u3067\u304d\u308b\u3088\u3046\u306b\u3001\u30a4\u30d9\u30f3\u30c8\u306e\u3042\u308b\u30c7\u30fc\u30bf\u7bc4\u56f2\u306e\u30e9\u30d9\u30eb\u304c\u592a\u5b57\u3067\u8868\u793a\u3055\u308c\u307e\u3059\u3002 \u5c0f\u3055\u3059\u304e\u308b\u30d0\u30fc\u3092\u8868\u793a\u3059\u308b\u9078\u629e\u80a2\u306f\u6b21\u306e3\u3064\u3067\u3059: \u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u7e26\u9818\u57df\u3092\u5e83\u3052\u308b\u305f\u3081\u306b\u30a6\u30a3\u30f3\u30c9\u30a6\u30b5\u30a4\u30ba\u3092\u8abf\u6574\u3001\u3088\u308a\u5927\u304d\u3044\u30d0\u30fc\u306e\u3042\u308b\u6642\u9593\u5468\u671f\u304c\u9664\u5916\u3055\u308c\u308b\u3088\u3046\u6642\u9593\u7bc4\u56f2\u3092\u8abf\u6574\u3001\u3082\u3057\u304f\u306f\u76ee\u76db\u8a2d\u5b9a\u3092\u5bfe\u6570\u306b\u8abf\u6574 +CountsViewPane.scaleHelpLog=\u5bfe\u6570\u76ee\u76db\u306f\u3001\u5927\u304d\u3044\u6570\u5b57\u3068\u5c0f\u3055\u3044\u6570\u5b57\u306e\u5dee\u3092\u5727\u7e2e\u3059\u308b\u975e\u7dda\u5f62\u306a\u65b9\u6cd5\u3067\u30a4\u30d9\u30f3\u30c8\u6570\u3092\u8868\u3057\u307e\u3059\u3002\u5bfe\u6570\u76ee\u76db\u3092\u7528\u3044\u3066\u3082\u3001\u6975\u7aef\u306b\u5927\u304d\u3044\u30ab\u30a6\u30f3\u30c8\u5dee\u306b\u3088\u3063\u3066\u5c0f\u3055\u3059\u304e\u3066\u8868\u793a\u3067\u304d\u306a\u3044\u30d0\u30fc\u304c\u751f\u6210\u3055\u308c\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 \u3053\u306e\u5834\u5408\u3001\u30a4\u30d9\u30f3\u30c8\u3092\u30d5\u30a3\u30eb\u30bf\u30fc\u3057\u3066\u30ab\u30a6\u30f3\u30c8\u5dee\u3092\u7e2e\u3081\u308b\u3053\u3068\u304c\u552f\u4e00\u306e\u9078\u629e\u80a2\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 \u6ce8: \u5bfe\u6570\u76ee\u76db\u306f\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7\u3054\u3068\u306b\u500b\u5225\u306b\u9069\u7528\u3055\u308c\u308b\u305f\u3081\u3001\u8907\u5408\u30d0\u30fc\u306e\u9ad8\u3055\u306e\u610f\u5473\u306f\u76f4\u611f\u7684\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u307e\u305f\u3001\u3053\u308c\u3092\u5f37\u8abf\u3059\u308b\u305f\u3081\u306b\u3001\u5bfe\u6570\u76ee\u76db\u3092\u7528\u3044\u305fy\u8ef8\u4e0a\u306b\u30e9\u30d9\u30eb\u3092\u8868\u793a\u3057\u307e\u305b\u3093\u3002\u5bfe\u6570\u76ee\u76db\u306f\u30011\u3064\u306e\u30bf\u30a4\u30d7\u5185\u306e\u6642\u9593\u5168\u57df\u3001\u307e\u305f\u306f1\u3064\u306e\u6642\u9593\u5468\u671f\u306e\u30bf\u30a4\u30d7\u5168\u57df\u3067 +CountsViewPane.scaleHelpLog2=\u30ab\u30a6\u30f3\u30c8\u3092\u8fc5\u901f\u306b\u6bd4\u8f03\u3059\u308b\u305f\u3081\u306b\u4f7f\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305f\u3060\u3057\u4e21\u65b9\u4e00\u5ea6\u306b\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +CountsViewPane.scaleHelpLog3=\ \u5b9f\u969b\u306e\u30ab\u30a6\u30f3\u30c8(\u30c4\u30fc\u30eb\u30c1\u30c3\u30d7\u307e\u305f\u306f\u7d50\u679c\u30d3\u30e5\u30fc\u3067\u5229\u7528\u53ef\u80fd)\u306f\u7d76\u5bfe\u6bd4\u8f03\u306b\u4f7f\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 \u614e\u91cd\u306b\u5bfe\u6570\u76ee\u76db\u3092\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +CountsViewPane.scaleLabel.text=\u76ee\u76db: +# {0} - \u30ab\u30a6\u30f3\u30c8 +# {1} - \u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7\u306e\u8868\u793a\u540d +# {2} - \u958b\u59cb\u65e5\u6642 +# {3} - \u7d42\u4e86\u65e5\u6642 +CountsViewPane.tooltip.text={2}\n\u3068 {3}\n\u9593\u306e {0} {1} \u30a4\u30d9\u30f3\u30c8 +ScaleType.Linear=\u7dda\u5f62 +ScaleType.Logarithmic=\u5bfe\u6570 Timeline.ui.countsview.menuItem.selectEventType=\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7\u3092\u9078\u629e Timeline.ui.countsview.menuItem.selectTimeandType=\u6642\u9593\u3068\u30bf\u30a4\u30d7\u3092\u9078\u629e Timeline.ui.countsview.menuItem.selectTimeRange=\u6642\u9593\u7bc4\u56f2\u3092\u9078\u629e -Timeline.ui.countsview.menuItem.zoomIntoTimeRange=\u6642\u9593\u7bc4\u56f2\u3078\u30ba\u30fc\u30e0\u30a4\u30f3 -CountsViewPane.loggedTask.name=\u30ab\u30a6\u30f3\u30c8\u30d3\u30e5\u30fc\u3092\u66f4\u65b0\u4e2d -CountsViewPane.loggedTask.updatingCounts=\u30d3\u30b8\u30e5\u30a2\u30e9\u30a4\u30bc\u30fc\u30b7\u30e7\u30f3\uff08\u53ef\u8996\u5316\uff09\u3092\u5165\u529b\u4e2d -CountsViewPane.tooltip.text={0} {1} \u30a4\u30d9\u30f3\u30c8\n{2}\u3068\n{3}\u306e\u9593 -CountsViewPane.linearRadio.text=\u30ea\u30cb\u30a2 -CountsViewPane.logRadio.text=\u5bfe\u6570\u7684 -CountsViewPane.scaleLabel.text=\u30b9\u30b1\u30fc\u30eb\uff1a -*=Autopsy\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30d6\u30e9\u30a6\u30b6 \ No newline at end of file +Timeline.ui.countsview.menuItem.zoomIntoTimeRange=\u6642\u9593\u7bc4\u56f2\u306b\u30ba\u30fc\u30e0\u30a4\u30f3 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java index 70197ff3e3..53e79210b2 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java @@ -56,7 +56,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; @@ -374,7 +374,7 @@ public class CountsViewPane extends AbstractTimelineChart implements private ContextMenu chartContextMenu; private final TimeLineController controller; - private final FilteredEventsModel filteredEvents; + private final EventsModel filteredEvents; private IntervalSelector intervalSelector; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle_ja.properties old mode 100755 new mode 100644 index bf305b6d72..d7ee0e7dd5 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle_ja.properties @@ -1,35 +1,40 @@ -DetailViewPane.loggedTask.name=\u8A73\u7D30\u30D3\u30E5\u30FC\u3092\u66F4\u65B0 -DetailViewPane.loggedTask.queryDb=\u30A4\u30D9\u30F3\u30C8\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u4E2D -DetailViewPane.loggedTask.updateUI=\u30D3\u30B8\u30E5\u30A2\u30E9\u30A4\u30BC\u30FC\u30B7\u30E7\u30F3\u306B\u60C5\u5831\u3092\u5165\u529B\u4E2D -EventDetailChart.chartContextMenu.placeMarker.name=\u30DE\u30FC\u30AB\u30FC\u3092\u8A2D\u7F6E -DetailViewPan.truncateAllBox.text=\u8AAC\u660E\u3092\u524A\u9664 -DetailViewPan.truncateAllBoxMenuItem.text=\u8AAC\u660E\u3092\u524A\u9664 -DetailViewPane.advancedLayoutOptionsButtonLabel.text=\u30A2\u30C9\u30D0\u30F3\u30B9\u30EC\u30A4\u30A2\u30A6\u30C8\u30AA\u30D7\u30B7\u30E7\u30F3 -DetailViewPane.countsRadio.text=\u30AB\u30A6\u30F3\u30C8\u306E\u307F\u8868\u793A -DetailViewPane.countsRadioMenuItem.text=\u30AB\u30A6\u30F3\u30C8\u306E\u307F\u8868\u793A -DetailViewPane.descVisSeparatorMenuItem.text=\u8AAC\u660E\u30D3\u30B8\u30D3\u30EA\u30C6\u30A3\uFF08\u53EF\u8996\u6027\uFF09 -DetailViewPane.hiddenRadio.text=\u8AAC\u660E\u3092\u96A0\u3059 -DetailViewPane.hiddenRadioMenuItem.text=\u8AAC\u660E\u3092\u96A0\u3059 -DetailViewPane.oneEventPerRowBox.text=1\u5217\u306B\u3064\u304D1\u30A4\u30D9\u30F3\u30C8 -DetailViewPane.oneEventPerRowBoxMenuItem.text=1\u5217\u306B\u3064\u304D1\u30A4\u30D9\u30F3\u30C8 -DetailViewPane.showRadio.text=\u8A73\u7D30\u8AAC\u660E\u3092\u8868\u793A -DetailViewPane.showRadioMenuItem.text=\u8A73\u7D30\u8AAC\u660E\u3092\u8868\u793A -DetailViewPane.truncateSlideLabelMenuItem.text=\u8AAC\u660E\u306E\u6700\u5927\u5E45\uFF08px\uFF09 -DetailViewPane.truncateSliderLabel.text=\u8AAC\u660E\u306E\u6700\u5927\u5E45\uFF08px\uFF09\uFF1A -DetailViewPane.bandByTypeBox.text=\u30D0\u30F3\u30C9\u30D0\u30A4\u30BF\u30A4\u30D7 -DetailViewPane.bandByTypeBoxMenuItem.text=\u30D0\u30F3\u30C9\u30D0\u30A4\u30BF\u30A4\u30D7 -CollapseClusterAction.text=\u30B3\u30E9\u30D7\u30B9 -DetailViewPane.loggedTask.backButton=\u623B\u308B\uFF08\u53D6\u6D88\uFF09 -DetailViewPane.loggedTask.continueButton=\u9032\u3080 -DetailViewPane.loggedTask.prompt={0}\u30A4\u30D9\u30F3\u30C8\u306E\u8A73\u7D30\u3092\u8868\u793A\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059\u3002\u3053\u308C\u306F\u975E\u5E38\u306B\u6642\u9593\u304C\u304B\u304B\u308B\u3001\u307E\u305F\u306FAutopsy\u3092\u30AF\u30E9\u30C3\u30B7\u30E5\u3059\u308B\u304B\u3082\u3057\u308C\u307E\u305B\u3093\u3002\n\n\u5B9F\u884C\u3057\u307E\u3059\u304B\uFF1F -EventBundleNodeBase.toolTip.hashSetHits=\n\n\u30CF\u30C3\u30B7\u30E5\u30BB\u30C3\u30C8\u30D2\u30C3\u30C8\n{0} -EventBundleNodeBase.toolTip.loading=\u8AAD\u307F\u8FBC\u307F\u4E2D... -EventBundleNodeBase.toolTip.loading2=\u30C4\u30FC\u30EB\u30C1\u30C3\u30D7\u3092\u8AAD\u307F\u8FBC\u307F\u4E2D -EventBundleNodeBase.toolTip.tags=\n\n\u30BF\u30B0\n{0} -EventBundleNodeBase.tooltip.text={0} {1} \u30A4\u30D9\u30F3\u30C8\n{2}\n{3}\u3068{4}\u306E\u9593 -EventStripeNode.loggedTask.name=\u30B5\u30D6\u30AF\u30E9\u30B9\u30BF\u3092\u8AAD\u307F\u8FBC\u307F\u4E2D -ExpandClusterAction.text=\u30A8\u30AF\u30B9\u30D1\u30F3\u30C9 -GuideLine.tooltip.text={0}\n\u53F3\u30AF\u30EA\u30C3\u30AF\u3067\u524A\u9664\u3002\n\u30C9\u30E9\u30C3\u30B0\u3057\u3066\u4F4D\u7F6E\u5909\u66F4\u3002 -HideDescriptionAction.displayMsg=\u8A73\u7D30\u30D3\u30E5\u30FC\u304B\u3089\u3053\u306E\u30B0\u30EB\u30FC\u30D7\u3092\u96A0\u3059\u3002 -HideDescriptionAction.displayName=\u96A0\u3059 -UnhideDescriptionAction.displayName=\u96A0\u3057\u305F\u3082\u306E\u3092\u8868\u793A \ No newline at end of file +CollapseClusterAction.text=\u6298\u308a\u305f\u305f\u3080 +DetailViewPane.advancedLayoutOptionsButtonLabel.text=\u9ad8\u5ea6\u306a\u30ec\u30a4\u30a2\u30a6\u30c8\u30aa\u30d7\u30b7\u30e7\u30f3 +DetailViewPane.bandByTypeBox.text=\u30bf\u30a4\u30d7\u5225\u306b\u7d50\u5408 +DetailViewPane.countsRadio.text=\u30ab\u30a6\u30f3\u30c8\u306e\u307f\u8868\u793a +DetailViewPane.hiddenRadio.text=\u8a18\u8ff0\u3092\u975e\u8868\u793a +DetailViewPane.loggedTask.backButton=\u623b\u308b(\u53d6\u308a\u6d88\u3057) +DetailViewPane.loggedTask.continueButton=\u7d9a\u884c +DetailViewPane.loggedTask.name=\u8a73\u7d30\u30d3\u30e5\u30fc\u306e\u66f4\u65b0\u4e2d\u3067\u3059 +# {0} - \u30a4\u30d9\u30f3\u30c8\u6570 +DetailViewPane.loggedTask.prompt= {0} \u30a4\u30d9\u30f3\u30c8\u306e\u8a73\u7d30\u3092\u8868\u793a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307e\u3059\u3002 \u3053\u308c\u306b\u306f\u6642\u9593\u304c\u304b\u304b\u308a\u3001\u4f7f\u7528\u53ef\u80fd\u306a\u30e1\u30e2\u30ea\u30fc\u304c\u6d88\u8cbb\u3055\u308c\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u7d9a\u884c\u3057\u307e\u3059\u304b? +DetailViewPane.loggedTask.queryDb=\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u306e\u691c\u7d22\u4e2d\u3067\u3059 +DetailViewPane.loggedTask.updateUI=\u30d3\u30e5\u30fc\u306e\u5165\u529b\u4e2d\u3067\u3059 +DetailViewPane.oneEventPerRowBox.text=1\u5217\u306b\u3064\u304d1\u3064 +DetailViewPane.pinnedLaneLabel.text=\u30d4\u30f3\u7559\u3081\u3057\u305f\u30a4\u30d9\u30f3\u30c8 +DetailViewPane.primaryLaneLabel.text=\u3059\u3079\u3066\u306e\u30a4\u30d9\u30f3\u30c8(\u30d5\u30a3\u30eb\u30bf\u30fc\u6e08\u307f) +DetailViewPane.showRadio.text=\u5b8c\u5168\u306a\u8a18\u8ff0\u3092\u8868\u793a +DetailViewPane.truncateAllBox.text=\u8a18\u8ff0\u3092\u4e00\u62ec\u524a\u9664 +DetailViewPane.truncateSliderLabel.text=\u6700\u5927\u8a18\u8ff0\u5e45(px): +EventBundleNodeBase.toolTip.loading=\u8aad\u307f\u8fbc\u307f\u4e2d... +EventClusterNode.loggedTask.name=\u30b5\u30d6\u30a4\u30d9\u30f3\u30c8\u3092\u8aad\u307f\u8fbc\u3080 +# {0} - \u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30ab\u30a6\u30f3\u30c8\u6587\u5b57\u5217 +EventNodeBase.toolTip.hashSetHits=\n\n\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u306e\u30d2\u30c3\u30c8\n{0} +EventNodeBase.toolTip.loading2=\u30c4\u30fc\u30eb\u30c1\u30c3\u30d7\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u3067\u3059 +# {0} - \u30bf\u30b0\u30ab\u30a6\u30f3\u30c8\u6587\u5b57\u5217 +EventNodeBase.toolTip.tags=\n\n\u30bf\u30b0\n{0} +# {0} - \u30ab\u30a6\u30f3\u30c8 +# {1} - \u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7 +# {2} - \u8a18\u8ff0 +# {3} - \u958b\u59cb\u65e5/\u6642\u9593 +# {4} - \u7d42\u4e86\u65e5/\u6642\u9593 +EventNodeBase.tooltip.text=\t{3}\n\u3068 \t{4}\n{2}\n\u9593\u306e {0} {1} \u30a4\u30d9\u30f3\u30c8 +ExpandClusterAction.text=\u5c55\u958b +# {0} - \u30ac\u30a4\u30c9\u30e9\u30a4\u30f3\u4f4d\u7f6e\u306e\u65e5\u4ed8/\u6642\u9593 +GuideLine.tooltip.text={0}\n\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3066\u524a\u9664\u3057\u307e\u3059\u3002\n\u30c9\u30e9\u30c3\u30b0\u3057\u3066\u518d\u914d\u7f6e\u3057\u307e\u3059\u3002 +HideDescriptionAction.displayMsg=\u3053\u306e\u30b0\u30eb\u30fc\u30d7\u3092\u8a73\u7d30\u30d3\u30e5\u30fc\u304b\u3089\u975e\u8868\u793a\u306b\u3057\u307e\u3059\u3002 +HideDescriptionAction.displayName=\u975e\u8868\u793a +PinEventAction.text=\u30d4\u30f3\u7559\u3081\u3059\u308b +PlaceMArkerAction.name=\u5834\u6240\u30de\u30fc\u30ab\u30fc +UnhideDescriptionAction.displayName=\u518d\u8868\u793a +UnPinEventAction.text=\u30d4\u30f3\u7559\u3081\u3092\u5916\u3059 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java index 8736c46103..004dd21a03 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -53,7 +53,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; @@ -61,7 +61,7 @@ import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.DetailViewEvent; import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.DetailsViewModel; import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.utils.MappedList; -import org.sleuthkit.autopsy.timeline.zooming.ZoomState; +import org.sleuthkit.autopsy.timeline.zooming.EventsModelParams; import org.sleuthkit.datamodel.TimelineLevelOfDetail; import org.sleuthkit.datamodel.TskCoreException; @@ -99,7 +99,7 @@ final public class DetailViewPane extends AbstractTimelineChart { this.pinnedDateAxis = pinnedDateAxis; this.selectedNodes = selectedNodes; - FilteredEventsModel eventsModel = getController().getEventsModel(); + EventsModel eventsModel = getController().getEventsModel(); /* * If the time range is changed, clear the guide line and the interval @@ -147,7 +147,7 @@ final class DetailsChart extends Control implements TimeLineChart { eventsModel.timeRangeProperty().addListener(observable -> clearTimeBasedUIElements()); //if the view paramaters change, clear the selection - eventsModel.zoomStateProperty().addListener(observable -> getSelectedNodes().clear()); + eventsModel.modelParamsProperty().addListener(observable -> getSelectedNodes().clear()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java index 402cefe2b6..581b79d7d6 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java @@ -58,7 +58,7 @@ import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.SingleDetailsViewE import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.SqlFilterState; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.DescriptionFilter; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; -import org.sleuthkit.autopsy.timeline.zooming.ZoomState; +import org.sleuthkit.autopsy.timeline.zooming.EventsModelParams; import org.sleuthkit.datamodel.TimelineLevelOfDetail; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TimelineEventType; @@ -177,12 +177,12 @@ final class EventClusterNode extends MultiEventNodeBase( new EventTypeFilter(getEventType()), true)); final Interval subClusterSpan = new Interval(getStartMillis(), getEndMillis() + 1000); final TimelineEventType.HierarchyLevel eventTypeZoomLevel = eventsModel.getEventTypeZoom(); - final ZoomState zoom = new ZoomState(subClusterSpan, eventTypeZoomLevel, subClusterFilter, getDescriptionLevel()); + final EventsModelParams zoom = new EventsModelParams(subClusterSpan, eventTypeZoomLevel, subClusterFilter, getDescriptionLevel()); DescriptionFilter descriptionFilter = new DescriptionFilter(getEvent().getDescriptionLevel(), getDescription()); /* diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java index 8caad5f6fb..7c37c274a6 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java @@ -67,7 +67,7 @@ import org.joda.time.DateTime; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.events.TagsAddedEvent; import org.sleuthkit.autopsy.timeline.events.TagsDeletedEvent; @@ -126,7 +126,7 @@ public abstract class EventNodeBase extends StackP final HBox controlsHBox = new HBox(5); final HBox infoHBox = new HBox(5, eventTypeImageView, hashIV, tagIV, descrLabel, countLabel, controlsHBox); final SleuthkitCase sleuthkitCase; - final FilteredEventsModel eventsModel; + final EventsModel eventsModel; private Timeline timeline; private Button pinButton; private final Border SELECTION_BORDER; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/datamodel/DetailsViewModel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/datamodel/DetailsViewModel.java index ff0b77d49c..4045c05ffd 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/datamodel/DetailsViewModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/datamodel/DetailsViewModel.java @@ -45,13 +45,13 @@ import org.joda.time.DateTimeZone; import org.joda.time.Interval; import org.joda.time.Period; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.UIFilter; import org.sleuthkit.autopsy.timeline.utils.CacheLoaderImpl; import org.sleuthkit.autopsy.timeline.utils.RangeDivision; import org.sleuthkit.autopsy.timeline.zooming.TimeUnits; -import org.sleuthkit.autopsy.timeline.zooming.ZoomState; +import org.sleuthkit.autopsy.timeline.zooming.EventsModelParams; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.TskCoreException; @@ -68,12 +68,12 @@ final public class DetailsViewModel { private final static Logger logger = Logger.getLogger(DetailsViewModel.class.getName()); - private final FilteredEventsModel eventsModel; - private final LoadingCache> eventCache; + private final EventsModel eventsModel; + private final LoadingCache> eventCache; private final TimelineManager eventManager; private final SleuthkitCase sleuthkitCase; - public DetailsViewModel(FilteredEventsModel eventsModel) { + public DetailsViewModel(EventsModel eventsModel) { this.eventsModel = eventsModel; this.eventManager = eventsModel.getEventManager(); this.sleuthkitCase = eventsModel.getSleuthkitCase(); @@ -86,7 +86,7 @@ final public class DetailsViewModel { } @Subscribe - void handleCacheInvalidation(FilteredEventsModel.CacheInvalidatedEvent event) { + void handleCacheInvalidation(EventsModel.CacheInvalidatedEvent event) { eventCache.invalidateAll(); } @@ -99,7 +99,7 @@ final public class DetailsViewModel { * * @throws org.sleuthkit.datamodel.TskCoreException */ - public List getEventStripes(ZoomState zoom) throws TskCoreException { + public List getEventStripes(EventsModelParams zoom) throws TskCoreException { return getEventStripes(UIFilter.getAllPassFilter(), zoom); } @@ -113,11 +113,11 @@ final public class DetailsViewModel { * * @throws org.sleuthkit.datamodel.TskCoreException */ - public List getEventStripes(UIFilter uiFilter, ZoomState zoom) throws TskCoreException { + public List getEventStripes(UIFilter uiFilter, EventsModelParams zoom) throws TskCoreException { DateTimeZone timeZone = TimeLineController.getJodaTimeZone(); //unpack params Interval timeRange = zoom.getTimeRange(); - TimelineLevelOfDetail descriptionLOD = zoom.getDescriptionLOD(); + TimelineLevelOfDetail descriptionLOD = zoom.getTimelineLOD(); //intermediate results Map> eventClusters = new HashMap<>(); @@ -156,10 +156,10 @@ final public class DetailsViewModel { * @throws org.sleuthkit.datamodel.TskCoreException If there is an error * querying the db. */ - private List getEvents(ZoomState zoom, DateTimeZone timeZone) throws TskCoreException { + private List getEvents(EventsModelParams zoom, DateTimeZone timeZone) throws TskCoreException { //unpack params Interval timeRange = zoom.getTimeRange(); - TimelineFilter.RootFilter activeFilter = zoom.getFilterState().getActiveFilter(); + TimelineFilter.RootFilter activeFilter = zoom.getEventFilterState().getActiveFilter(); return eventManager.getEvents(timeRange, activeFilter); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/Bundle_ja.properties old mode 100755 new mode 100644 index 5252dcf4f3..ce6bd8ed7d --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/Bundle_ja.properties @@ -1,4 +1,4 @@ -EventsTree.Label.text=\u30BD\u30FC\u30C8\u30D0\u30A4\uFF1A -TreeComparator.Count.displayName=\u30AB\u30A6\u30F3\u30C8 -TreeComparator.Description.displayName=\u8AAC\u660E -TreeComparator.Type.displayName=\u30BF\u30A4\u30D7 \ No newline at end of file +EventsTree.Label.text=\u6b21\u3067\u30bd\u30fc\u30c8: +TreeComparator.Count.displayName=\u30ab\u30a6\u30f3\u30c8 +TreeComparator.Description.displayName=\u8a18\u8ff0 +TreeComparator.Type.displayName=\u30bf\u30a4\u30d7 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/Bundle.properties-MERGED index 149c2da610..e637c111e2 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/Bundle.properties-MERGED @@ -1,7 +1,7 @@ FilsetSetPanel.hiddenDescriptionsPane.displayName=Hidden Descriptions FilterSetPanel.applyButton.longText=(Re)Apply filters FilterSetPanel.applyButton.text=Apply -FilterSetPanel.defaultButton.text=Default +FilterSetPanel.defaultButton.text=Reset FilterSetPanel.hiddenDescriptionsListView.remove=Remove from list FilterSetPanel.hiddenDescriptionsListView.unhideAndRemove=Unhide and remove from list Timeline.ui.filtering.menuItem.all=all diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/Bundle_ja.properties old mode 100755 new mode 100644 index bad4e2e9ca..6c9352b270 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/Bundle_ja.properties @@ -1,12 +1,12 @@ -Timeline.ui.filtering.menuItem.all=\u5168\u3066 -Timeline.ui.filtering.menuItem.none=\u7121\u3057 -Timeline.ui.filtering.menuItem.only=\u4E0B\u8A18\u306B\u9650\u5B9A -Timeline.ui.filtering.menuItem.others=\u305D\u306E\u4ED6 -Timeline.ui.filtering.menuItem.select=\u9078\u629E -Timeline.ui.filtering.promptText=\u30D5\u30A3\u30EB\u30BF\u30B9\u30C8\u30EA\u30F3\u30B0\u3092\u5165\u529B +FilsetSetPanel.hiddenDescriptionsPane.displayName=\u975e\u8868\u793a\u306e\u8aac\u660e +FilterSetPanel.applyButton.longText=\u30d5\u30a3\u30eb\u30bf\u30fc\u3092(\u6700)\u9069\u7528 FilterSetPanel.applyButton.text=\u9069\u7528 -FilterSetPanel.defaultButton.text=\u30C7\u30D5\u30A9\u30EB\u30C8 -FilsetSetPanel.hiddenDescriptionsPane.displayName=\u96A0\u3055\u308C\u305F\u8AAC\u660E -FilterSetPanel.applyButton.longText=\u30D5\u30A3\u30EB\u30BF\u30FC\u3092\uFF08\u518D\uFF09\u9069\u7528 -FilterSetPanel.hiddenDescriptionsListView.remove=\u30EA\u30B9\u30C8\u304B\u3089\u524A\u9664 -FilterSetPanel.hiddenDescriptionsListView.unhideAndRm=\u96A0\u3057\u305F\u3082\u306E\u3092\u8868\u793A\u3057\u3001\u30EA\u30B9\u30C8\u304B\u3089\u524A\u9664 \ No newline at end of file +FilterSetPanel.defaultButton.text=\u30c7\u30d5\u30a9\u30eb\u30c8 +FilterSetPanel.hiddenDescriptionsListView.remove=\u30ea\u30b9\u30c8\u304b\u3089\u524a\u9664 +FilterSetPanel.hiddenDescriptionsListView.unhideAndRemove=\u975e\u8868\u793a\u306b\u3057\u3066\u30ea\u30b9\u30c8\u304b\u3089\u524a\u9664 +Timeline.ui.filtering.menuItem.all=\u3059\u3079\u3066 +Timeline.ui.filtering.menuItem.none=\u306a\u3057 +Timeline.ui.filtering.menuItem.only=\u306e\u307f +Timeline.ui.filtering.menuItem.others=\u305d\u306e\u4ed6 +Timeline.ui.filtering.menuItem.select=\u9078\u629e +Timeline.ui.filtering.promptText=\u30d5\u30a3\u30eb\u30bf\u30fc\u6587\u5b57\u5217\u3092\u5165\u529b diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index 5d28e24c82..1fa268ffc2 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -40,7 +40,7 @@ import org.controlsfx.control.action.Action; import org.controlsfx.control.action.ActionUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.FXMLConstructor; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.actions.ResetFilters; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.DescriptionFilterState; @@ -79,7 +79,7 @@ final public class FilterSetPanel extends BorderPane { @FXML private SplitPane splitPane; - private final FilteredEventsModel filteredEvents; + private final EventsModel filteredEvents; private final TimeLineController controller; /** @@ -90,7 +90,7 @@ final public class FilterSetPanel extends BorderPane { private double dividerPosition; @NbBundle.Messages({ - "FilterSetPanel.defaultButton.text=Default", + "FilterSetPanel.defaultButton.text=Reset", "FilsetSetPanel.hiddenDescriptionsPane.displayName=Hidden Descriptions"}) @FXML void initialize() { @@ -115,16 +115,16 @@ final public class FilterSetPanel extends BorderPane { legendColumn.setCellFactory(col -> new LegendCell(this.controller)); //type is the only filter expanded initialy - expansionMap.put(filteredEvents.getFilterState().getFilter(), true); - expansionMap.put(filteredEvents.getFilterState().getEventTypeFilterState().getFilter(), true); + expansionMap.put(filteredEvents.getEventFilterState().getFilter(), true); + expansionMap.put(filteredEvents.getEventFilterState().getEventTypeFilterState().getFilter(), true); InvalidationListener applyFiltersListener = observable -> applyFilters(); - filteredEvents.eventTypeZoomProperty().addListener(applyFiltersListener); + filteredEvents.eventTypesHierarchyLevelProperty().addListener(applyFiltersListener); filteredEvents.descriptionLODProperty().addListener(applyFiltersListener); filteredEvents.timeRangeProperty().addListener(applyFiltersListener); - filteredEvents.filterProperty().addListener(observable -> refreshFilterUI()); + filteredEvents.eventFilterProperty().addListener(observable -> refreshFilterUI()); refreshFilterUI(); hiddenDescriptionsListView.setItems(controller.getQuickHideFilters()); @@ -164,7 +164,7 @@ final public class FilterSetPanel extends BorderPane { private void refreshFilterUI() { Platform.runLater(() - -> filterTreeTable.setRoot(new FilterTreeItem(filteredEvents.filterProperty().get().copyOf(), expansionMap))); + -> filterTreeTable.setRoot(new FilterTreeItem(filteredEvents.eventFilterProperty().get().copyOf(), expansionMap))); } private void applyFilters() { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/LegendCell.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/LegendCell.java index 53ffec84a6..9a4b6cd080 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/LegendCell.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/LegendCell.java @@ -28,13 +28,13 @@ import javafx.scene.layout.HBox; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.ui.EventTypeUtils; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; import org.sleuthkit.datamodel.TimelineEventType; -import org.sleuthkit.datamodel.TimelineFilter.TextFilter; import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.TextFilterState; /** * A TreeTableCell that shows an icon and color corresponding to the represented @@ -46,7 +46,7 @@ final class LegendCell extends TreeTableCell, FilterState> { private final TimeLineController controller; - private final FilteredEventsModel filteredEvents; + private final EventsModel filteredEvents; //We need a controller so we can listen to changes in EventTypeZoom to show/hide legends LegendCell(TimeLineController controller) { @@ -65,9 +65,16 @@ final class LegendCell extends TreeTableCell, FilterState> { setBackground(null); }); } else { - + //TODO: make some subclasses rather than use this if else chain. - if (item.getFilter() instanceof EventTypeFilter) { + if (item instanceof TextFilterState) { + TextFilterState filterState = (TextFilterState)item; + TextField textField = new TextField(); + textField.setPromptText(Bundle.Timeline_ui_filtering_promptText()); + textField.textProperty().bindBidirectional(filterState.descriptionSubstringProperty()); + Platform.runLater(() -> setGraphic(textField)); + + } else if (item.getFilter() instanceof EventTypeFilter) { EventTypeFilter filter = (EventTypeFilter) item.getFilter(); Rectangle rect = new Rectangle(20, 20); @@ -75,7 +82,7 @@ final class LegendCell extends TreeTableCell, FilterState> { rect.setArcWidth(5); rect.setStrokeWidth(3); setLegendColor(filter, rect, this.filteredEvents.getEventTypeZoom()); - this.filteredEvents.eventTypeZoomProperty().addListener((obs, oldZoomLevel, newZoomLevel) -> { + this.filteredEvents.eventTypesHierarchyLevelProperty().addListener((obs, oldZoomLevel, newZoomLevel) -> { setLegendColor(filter, rect, newZoomLevel); }); @@ -88,13 +95,6 @@ final class LegendCell extends TreeTableCell, FilterState> { setContentDisplay(ContentDisplay.CENTER); }); - } else if (item.getFilter() instanceof TextFilter) { - TextFilter filter = (TextFilter) item.getFilter(); - TextField textField = new TextField(); - textField.setPromptText(Bundle.Timeline_ui_filtering_promptText()); - textField.textProperty().bindBidirectional(filter.substringProperty()); - Platform.runLater(() -> setGraphic(textField)); - } else { Platform.runLater(() -> { setGraphic(null); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/AbstractFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/AbstractFilterState.java index ca902eb53c..eeea44bbad 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/AbstractFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/AbstractFilterState.java @@ -26,26 +26,41 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; /** - * Abstract base class for FilterStates. Provides selected, disabled, and active - * properties. + * An abstract base class for implementations of the FilterState interface. + * Filter state classes are wrappers that adapt a timeline data model filtering + * object for display by the timeline GUI by providing selected, disabled, and + * active properties for the wrapped filter. + * + * @param The type of the wrapped filter. */ -abstract class AbstractFilterState implements FilterState { +abstract class AbstractFilterState implements FilterState { private final SimpleBooleanProperty selected = new SimpleBooleanProperty(false); private final SimpleBooleanProperty disabled = new SimpleBooleanProperty(false); - private final BooleanBinding activeProp = Bindings.and(selected, disabled.not()); - private final F filter; + private final BooleanBinding active = Bindings.and(selected, disabled.not()); + private final FilterType filter; @Override - public F getFilter() { + public FilterType getFilter() { return filter; } - AbstractFilterState(F filter, Boolean select) { + /** + * Constructs the base class part for implementations of the FilterState + * interface. Filter state classes are wrappers that adapt a timeline data + * model filtering object for display by the timeline GUI by providing + * selected, disabled, and active properties for the wrapped filter. + * + * @param filter The filter to be wrapped. + * @param selected Whether or not the filter is selected. The filter is + * disabled by default and is therefore not active by + * default. + */ + AbstractFilterState(FilterType filter, Boolean selected) { this.filter = filter; - selected.set(select); - } - + this.selected.set(selected); + } + @Override public BooleanProperty selectedProperty() { return selected; @@ -83,11 +98,11 @@ abstract class AbstractFilterState implements FilterState { @Override public BooleanExpression activeProperty() { - return activeProp; + return active; } @Override - public F getActiveFilter() { + public FilterType getActiveFilter() { return isActive() ? getFilter() : null; } @@ -120,4 +135,5 @@ abstract class AbstractFilterState implements FilterState { } return Objects.equals(this.isDisabled(), other.isDisabled()); } + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterState.java index e85fabe4da..6884d275a8 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/CompoundFilterState.java @@ -24,9 +24,7 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; -import org.openide.util.Exceptions; import org.sleuthkit.datamodel.TimelineFilter; import org.sleuthkit.datamodel.TimelineFilter.CompoundFilter; @@ -81,30 +79,6 @@ public class CompoundFilterState change) -> { - while (change.next()) { - change.getAddedSubList().forEach((SubFilterType newSubFilter) -> { - //if there is not already a state for this filter - if (getSubFilterStates().stream().map(FilterState::getFilter).noneMatch(newSubFilter::equals)) { - //add the appropriate filter type: default or compound - if (newSubFilter instanceof CompoundFilter) { - @SuppressWarnings("unchecked") - FilterState compoundFilterState = (FilterState) new CompoundFilterState<>((CompoundFilter) newSubFilter); - addSubFilterStateInternal(compoundFilterState); - - } else { - addSubFilterStateInternal(new SqlFilterState<>(newSubFilter)); - } - - } - }); - } - }); - } catch (Exception e) { - Exceptions.printStackTrace(e); - } activeProperty().addListener(activeProperty -> disableSubFiltersIfNotActive()); disableSubFiltersIfNotActive(); @@ -150,7 +124,7 @@ public class CompoundFilterState { - //set this compound filter state selected af any of the subfilters are selected. + //set this compound filter state selected if any of the subfilters are selected. setSelected(subFilterStates.stream().anyMatch(FilterState::isSelected)); }); newSubFilterState.setDisabled(isActive() == false); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/FilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/FilterState.java index a32267f3b5..298967acee 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/FilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/FilterState.java @@ -22,34 +22,102 @@ import javafx.beans.binding.BooleanExpression; import javafx.beans.property.BooleanProperty; /** - * The state of a filter: selected, disabled, active, etc. + * An interface for filter state classes. Filter state classes are wrappers that + * adapt a timeline data model filtering object for display by the timeline GUI + * by providing selected, disabled, and active properties for the wrapped + * filter. A filter that is selected and not disabled is active. * - * @param The type of filter this is the state for. + * @param The type of the wrapped filter. */ public interface FilterState { + /** + * Gets the display name that will be used to identify the wrapped filter in + * the timeline GUI. + * + * @return The display name of the wrapped filter. + */ String getDisplayName(); + /** + * Gets the wrapped filter. + * + * @return The wrapped filter. + */ FilterType getFilter(); + /** + * Gets the wrapped filter only if it is active. A filter that is selected + * and not disabled is active. If the wrapped filter is not active, null is + * returned. + * + * @return The wrapped filter or null. + */ FilterType getActiveFilter(); + /** + * Makes a deep copy of this filter state object. + * + * @return The copy. + */ FilterState copyOf(); + /** + * Gets the active property of this filter state object. A filter that is + * selected and not disabled is active. + * + * @return The active property. + */ BooleanExpression activeProperty(); + /** + * Gets the value of the active property of this filter state object. A + * filter that is selected and not disabled is active. + * + * @return True or false. + */ boolean isActive(); + /** + * Gets the disabled property of this filter state object. + * + * @return The disabled property. + */ BooleanProperty disabledProperty(); + /** + * Gets the value of the disabled property of this filter state object. + * + * @return True or false. + */ boolean isDisabled(); - void setDisabled(Boolean act); + /** + * Sets the value of the disabled property of this filter state object. + * + * @param value True or false. + */ + void setDisabled(Boolean value); + /** + * Gets the selected property of this filter state object. + * + * @return The selected property. + */ BooleanProperty selectedProperty(); + /** + * Gets the value of the selected property of this filter state object. + * + * @return True or false. + */ boolean isSelected(); - void setSelected(Boolean act); + /** + * Sets the value of the selected property of this filter state object. + * + * @param value True or false. + */ + void setSelected(Boolean value); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/HashHitsFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/HashHitsFilterState.java new file mode 100755 index 0000000000..63917f5b45 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/HashHitsFilterState.java @@ -0,0 +1,68 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019-2019 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.timeline.ui.filtering.datamodel; + +import org.sleuthkit.datamodel.TimelineFilter; + +/** + * A wrapper for a TimelineFilter.HashHitsFilter object that allows it to be + * displayed by the timeline GUI via the filter panel by providing selected, + * disabled, and active properties for the HashHitsFilter. + */ +public class HashHitsFilterState extends SqlFilterState { + + /** + * Constructs a wrapper for a TimelineFilter.HashHitsFilter object that + * allows it to be displayed by the timeline GUI via the filter panel by + * providing selected, disabled, and active properties for the + * HashHitsFilter. + * + * @param hashHitsFilter A TimelineFilter.HashHitsFilter object. + */ + public HashHitsFilterState(TimelineFilter.HashHitsFilter hashHitsFilter) { + super(hashHitsFilter); + addSelectionListener(); + } + + /** + * "Copy constructs" a wrapper for a TimelineFilter.HashHitsFilter object + * that allows it to be displayed by the timeline GUI via the filter panel + * by providing selected, disabled, and active properties for the + * HashHitsFilter. + * + * @param other A HashHitsFilterState object. + */ + public HashHitsFilterState(HashHitsFilterState other) { + super(other.getFilter().copyOf()); + setSelected(other.isSelected()); + setDisabled(other.isDisabled()); + addSelectionListener(); + } + + /* + * Adds a listener to the selected property that updates the flag that turns + * the wrapped hash hits filter on/off. + */ + private void addSelectionListener() { + selectedProperty().addListener(selectedProperty -> { + getFilter().setEventSourcesHaveHashSetHits(isSelected()); + }); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java index e3c8b8d6d5..2c17398db3 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java @@ -40,45 +40,73 @@ import org.sleuthkit.datamodel.TimelineFilter.RootFilter; import org.sleuthkit.datamodel.TimelineFilter.TextFilter; import org.sleuthkit.datamodel.TimelineFilter.TagsFilter; -/** A FilterState for RootFilters. Provides named access to the sub - * filter states. +/** + * An implementation of the FilterState interface that wraps a RootFilter object + * for display via the timeline filter panel by providing selected, disabled, + * and active properties for the object. The wrapped root filter is a compound + * filter, so additional behavior is provided for the management of child + * subfilter state objects. */ public class RootFilterState extends CompoundFilterState { + private final static BooleanProperty ALWAYS_TRUE = new SimpleBooleanProperty(true); + private final static BooleanProperty ALWAYS_FALSE = new SimpleBooleanProperty(false); private final CompoundFilterState eventTypeFilterState; private final SqlFilterState knownFilterState; - private final SqlFilterState textFilterState; - private final SqlFilterState tagsFilterState; - private final SqlFilterState hashHitsFilterState; + private final TextFilterState textFilterState; + private final TagsFilterState tagsFilterState; + private final HashHitsFilterState hashHitsFilterState; private final CompoundFilterState dataSourcesFilterState; private final CompoundFilterState fileTypesFilterState; - - private static final BooleanProperty ALWAYS_TRUE = new SimpleBooleanProperty(true); - private final static BooleanProperty ALWAYS_FALSE = new SimpleBooleanProperty(false); - private final Set> namedFilterStates = new HashSet<>(); - public RootFilterState(RootFilter delegate) { - this(delegate, - new CompoundFilterState<>(delegate.getEventTypeFilter()), - new SqlFilterState<>(delegate.getKnownFilter()), - new SqlFilterState<>(delegate.getTextFilter()), - new SqlFilterState<>(delegate.getTagsFilter()), - new SqlFilterState<>(delegate.getHashHitsFilter()), - new CompoundFilterState<>(delegate.getDataSourcesFilter()), - new CompoundFilterState<>(delegate.getFileTypesFilter()) + /** + * Constructs an implementation of the FilterState interface that wraps a + * RootFilter object for display via the timeline filter panel by providing + * selected, disabled, and active properties for the object. The underlying + * root filter is a compound filter, so additional behavior is provided for + * the management of child subfilter state objects. + * + * @param rootFilter The TimelineFilter.RootFilter object to be wrapped. + */ + public RootFilterState(RootFilter rootFilter) { + this(rootFilter, + new CompoundFilterState<>(rootFilter.getEventTypeFilter()), + new SqlFilterState<>(rootFilter.getKnownFilter()), + new TextFilterState(rootFilter.getTextFilter()), + new TagsFilterState(rootFilter.getTagsFilter()), + new HashHitsFilterState(rootFilter.getHashHitsFilter()), + new CompoundFilterState<>(rootFilter.getDataSourcesFilter()), + new CompoundFilterState<>(rootFilter.getFileTypesFilter()) ); } - private RootFilterState(RootFilter filter, - CompoundFilterState eventTypeFilterState, - SqlFilterState knownFilterState, - SqlFilterState textFilterState, - SqlFilterState tagsFilterState, - SqlFilterState hashHitsFilterState, - CompoundFilterState dataSourcesFilterState, - CompoundFilterState fileTypesFilterState) { - super(filter, Arrays.asList(eventTypeFilterState, knownFilterState, textFilterState, tagsFilterState, hashHitsFilterState, dataSourcesFilterState, fileTypesFilterState)); + /** + * Constructs an implementation of the FilterState interface that wraps a + * RootFilter object for display via the timeline filter panel by providing + * selected, disabled, and active properties for the object. The underlying + * root filter is a compound filter, so additional behavior is provided for + * the management of child subfilter state objects. + * + * @param rootFilter The TimelineFilter.RootFilter object to be + * wrapped. + * @param eventTypeFilterState The top-level event types subfilter. + * @param knownFilterState The known state subfilter. + * @param textFilterState The text subfilter. + * @param tagsFilterState The tags subfilter. + * @param hashHitsFilterState The hash set hits subfilter. + * @param dataSourcesFilterState The data sources subfilter. + * @param fileTypesFilterState The file types subfilter. + */ + private RootFilterState(RootFilter rootFilter, + CompoundFilterState eventTypeFilterState, + SqlFilterState knownFilterState, + TextFilterState textFilterState, + TagsFilterState tagsFilterState, + HashHitsFilterState hashHitsFilterState, + CompoundFilterState dataSourcesFilterState, + CompoundFilterState fileTypesFilterState) { + super(rootFilter, Arrays.asList(eventTypeFilterState, knownFilterState, textFilterState, tagsFilterState, hashHitsFilterState, dataSourcesFilterState, fileTypesFilterState)); this.eventTypeFilterState = eventTypeFilterState; this.knownFilterState = knownFilterState; this.textFilterState = textFilterState; @@ -86,21 +114,21 @@ public class RootFilterState extends CompoundFilterState otherFilter) { + public RootFilterState intersect(FilterState< ? extends TimelineFilter> other) { RootFilterState copyOf = copyOf(); - copyOf.addSubFilterState(otherFilter); + copyOf.addSubFilterState(other); return copyOf; } @@ -109,9 +137,9 @@ public class RootFilterState extends CompoundFilterState { - if (filter instanceof TimelineFilter.TagsFilter) { - ((TimelineFilter.TagsFilter)filter).setEventSourcesAreTagged(isSelected()); - } else if (filter instanceof TimelineFilter.HashHitsFilter) { - ((TimelineFilter.HashHitsFilter)filter).setEventSourcesHaveHashSetHits(isSelected()); - } - }); - + this(filter, false); } /** - * Disabled = false + * /** + * Constructs an implementation of the FilterState interface for wrapping + * TimelineFilter objects. TimelineFilter objects provide SQL WHERE clauses + * for querying the case database and are displayed in the timeline GUI via + * the filters panel. * - * @param filter - * @param selected True to select this filter initially. + * @param filter The TimelineFilter object to be wrapped. + * @param selected The initial value for the selected property. */ public SqlFilterState(FilterType filter, boolean selected) { super(filter, selected); @@ -74,9 +76,10 @@ public class SqlFilterState extends AbstractF @Override public String toString() { return "TimelineFilterState{" - + " filter=" + getFilter().toString() - + ", selected=" + isSelected() - + ", disabled=" + isDisabled() - + ", activeProp=" + isActive() + '}'; //NON-NLS + + " filter=" + getFilter().toString() + + ", selected=" + isSelected() + + ", disabled=" + isDisabled() + + ", activeP=" + isActive() + '}'; //NON-NLS } + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TagsFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TagsFilterState.java new file mode 100755 index 0000000000..4e56c47633 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TagsFilterState.java @@ -0,0 +1,67 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019-2019 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.timeline.ui.filtering.datamodel; + +import org.sleuthkit.datamodel.TimelineFilter; + +/** + * A wrapper for a TimelineFilter.TagsFilter object that allows it to be + * displayed by the timeline GUI via the filter panel by providing selected, + * disabled, and active properties for the TagsFilter. + */ +public class TagsFilterState extends SqlFilterState { + + /** + * Constructs a wrapper for a TimelineFilter.TagsFilter object that allows + * it to be displayed by the timeline GUI via the filter panel by providing + * selected, disabled, and active properties for the TagsFilter. + * + * @param tagsFilter A TimelineFilter.TagsFilter object. + */ + public TagsFilterState(TimelineFilter.TagsFilter tagsFilter) { + super(tagsFilter); + addSelectionListener(); + } + + /** + * "Copy constructs" a wrapper for a TimelineFilter.TagsFilter object + * that allows it to be displayed by the timeline GUI via the filter panel + * by providing selected, disabled, and active properties for the + * TagsFilter. + * + * @param other A TagsFilterState object. + */ + public TagsFilterState(TagsFilterState other) { + super(other.getFilter().copyOf()); + setSelected(other.isSelected()); + setDisabled(other.isDisabled()); + addSelectionListener(); + } + + /* + * Adds a listener to the selected property that updates the flag that turns + * the wrapped tags filter on/off. + */ + private void addSelectionListener() { + selectedProperty().addListener(selectedProperty -> { + getFilter().setEventSourcesAreTagged(isSelected()); + }); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TextFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TextFilterState.java new file mode 100755 index 0000000000..188b2d7247 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TextFilterState.java @@ -0,0 +1,85 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019-2019 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.timeline.ui.filtering.datamodel; + +import org.sleuthkit.datamodel.TimelineFilter; +import javafx.beans.property.Property; +import javafx.beans.property.SimpleStringProperty; + +/** + * A wrapper for a TimelineFilter.TextFilter object that allows it to be + * displayed by the timeline GUI via the filter panel by providing selected, + * disabled, and active properties for the TextFilter. The description substring + * of the TextFilter is also exposed as a JFX property so that it can be bound + * to the text filter text field of the filter panel. + */ +public class TextFilterState extends SqlFilterState { + + private final SimpleStringProperty descriptionSubstring; + + /** + * Constructs a wrapper for a TimelineFilter.TextFilter object that allows + * it to be displayed by the timeline GUI via filter panel by providing + * selected, disabled, and active properties for the TextFilter. The + * description substring of the TextFilter is also exposed as a JFX property + * so that it can be bound to the text filter text field of the filter + * panel. + * + * @param textFilter A TimelineFilter.TextFilter object. + */ + public TextFilterState(TimelineFilter.TextFilter textFilter) { + super(textFilter); + this.descriptionSubstring = new SimpleStringProperty(textFilter.getDescriptionSubstring()); + } + + /** + * "Copy constructs" a wrapper for a TimelineFilter.TextFilter object + * that allows it to be displayed by the timeline GUI via the filter panel + * by providing selected, disabled, and active properties for the + * TextFilter. + * + * @param other A HashHitsFilterState object. + */ + public TextFilterState(TextFilterState other) { + super(other.getFilter().copyOf()); + setSelected(other.isSelected()); + setDisabled(other.isDisabled()); + this.descriptionSubstring = new SimpleStringProperty(other.getFilter().getDescriptionSubstring()); + } + + @Override + public TimelineFilter.TextFilter getFilter() { + TimelineFilter.TextFilter textFilter = super.getFilter(); + textFilter.setDescriptionSubstring(descriptionSubstringProperty().getValue()); + return textFilter; + } + + /** + * Gets the substring that must be present in one or more of the + * descriptions of each event that passes the filter. The property is + * intended to be bound to the text filter text field of the filter panel in + * the timeline GUI. + * + * @return The required substring as a Property. + */ + public Property descriptionSubstringProperty() { + return descriptionSubstring; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/Bundle_ja.properties new file mode 100644 index 0000000000..7b5a853206 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/Bundle_ja.properties @@ -0,0 +1,16 @@ +ListChart.errorMsg=\u9078\u629e\u3057\u305f\u30a4\u30d9\u30f3\u30c8\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u53d6\u5f97\u4e2d\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3057\u305f\u3002 +# {0} - \u30a4\u30d9\u30f3\u30c8\u6570 +ListTimeline.eventCountLabel.text={0} \u30a4\u30d9\u30f3\u30c8 +ListTimeline.hashHitTooltip.error=\u9078\u629e\u3057\u305f\u30a4\u30d9\u30f3\u30c8\u306e\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d\u3092\u53d6\u5f97\u4e2d\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3057\u305f\u3002 +# {0} - \u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d +ListTimeline.hashHitTooltip.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8:\n{0} +ListTimeline.taggedTooltip.error=\u9078\u629e\u3057\u305f\u30a4\u30d9\u30f3\u30c8\u306e\u30bf\u30b0\u540d\u3092\u53d6\u5f97\u4e2d\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3057\u305f\u3002 +# {0} - \u30bf\u30b0\u540d +ListTimeline.taggedTooltip.text=\u30bf\u30b0:\n{0} +ListView.EventTypeCell.accessedTooltip=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb( A ) +ListView.EventTypeCell.changedTooltip=\u5909\u66f4\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb( C ) +ListView.EventTypeCell.createdTooltip=\u4f5c\u6210\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb( B, for Born ) +ListView.EventTypeCell.modifiedTooltip=\u4fee\u6b63\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb( M ) +ListViewPane.loggedTask.name=\u30ea\u30b9\u30c8\u30d3\u30e5\u30fc\u306e\u66f4\u65b0\u4e2d\u3067\u3059 +ListViewPane.loggedTask.queryDb=\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u306e\u691c\u7d22\u4e2d\u3067\u3059 +ListViewPane.loggedTask.updateUI=\u30d3\u30e5\u30fc\u306e\u5165\u529b\u4e2d\u3067\u3059 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java index b94e7956bd..6cb7d55160 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -28,7 +28,7 @@ import javafx.concurrent.Task; import javafx.scene.Node; import org.joda.time.Interval; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.events.ViewInTimelineRequestedEvent; @@ -116,7 +116,7 @@ public class ListViewPane extends AbstractTimeLineView { return null; } - FilteredEventsModel eventsModel = getEventsModel(); + EventsModel eventsModel = getEventsModel(); Set selectedEventIDs; TimeLineController controller = getController(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/datamodel/ListViewModel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/datamodel/ListViewModel.java index 957b17265c..d51e454247 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/datamodel/ListViewModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/datamodel/ListViewModel.java @@ -29,7 +29,7 @@ import java.util.Map.Entry; import java.util.Objects; import static java.util.stream.Collectors.groupingBy; import org.joda.time.Interval; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.TskCoreException; @@ -44,10 +44,10 @@ import org.sleuthkit.datamodel.TimelineLevelOfDetail; */ public class ListViewModel { - private final FilteredEventsModel eventsModel; + private final EventsModel eventsModel; private final TimelineManager eventManager; - public ListViewModel(FilteredEventsModel eventsModel) { + public ListViewModel(EventsModel eventsModel) { this.eventsModel = eventsModel; this.eventManager = eventsModel.getEventManager(); } @@ -63,7 +63,7 @@ public class ListViewModel { * @throws org.sleuthkit.datamodel.TskCoreException */ public List getCombinedEvents() throws TskCoreException { - return getCombinedEvents(eventsModel.getTimeRange(), eventsModel.getFilterState()); + return getCombinedEvents(eventsModel.getTimeRange(), eventsModel.getEventFilterState()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java index 52d8fa4ddf..53c84755f8 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java @@ -54,12 +54,10 @@ public final class FilterUtils { "FilterUtils.otherFilter.displayName=Other"}) public static FileTypesFilter createDefaultFileTypesFilter() { FileTypesFilter fileTypesFilter = new FileTypesFilter(); - fileTypesFilter.addSubFilter(new FileTypeFilter(MEDIA.getDisplayName(), MEDIA.getMediaTypes())); fileTypesFilter.addSubFilter(new FileTypeFilter(DOCUMENTS.getDisplayName(), DOCUMENTS.getMediaTypes())); fileTypesFilter.addSubFilter(new FileTypeFilter(EXECUTABLE.getDisplayName(), EXECUTABLE.getMediaTypes())); fileTypesFilter.addSubFilter(new InverseFileTypeFilter(Bundle.FilterUtils_otherFilter_displayName(), NON_OTHER_MIME_TYPES)); - return fileTypesFilter; } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/zooming/Bundle_ja.properties old mode 100755 new mode 100644 index 29c96aaa13..16cb968d63 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/Bundle_ja.properties @@ -1,10 +1,11 @@ -EventTypeZoomLevel.baseType=\u30d9\u30fc\u30b9\u30bf\u30a4\u30d7 +DescriptionLOD.short=\u77ed +DescriptionLOD.medium=\u4e2d +DescriptionLOD.full=\u9577 + +EventTypeZoomLevel.baseType=\u57fa\u672c\u30bf\u30a4\u30d7 EventTypeZoomLevel.rootType=\u30eb\u30fc\u30c8\u30bf\u30a4\u30d7 EventTypeZoomLevel.subType=\u30b5\u30d6\u30bf\u30a4\u30d7 -DescriptionLOD.short=\u7c21\u6f54 -DescriptionLOD.medium=\u6982\u8981 -DescriptionLOD.full=\u8a73\u7d30 -ZoomSettingsPane.descrLODLabel.text=\u8a73\u7d30\u8aac\u660e\uff1a -ZoomSettingsPane.historyLabel.text=\u5c65\u6b74\uff1a -ZoomSettingsPane.timeUnitLabel.text=\u6642\u9593\u5358\u4f4d\uff1a -ZoomSettingsPane.typeZoomLabel.text=\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7\uff1a \ No newline at end of file +ZoomSettingsPane.descrLODLabel.text=\u8aac\u660e\u306e\u8a73\u7d30: +ZoomSettingsPane.timeUnitLabel.text=\u6642\u9593\u5358\u4f4d: +ZoomSettingsPane.typeZoomLabel.text=\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7: +ZoomSettingsPane.zoomLabel.text=\u30ba\u30fc\u30e0 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventsModelParams.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventsModelParams.java new file mode 100755 index 0000000000..b3010fe64c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventsModelParams.java @@ -0,0 +1,135 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-2019 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.timeline.zooming; + +import java.util.Objects; +import org.joda.time.Interval; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; +import org.sleuthkit.datamodel.TimelineEventType; +import org.sleuthkit.datamodel.TimelineLevelOfDetail; + +/** + * A container that bundles the user-specified parameters for the events model + * so that they can be passed around and saved as mementos to support a + * navigable (forwards-backwards) history feature for the events model. + */ +final public class EventsModelParams { + + private final Interval timeRange; + private final TimelineEventType.HierarchyLevel eventTypesHierarchyLevel; + private final RootFilterState eventFilterState; + private final TimelineLevelOfDetail timelineLOD; + + public EventsModelParams(Interval timeRange, TimelineEventType.HierarchyLevel eventTypesHierarchyLevel, RootFilterState eventFilterState, TimelineLevelOfDetail timelineLOD) { + this.timeRange = timeRange; + this.eventTypesHierarchyLevel = eventTypesHierarchyLevel; + this.eventFilterState = eventFilterState; + this.timelineLOD = timelineLOD; + } + + public Interval getTimeRange() { + return timeRange; + } + + public TimelineEventType.HierarchyLevel getEventTypesHierarchyLevel() { + return eventTypesHierarchyLevel; + } + + public RootFilterState getEventFilterState() { + return eventFilterState; + } + + public TimelineLevelOfDetail getTimelineLOD() { + return timelineLOD; + } + + public EventsModelParams withTimeAndType(Interval timeRange, TimelineEventType.HierarchyLevel zoomLevel) { + return new EventsModelParams(timeRange, zoomLevel, eventFilterState, timelineLOD); + } + + public EventsModelParams withTypeZoomLevel(TimelineEventType.HierarchyLevel zoomLevel) { + return new EventsModelParams(timeRange, zoomLevel, eventFilterState, timelineLOD); + } + + public EventsModelParams withTimeRange(Interval timeRange) { + return new EventsModelParams(timeRange, eventTypesHierarchyLevel, eventFilterState, timelineLOD); + } + + public EventsModelParams withDescrLOD(TimelineLevelOfDetail descrLOD) { + return new EventsModelParams(timeRange, eventTypesHierarchyLevel, eventFilterState, descrLOD); + } + + public EventsModelParams withFilterState(RootFilterState filter) { + return new EventsModelParams(timeRange, eventTypesHierarchyLevel, filter, timelineLOD); + } + + public boolean hasFilterState(RootFilterState filterSet) { + return this.eventFilterState.equals(filterSet); + } + + public boolean hasTypeZoomLevel(TimelineEventType.HierarchyLevel typeZoom) { + return this.eventTypesHierarchyLevel.equals(typeZoom); + } + + public boolean hasTimeRange(Interval timeRange) { + return this.timeRange != null && this.timeRange.equals(timeRange); + } + + public boolean hasDescrLOD(TimelineLevelOfDetail newLOD) { + return this.timelineLOD.equals(newLOD); + } + + @Override + public int hashCode() { + int hash = 3; + hash = 97 * hash + Objects.hashCode(this.timeRange.getStartMillis()); + hash = 97 * hash + Objects.hashCode(this.timeRange.getEndMillis()); + hash = 97 * hash + Objects.hashCode(this.eventTypesHierarchyLevel); + hash = 97 * hash + Objects.hashCode(this.eventFilterState); + hash = 97 * hash + Objects.hashCode(this.timelineLOD); + + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final EventsModelParams other = (EventsModelParams) obj; + if (!Objects.equals(this.timeRange, other.getTimeRange())) { + return false; + } + if (this.eventTypesHierarchyLevel != other.getEventTypesHierarchyLevel()) { + return false; + } + if (this.eventFilterState.equals(other.getEventFilterState()) == false) { + return false; + } + return this.timelineLOD == other.getTimelineLOD(); + } + + @Override + public String toString() { + return "ZoomState{" + "timeRange=" + timeRange + ", typeZoomLevel=" + eventTypesHierarchyLevel + ", filter=" + eventFilterState.getActiveFilter().toString() + ", descrLOD=" + timelineLOD + '}'; //NON-NLS + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java index 0d832e0108..0f576ee499 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java @@ -34,7 +34,7 @@ import org.controlsfx.control.Notifications; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.FXMLConstructor; -import org.sleuthkit.autopsy.timeline.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.EventsModel; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.utils.RangeDivision; @@ -72,7 +72,7 @@ public class ZoomSettingsPane extends TitledPane { private Slider timeUnitSlider; private final TimeLineController controller; - private final FilteredEventsModel filteredEvents; + private final EventsModel filteredEvents; /** * Constructor @@ -97,7 +97,7 @@ public class ZoomSettingsPane extends TitledPane { typeZoomSlider.setMax(TimelineEventType.HierarchyLevel.values().length - 1); configureSliderListeners(typeZoomSlider, controller::pushEventTypeZoom, - filteredEvents.eventTypeZoomProperty(), + filteredEvents.eventTypesHierarchyLevelProperty(), TimelineEventType.HierarchyLevel.class, TimelineEventType.HierarchyLevel::ordinal, Function.identity()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomState.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomState.java deleted file mode 100755 index 2588a32cf7..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomState.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2013-18 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.timeline.zooming; - -import java.util.Objects; -import org.joda.time.Interval; -import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; -import org.sleuthkit.datamodel.TimelineEvent; -import org.sleuthkit.datamodel.TimelineEventType; -import org.sleuthkit.datamodel.TimelineLevelOfDetail; - -/** - * This class encapsulates all the zoom(and filter) parameters into one object - * for passing around and as a memento of the zoom/filter state. - */ -final public class ZoomState { - - private final Interval timeRange; - - private final TimelineEventType.HierarchyLevel typeZoomLevel; - - private final RootFilterState filter; - - private final TimelineLevelOfDetail descrLOD; - - public Interval getTimeRange() { - return timeRange; - } - - public TimelineEventType.HierarchyLevel getTypeZoomLevel() { - return typeZoomLevel; - } - - public RootFilterState getFilterState() { - return filter; - } - - public TimelineLevelOfDetail getDescriptionLOD() { - return descrLOD; - } - - public ZoomState(Interval timeRange, TimelineEventType.HierarchyLevel zoomLevel, RootFilterState filter, TimelineLevelOfDetail descrLOD) { - this.timeRange = timeRange; - this.typeZoomLevel = zoomLevel; - this.filter = filter; - this.descrLOD = descrLOD; - } - - public ZoomState withTimeAndType(Interval timeRange, TimelineEventType.HierarchyLevel zoomLevel) { - return new ZoomState(timeRange, zoomLevel, filter, descrLOD); - } - - public ZoomState withTypeZoomLevel(TimelineEventType.HierarchyLevel zoomLevel) { - return new ZoomState(timeRange, zoomLevel, filter, descrLOD); - } - - public ZoomState withTimeRange(Interval timeRange) { - return new ZoomState(timeRange, typeZoomLevel, filter, descrLOD); - } - - public ZoomState withDescrLOD(TimelineLevelOfDetail descrLOD) { - return new ZoomState(timeRange, typeZoomLevel, filter, descrLOD); - } - - public ZoomState withFilterState(RootFilterState filter) { - return new ZoomState(timeRange, typeZoomLevel, filter, descrLOD); - } - - public boolean hasFilterState(RootFilterState filterSet) { - return this.filter.equals(filterSet); - } - - public boolean hasTypeZoomLevel(TimelineEventType.HierarchyLevel typeZoom) { - return this.typeZoomLevel.equals(typeZoom); - } - - public boolean hasTimeRange(Interval timeRange) { - return this.timeRange != null && this.timeRange.equals(timeRange); - } - - public boolean hasDescrLOD(TimelineLevelOfDetail newLOD) { - return this.descrLOD.equals(newLOD); - } - - @Override - public int hashCode() { - int hash = 3; - hash = 97 * hash + Objects.hashCode(this.timeRange.getStartMillis()); - hash = 97 * hash + Objects.hashCode(this.timeRange.getEndMillis()); - hash = 97 * hash + Objects.hashCode(this.typeZoomLevel); - hash = 97 * hash + Objects.hashCode(this.filter); - hash = 97 * hash + Objects.hashCode(this.descrLOD); - - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ZoomState other = (ZoomState) obj; - if (!Objects.equals(this.timeRange, other.timeRange)) { - return false; - } - if (this.typeZoomLevel != other.typeZoomLevel) { - return false; - } - if (this.filter.equals(other.filter) == false) { - return false; - } - return this.descrLOD == other.descrLOD; - } - - @Override - public String toString() { - return "ZoomState{" + "timeRange=" + timeRange + ", typeZoomLevel=" + typeZoomLevel + ", filter=" + filter.getActiveFilter().toString() + ", descrLOD=" + descrLOD + '}'; //NON-NLS - } -} diff --git a/CoreLibs/src/org/sleuthkit/autopsy/corelibs/Bundle_ja.properties b/CoreLibs/src/org/sleuthkit/autopsy/corelibs/Bundle_ja.properties index bbc1bac4b1..2935be7f4e 100644 --- a/CoreLibs/src/org/sleuthkit/autopsy/corelibs/Bundle_ja.properties +++ b/CoreLibs/src/org/sleuthkit/autopsy/corelibs/Bundle_ja.properties @@ -1,7 +1,7 @@ -OpenIDE-Module-Display-Category=\u30E9\u30A4\u30D6\u30E9\u30EA -OpenIDE-Module-Name=Autopsy-\u30B3\u30A2\u30E9\u30A4\u30D6\u30E9\u30EA +OpenIDE-Module-Display-Category=\u30e9\u30a4\u30d6\u30e9\u30ea +OpenIDE-Module-Name=Autopsy-\u30b3\u30a2\u30e9\u30a4\u30d6\u30e9\u30ea OpenIDE-Module-Long-Description=\ - \u30B3\u30A2\u30E2\u30B8\u30E5\u30FC\u30EB\u7528\u306E\u5916\u90E8\u30E9\u30A4\u30D6\u30E9\u30EA\u3078\u306E\u4F9D\u5B58\u95A2\u4FC2\u3002\n\ - \u30E9\u30A4\u30D6\u30E9\u30EA\u306F\u4ED6\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u304B\u3089\u3082\u30A4\u30F3\u30DD\u30FC\u30C8\u3067\u304D\u307E\u3059\u3002 -OpenIDE-Module-Short-Description=Autopsy\u30B3\u30A2\u30E2\u30B8\u30E5\u30FC\u30EB\u306E\u5916\u90E8\u30E9\u30A4\u30D6\u30E9\u30EA -SigarLoader.linkErr.msg=Sigar\u30E9\u30A4\u30D6\u30E9\u30EA\u3092\u304A\u4F7F\u3044\u306E\u74B0\u5883\u306B\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F\uFF08\u91CD\u5927\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF09\u3001OS\u30EC\u30D9\u30EB\u306E\u30E1\u30C8\u30EA\u30AF\u30B9\u306F\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002 \ No newline at end of file + \u30b3\u30a2\u30e2\u30b8\u30e5\u30fc\u30eb\u7528\u306e\u5916\u90e8\u30e9\u30a4\u30d6\u30e9\u30ea\u3078\u306e\u4f9d\u5b58\u95a2\u4fc2\u3002\n\ + \u30e9\u30a4\u30d6\u30e9\u30ea\u306f\u4ed6\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u3089\u3082\u30a4\u30f3\u30dd\u30fc\u30c8\u3067\u304d\u307e\u3059\u3002 +OpenIDE-Module-Short-Description=Autopsy\u30b3\u30a2\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u5916\u90e8\u30e9\u30a4\u30d6\u30e9\u30ea +SigarLoader.linkErr.msg=Sigar\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u304a\u4f7f\u3044\u306e\u74b0\u5883\u306b\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\uff08\u91cd\u5927\u3067\u306f\u3042\u308a\u307e\u305b\u3093\uff09\u3001OS\u30ec\u30d9\u30eb\u306e\u30e1\u30c8\u30ea\u30af\u30b9\u306f\u78ba\u8a8d\u3067\u304d\u307e\u305b\u3093\u3002 diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java index b41aca0592..c2b6eada91 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java @@ -28,7 +28,7 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.experimental.autoingest.AutoIngestDashboardOpenAction") -@ActionReference(path = "Menu/Tools", position = 103) +@ActionReference(path = "Menu/Tools", position = 104) @ActionRegistration(displayName = "#CTL_AutoIngestDashboardOpenAction", lazy = false) @Messages({"CTL_AutoIngestDashboardOpenAction=Auto Ingest Dashboard"}) public final class AutoIngestDashboardOpenAction extends CallableSystemAction { diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExportRuleSet.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExportRuleSet.java index 399ae574ba..198dd8c5bd 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExportRuleSet.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExportRuleSet.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015-2018 Basis Technology Corp. + * Copyright 2015-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -178,6 +178,20 @@ final class FileExportRuleSet implements Serializable, Comparable(); } + /** + * Sort the file size conditions of this rule. + */ + void sortFileSizeConditions() { + this.fileSizeConditions.sort(null); + } + + /** + * Sort the artifact conditions of this rule. + */ + void sortArtifactConditions() { + this.artifactConditions.sort(null); + } + /** * Gets the name of the rule. * @@ -336,13 +350,13 @@ final class FileExportRuleSet implements Serializable, Comparable evaluate(long dataSourceId) throws ExportRulesException { try { SleuthkitCase db = Case.getCurrentCaseThrows().getSleuthkitCase(); - try (SleuthkitCase.CaseDbQuery queryResult = db.executeQuery(getQuery(dataSourceId))) { - ResultSet resultSet = queryResult.getResultSet(); + try (SleuthkitCase.CaseDbQuery queryResult = db.executeQuery(getQuery(dataSourceId)); + ResultSet resultSet = queryResult.getResultSet();) { List fileIds = new ArrayList<>(); while (resultSet.next()) { fileIds.add(resultSet.getLong("obj_id")); @@ -889,7 +903,7 @@ final class FileExportRuleSet implements Serializable, Comparable\u30B9\u30BF\u30FC\u30C8\u30A2\u30C3\u30D7\u6642\u9593\u3092\u6700\u5C0F\u9650\u306B\u3059\u308B\u305F\u3081\u3001\u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC\u306F\u7D99\u7D9A\u7684\u306B\u5185\u90E8\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u66F4\u65B0\u3057\u307E\u3059\u3002
    \u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC\u306E\u6A5F\u80FD\u304C\u5FC5\u8981\u3067\u306A\u3044\u5834\u5408\u3001\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u3092\u9045\u304F\u3059\u308B\u304B\u3082\u3057\u308C\u307E\u305B\u3093\u3002
    \u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC\u304C\u5FC5\u8981\u3067\u306A\u3044\u5834\u5408\u3001\u3053\u306E\u8A2D\u5B9A\u3092\u4F7F\u3044\u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC\u3092\u7121\u52B9\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002 -ImageGalleryOptionsPanel.furtherDescriptionArea.text=\u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC\u304C\u7121\u52B9\u306A\u5834\u5408\u3001\u66F4\u65B0\u304C\u5FC5\u8981\u3068\u3044\u3046\u3053\u3068\u3060\u3051\u8A18\u9332\u3055\u308C\u307E\u3059\u3002\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u5F8C\u306B\u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC\u304C\u6709\u52B9\u5316\u3055\u308C\u305F\u5834\u5408\u3001\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u306E\u7D50\u679C\u3092\u5143\u306B\u4E00\u5EA6\u306B\u307E\u3068\u3081\u3066\u66F4\u65B0\u3092\u3057\u307E\u3059\u3002\u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC\u304C\u7121\u52B9\u306E\u5834\u5408\u3001\u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC\u306E\u30A6\u30A3\u30F3\u30C9\u30A6\u3092\u958B\u3053\u3046\u3068\u3057\u305F\u969B\u306B\u6709\u52B9\u5316\u3059\u308B\u3088\u3046\u306B\u6307\u793A\u3055\u308C\u307E\u3059\u3002 -ImageGalleryOptionsPanel.unavailableDuringInjestLabel.text=\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u4E2D\u306F\u3053\u306E\u8A2D\u5B9A\u306F\u4F7F\u3048\u307E\u305B\u3093\u3002 -OptionsCategory_Keywords_Options=\u30A4\u30E1\u30FC\u30B8\u30D3\u30C7\u30AA\u30AE\u30E3\u30E9\u30EA\u30FC\u30AB\u30C6\u30B4\u30EA\u30FC -OptionsCategory_Name_Options=\u30A4\u30E1\u30FC\u30B8\uFF0F\u30D3\u30C7\u30AA\u30AE\u30E3\u30E9\u30EA\u30FC -PrePopulateDataSourceFiles.committingDb.status=\u30A4\u30E1\u30FC\u30B8\uFF0F\u30D3\u30C7\u30AA\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u30B3\u30DF\u30C3\u30C8\u4E2D -PrePopulateDataSourceFiles.prepopulatingDb.status=\u30A4\u30E1\u30FC\u30B8\uFF0F\u30D3\u30C7\u30AA\u30AE\u30E3\u30E9\u30EA\u30FC\u306B\u60C5\u5831\u3092\u4E8B\u524D\u306B\u5165\u529B\u4E2D \ No newline at end of file + \u30a4\u30e1\u30fc\u30b8\u96c6\u4e2d\u8abf\u67fb\u306e\u5b9f\u884c\u3092\u3088\u308a\u52b9\u7387\u7684\u306b\u3059\u308b\u3088\u3046\u306b\u8a2d\u8a08\u3055\u308c\u305f\u3001\u65b0\u3057\u3044\u30a4\u30e1\u30fc\u30b8\u3068\u52d5\u753b\u306e\u30ae\u30e3\u30e9\u30ea\u30fc\u3067\u3059\u3002 \ + \u3053\u306e\u4f5c\u54c1\u306fDHS S&T\u306e\u8cc7\u91d1\u63f4\u52a9\u3092\u53d7\u3051\u3066\u3044\u307e\u3059\u3002\u306a\u304a\u3001\u3053\u308c\u306f\u30d9\u30fc\u30bf\u30ea\u30ea\u30fc\u30b9\u3067\u3059\u3002\ + sleuthkit.org\u30b5\u30a4\u30c8\u304b\u3089\u5165\u624b\u3067\u304d\u307e\u305b\u3093\u3002\u9650\u5b9a\u30e6\u30fc\u30b6\u30fc\u306b\u914d\u5e03\u3055\u308c\u3066\u3044\u307e\u3059\u3002 +OpenIDE-Module-Name=ImageGallery +OpenIDE-Module-Short-Description=\u9ad8\u5ea6\u306a\u30a4\u30e1\u30fc\u30b8\u3068\u52d5\u753b\u306e\u30ae\u30e3\u30e9\u30ea\u30fc +ImageGalleryOptionsPanel.enabledForCaseBox.text=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306e\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u306e\u66f4\u65b0\u3092\u6709\u52b9\u306b\u3057\u307e\u3059\u3002 +ImageGalleryOptionsPanel.enabledByDefaultBox.text=\u65b0\u3057\u3044\u30b1\u30fc\u30b9\u306e\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u3092\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u6709\u52b9\u5316\u3057\u307e\u3059\u3002 +ImageGalleryOptionsPanel.enabledForCaseBox.toolTipText=\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u304c\u7121\u52b9\u306e\u5834\u5408\u3001\u66f4\u65b0\u304c\u5fc5\u8981\u3067\u3042\u308b\u3068\u3044\u3046\u4e8b\u5b9f\u3060\u3051\u304c\u8a18\u9332\u3055\u308c\u307e\u3059\u3002 \u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5f8c\u306b\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u3092\u6709\u52b9\u5316\u3057\u305f\u5834\u5408\u3001\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u7d50\u679c\u306b\u57fa\u3065\u3044\u3066\u4e00\u62ec\u66f4\u65b0\u30921\u56de\u884c\u3044\u307e\u3059\u3002 \u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u304c\u7121\u52b9\u306e\u5834\u5408\u3001\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u3092\u958b\u3053\u3046\u3068\u8a66\u307f\u308b\u3068\u3001\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u3092\u6709\u52b9\u5316\u3059\u308b\u3088\u3046\u4fc3\u3055\u308c\u307e\u3059\u3002 +ImageGalleryOptionsPanel.descriptionLabel.text=\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u306e\u8d77\u52d5\u6642\u9593\u3092\u6700\u5c0f\u5316\u3059\u308b\u305f\u3081\u306b\u3001\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u306e\u5185\u90e8\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u5e38\u6642\u66f4\u65b0\u3055\u308c\u307e\u3059\u3002
    \u3053\u308c\u306b\u3088\u308a\u3001\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u306e\u6a5f\u80fd\u3092\u5fc5\u8981\u3068\u3057\u306a\u3044\u5834\u5408\u3001\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u5b9f\u884c\u304c\u9045\u304f\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002
    \u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u3092\u5fc5\u8981\u3068\u3057\u306a\u3044\u5834\u5408\u3001\u3053\u308c\u3089\u306e\u8a2d\u5b9a\u3092\u4f7f\u7528\u3057\u3066\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u3092\u7121\u52b9\u5316\u3057\u307e\u3059\u3002 +ImageGalleryOptionsPanel.furtherDescriptionArea.text=\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u304c\u7121\u52b9\u306e\u5834\u5408\u3001\u66f4\u65b0\u304c\u5fc5\u8981\u3067\u3042\u308b\u3068\u3044\u3046\u4e8b\u5b9f\u3060\u3051\u304c\u8a18\u9332\u3055\u308c\u307e\u3059\u3002 \u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5f8c\u306b\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u3092\u6709\u52b9\u5316\u3057\u305f\u5834\u5408\u3001\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u7d50\u679c\u306b\u57fa\u3065\u3044\u3066\u4e00\u62ec\u66f4\u65b0\u30921\u56de\u884c\u3044\u307e\u3059\u3002 \u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u304c\u7121\u52b9\u306e\u5834\u5408\u3001\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u3092\u958b\u3053\u3046\u3068\u8a66\u307f\u308b\u3068\u3001\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u3092\u6709\u52b9\u5316\u3059\u308b\u3088\u3046\u4fc3\u3055\u308c\u307e\u3059\u3002 +ImageGalleryOptionsPanel.unavailableDuringInjestLabel.text=\u3053\u306e\u8a2d\u5b9a\u306f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5b9f\u884c\u4e2d\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +ImageGalleryOptionsPanel.groupCategorizationWarningBox.text=\u30b0\u30eb\u30fc\u30d7\u5168\u4f53\u306b\u52b9\u529b\u3092\u6301\u305f\u305b\u308b\u3053\u3068\u3067\u3001\u30ab\u30c6\u30b4\u30ea\u30fc\u306e\u4e0a\u66f8\u304d\u4e2d\u306b\u8b66\u544a\u3092\u8868\u793a\u3057\u307e\u305b\u3093\u3002 +CTL_OpenAction=\u30a4\u30e1\u30fc\u30b8/\u52d5\u753b\u3092\u958b\u304f +OptionsCategory_Keywords_Options=\u30a4\u30e1\u30fc\u30b8/\u52d5\u753b\u30ae\u30e3\u30e9\u30ea\u30fc\u306e\u30ab\u30c6\u30b4\u30ea\u30fc +OptionsCategory_Name_Options=\u30a4\u30e1\u30fc\u30b8/\u52d5\u753b\u30ae\u30e3\u30e9\u30ea\u30fc diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index 624c914d56..9d8c8b1269 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -23,6 +23,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.sql.SQLException; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -55,6 +56,7 @@ import org.sleuthkit.autopsy.casemodule.Case.CaseType; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent; import org.sleuthkit.autopsy.coreutils.History; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; @@ -102,7 +104,8 @@ public final class ImageGalleryController { Case.Events.CURRENT_CASE, Case.Events.DATA_SOURCE_ADDED, Case.Events.CONTENT_TAG_ADDED, - Case.Events.CONTENT_TAG_DELETED + Case.Events.CONTENT_TAG_DELETED, + Case.Events.DATA_SOURCE_DELETED ); /* @@ -798,7 +801,22 @@ public final class ImageGalleryController { if (((AutopsyEvent) event).getSourceType() == AutopsyEvent.SourceType.LOCAL) { Content newDataSource = (Content) event.getNewValue(); if (isListeningEnabled()) { - drawableDB.insertOrUpdateDataSource(newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN); + try { + drawableDB.insertOrUpdateDataSource(newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN); + } catch (SQLException ex) { + logger.log(Level.SEVERE, String.format("Error updating datasources table (data source object ID = %d, status = %s)", newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN.toString()), ex); //NON-NLS + } + } + } + break; + case DATA_SOURCE_DELETED: + if (((AutopsyEvent) event).getSourceType() == AutopsyEvent.SourceType.LOCAL) { + final DataSourceDeletedEvent dataSourceDeletedEvent = (DataSourceDeletedEvent) event; + long dataSourceObjId = dataSourceDeletedEvent.getDataSourceId(); + try { + drawableDB.deleteDataSource(dataSourceObjId); + } catch (SQLException | TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Failed to delete data source (obj_id = %d)", dataSourceObjId), ex); //NON-NLS } } break; @@ -869,7 +887,7 @@ public final class ImageGalleryController { default: break; } - } catch (TskCoreException ex) { + } catch (TskCoreException | SQLException ex) { logger.log(Level.SEVERE, String.format("Failed to handle %s event for %s (objId=%d)", dataSourceEvent.getPropertyName(), dataSource.getName(), dataSourceObjId), ex); } } @@ -884,7 +902,7 @@ public final class ImageGalleryController { * @throws TskCoreException If there is an error adding the data source to * the database. */ - private void handleDataSourceAnalysisStarted(DataSourceAnalysisEvent event) throws TskCoreException { + private void handleDataSourceAnalysisStarted(DataSourceAnalysisEvent event) throws TskCoreException, SQLException { if (event.getSourceType() == AutopsyEvent.SourceType.LOCAL && isListeningEnabled()) { Content dataSource = event.getDataSource(); long dataSourceObjId = dataSource.getId(); @@ -905,7 +923,7 @@ public final class ImageGalleryController { * @throws TskCoreException If there is an error updating the state ot the * data source in the database. */ - private void handleDataSourceAnalysisCompleted(DataSourceAnalysisEvent event) throws TskCoreException { + private void handleDataSourceAnalysisCompleted(DataSourceAnalysisEvent event) throws TskCoreException, SQLException { if (event.getSourceType() == AutopsyEvent.SourceType.LOCAL) { Content dataSource = event.getDataSource(); long dataSourceObjId = dataSource.getId(); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/Bundle_ja.properties b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/Bundle_ja.properties index 122bcaf610..06c4149dec 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/Bundle_ja.properties +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/Bundle_ja.properties @@ -1,23 +1,42 @@ -AddDrawableTagAction.addTagsToFiles.alert={0}\u30D5\u30A1\u30A4\u30EB\u3092\u30BF\u30B0\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -AddDrawableTagAction.displayName.plural=\u30D5\u30A1\u30A4\u30EB\u3092\u30BF\u30B0 -AddDrawableTagAction.displayName.singular=\u30D5\u30A1\u30A4\u30EB\u3092\u30BF\u30B0 -AddTagAction.menuItem.newTag=\u65B0\u898F\u30BF\u30B0... -AddTagAction.menuItem.noTags=\u30BF\u30B0\u7121\u3057 -AddTagAction.menuItem.quickTag=\u30AF\u30A4\u30C3\u30AF\u30BF\u30B0 -AddTagAction.menuItem.tagAndComment=\u30BF\u30B0\uFF0F\u30B3\u30E1\u30F3\u30C8\u3059\u308B... -Back_diplayName=\u623B\u308B -CategorizeAction.displayName=\u30AB\u30C6\u30B4\u30E9\u30A4\u30BA -CategorizeTask.errorUnable.msg={0}\u3092\u30AB\u30C6\u30B4\u30E9\u30A4\u30BA\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -CategorizeTask.errorUnable.title=\u30AB\u30C6\u30B4\u30E9\u30A4\u30BA\u30A8\u30E9\u30FC -CTL_OpenAction=\u30A4\u30E1\u30FC\u30B8\uFF0F\u30D3\u30C7\u30AA\u3092\u8868\u793A -CTL_OpenHelpAction=\u30A4\u30E1\u30FC\u30B8\uFF0F\u30D3\u30C7\u30AA\u30AE\u30E3\u30E9\u30EA\u30FC\u30D8\u30EB\u30D7 -DeleteFollwUpTagAction.displayName=\u30D5\u30A9\u30ED\u30FC\u30A2\u30C3\u30D7\u30BF\u30B0\u3092\u524A\u9664 +# {0} - fileID +AddDrawableTagAction.addTagsToFiles.alert=\u30d5\u30a1\u30a4\u30eb {0} \u3092\u30bf\u30b0\u4ed8\u3051\u3067\u304d\u307e\u305b\u3093\u3002 +AddDrawableTagAction.displayName.plural=\u30d5\u30a1\u30a4\u30eb\u3092\u30bf\u30b0\u4ed8\u3051 +AddDrawableTagAction.displayName.singular=\u30d5\u30a1\u30a4\u30eb\u3092\u30bf\u30b0\u4ed8\u3051 +AddTagAction.menuItem.newTag=\u65b0\u898f\u30bf\u30b0... +AddTagAction.menuItem.noTags=\u30bf\u30b0\u306a\u3057 +AddTagAction.menuItem.quickTag=\u30af\u30a4\u30c3\u30af\u30bf\u30b0 +AddTagAction.menuItem.tagAndComment=\u30bf\u30b0\u3068\u30b3\u30e1\u30f3\u30c8... +Back_diplayName=\u623b\u308b +CategorizeAction.displayName=\u5206\u985e +# {0} - fileID\u756a\u53f7 +CategorizeDrawableFileTask.errorUnable.msg={0} \u3092\u5206\u985e\u3067\u304d\u307e\u305b\u3093\u3002 +CategorizeDrawableFileTask.errorUnable.title=\u30a8\u30e9\u30fc\u3092\u5206\u985e\u4e2d\u3067\u3059 +CategorizeGroupAction.dontShowAgain=\u3053\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u518d\u8868\u793a\u3057\u306a\u3044 +CategorizeGroupAction.fileCountHeader=\u6b21\u306e\u30ab\u30c6\u30b4\u30ea\u30fc\u306e\u30d5\u30a1\u30a4\u30eb\u306f\u81ea\u8eab\u306e\u30ab\u30c6\u30b4\u30ea\u30fc\u3092\u4e0a\u66f8\u304d\u3057\u307e\u3059: +# {0} - \u305d\u306e\u30ab\u30c6\u30b4\u30ea\u30fc\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u6570 +# {1} - \u30ab\u30c6\u30b4\u30ea\u30fc\u540d +CategorizeGroupAction.fileCountMessage={1} \u3092\u6301\u3064 {0} +CategorizeGroupAction.OverwriteButton.text=\u4e0a\u66f8\u304d +CTL_AddImage=\u30a4\u30e1\u30fc\u30b8/\u52d5\u753b\u3092\u8868\u793a +CTL_OpenAction=\u30a4\u30e1\u30fc\u30b8/\u52d5\u753b +# {0} - fileID +DeleteDrawableTagAction.deleteTag.alert=\u30d5\u30a1\u30a4\u30eb {0} \u3092\u30bf\u30b0\u306a\u3057\u306b\u3067\u304d\u307e\u305b\u3093\u3002 +DeleteDrawableTagAction.displayName=\u30d5\u30a1\u30a4\u30eb\u30bf\u30b0\u3092\u524a\u9664 +DeleteFollwUpTagAction.displayName=\u30d5\u30a9\u30ed\u30fc\u30a2\u30c3\u30d7\u30bf\u30b0\u3092\u524a\u9664 Forward.displayName=\u9032\u3080 -MediaViewImagePanel.externalViewerButton.text=\u5916\u90E8\u30D3\u30E5\u30FC\u30A2\u30FC\u3067\u958B\u304F -NextUnseenGroup.markGroupSeen=\u78BA\u8A8D\u6E08\u307F\u306E\u30B0\u30EB\u30FC\u30D7\u3092\u30DE\u30FC\u30AF\u3059\u308B -NextUnseenGroup.nextUnseenGroup=\u6B21\u306E\u672A\u78BA\u8A8D\u306E\u30B0\u30EB\u30FC\u30D7 -OpenAction.stale.confDlg.msg=\u30A4\u30E1\u30FC\u30B8\uFF0F\u30D3\u30C7\u30AA\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u304C\u6700\u65B0\u3067\u306F\u306A\u3044\u304B\u3082\u3057\u308C\u307E\u305B\u3093\u3002\u66F4\u65B0\u3057\u3066\u3001\u65B0\u305F\u306A\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u7D50\u679C\u3092\u78BA\u8A8D\u3057\u307E\u3059\u304B\uFF1F\n\u300C\u306F\u3044\u300D\u3092\u9078\u629E\u3059\u308C\u3070\u3001\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u66F4\u65B0\u3057\u3001\u65B0\u305F\u306A\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u3092\u78BA\u8A8D\u3059\u308B\u3053\u3068\u304C\u53EF\u80FD\u306B\u306A\u308A\u307E\u3059\u3002 -OpenAction.stale.confDlg.title=\u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC -OpenExternalViewerAction.displayName=\u5916\u90E8\u30D3\u30E5\u30FC\u30A2\u30FC -RedoAction.name=\u518D\u5B9F\u884C -UndoAction.displayName=\u53D6\u308A\u6D88\u3057 \ No newline at end of file +MediaViewImagePanel.externalViewerButton.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f +NextUnseenGroup.allGroupsSeen=\u3059\u3079\u3066\u306e\u30b0\u30eb\u30fc\u30d7\u304c\u8868\u793a\u3055\u308c\u307e\u3057\u305f +NextUnseenGroup.markGroupSeen=\u8868\u793a\u3057\u305f\u30b0\u30eb\u30fc\u30d7\u3092\u30de\u30fc\u30af\u3059\u308b +NextUnseenGroup.nextUnseenGroup=\u6b21\u306e\u975e\u8868\u793a\u30b0\u30eb\u30fc\u30d7 +OpenAction.dialogTitle=\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc +OpenAction.multiUserDialog.checkBox.text=\u3053\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u518d\u8868\u793a\u3057\u307e\u305b\u3093\u3002 +OpenAction.multiUserDialog.ContentText=\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u306f\u30b7\u30f3\u30b0\u30eb\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u3068\u306f\u7570\u306a\u308b\u65b9\u6cd5\u3067\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u306b\u5bfe\u3057\u3066\u66f4\u65b0\u3057\u307e\u3059\u3002\u9855\u8457:\n\nIf \u304a\u4f7f\u3044\u306e\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u30fc\u304c\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u5206\u6790\u4e2d\u3067\u3059\u3002\u30d5\u30a1\u30a4\u30eb\u304c\u5206\u6790\u3055\u308c\u308b\u3068(\u30cf\u30c3\u30b7\u30e5\u5316\u3001EXIF\u306a\u3069)\u3001\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u306e\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u66f4\u65b0\u3092\u53d6\u5f97\u3057\u307e\u3059\u3002\u3053\u308c\u306f\u30b7\u30f3\u30b0\u30eb\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u3068\u540c\u3058\u6319\u52d5\u3067\u3059\u3002\n\nIf \u6240\u5c5e\u5148\u306e\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30af\u30e9\u30b9\u30bf\u30fc\u306e\u5225\u306e\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u30fc\u304c\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u5206\u6790\u4e2d\u3067\u3059\u3002\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u3092\u8d77\u52d5\u3059\u308b\u3068\u3001\u305d\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u4e0a\u306e\u5225\u306e\u30d5\u30a1\u30a4\u30eb\u306e\u66f4\u65b0\u3092\u53d6\u5f97\u3057\u307e\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u305d\u306e\u4ed6\u306e\u30ce\u30fc\u30c9\u306e\u7d50\u679c\u306b\u57fa\u3065\u3044\u3066\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u518d\u69cb\u7bc9\u3055\u308c\u307e\u3059\u3002 +OpenAction.multiUserDialog.Header=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u306e\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc +OpenAction.noControllerDialog.header=\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u3092\u958b\u3051\u307e\u305b\u3093 +OpenAction.noControllerDialog.text=\u521d\u671f\u5316\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\n\u8a73\u7d30\u306f\u30ed\u30b0\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +OpenAction.notAnalyzedDlg.msg=\u8868\u793a\u5bfe\u8c61\u3068\u306a\u308b\u30a4\u30e1\u30fc\u30b8/\u52d5\u753b\u30d5\u30a1\u30a4\u30eb\u304c\u307e\u3060\u3042\u308a\u307e\u305b\u3093\u3002\nFileType\u307e\u305f\u306fEXIF\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +OpenAction.stale.confDlg.msg=\u30a4\u30e1\u30fc\u30b8/\u52d5\u753b\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u66f4\u65b0\u3057\u3066\u3055\u3089\u306a\u308b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u7d50\u679c\u3092\u30ea\u30c3\u30b9\u30f3\u3057\u307e\u3059\u304b?\n [\u306f\u3044] \u3092\u9078\u629e\u3059\u308b\u3068\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u66f4\u65b0\u3055\u308c\u3001\u3055\u3089\u306a\u308b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u30ea\u30c3\u30b9\u30f3\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002 +OpenAction.stale.confDlg.title=\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc +OpenExternalViewerAction.displayName=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc +RedoAction.name=\u3084\u308a\u76f4\u3057 +UndoAction.displayName=\u5143\u306b\u623b\u3059 diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/Bundle_ja.properties b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/Bundle_ja.properties index dca1145fd0..e611c05780 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/Bundle_ja.properties +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/Bundle_ja.properties @@ -1,23 +1,18 @@ -Category.five=CAT-5\uFF1A\u95A2\u9023\u7121\u3057 -Category.four=CAT-4\uFF1A\u898B\u672C\uFF0F\u6BD4\u8F03\u5BFE\u8C61\uFF08\u5185\u90E8\u4F7F\u7528\u306E\u307F\uFF09 -Category.one=CAT-1\uFF1A\u5150\u7AE5\u643E\u53D6\uFF08\u9055\u6CD5\uFF09 -Category.three=CAT-3\uFF1ACGI/\u30A2\u30CB\u30E1\u30FC\u30B7\u30E7\u30F3\uFF08\u5150\u7AE5\u643E\u53D6\u7684\u306A\uFF09 -Category.two=CAT-2\uFF1A\u5150\u7AE5\u643E\u53D6\uFF08\u5408\u6CD5\uFF0F\u5E74\u9F62\u4E0D\u660E\uFF09 -Category.zero=CAT-0\uFF1A\u30AB\u30C6\u30B4\u30E9\u30A4\u30BA\u3055\u308C\u3066\u3044\u306A\u3044 -DrawableAttribute.analyzed=\u89E3\u6790\u6E08\u307F -DrawableAttribute.cameraMake=\u30AB\u30E1\u30E9\u88FD\u9020\u8005 -DrawableAttribute.cameraModel=\u30AB\u30E1\u30E9\u578B\u756A -DrawableAttribute.category=\u30AB\u30C6\u30B4\u30EA\u30FC -DrawableAttribute.createdTime=\u4F5C\u6210\u65E5\u6642 -DrawableAttribute.hashSet=\u30CF\u30C3\u30B7\u30E5\u30BB\u30C3\u30C8 -DrawableAttribute.height=\u9AD8\u3055 -DrawableAttribute.intObjID=\u5185\u90E8\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8ID -DrawableAttribute.md5hash=MD\uFF15\u30CF\u30C3\u30B7\u30E5 -DrawableAttribute.mimeType=MIME\u30BF\u30A4\u30D7 -DrawableAttribute.modifiedTime=\u4FEE\u6B63\u65E5\u6642 -DrawableAttribute.name=\u540D\u524D -DrawableAttribute.path=\u30D1\u30B9 -DrawableAttribute.tags=\u30BF\u30B0 -DrawableAttribute.width=\u5E45 -DrawableTagsManager.followUp=\u30D5\u30A9\u30ED\u30FC\u30A2\u30C3\u30D7 -VideoFile.getMedia.progress=\u30C7\u30A3\u30B9\u30AF\u306B\u4E00\u6642\u30D5\u30A1\u30A4\u30EB\u3092\u66F8\u304D\u8FBC\u307F\u4E2D \ No newline at end of file +DrawableAttribute.analyzed=\u5206\u6790\u6e08\u307f +DrawableAttribute.cameraMake=\u30ab\u30e1\u30e9\u578b\u5f0f +DrawableAttribute.cameraModel=\u30ab\u30e1\u30e9\u30e2\u30c7\u30eb +DrawableAttribute.category=\u30ab\u30c6\u30b4\u30ea\u30fc +DrawableAttribute.createdTime=\u4f5c\u6210\u6e08\u307f\u6642\u523b +DrawableAttribute.hashSet=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8 +DrawableAttribute.height=\u9ad8\u3055 +DrawableAttribute.intObjID=\u5185\u90e8\u30aa\u30d6\u30b8\u30a7\u30af\u30c8ID +DrawableAttribute.md5hash=MD5\u30cf\u30c3\u30b7\u30e5 +DrawableAttribute.mimeType=MIME\u30bf\u30a4\u30d7 +DrawableAttribute.modifiedTime=\u4fee\u6b63\u6e08\u307f\u6642\u523b +DrawableAttribute.name=\u540d\u524d +DrawableAttribute.path=\u30d1\u30b9 +DrawableAttribute.tags=\u30bf\u30b0 +DrawableAttribute.width=\u5e45 +DrawableTagsManager.bookMark=\u30d6\u30c3\u30af\u30de\u30fc\u30af +DrawableTagsManager.followUp=\u30d5\u30a9\u30ed\u30fc\u30a2\u30c3\u30d7 +VideoFile.getMedia.progress=\u4e00\u6642\u30d5\u30a1\u30a4\u30eb\u306b\u30c7\u30a3\u30b9\u30af\u3092\u66f8\u304d\u8fbc\u307f\u4e2d\u3067\u3059 diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index e3310007fd..bae9894de9 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -49,7 +49,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; -import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.concurrent.GuardedBy; import javax.swing.SortOrder; @@ -80,128 +79,160 @@ import org.sleuthkit.datamodel.TskData.DbType; import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.VersionNumber; import org.sqlite.SQLiteJDBCLoader; +import java.util.stream.Collectors; /** - * Provides access to the drawables database and selected tables in the case + * Provides access to the image gallery database and selected tables in the case * database. */ public final class DrawableDB { private static final Logger logger = Logger.getLogger(DrawableDB.class.getName()); - //column name constants////////////////////// - private static final String ANALYZED = "analyzed"; //NON-NLS - - private static final String OBJ_ID = "obj_id"; //NON-NLS - - private static final String HASH_SET_NAME = "hash_set_name"; //NON-NLS - - private static final String GROUPS_TABLENAME = "image_gallery_groups"; //NON-NLS - private static final String GROUPS_SEEN_TABLENAME = "image_gallery_groups_seen"; //NON-NLS - - private static final String IG_DB_INFO_TABLE = "image_gallery_db_info"; - + /* + * Schema version management constants. + */ + private static final VersionNumber IG_STARTING_SCHEMA_VERSION = new VersionNumber(1, 0, 0); // Historical - DO NOT CHANGE + private static final VersionNumber IG_SCHEMA_VERSION = new VersionNumber(1, 2, 0); // Current schema version private static final String IG_SCHEMA_MAJOR_VERSION_KEY = "IG_SCHEMA_MAJOR_VERSION"; private static final String IG_SCHEMA_MINOR_VERSION_KEY = "IG_SCHEMA_MINOR_VERSION"; private static final String IG_CREATION_SCHEMA_MAJOR_VERSION_KEY = "IG_CREATION_SCHEMA_MAJOR_VERSION"; private static final String IG_CREATION_SCHEMA_MINOR_VERSION_KEY = "IG_CREATION_SCHEMA_MINOR_VERSION"; - - private static final VersionNumber IG_STARTING_SCHEMA_VERSION = new VersionNumber(1, 0, 0); // IG Schema Starting version - private static final VersionNumber IG_SCHEMA_VERSION = new VersionNumber(1, 1, 0); // IG Schema Current version - - private PreparedStatement insertHashSetStmt; + private static final String DB_INFO_TABLE_NAME = "image_gallery_db_info"; - private List preparedStatements = new ArrayList<>(); - - private PreparedStatement removeFileStmt; - - private PreparedStatement selectHashSetStmt; - - private PreparedStatement selectHashSetNamesStmt; - - private PreparedStatement insertHashHitStmt; - - private PreparedStatement removeHashHitStmt; - - private PreparedStatement updateDataSourceStmt; - - private PreparedStatement updateFileStmt; - private PreparedStatement insertFileStmt; - - private PreparedStatement pathGroupStmt; - - private PreparedStatement nameGroupStmt; - - private PreparedStatement created_timeGroupStmt; - - private PreparedStatement modified_timeGroupStmt; - - private PreparedStatement makeGroupStmt; - - private PreparedStatement modelGroupStmt; - - private PreparedStatement analyzedGroupStmt; - - private PreparedStatement hashSetGroupStmt; - - private PreparedStatement pathGroupFilterByDataSrcStmt; - - /** - * map from {@link DrawableAttribute} to the {@link PreparedStatement} that - * is used to select groups for that attribute + /* + * The image gallery stores data in both the case database and the image + * gallery database. The use of image gallery tables in the case database + * enables sharing of selected data between users of multi-user cases. This + * is necessary because the image gallery database is otherwise private to + * one node/machine. + * + * TODO: Consider refactoring to separate the image gallery database code + * from the case database code. */ + private static final String CASE_DB_GROUPS_TABLENAME = "image_gallery_groups"; //NON-NLS + private static final String CASE_DB_GROUPS_SEEN_TABLENAME = "image_gallery_groups_seen"; //NON-NLS + private final SleuthkitCase caseDb; + + /* + * The image gallery database is an SQLite database, so it has a local file + * path. For multi-user cases, there is a private image gallery database for + * each node/machine. + */ + private final Path dbPath; + + /* + * The write lock of a reentrant read-write lock is used to serialize access + * to the image gallery database. Empirically, this provides better + * performance than relying on internal SQLite locking. + */ + private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); //use fairness policy + private final Lock dbLock = rwLock.writeLock(); + @GuardedBy("dbLock") + private Connection con; + + /* + * Prepared statements. + */ + private List preparedStatements = new ArrayList<>(); + private PreparedStatement selectCountDataSourceIDs; + private PreparedStatement insertDataSourceStmt; + private PreparedStatement updateDataSourceStmt; + private PreparedStatement deleteDataSourceStmt; + private PreparedStatement insertFileStmt; + private PreparedStatement updateFileStmt; + private PreparedStatement deleteFileStmt; + private PreparedStatement insertHashSetStmt; + private PreparedStatement selectHashSetStmt; + private PreparedStatement selectHashSetNamesStmt; + private PreparedStatement insertHashHitStmt; + private PreparedStatement deleteHashHitStmt; + private PreparedStatement pathGroupStmt; // Not unused, used via collections below + private PreparedStatement nameGroupStmt; // Not unused, used via collections below + private PreparedStatement createdTimeGroupStmt; // Not unused, used via collections below + private PreparedStatement modifiedTimeGroupStmt; // Not unused, used via collections below + private PreparedStatement makeGroupStmt; // Not unused, used via collections below + private PreparedStatement modelGroupStmt; // Not unused, used via collections below + private PreparedStatement analyzedGroupStmt; // Not unused, used via collections below + private PreparedStatement hashSetGroupStmt; // Not unused, used via collections below + private PreparedStatement pathGroupFilterByDataSrcStmt; // Not unused, used via collections below private final Map, PreparedStatement> groupStatementMap = new HashMap<>(); private final Map, PreparedStatement> groupStatementFilterByDataSrcMap = new HashMap<>(); + /* + * Various caches are used to reduce the need for database queries. + */ + private final Cache groupCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(); + private final Cache, Boolean> groupSeenCache = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).build(); + private final Set hasTagsCache = new HashSet<>(); // Object IDs of files with tags + private final Set hasHashHitsCache = new HashSet<>(); // Object IDs of files with hash set hits + private final Set hasExifDataCache = new HashSet<>(); // Object IDs of files with EXIF data (make/model) + private final Object cacheLock = new Object(); + private boolean areCachesLoaded = false; + private int cacheBuildCount = 0; // Number of tasks that requested the caches be built + + /* + * This class is coupled to the image gallery controller and group manager. + * + * TODO: It would be better to reduce the coupling so that the controller + * and group manager call this class, but this class does not call them. + */ + private final ImageGalleryController controller; private final GroupManager groupManager; - private final Path dbPath; - - @GuardedBy("DBLock") - private Connection con; - - private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); //use fairness policy - - private final Lock DBLock = rwLock.writeLock(); // Currently serializing everything with one database connection - - // caches to make inserts / updates faster - private Cache groupCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(); - private final Cache, Boolean> groupSeenCache = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).build(); - private final Object cacheLock = new Object(); // protects access to the below cache-related objects - private boolean areCachesLoaded = false; // if true, the below caches contain valid data - private Set hasTagCache = new HashSet<>(); // contains obj id of files with tags - private Set hasHashCache = new HashSet<>(); // obj id of files with hash set hits - private Set hasExifCache = new HashSet<>(); // obj id of files with EXIF (make/model) - private int cacheBuildCount = 0; // number of tasks taht requested the caches be built - - static {//make sure sqlite driver is loaded // possibly redundant + /* + * Make sure the SQLite JDBC driver is loaded. + */ + static { try { Class.forName("org.sqlite.JDBC"); } catch (ClassNotFoundException ex) { logger.log(Level.SEVERE, "Failed to load sqlite JDBC driver", ex); //NON-NLS } } - private final SleuthkitCase tskCase; - private final ImageGalleryController controller; /** - * Enum to track Image gallery db rebuild status for a data source + * Enum for tracking the status of the image gallery database with respect + * to the data sources in the case. * - * DO NOT add in the middle. + * IMPORTANT: ADD NEW STATUSES TO THE END OF THE LIST + * + * TODO: I'm (RC) not sure why this is required, it looks like the enum + * element names are stored in the image gallery database. Are the raw + * cardinal values used somewhere? */ public enum DrawableDbBuildStatusEnum { - UNKNOWN, /// no known status - not yet analyzed - IN_PROGRESS, /// ingest or db rebuild is in progress - COMPLETE, /// At least one file in the data source had a MIME type. Ingest filters may have been applied. - REBUILT_STALE; /// data source was rebuilt, but MIME types were missing during rebuild + /** + * The data source has been added to the database, but no other data + * pertaining to it has been added. + */ + UNKNOWN, + /** + * Analyis (an ingest job or image gallery database rebuild) for the + * data source is in progress. + */ + IN_PROGRESS, + /** + * Analyis (an ingest job or image gallery database rebuild) for the + * data source has been completed and at least one file in the data + * source has a MIME type (ingest filters may have been applied, so some + * files may not have been typed). + */ + COMPLETE, + /** + * Analyis (an ingest job or image gallery database rebuild) for the + * data source has been completed, but the files for the data source + * were not assigned a MIME type (file typing was not enabled). + */ + REBUILT_STALE; } private void dbWriteLock() { - DBLock.lock(); + dbLock.lock(); } private void dbWriteUnlock() { - DBLock.unlock(); + dbLock.unlock(); } /** @@ -222,13 +253,13 @@ public final class DrawableDB { private DrawableDB(Path dbPath, ImageGalleryController controller) throws IOException, SQLException, TskCoreException { this.dbPath = dbPath; this.controller = controller; - tskCase = this.controller.getCaseDatabase(); + caseDb = this.controller.getCaseDatabase(); groupManager = this.controller.getGroupManager(); Files.createDirectories(this.dbPath.getParent()); dbWriteLock(); try { con = DriverManager.getConnection("jdbc:sqlite:" + dbPath.toString()); //NON-NLS - if (!initializeDBSchema() || !upgradeDBSchema() || !prepareStatements() || !initializeStandardGroups() || !initializeImageList()) { + if (!initializeDBSchema() || !upgradeDBSchema() || !prepareStatements() || !initializeStandardGroups() || !removeDeletedDataSources() || !initializeImageList()) { close(); throw new TskCoreException("Failed to initialize drawables database for Image Gallery use"); //NON-NLS } @@ -239,31 +270,27 @@ public final class DrawableDB { private boolean prepareStatements() { try { - updateFileStmt = prepareStatement( - "INSERT OR REPLACE INTO drawable_files (obj_id, data_source_obj_id, path, name, created_time, modified_time, make, model, analyzed) " //NON-NLS - + "VALUES (?,?,?,?,?,?,?,?,?)"); //NON-NLS - insertFileStmt = prepareStatement( - "INSERT OR IGNORE INTO drawable_files (obj_id, data_source_obj_id, path, name, created_time, modified_time, make, model, analyzed) " //NON-NLS - + "VALUES (?,?,?,?,?,?,?,?,?)"); //NON-NLS - updateDataSourceStmt = prepareStatement( - "INSERT OR REPLACE INTO datasources (ds_obj_id, drawable_db_build_status) " //NON-NLS - + " VALUES (?,?)"); //NON-NLS - removeFileStmt = prepareStatement("DELETE FROM drawable_files WHERE obj_id = ?"); //NON-NLS + insertDataSourceStmt = prepareStatement("INSERT INTO datasources (ds_obj_id, drawable_db_build_status) VALUES (?,?) ON CONFLICT (ds_obj_id) DO UPDATE SET drawable_db_build_status = ?"); //NON-NLS + deleteDataSourceStmt = prepareStatement("DELETE FROM datasources where ds_obj_id = ?"); //NON-NLS + insertFileStmt = prepareStatement("INSERT OR IGNORE INTO drawable_files (obj_id, data_source_obj_id, path, name, created_time, modified_time, make, model, analyzed) VALUES (?,?,?,?,?,?,?,?,?)"); //NON-NLS + updateFileStmt = prepareStatement("INSERT INTO drawable_files (obj_id, data_source_obj_id, path, name, created_time, modified_time, make, model, analyzed) VALUES (?,?,?,?,?,?,?,?,?) ON CONFLICT (obj_id) DO UPDATE SET data_source_obj_id = ?, path = ?, name = ?, created_time = ?, modified_time = ?, make = ?, model = ?, analyzed = ?"); //NON-NLS + deleteFileStmt = prepareStatement("DELETE FROM drawable_files WHERE obj_id = ?"); //NON-NLS + insertHashSetStmt = prepareStatement("INSERT OR IGNORE INTO hash_sets (hash_set_name) VALUES (?)"); //NON-NLS + selectHashSetStmt = prepareStatement("SELECT hash_set_id FROM hash_sets WHERE hash_set_name = ?"); //NON-NLS + selectHashSetNamesStmt = prepareStatement("SELECT DISTINCT hash_set_name FROM hash_sets"); //NON-NLS + insertHashHitStmt = prepareStatement("INSERT OR IGNORE INTO hash_set_hits (hash_set_id, obj_id) VALUES (?,?)"); //NON-NLS + deleteHashHitStmt = prepareStatement("DELETE FROM hash_set_hits WHERE obj_id = ?"); //NON-NLS pathGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE path = ? ", DrawableAttribute.PATH); //NON-NLS nameGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE name = ? ", DrawableAttribute.NAME); //NON-NLS - created_timeGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE created_time = ? ", DrawableAttribute.CREATED_TIME); //NON-NLS - modified_timeGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE modified_time = ? ", DrawableAttribute.MODIFIED_TIME); //NON-NLS + createdTimeGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE created_time = ? ", DrawableAttribute.CREATED_TIME); //NON-NLS + modifiedTimeGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE modified_time = ? ", DrawableAttribute.MODIFIED_TIME); //NON-NLS makeGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE make = ? ", DrawableAttribute.MAKE); //NON-NLS modelGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE model = ? ", DrawableAttribute.MODEL); //NON-NLS analyzedGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE analyzed = ?", DrawableAttribute.ANALYZED); //NON-NLS hashSetGroupStmt = prepareStatement("SELECT drawable_files.obj_id AS obj_id, analyzed FROM drawable_files , hash_sets , hash_set_hits WHERE drawable_files.obj_id = hash_set_hits.obj_id AND hash_sets.hash_set_id = hash_set_hits.hash_set_id AND hash_sets.hash_set_name = ?", DrawableAttribute.HASHSET); //NON-NLS pathGroupFilterByDataSrcStmt = prepareFilterByDataSrcStatement("SELECT obj_id , analyzed FROM drawable_files WHERE path = ? AND data_source_obj_id = ?", DrawableAttribute.PATH); - selectHashSetNamesStmt = prepareStatement("SELECT DISTINCT hash_set_name FROM hash_sets"); //NON-NLS - insertHashSetStmt = prepareStatement("INSERT OR IGNORE INTO hash_sets (hash_set_name) VALUES (?)"); //NON-NLS - selectHashSetStmt = prepareStatement("SELECT hash_set_id FROM hash_sets WHERE hash_set_name = ?"); //NON-NLS - insertHashHitStmt = prepareStatement("INSERT OR IGNORE INTO hash_set_hits (hash_set_id, obj_id) VALUES (?,?)"); //NON-NLS - removeHashHitStmt = prepareStatement("DELETE FROM hash_set_hits WHERE obj_id = ?"); //NON-NLS return true; + } catch (TskCoreException | SQLException ex) { logger.log(Level.SEVERE, "Failed to prepare all statements", ex); //NON-NLS return false; @@ -273,7 +300,7 @@ public final class DrawableDB { private boolean initializeStandardGroups() { CaseDbTransaction caseDbTransaction = null; try { - caseDbTransaction = tskCase.beginTransaction(); + caseDbTransaction = caseDb.beginTransaction(); for (DhsImageCategory cat : DhsImageCategory.values()) { insertGroup(cat.getDisplayName(), DrawableAttribute.CATEGORY, caseDbTransaction); } @@ -371,6 +398,62 @@ public final class DrawableDB { } } + /** + * Removes any data sources from the local drawables database that have been + * deleted from the case database. This is necessary for multi-user cases + * where the case database is shared, but each user has his or her own local + * drawables database and may not have had the case open when a data source + * was deleted. + * + * @return True on success, false on failure. + */ + private boolean removeDeletedDataSources() { + dbWriteLock(); + try (SleuthkitCase.CaseDbQuery caseDbQuery = caseDb.executeQuery("SELECT obj_id FROM data_source_info"); //NON-NLS + Statement drawablesDbStmt = con.createStatement()) { + /* + * Get the data source object IDs from the case database. + */ + ResultSet caseDbResults = caseDbQuery.getResultSet(); + Set currentDataSourceObjIDs = new HashSet<>(); + while (caseDbResults.next()) { + currentDataSourceObjIDs.add(caseDbResults.getLong(1)); + } + + /* + * Get the data source object IDs from the drawables database and + * determine which ones, if any, have been deleted from the case + * database. + */ + List staleDataSourceObjIDs = new ArrayList<>(); + try (ResultSet drawablesDbResults = drawablesDbStmt.executeQuery("SELECT ds_obj_id FROM datasources")) { //NON-NLS + while (drawablesDbResults.next()) { + long dataSourceObjID = drawablesDbResults.getLong(1); + if (!currentDataSourceObjIDs.contains(dataSourceObjID)) { + staleDataSourceObjIDs.add(dataSourceObjID); + } + } + } + + /* + * Delete the surplus data sources from this local drawables + * database. The delete cascades. + */ + if (!staleDataSourceObjIDs.isEmpty()) { + String deleteCommand = "DELETE FROM datasources where ds_obj_id IN (" + StringUtils.join(staleDataSourceObjIDs, ',') + ")"; //NON-NLS + drawablesDbStmt.execute(deleteCommand); + } + return true; + + } catch (TskCoreException | SQLException ex) { + logger.log(Level.SEVERE, "Failed to remove deleted data sources from drawables database", ex); //NON-NLS + return false; + + } finally { + dbWriteUnlock(); + } + } + /** * Public factory method. Creates and opens a connection to a new database * * at the given path. If there is already a db at the path, it is checked @@ -404,11 +487,12 @@ public final class DrawableDB { /** * Checks if the specified table exists in Drawable DB - * + * * @param tableName table to check + * * @return true if the table exists in the database - * - * @throws SQLException + * + * @throws SQLException */ private boolean doesTableExist(String tableName) throws SQLException { ResultSet tableQueryResults = null; @@ -421,15 +505,14 @@ public final class DrawableDB { break; } } - } - finally { + } finally { if (tableQueryResults != null) { tableQueryResults.close(); - } + } } return tableExists; } - + private static void deleteDatabaseIfOlderVersion(Path dbPath) throws SQLException, IOException { if (Files.exists(dbPath)) { boolean hasDrawableFilesTable = false; @@ -475,7 +558,7 @@ public final class DrawableDB { //allow to query while in transaction - no need read locks statement.execute("PRAGMA read_uncommitted = True;"); //NON-NLS - //TODO: do we need this? + //used for data source deletion statement.execute("PRAGMA foreign_keys = ON"); //NON-NLS //TODO: test this @@ -515,7 +598,7 @@ public final class DrawableDB { dbWriteLock(); try { boolean drawableDbTablesExist = true; - + if (isClosed()) { logger.log(Level.SEVERE, "The drawables database is closed"); //NON-NLS return false; @@ -532,31 +615,31 @@ public final class DrawableDB { * Create tables in the drawables database. */ try (Statement stmt = con.createStatement()) { - + // Check if the database is new or an existing database drawableDbTablesExist = doesTableExist("drawable_files"); - if (false == doesTableExist(IG_DB_INFO_TABLE)) { + if (false == doesTableExist(DB_INFO_TABLE_NAME)) { try { - VersionNumber ig_creation_schema_version = drawableDbTablesExist - ? IG_STARTING_SCHEMA_VERSION - : IG_SCHEMA_VERSION; - - stmt.execute("CREATE TABLE IF NOT EXISTS " + IG_DB_INFO_TABLE + " (name TEXT PRIMARY KEY, value TEXT NOT NULL)"); - + VersionNumber ig_creation_schema_version = drawableDbTablesExist + ? IG_STARTING_SCHEMA_VERSION + : IG_SCHEMA_VERSION; + + stmt.execute("CREATE TABLE IF NOT EXISTS " + DB_INFO_TABLE_NAME + " (name TEXT PRIMARY KEY, value TEXT NOT NULL)"); + // backfill creation schema ver - stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_CREATION_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor() )); - stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_CREATION_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor() )); - + stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", DB_INFO_TABLE_NAME, IG_CREATION_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor())); + stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", DB_INFO_TABLE_NAME, IG_CREATION_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor())); + // set current schema ver: at DB initialization - current version is same as starting version - stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor() )); - stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor() )); - + stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", DB_INFO_TABLE_NAME, IG_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor())); + stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", DB_INFO_TABLE_NAME, IG_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor())); + } catch (SQLException ex) { logger.log(Level.SEVERE, "Failed to create ig_db_info table", ex); //NON-NLS return false; } } - + try { String sql = "CREATE TABLE IF NOT EXISTS datasources " //NON-NLS + "( id INTEGER PRIMARY KEY, " //NON-NLS @@ -578,7 +661,8 @@ public final class DrawableDB { + " modified_time integer, " //NON-NLS + " make TEXT DEFAULT NULL, " //NON-NLS + " model TEXT DEFAULT NULL, " //NON-NLS - + " analyzed integer DEFAULT 0)"; //NON-NLS + + " analyzed integer DEFAULT 0, " //NON-NLS + + " FOREIGN KEY (data_source_obj_id) REFERENCES datasources(ds_obj_id) ON DELETE CASCADE)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { logger.log(Level.SEVERE, "Failed to create drawable_files table", ex); //NON-NLS @@ -598,8 +682,9 @@ public final class DrawableDB { try { String sql = "CREATE TABLE if not exists hash_set_hits " //NON-NLS + "(hash_set_id INTEGER REFERENCES hash_sets(hash_set_id) not null, " //NON-NLS - + " obj_id BIGINT REFERENCES drawable_files(obj_id) not null, " //NON-NLS - + " PRIMARY KEY (hash_set_id, obj_id))"; //NON-NLS + + " obj_id BIGINT NOT NULL, " //NON-NLS + + " PRIMARY KEY (hash_set_id, obj_id), " //NON-NLS + + " FOREIGN KEY (obj_id) REFERENCES drawable_files(obj_id) ON DELETE CASCADE)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { logger.log(Level.SEVERE, "Failed to create hash_set_hits table", ex); //NON-NLS @@ -648,46 +733,46 @@ public final class DrawableDB { /* * Create tables in the case database. */ - String autogenKeyType = (DbType.POSTGRESQL == tskCase.getDatabaseType()) ? "BIGSERIAL" : "INTEGER"; - + String autogenKeyType = (DbType.POSTGRESQL == caseDb.getDatabaseType()) ? "BIGSERIAL" : "INTEGER"; + try { - boolean caseDbTablesExist = tskCase.getCaseDbAccessManager().tableExists(GROUPS_TABLENAME); - VersionNumber ig_creation_schema_version = caseDbTablesExist - ? IG_STARTING_SCHEMA_VERSION - : IG_SCHEMA_VERSION; - - String tableSchema = "( id " + autogenKeyType + " PRIMARY KEY, " - + " name TEXT UNIQUE NOT NULL," - + " value TEXT NOT NULL )"; - tskCase.getCaseDbAccessManager().createTable(IG_DB_INFO_TABLE, tableSchema); - - // backfill creation version - String creationMajorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_CREATION_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor()); - String creationMinorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_CREATION_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor()); - - // set current version - at the onset, current version is same as creation version - String currentMajorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor()); - String currentMinorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor()); - - if (DbType.POSTGRESQL == tskCase.getDatabaseType()) { - creationMajorVerSQL += " ON CONFLICT DO NOTHING "; - creationMinorVerSQL += " ON CONFLICT DO NOTHING "; - - currentMajorVerSQL += " ON CONFLICT DO NOTHING "; - currentMinorVerSQL += " ON CONFLICT DO NOTHING "; - } - - tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, creationMajorVerSQL); - tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, creationMinorVerSQL); - - tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, currentMajorVerSQL); - tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, currentMinorVerSQL); - - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Failed to create ig_db_info table in Case database", ex); //NON-NLS - return false; - } - + boolean caseDbTablesExist = caseDb.getCaseDbAccessManager().tableExists(CASE_DB_GROUPS_TABLENAME); + VersionNumber ig_creation_schema_version = caseDbTablesExist + ? IG_STARTING_SCHEMA_VERSION + : IG_SCHEMA_VERSION; + + String tableSchema = "( id " + autogenKeyType + " PRIMARY KEY, " + + " name TEXT UNIQUE NOT NULL," + + " value TEXT NOT NULL )"; + caseDb.getCaseDbAccessManager().createTable(DB_INFO_TABLE_NAME, tableSchema); + + // backfill creation version + String creationMajorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_CREATION_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor()); + String creationMinorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_CREATION_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor()); + + // set current version - at the onset, current version is same as creation version + String currentMajorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor()); + String currentMinorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor()); + + if (DbType.POSTGRESQL == caseDb.getDatabaseType()) { + creationMajorVerSQL += " ON CONFLICT DO NOTHING "; + creationMinorVerSQL += " ON CONFLICT DO NOTHING "; + + currentMajorVerSQL += " ON CONFLICT DO NOTHING "; + currentMinorVerSQL += " ON CONFLICT DO NOTHING "; + } + + caseDb.getCaseDbAccessManager().insert(DB_INFO_TABLE_NAME, creationMajorVerSQL); + caseDb.getCaseDbAccessManager().insert(DB_INFO_TABLE_NAME, creationMinorVerSQL); + + caseDb.getCaseDbAccessManager().insert(DB_INFO_TABLE_NAME, currentMajorVerSQL); + caseDb.getCaseDbAccessManager().insert(DB_INFO_TABLE_NAME, currentMinorVerSQL); + + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to create ig_db_info table in Case database", ex); //NON-NLS + return false; + } + try { String tableSchema = "( group_id " + autogenKeyType + " PRIMARY KEY, " //NON-NLS @@ -697,9 +782,9 @@ public final class DrawableDB { + " is_analyzed integer DEFAULT 0, " + " UNIQUE(data_source_obj_id, value, attribute) )"; //NON-NLS - tskCase.getCaseDbAccessManager().createTable(GROUPS_TABLENAME, tableSchema); + caseDb.getCaseDbAccessManager().createTable(CASE_DB_GROUPS_TABLENAME, tableSchema); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Failed to create %s table in case database", GROUPS_TABLENAME), ex); //NON-NLS + logger.log(Level.SEVERE, String.format("Failed to create %s table in case database", CASE_DB_GROUPS_TABLENAME), ex); //NON-NLS return false; } try { @@ -710,13 +795,13 @@ public final class DrawableDB { + " examiner_id integer not null, " //NON-NLS + " seen integer DEFAULT 0, " //NON-NLS + " UNIQUE(group_id, examiner_id)," - + " FOREIGN KEY(group_id) REFERENCES " + GROUPS_TABLENAME + "(group_id)," + + " FOREIGN KEY(group_id) REFERENCES " + CASE_DB_GROUPS_TABLENAME + "(group_id) ON DELETE CASCADE," + " FOREIGN KEY(examiner_id) REFERENCES tsk_examiners(examiner_id)" + " )"; //NON-NLS - tskCase.getCaseDbAccessManager().createTable(GROUPS_SEEN_TABLENAME, tableSchema); + caseDb.getCaseDbAccessManager().createTable(CASE_DB_GROUPS_SEEN_TABLENAME, tableSchema); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Failed to create %s table in case database", GROUPS_SEEN_TABLENAME), ex); //NON-NLS + logger.log(Level.SEVERE, String.format("Failed to create %s table in case database", CASE_DB_GROUPS_SEEN_TABLENAME), ex); //NON-NLS return false; } @@ -729,20 +814,21 @@ public final class DrawableDB { /** * Gets the Schema version from DrawableDB - * + * * @return image gallery schema version in DrawableDB + * * @throws SQLException - * @throws TskCoreException + * @throws TskCoreException */ private VersionNumber getDrawableDbIgSchemaVersion() throws SQLException, TskCoreException { - + Statement statement = con.createStatement(); ResultSet resultSet = null; - try { + try { int majorVersion = -1; String majorVersionStr = null; - resultSet = statement.executeQuery(String.format("SELECT value FROM %s WHERE name='%s'", IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY)); + resultSet = statement.executeQuery(String.format("SELECT value FROM %s WHERE name='%s'", DB_INFO_TABLE_NAME, IG_SCHEMA_MAJOR_VERSION_KEY)); if (resultSet.next()) { majorVersionStr = resultSet.getString("value"); try { @@ -751,12 +837,12 @@ public final class DrawableDB { throw new TskCoreException("Bad value for schema major version = " + majorVersionStr, ex); } } else { - throw new TskCoreException("Failed to read schema major version from ig_db_info table"); + throw new TskCoreException("Failed to read schema major version from ig_db_info table"); } - + int minorVersion = -1; String minorVersionStr = null; - resultSet = statement.executeQuery(String.format("SELECT value FROM %s WHERE name='%s'", IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY)); + resultSet = statement.executeQuery(String.format("SELECT value FROM %s WHERE name='%s'", DB_INFO_TABLE_NAME, IG_SCHEMA_MINOR_VERSION_KEY)); if (resultSet.next()) { minorVersionStr = resultSet.getString("value"); try { @@ -768,9 +854,8 @@ public final class DrawableDB { throw new TskCoreException("Failed to read schema minor version from ig_db_info table"); } - return new VersionNumber(majorVersion, minorVersion, 0 ); - } - finally { + return new VersionNumber(majorVersion, minorVersion, 0); + } finally { if (resultSet != null) { resultSet.close(); } @@ -782,27 +867,28 @@ public final class DrawableDB { /** * Gets the ImageGallery schema version from CaseDB - * + * * @return image gallery schema version in CaseDB + * * @throws SQLException - * @throws TskCoreException + * @throws TskCoreException */ private VersionNumber getCaseDbIgSchemaVersion() throws TskCoreException { - + // Callback to process result of get version query class GetSchemaVersionQueryResultProcessor implements CaseDbAccessQueryCallback { private int version = -1; - - int getVersion() { + + int getVersion() { return version; } - + @Override public void process(ResultSet resultSet) { - try { - if (resultSet.next()) { - String versionStr = resultSet.getString("value"); + try { + if (resultSet.next()) { + String versionStr = resultSet.getString("value"); try { version = Integer.parseInt(versionStr); } catch (NumberFormatException ex) { @@ -811,100 +897,96 @@ public final class DrawableDB { } else { logger.log(Level.SEVERE, "Failed to get version"); } - } - catch (SQLException ex) { + } catch (SQLException ex) { logger.log(Level.SEVERE, "Failed to get version", ex); //NON-NLS } } } - + GetSchemaVersionQueryResultProcessor majorVersionResultProcessor = new GetSchemaVersionQueryResultProcessor(); GetSchemaVersionQueryResultProcessor minorVersionResultProcessor = new GetSchemaVersionQueryResultProcessor(); - - String versionQueryTemplate = "value FROM %s WHERE name = \'%s\' "; - tskCase.getCaseDbAccessManager().select(String.format(versionQueryTemplate, IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY), majorVersionResultProcessor); - tskCase.getCaseDbAccessManager().select(String.format(versionQueryTemplate, IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY), minorVersionResultProcessor); - + + String versionQueryTemplate = "value FROM %s WHERE name = \'%s\' "; + caseDb.getCaseDbAccessManager().select(String.format(versionQueryTemplate, DB_INFO_TABLE_NAME, IG_SCHEMA_MAJOR_VERSION_KEY), majorVersionResultProcessor); + caseDb.getCaseDbAccessManager().select(String.format(versionQueryTemplate, DB_INFO_TABLE_NAME, IG_SCHEMA_MINOR_VERSION_KEY), minorVersionResultProcessor); + return new VersionNumber(majorVersionResultProcessor.getVersion(), minorVersionResultProcessor.getVersion(), 0); } - + /** * Updates the IG schema version in the Drawable DB - * - * @param version new version number + * + * @param version new version number * @param transaction transaction under which the update happens - * + * * @throws SQLException */ private void updateDrawableDbIgSchemaVersion(VersionNumber version, DrawableTransaction transaction) throws SQLException, TskCoreException { - + if (transaction == null) { - throw new TskCoreException("Schema version update must be done in a transaction"); + throw new TskCoreException("Schema version update must be done in a transaction"); } - + dbWriteLock(); try { Statement statement = con.createStatement(); - + // update schema version - statement.execute(String.format("UPDATE %s SET value = '%s' WHERE name = '%s'", IG_DB_INFO_TABLE, version.getMajor(), IG_SCHEMA_MAJOR_VERSION_KEY )); - statement.execute(String.format("UPDATE %s SET value = '%s' WHERE name = '%s'", IG_DB_INFO_TABLE, version.getMinor(), IG_SCHEMA_MINOR_VERSION_KEY )); - + statement.execute(String.format("UPDATE %s SET value = '%s' WHERE name = '%s'", DB_INFO_TABLE_NAME, version.getMajor(), IG_SCHEMA_MAJOR_VERSION_KEY)); + statement.execute(String.format("UPDATE %s SET value = '%s' WHERE name = '%s'", DB_INFO_TABLE_NAME, version.getMinor(), IG_SCHEMA_MINOR_VERSION_KEY)); + statement.close(); - } - finally { + } finally { dbWriteUnlock(); } } - + /** * Updates the IG schema version in CaseDB - * - * @param version new version number + * + * @param version new version number * @param caseDbTransaction transaction to use to update the CaseDB - * + * * @throws SQLException */ private void updateCaseDbIgSchemaVersion(VersionNumber version, CaseDbTransaction caseDbTransaction) throws TskCoreException { - + String updateSQLTemplate = " SET value = %s WHERE name = '%s' "; - tskCase.getCaseDbAccessManager().update(IG_DB_INFO_TABLE, String.format(updateSQLTemplate, version.getMajor(), IG_SCHEMA_MAJOR_VERSION_KEY), caseDbTransaction); - tskCase.getCaseDbAccessManager().update(IG_DB_INFO_TABLE, String.format(updateSQLTemplate, version.getMinor(), IG_SCHEMA_MINOR_VERSION_KEY), caseDbTransaction); + caseDb.getCaseDbAccessManager().update(DB_INFO_TABLE_NAME, String.format(updateSQLTemplate, version.getMajor(), IG_SCHEMA_MAJOR_VERSION_KEY), caseDbTransaction); + caseDb.getCaseDbAccessManager().update(DB_INFO_TABLE_NAME, String.format(updateSQLTemplate, version.getMinor(), IG_SCHEMA_MINOR_VERSION_KEY), caseDbTransaction); } - - + /** * Upgrades the DB schema. * * @return true if the upgrade is successful - * + * * @throws SQLException - * + * */ private boolean upgradeDBSchema() throws TskCoreException, SQLException { - + // Read current version from the DBs - VersionNumber drawableDbIgSchemaVersion = getDrawableDbIgSchemaVersion(); + VersionNumber drawableDbIgSchemaVersion = getDrawableDbIgSchemaVersion(); VersionNumber caseDbIgSchemaVersion = getCaseDbIgSchemaVersion(); // Upgrade Schema in both DrawableDB and CaseDB - CaseDbTransaction caseDbTransaction = tskCase.beginTransaction(); + CaseDbTransaction caseDbTransaction = caseDb.beginTransaction(); DrawableTransaction transaction = beginTransaction(); - + try { caseDbIgSchemaVersion = upgradeCaseDbIgSchema1dot0TO1dot1(caseDbIgSchemaVersion, caseDbTransaction); drawableDbIgSchemaVersion = upgradeDrawableDbIgSchema1dot0TO1dot1(drawableDbIgSchemaVersion, transaction); // update the versions in the tables - updateCaseDbIgSchemaVersion(caseDbIgSchemaVersion, caseDbTransaction ); - updateDrawableDbIgSchemaVersion(drawableDbIgSchemaVersion, transaction); - + updateCaseDbIgSchemaVersion(caseDbIgSchemaVersion, caseDbTransaction); + updateDrawableDbIgSchemaVersion(drawableDbIgSchemaVersion, transaction); + caseDbTransaction.commit(); caseDbTransaction = null; commitTransaction(transaction, false); transaction = null; - } - catch (TskCoreException | SQLException ex) { + } catch (TskCoreException | SQLException ex) { if (null != caseDbTransaction) { try { caseDbTransaction.rollback(); @@ -921,57 +1003,59 @@ public final class DrawableDB { } throw ex; } - return true; + return true; } - + /** - * Upgrades IG tables in CaseDB from 1.0 to 1.1 - * Does nothing if the incoming version is not 1.0 - * - * @param currVersion version to upgrade from + * Upgrades IG tables in CaseDB from 1.0 to 1.1 Does nothing if the incoming + * version is not 1.0 + * + * @param currVersion version to upgrade from * @param caseDbTransaction transaction to use for all updates - * + * * @return new version number - * @throws TskCoreException + * + * @throws TskCoreException */ - private VersionNumber upgradeCaseDbIgSchema1dot0TO1dot1(VersionNumber currVersion, CaseDbTransaction caseDbTransaction ) throws TskCoreException { - + private VersionNumber upgradeCaseDbIgSchema1dot0TO1dot1(VersionNumber currVersion, CaseDbTransaction caseDbTransaction) throws TskCoreException { + // Upgrade if current version is 1.0 // or 1.1 - a bug in versioning alllowed some databases to be versioned as 1.1 without the actual corresponding upgrade. This allows such databases to be fixed, if needed. - if (!(currVersion.getMajor() == 1 && - (currVersion.getMinor() == 0 || currVersion.getMinor() == 1))) { + if (!(currVersion.getMajor() == 1 + && (currVersion.getMinor() == 0 || currVersion.getMinor() == 1))) { return currVersion; } - + // Add a 'is_analyzed' column to groups table in CaseDB String alterSQL = " ADD COLUMN is_analyzed integer DEFAULT 1 "; //NON-NLS - if (false == tskCase.getCaseDbAccessManager().columnExists(GROUPS_TABLENAME, "is_analyzed", caseDbTransaction )) { - tskCase.getCaseDbAccessManager().alterTable(GROUPS_TABLENAME, alterSQL, caseDbTransaction); - } - return new VersionNumber(1,1,0); + if (false == caseDb.getCaseDbAccessManager().columnExists(CASE_DB_GROUPS_TABLENAME, "is_analyzed", caseDbTransaction)) { + caseDb.getCaseDbAccessManager().alterTable(CASE_DB_GROUPS_TABLENAME, alterSQL, caseDbTransaction); + } + return new VersionNumber(1, 1, 0); } - + /** - * Upgrades IG tables in DrawableDB from 1.0 to 1.1 - * Does nothing if the incoming version is not 1.0 - * - * @param currVersion version to upgrade from + * Upgrades IG tables in DrawableDB from 1.0 to 1.1 Does nothing if the + * incoming version is not 1.0 + * + * @param currVersion version to upgrade from * @param transaction transaction to use for all updates - * + * * @return new version number - * @throws TskCoreException + * + * @throws TskCoreException */ - private VersionNumber upgradeDrawableDbIgSchema1dot0TO1dot1(VersionNumber currVersion, DrawableTransaction transaction ) throws TskCoreException { - - if (currVersion.getMajor() != 1 || - currVersion.getMinor() != 0) { + private VersionNumber upgradeDrawableDbIgSchema1dot0TO1dot1(VersionNumber currVersion, DrawableTransaction transaction) throws TskCoreException { + + if (currVersion.getMajor() != 1 + || currVersion.getMinor() != 0) { return currVersion; } - + // There are no changes in DrawableDB schema in 1.0 -> 1.1 - return new VersionNumber(1,1,0); + return new VersionNumber(1, 1, 0); } - + @Override protected void finalize() throws Throwable { /* @@ -1043,7 +1127,7 @@ public final class DrawableDB { */ Set getHashSetsForFile(long fileID) throws TskCoreException { Set hashNames = new HashSet<>(); - ArrayList artifacts = tskCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, fileID); + ArrayList artifacts = caseDb.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, fileID); for (BlackboardArtifact a : artifacts) { BlackboardAttribute attribute = a.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); @@ -1065,7 +1149,7 @@ public final class DrawableDB { dbWriteLock(); try (ResultSet rs = selectHashSetNamesStmt.executeQuery();) { while (rs.next()) { - names.add(rs.getString(HASH_SET_NAME)); + names.add(rs.getString("hash_set_name")); } } catch (SQLException sQLException) { logger.log(Level.WARNING, "failed to get hash set names", sQLException); //NON-NLS @@ -1077,7 +1161,7 @@ public final class DrawableDB { static private String getGroupIdQuery(GroupKey groupKey) { // query to find the group id from attribute/value - return String.format(" SELECT group_id FROM " + GROUPS_TABLENAME + return String.format(" SELECT group_id FROM " + CASE_DB_GROUPS_TABLENAME + " WHERE attribute = \'%s\' AND value = \'%s\' AND data_source_obj_id = %d", SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()), SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), @@ -1127,12 +1211,12 @@ public final class DrawableDB { GroupSeenQueryResultProcessor queryResultProcessor = new GroupSeenQueryResultProcessor(); try { - String groupSeenQueryStmt = "COUNT(*) as count FROM " + GROUPS_SEEN_TABLENAME + String groupSeenQueryStmt = "COUNT(*) as count FROM " + CASE_DB_GROUPS_SEEN_TABLENAME + " WHERE seen = 1 " + " AND group_id in ( " + getGroupIdQuery(groupKey) + ")" + (examinerId > 0 ? " AND examiner_id = " + examinerId : "");// query to find the group id from attribute/value - tskCase.getCaseDbAccessManager().select(groupSeenQueryStmt, queryResultProcessor); + caseDb.getCaseDbAccessManager().select(groupSeenQueryStmt, queryResultProcessor); return queryResultProcessor.get(); } catch (ExecutionException | InterruptedException | TskCoreException ex) { String msg = String.format("Failed to get is group seen for group key %s", groupKey.getValueDisplayName()); //NON-NLS @@ -1143,10 +1227,10 @@ public final class DrawableDB { } /** - * Record in the DB that the group with the given key is seen - * by given examiner id. + * Record in the DB that the group with the given key is seen by given + * examiner id. * - * @param groupKey key identifying the group. + * @param groupKey key identifying the group. * @param examinerID examiner id. * * @throws TskCoreException @@ -1154,34 +1238,34 @@ public final class DrawableDB { public void markGroupSeen(GroupKey groupKey, long examinerID) throws TskCoreException { /* - * Check the groupSeenCache to see if the seen status for this group was set recently. - * If recently set to seen, there's no need to update it + * Check the groupSeenCache to see if the seen status for this group was + * set recently. If recently set to seen, there's no need to update it */ Boolean cachedValue = groupSeenCache.getIfPresent(groupKey); if (cachedValue != null && cachedValue == true) { return; } - + // query to find the group id from attribute/value - String innerQuery = String.format("( SELECT group_id FROM " + GROUPS_TABLENAME //NON-NLS + String innerQuery = String.format("( SELECT group_id FROM " + CASE_DB_GROUPS_TABLENAME//NON-NLS + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d )", //NON-NLS SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()), SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0); String insertSQL = String.format(" (group_id, examiner_id, seen) VALUES (%s, %d, %d)", innerQuery, examinerID, 1); //NON-NLS - if (DbType.POSTGRESQL == tskCase.getDatabaseType()) { + if (DbType.POSTGRESQL == caseDb.getDatabaseType()) { insertSQL += String.format(" ON CONFLICT (group_id, examiner_id) DO UPDATE SET seen = %d", 1); //NON-NLS } - tskCase.getCaseDbAccessManager().insertOrUpdate(GROUPS_SEEN_TABLENAME, insertSQL); + caseDb.getCaseDbAccessManager().insertOrUpdate(CASE_DB_GROUPS_SEEN_TABLENAME, insertSQL); groupSeenCache.put(groupKey, true); } /** - * Record in the DB that given group is unseen. - * The group is marked unseen for ALL examiners that have seen the group. + * Record in the DB that given group is unseen. The group is marked unseen + * for ALL examiners that have seen the group. * * @param groupKey key identifying the group. * @@ -1190,20 +1274,20 @@ public final class DrawableDB { public void markGroupUnseen(GroupKey groupKey) throws TskCoreException { /* - * Check the groupSeenCache to see if the seen status for this group was set recently. - * If recently set to unseen, there's no need to update it + * Check the groupSeenCache to see if the seen status for this group was + * set recently. If recently set to unseen, there's no need to update it */ Boolean cachedValue = groupSeenCache.getIfPresent(groupKey); if (cachedValue != null && cachedValue == false) { return; } - - String updateSQL = String.format(" SET seen = 0 WHERE group_id in ( " + getGroupIdQuery(groupKey) + ")" ); //NON-NLS - tskCase.getCaseDbAccessManager().update(GROUPS_SEEN_TABLENAME, updateSQL); - + + String updateSQL = String.format(" SET seen = 0 WHERE group_id in ( " + getGroupIdQuery(groupKey) + ")"); //NON-NLS + caseDb.getCaseDbAccessManager().update(CASE_DB_GROUPS_SEEN_TABLENAME, updateSQL); + groupSeenCache.put(groupKey, false); } - + /** * Sets the isAnalysed flag in the groups table for the given group to true. * @@ -1213,7 +1297,6 @@ public final class DrawableDB { */ public void markGroupAnalyzed(GroupKey groupKey) throws TskCoreException { - String updateSQL = String.format(" SET is_analyzed = %d " + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d ", 1, @@ -1221,9 +1304,9 @@ public final class DrawableDB { SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0); - tskCase.getCaseDbAccessManager().update(GROUPS_TABLENAME, updateSQL); + caseDb.getCaseDbAccessManager().update(CASE_DB_GROUPS_TABLENAME, updateSQL); } - + /** * Removes a file from the drawables databse. * @@ -1254,18 +1337,18 @@ public final class DrawableDB { /** * Updates the image file. - * + * * @param f file to update. - * + * * @throws TskCoreException - * @throws SQLException + * @throws SQLException */ public void updateFile(DrawableFile f) throws TskCoreException, SQLException { DrawableTransaction trans = null; CaseDbTransaction caseDbTransaction = null; try { trans = beginTransaction(); - caseDbTransaction = tskCase.beginTransaction(); + caseDbTransaction = caseDb.beginTransaction(); updateFile(f, trans, caseDbTransaction); caseDbTransaction.commit(); commitTransaction(trans, true); @@ -1288,14 +1371,13 @@ public final class DrawableDB { } } - /** * Update an existing entry (or make a new one) into the DB that includes * group information. Called when a file has been analyzed or during a bulk * rebuild * - * @param f file to update - * @param tr + * @param f file to update + * @param tr * @param caseDbTransaction */ public void updateFile(DrawableFile f, DrawableTransaction tr, CaseDbTransaction caseDbTransaction) { @@ -1315,11 +1397,11 @@ public final class DrawableDB { try { // get tags - try (SleuthkitCase.CaseDbQuery dbQuery = tskCase.executeQuery("SELECT obj_id FROM content_tags")) { + try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery("SELECT obj_id FROM content_tags")) { ResultSet rs = dbQuery.getResultSet(); while (rs.next()) { long id = rs.getLong("obj_id"); - hasTagCache.add(id); + hasTagsCache.add(id); } } catch (SQLException ex) { logger.log(Level.SEVERE, "Error getting tags from DB", ex); //NON-NLS @@ -1330,11 +1412,11 @@ public final class DrawableDB { try { // hash sets - try (SleuthkitCase.CaseDbQuery dbQuery = tskCase.executeQuery("SELECT obj_id FROM blackboard_artifacts WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID())) { + try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery("SELECT obj_id FROM blackboard_artifacts WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID())) { ResultSet rs = dbQuery.getResultSet(); while (rs.next()) { long id = rs.getLong("obj_id"); - hasHashCache.add(id); + hasHashHitsCache.add(id); } } catch (SQLException ex) { @@ -1346,11 +1428,11 @@ public final class DrawableDB { try { // EXIF - try (SleuthkitCase.CaseDbQuery dbQuery = tskCase.executeQuery("SELECT obj_id FROM blackboard_artifacts WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID())) { + try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery("SELECT obj_id FROM blackboard_artifacts WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID())) { ResultSet rs = dbQuery.getResultSet(); while (rs.next()) { long id = rs.getLong("obj_id"); - hasExifCache.add(id); + hasExifDataCache.add(id); } } catch (SQLException ex) { @@ -1375,7 +1457,7 @@ public final class DrawableDB { if (cacheBuildCount == 0) { return; } - hasExifCache.add(objectID); + hasExifDataCache.add(objectID); } } @@ -1390,7 +1472,7 @@ public final class DrawableDB { if (cacheBuildCount == 0) { return; } - hasHashCache.add(objectID); + hasHashHitsCache.add(objectID); } } @@ -1405,7 +1487,7 @@ public final class DrawableDB { if (cacheBuildCount == 0) { return; } - hasTagCache.add(objectID); + hasTagsCache.add(objectID); } } @@ -1420,9 +1502,9 @@ public final class DrawableDB { } areCachesLoaded = false; - hasTagCache.clear(); - hasHashCache.clear(); - hasExifCache.clear(); + hasTagsCache.clear(); + hasHashHitsCache.clear(); + hasExifDataCache.clear(); } } @@ -1462,9 +1544,9 @@ public final class DrawableDB { boolean hasTag = true; synchronized (cacheLock) { if (areCachesLoaded) { - hasExif = hasExifCache.contains(f.getId()); - hasHashSet = hasHashCache.contains(f.getId()); - hasTag = hasTagCache.contains(f.getId()); + hasExif = hasExifDataCache.contains(f.getId()); + hasHashSet = hasHashHitsCache.contains(f.getId()); + hasTag = hasTagsCache.contains(f.getId()); } } @@ -1477,21 +1559,50 @@ public final class DrawableDB { dbWriteLock(); try { - // "INSERT OR IGNORE/ INTO drawable_files (obj_id, data_source_obj_id, path, name, created_time, modified_time, make, model, analyzed)" - stmt.setLong(1, f.getId()); - stmt.setLong(2, f.getAbstractFile().getDataSourceObjectId()); - stmt.setString(3, f.getDrawablePath()); - stmt.setString(4, f.getName()); - stmt.setLong(5, f.getCrtime()); - stmt.setLong(6, f.getMtime()); - if (hasExif) { - stmt.setString(7, f.getMake()); - stmt.setString(8, f.getModel()); + if (addGroups) { + // "INSERT INTO drawable_files (obj_id, data_source_obj_id, path, name, created_time, modified_time, make, model, analyzed) VALUES (?,?,?,?,?,?,?,?,?) ON CONFLICT (obj_id) DO UPDATE SET data_source_obj_id = ?, path = ?, name = ?, created_time = ?, modified_time = ?, make = ?, model = ?, analyzed = ?" + stmt.setLong(1, f.getId()); + stmt.setLong(2, f.getAbstractFile().getDataSourceObjectId()); + stmt.setLong(10, f.getAbstractFile().getDataSourceObjectId()); + stmt.setString(3, f.getDrawablePath()); + stmt.setString(11, f.getDrawablePath()); + stmt.setString(4, f.getName()); + stmt.setString(12, f.getName()); + stmt.setLong(5, f.getCrtime()); + stmt.setLong(13, f.getCrtime()); + stmt.setLong(6, f.getMtime()); + stmt.setLong(14, f.getMtime()); + if (hasExif) { + stmt.setString(7, f.getMake()); + stmt.setString(15, f.getMake()); + stmt.setString(8, f.getModel()); + stmt.setString(16, f.getModel()); + } else { + stmt.setString(7, ""); + stmt.setString(15, ""); + stmt.setString(8, ""); + stmt.setString(16, ""); + } + stmt.setBoolean(9, f.isAnalyzed()); + stmt.setBoolean(17, f.isAnalyzed()); } else { - stmt.setString(7, ""); - stmt.setString(8, ""); + // "INSERT OR IGNORE/ INTO drawable_files (obj_id, data_source_obj_id, path, name, created_time, modified_time, make, model, analyzed)" + stmt.setLong(1, f.getId()); + stmt.setLong(2, f.getAbstractFile().getDataSourceObjectId()); + stmt.setString(3, f.getDrawablePath()); + stmt.setString(4, f.getName()); + stmt.setLong(5, f.getCrtime()); + stmt.setLong(6, f.getMtime()); + if (hasExif) { + stmt.setString(7, f.getMake()); + stmt.setString(8, f.getModel()); + } else { + stmt.setString(7, ""); + stmt.setString(8, ""); + } + stmt.setBoolean(9, f.isAnalyzed()); } - stmt.setBoolean(9, f.isAnalyzed()); + stmt.executeUpdate(); // Update the list of file IDs in memory @@ -1601,44 +1712,42 @@ public final class DrawableDB { } return map; } - + /** - * Get the build status for the given data source. - * Will return UNKNOWN if the data source is not yet in the database. - * + * Get the build status for the given data source. Will return UNKNOWN if + * the data source is not yet in the database. + * * @param dataSourceId - * + * * @return The status of the data source or UKNOWN if it is not found. - * - * @throws TskCoreException + * + * @throws TskCoreException */ - public DrawableDbBuildStatusEnum getDataSourceDbBuildStatus(Long dataSourceId) throws TskCoreException { + public DrawableDbBuildStatusEnum getDataSourceDbBuildStatus(Long dataSourceId) throws TskCoreException { Map statusMap = getDataSourceDbBuildStatus(); if (statusMap.containsKey(dataSourceId) == false) { return DrawableDbBuildStatusEnum.UNKNOWN; } return statusMap.get(dataSourceId); - } + } /** - * Insert/update given data source object id and it's DB rebuild status in - * the datasources table. + * Inserts the given data source object ID and its status into the + * datasources table. If a record for the data source already exists, an + * update of the status is done instead. * - * If the object id exists in the table already, it updates the status - * - * @param dsObjectId data source object id to insert - * @param status The db build statsus for datasource. + * @param dataSourceObjectID A data source object ID from the case database. + * @param status The status of the data source with respect to + * populating the image gallery database. */ - public void insertOrUpdateDataSource(long dsObjectId, DrawableDbBuildStatusEnum status) { + public void insertOrUpdateDataSource(long dataSourceObjectID, DrawableDbBuildStatusEnum status) throws SQLException { dbWriteLock(); try { - // "INSERT OR REPLACE INTO datasources (ds_obj_id, drawable_db_build_status) " //NON-NLS - updateDataSourceStmt.setLong(1, dsObjectId); - updateDataSourceStmt.setString(2, status.name()); - - updateDataSourceStmt.executeUpdate(); - } catch (SQLException | NullPointerException ex) { - logger.log(Level.SEVERE, "failed to insert/update datasources table", ex); //NON-NLS + // INSERT INTO datasources (ds_obj_id, drawable_db_build_status) values (?,?) ON CONFLICT(ds_obj_id) DO UPDATE SET drawable_db_build_status = ?; + insertDataSourceStmt.setLong(1, dataSourceObjectID); + insertDataSourceStmt.setString(2, status.name()); + insertDataSourceStmt.setString(3, status.name()); + insertDataSourceStmt.executeUpdate(); } finally { dbWriteUnlock(); } @@ -1677,7 +1786,7 @@ public final class DrawableDB { //Can't make this a preprared statement because of the IN ( ... ) ResultSet analyzedQuery = stmt.executeQuery("SELECT COUNT(analyzed) AS analyzed FROM drawable_files WHERE analyzed = 1 AND obj_id IN (" + StringUtils.join(fileIds, ", ") + ")"); //NON-NLS while (analyzedQuery.next()) { - return analyzedQuery.getInt(ANALYZED) == fileIds.size(); + return analyzedQuery.getInt("analyzed") == fileIds.size(); } return false; } @@ -1687,47 +1796,50 @@ public final class DrawableDB { } /** - * Returns whether or not the given group is analyzed and ready to be viewed. - * + * Returns whether or not the given group is analyzed and ready to be + * viewed. + * * @param groupKey group key. + * * @return true if the group is analyzed. + * * @throws SQLException - * @throws TskCoreException + * @throws TskCoreException */ public Boolean isGroupAnalyzed(GroupKey groupKey) throws SQLException, TskCoreException { - + // Callback to process result of isAnalyzed query class IsGroupAnalyzedQueryResultProcessor implements CaseDbAccessQueryCallback { private boolean isAnalyzed = false; - - boolean getIsAnalyzed() { + + boolean getIsAnalyzed() { return isAnalyzed; } - + @Override public void process(ResultSet resultSet) { - try { - if (resultSet.next()) { - isAnalyzed = resultSet.getInt("is_analyzed") == 1 ? true: false; + try { + if (resultSet.next()) { + isAnalyzed = resultSet.getInt("is_analyzed") == 1 ? true : false; } } catch (SQLException ex) { logger.log(Level.SEVERE, "Failed to get group is_analyzed", ex); //NON-NLS } } } - + IsGroupAnalyzedQueryResultProcessor queryResultProcessor = new IsGroupAnalyzedQueryResultProcessor(); try { - String groupAnalyzedQueryStmt = String.format("is_analyzed FROM " + GROUPS_TABLENAME - + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d ", - SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()), - SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), - groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0); - - tskCase.getCaseDbAccessManager().select(groupAnalyzedQueryStmt, queryResultProcessor); + String groupAnalyzedQueryStmt = String.format("is_analyzed FROM " + CASE_DB_GROUPS_TABLENAME + + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d ", + SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()), + SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), + groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0); + + caseDb.getCaseDbAccessManager().select(groupAnalyzedQueryStmt, queryResultProcessor); return queryResultProcessor.getIsAnalyzed(); - } catch ( TskCoreException ex) { + } catch (TskCoreException ex) { String msg = String.format("Failed to get group is_analyzed for group key %s", groupKey.getValueDisplayName()); //NON-NLS logger.log(Level.SEVERE, msg, ex); } @@ -1833,7 +1945,7 @@ public final class DrawableDB { // skip any null/blank values query.append("WHERE LENGTH(" + groupBy.attrName.toString() + ") > 0 "); - + if (dataSource != null) { query.append(" AND data_source_obj_id = ").append(dataSource.getId()); } @@ -1877,7 +1989,7 @@ public final class DrawableDB { * wrong, we know this should be of type A even if * JAVA doesn't */ - values.put(tskCase.getDataSource(results.getLong("data_source_obj_id")), + values.put(caseDb.getDataSource(results.getLong("data_source_obj_id")), (A) results.getObject(groupBy.attrName.toString())); } return values; @@ -1922,13 +2034,13 @@ public final class DrawableDB { return; } - int isAnalyzed = (groupBy == DrawableAttribute.PATH) ? 0 : 1; + int isAnalyzed = (groupBy == DrawableAttribute.PATH) ? 0 : 1; String insertSQL = String.format(" (data_source_obj_id, value, attribute, is_analyzed) VALUES (%d, \'%s\', \'%s\', %d)", ds_obj_id, SleuthkitCase.escapeSingleQuotes(value), SleuthkitCase.escapeSingleQuotes(groupBy.attrName.toString()), isAnalyzed); - if (DbType.POSTGRESQL == tskCase.getDatabaseType()) { + if (DbType.POSTGRESQL == caseDb.getDatabaseType()) { insertSQL += " ON CONFLICT DO NOTHING"; } - tskCase.getCaseDbAccessManager().insert(GROUPS_TABLENAME, insertSQL, caseDbTransaction); + caseDb.getCaseDbAccessManager().insert(CASE_DB_GROUPS_TABLENAME, insertSQL, caseDbTransaction); groupCache.put(cacheKey, Boolean.TRUE); } @@ -1941,7 +2053,7 @@ public final class DrawableDB { * {@link SleuthkitCase} */ public DrawableFile getFileFromID(Long id) throws TskCoreException { - AbstractFile f = tskCase.getAbstractFileById(id); + AbstractFile f = caseDb.getAbstractFileById(id); try { return DrawableFile.create(f, areFilesAnalyzed(Collections.singleton(id)), isVideoFile(f)); } catch (SQLException ex) { @@ -1969,7 +2081,7 @@ public final class DrawableDB { try (ResultSet valsResults = statement.executeQuery()) { while (valsResults.next()) { - files.add(valsResults.getLong(OBJ_ID)); + files.add(valsResults.getLong("obj_id")); } } } catch (SQLException ex) { @@ -1995,7 +2107,6 @@ public final class DrawableDB { return countFilesWhere(" 1 "); } - /** * delete the row with obj_id = id. * @@ -2011,12 +2122,12 @@ public final class DrawableDB { removeImageFileFromList(id); //"delete from hash_set_hits where (obj_id = " + id + ")" - removeHashHitStmt.setLong(1, id); - removeHashHitStmt.executeUpdate(); + deleteHashHitStmt.setLong(1, id); + deleteHashHitStmt.executeUpdate(); //"delete from drawable_files where (obj_id = " + id + ")" - removeFileStmt.setLong(1, id); - removeFileStmt.executeUpdate(); + deleteFileStmt.setLong(1, id); + deleteFileStmt.executeUpdate(); tr.addRemovedFile(id); } catch (SQLException ex) { @@ -2027,6 +2138,38 @@ public final class DrawableDB { } } + /** + * Deletes a cascading delete of a data source, starting from the + * datasources table. + * + * @param dataSourceID The object ID of the data source to delete. + * + * @throws SQLException + * @throws TskCoreException + */ + public void deleteDataSource(long dataSourceID) throws SQLException, TskCoreException { + dbWriteLock(); + DrawableTransaction trans = null; + try { + trans = beginTransaction(); + deleteDataSourceStmt.setLong(1, dataSourceID); + deleteDataSourceStmt.executeUpdate(); + commitTransaction(trans, true); + } catch (SQLException | TskCoreException ex) { + if (null != trans) { + try { + rollbackTransaction(trans); + } catch (SQLException ex2) { + logger.log(Level.SEVERE, String.format("Failed to roll back drawables db transaction after error: %s", ex.getMessage()), ex2); //NON-NLS + } + } + throw ex; + } finally { + dbWriteUnlock(); + + } + } + public class MultipleTransactionException extends IllegalStateException { public MultipleTransactionException() { @@ -2076,7 +2219,7 @@ public final class DrawableDB { try (Statement stmt = con.createStatement()) { ResultSet analyzedQuery = stmt.executeQuery("select obj_id from drawable_files"); while (analyzedQuery.next()) { - addImageFileToList(analyzedQuery.getLong(OBJ_ID)); + addImageFileToList(analyzedQuery.getLong("obj_id")); } return true; } catch (SQLException ex) { @@ -2125,7 +2268,7 @@ public final class DrawableDB { try { TagName tagName = controller.getTagsManager().getTagName(cat); if (nonNull(tagName)) { - return tskCase.getContentTagsByTagName(tagName).stream() + return caseDb.getContentTagsByTagName(tagName).stream() .map(ContentTag::getContent) .map(Content::getId) .filter(this::isInDB) @@ -2177,7 +2320,7 @@ public final class DrawableDB { String name = "SELECT COUNT(obj_id) as obj_count FROM tsk_files where obj_id IN " + fileIdsList //NON-NLS + " AND obj_id NOT IN (SELECT obj_id FROM content_tags WHERE content_tags.tag_name_id IN " + catTagNameIDs + ")"; //NON-NLS - try (SleuthkitCase.CaseDbQuery executeQuery = tskCase.executeQuery(name); + try (SleuthkitCase.CaseDbQuery executeQuery = caseDb.executeQuery(name); ResultSet resultSet = executeQuery.getResultSet();) { while (resultSet.next()) { return resultSet.getLong("obj_count"); //NON-NLS @@ -2203,11 +2346,11 @@ public final class DrawableDB { // The files are processed ORDERED BY parent path // We want to preserve that order here, so that we can detect a - // change in path, and thus mark the path group as analyzed - // Hence we use a LinkedHashSet here. + // change in path, and thus mark the path group as analyzed + // Hence we use a LinkedHashSet here. private final Set updatedFiles = new LinkedHashSet<>(); private final Set removedFiles = new LinkedHashSet<>(); - + private boolean completed; private DrawableTransaction() throws TskCoreException, SQLException { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/Bundle_ja.properties b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/Bundle_ja.properties index 5dc38728de..8038238888 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/Bundle_ja.properties +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/Bundle_ja.properties @@ -1,6 +1,6 @@ -GroupSortBy.groupName=\u30B0\u30EB\u30FC\u30D7\u540D -GroupSortBy.groupSize=\u30B0\u30EB\u30FC\u30D7\u30B5\u30A4\u30BA -GroupSortBy.none=\u7121\u3057 -GroupSortBy.priority=\u512A\u5148\u5EA6 -ReGroupTask.displayTitle={0}\u306B\u57FA\u3065\u3044\u3066\u30D5\u30A1\u30A4\u30EB\u3092\u518D\u5EA6\u30B0\u30EB\u30FC\u30D7\u5316\u3002{2}\u306E\u9806\u756A\u3067{1}\u306B\u3088\u308A\u30BD\u30FC\u30C8\u3002 -ReGroupTask.progressUpdate={0} \: {1}\u306B\u57FA\u3065\u3044\u3066\u30D5\u30A1\u30A4\u30EB\u3092\u518D\u5EA6\u30B0\u30EB\u30FC\u30D7\u5316 \ No newline at end of file +GroupSortBy.groupName=\u30b0\u30eb\u30fc\u30d7\u540d +GroupSortBy.groupSize=\u30b0\u30eb\u30fc\u30d7\u30b5\u30a4\u30ba +GroupSortBy.none=\u306a\u3057 +GroupSortBy.priority=\u512a\u5148\u5ea6 +# {0} - groupBy\u5c5e\u6027\u540d +ReGroupTask.displayTitle={0} \u3067\u518d\u30b0\u30eb\u30fc\u30d7\u5316\u4e2d\u3067\u3059: diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Bundle_ja.properties b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Bundle_ja.properties index 7a18ada46d..5b68e9874b 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Bundle_ja.properties +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Bundle_ja.properties @@ -1,13 +1,20 @@ -StatuBar.toolTip=\u4E00\u90E8\u306E\u30C7\u30FC\u30BF\u304C\u6700\u65B0\u3067\u306F\u306A\u3044\u304B\u3082\u3057\u308C\u307E\u305B\u3093\u3002\u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC\u30C7\u30FC\u30BF\u3092\u66F4\u65B0\u3059\u308B\u305F\u3081\u3001\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u304C\u5B8C\u4E86\u5F8C\u30C4\u30FC\u30EB|\u30AA\u30D7\u30B7\u30E7\u30F3|\u30A4\u30E1\u30FC\u30B8\uFF0F\u30D3\u30C7\u30AA\u30AE\u30E3\u30E9\u30EA\u30FC\u3067\u30A4\u30E1\u30FC\u30B8\u30AE\u30E3\u30E9\u30EA\u30FC\u3092\u6709\u52B9\u5316\u3057\u3066\u304F\u3060\u3055\u3044\u3002 -StatusBar.bgTaskLabel.text=\u518D\u5EA6\u30B0\u30EB\u30FC\u30D7\u5316 -StatusBar.fileUpdateTaskLabel.text=\u30D5\u30A1\u30A4\u30EB\u66F4\u65B0\u30BF\u30B9\u30AF -SummaryTablePane.catColumn=\u30AB\u30C6\u30B4\u30EA\u30FC -SummaryTablePane.countColumn=\#\u30D5\u30A1\u30A4\u30EB -Toolbar.ascRadio=\u30A2\u30BB\u30F3\u30C9\uFF08\u4E0B\u304B\u3089\u4E0A\u3078\uFF09 -Toolbar.categoryImageViewLabel=\u30B0\u30EB\u30FC\u30D7\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u30AB\u30C6\u30B4\u30E9\u30A4\u30BA\uFF1A -Toolbar.descRadio=\u30C7\u30A3\u30BB\u30F3\u30C9\uFF08\u4E0A\u304B\u3089\u4E0B\u3078\uFF09 -Toolbar.groupByLabel=\u30B0\u30EB\u30FC\u30D7\u30D0\u30A4\uFF1A -Toolbar.sortByLabel=\u30BD\u30FC\u30C8\u30D0\u30A4\uFF1A -Toolbar.tagImageViewLabel=\u30B0\u30EB\u30FC\u30D7\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u30BF\u30B0\uFF1A -Toolbar.thumbnailSizeLabel=\u30B5\u30E0\u30CD\u30A4\u30EB\u30B5\u30A4\u30BA\uFF08px\uFF09\uFF1A -VideoPlayer.errNotice={0}\n\u8A73\u7D30\u3092\u30ED\u30B0\u3067\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 \ No newline at end of file +StatuBar.toolTip=\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 \u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u5b8c\u4e86\u5f8c\u3001[\u30c4\u30fc\u30eb] | [\u30aa\u30d7\u30b7\u30e7\u30f3] | [\u30a4\u30e1\u30fc\u30b8 / \u52d5\u753b\u30ae\u30e3\u30e9\u30ea\u30fc] \u3067 [\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc] \u3092\u6709\u52b9\u306b\u3057\u3001\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u306e\u30c7\u30fc\u30bf\u3092\u66f4\u65b0\u3057\u307e\u3059\u3002 +StatusBar.bgTaskLabel.text=\u518d\u30b0\u30eb\u30fc\u30d7\u5316\u4e2d\u3067\u3059 +StatusBar.fileUpdateTaskLabel.text=\ \u30d5\u30a1\u30a4\u30eb\u66f4\u65b0\u30bf\u30b9\u30af +SummaryTablePane.catColumn=\u30ab\u30c6\u30b4\u30ea\u30fc +SummaryTablePane.countColumn=\# \u30d5\u30a1\u30a4\u30eb +Toolbar.ascRadio=\u6607\u9806 +Toolbar.categoryImageViewLabel=\u30b0\u30eb\u30fc\u30d7\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u5206\u985e: +Toolbar.descRadio=\u964d\u9806 +Toolbar.getDataSources.errMessage=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002 +Toolbar.groupByLabel=\u6b21\u3067\u30b0\u30eb\u30fc\u30d7\u5316: +Toolbar.nonPathGroupingWarning.content=\u518d\u30b0\u30eb\u30fc\u30d7\u5316\u3092\u7d9a\u884c\u3057\u307e\u3059\u304b? +Toolbar.nonPathGroupingWarning.header=\u30d1\u30b9\u4ee5\u5916\u306e\u5c5e\u6027\u306b\u3088\u308b\u30b0\u30eb\u30fc\u30d7\u5316\u306f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u305b\u3093\u3002\n\u3059\u3079\u3066\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u3068\u30b0\u30eb\u30fc\u30d7\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +Toolbar.nonPathGroupingWarning.title=\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc +Toolbar.sortByLabel=\u6b21\u3067\u30bd\u30fc\u30c8: +Toolbar.sortHelp=\u30bd\u30fc\u30c8\u9806(\u6607\u9806/\u964d\u9806)\u306f\u30a4\u30e1\u30fc\u30b8\u30ae\u30e3\u30e9\u30ea\u30fc\u304c\u7dad\u6301\u3059\u308b\u975e\u8868\u793a\u30b0\u30eb\u30fc\u30d7\u306e\u30ad\u30e5\u30fc\u306b\u5f71\u97ff\u3057\u307e\u3059\u304c\u3001\u3053\u306e\u30ad\u30e5\u30fc\u3078\u306e\u5909\u66f4\u306f[\u6b21\u306e\u975e\u8868\u793a\u30b0\u30eb\u30fc\u30d7]\u30dc\u30bf\u30f3\u304c\u62bc\u3055\u308c\u308b\u307e\u3067\u660e\u3089\u304b\u306b\u306a\u308a\u307e\u305b\u3093\u3002 +Toolbar.sortHelpTitle=\u30b0\u30eb\u30fc\u30d7\u306e\u4e26\u3079\u66ff\u3048 +Toolbar.tagImageViewLabel=\u30b0\u30eb\u30fc\u30d7\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u30bf\u30b0\u4ed8\u3051: +Toolbar.thumbnailSizeLabel=\u30b5\u30e0\u30cd\u30a4\u30eb\u30b5\u30a4\u30ba(px): +# {0} - \u4f8b\u5916\u30bf\u30a4\u30d7 +VideoPlayer.errNotice={0}\n\u8a73\u7d30\u306f\u30ed\u30b0\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002 diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/Bundle_ja.properties b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/Bundle_ja.properties index 2d5f17cc1c..5a7a8f3b89 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/Bundle_ja.properties +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/Bundle_ja.properties @@ -1,18 +1,24 @@ -DrawableTileBase.externalViewerAction.text=\u5916\u90E8\u30D3\u30E5\u30FC\u30A2\u30FC\u3067\u958B\u304F -DrawableTileBase.menuItem.extractFiles=\u30D5\u30A1\u30A4\u30EB\u3092\u62BD\u51FA -DrawableTileBase.menuItem.showContentViewer=\u30B3\u30F3\u30C6\u30F3\u30C4\u30D3\u30E5\u30FC\u30A2\u30FC\u3092\u8868\u793A -DrawableUIBase.errorLabel.OOMText=\u30E1\u30E2\u30EA\u4E0D\u8DB3 -DrawableUIBase.errorLabel.text=\u30D5\u30A1\u30A4\u30EB\u3092\u8AAD\u307F\u53D6\u308C\u307E\u305B\u3093\u3067\u3057\u305F -GroupPane.bottomLabel.displayText=\u30B0\u30EB\u30FC\u30D7\u30D3\u30E5\u30FC\u30A4\u30F3\u30B0\u5C65\u6B74\uFF1A -GroupPane.catContainerLabel.displayText=\u9078\u629E\u3057\u305F\u30D5\u30A1\u30A4\u30EB\u3092\u30AB\u30C6\u30B4\u30E9\u30A4\u30BA\uFF1A -GroupPane.catHeadingLabel.displayText=\u30AB\u30C6\u30B4\u30EA\u30FC\uFF1A -GroupPane.gridViewContextMenuItem.extractFiles=\u30D5\u30A1\u30A4\u30EB\u3092\u62BD\u51FA -GroupPane.headerString={0} -- {1} \u30CF\u30C3\u30B7\u30E5\u30BB\u30C3\u30C8\u30D2\u30C3\u30C8\uFF0F{2}\u30D5\u30A1\u30A4\u30EB -GroupPane.hederLabel.displayText=\u9078\u629E\u3057\u305F\u30D5\u30A1\u30A4\u30EB\u3092\u30BF\u30B0\uFF1A -MediaLoadTask.messageText={0}\u30D3\u30C7\u30AA\u3092\u8AAD\u307F\u53D6\u308A\u4E2D -MetaDataPane.attributeColumn.headingName=\u5C5E\u6027 -MetaDataPane.copyMenuItem.text=\u30B3\u30D4\u30FC -MetaDataPane.tableView.placeholder=\u3053\u3053\u306B\u8A73\u7D30\u3092\u8868\u793A\u3059\u308B\u30D5\u30A1\u30A4\u30EB\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002 -MetaDataPane.titledPane.displayName=\u8A73\u7D30 -MetaDataPane.valueColumn.headingName=\u30D0\u30EA\u30E5\u30FC -SlideShowView.supplementalText={1}\u306E\u3046\u3061{0}\u304C\u30B0\u30EB\u30FC\u30D7\u306B\u3042\u308A\u307E\u3059 \ No newline at end of file +DrawableTileBase.externalViewerAction.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f +DrawableTileBase.menuItem.extractFiles=\u30d5\u30a1\u30a4\u30eb\u3092\u62bd\u51fa +DrawableTileBase.menuItem.showContentViewer=\u30b3\u30f3\u30c6\u30f3\u30c4\u30d3\u30e5\u30fc\u30ef\u30fc\u3092\u8868\u793a +DrawableUIBase.errorLabel.OOMText=\u30e1\u30e2\u30ea\u30fc\u4e0d\u8db3 +DrawableUIBase.errorLabel.text=\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f +GroupPane.bottomLabel.displayText=\u30b0\u30eb\u30fc\u30d7\u95b2\u89a7\u5c65\u6b74: +GroupPane.catContainerLabel.displayText=\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u5206\u985e: +GroupPane.catHeadingLabel.displayText=\u30ab\u30c6\u30b4\u30ea\u30fc: +GroupPane.gridViewContextMenuItem.extractFiles=\u30d5\u30a1\u30a4\u30eb\u3092\u62bd\u51fa +# {0} - \u30c7\u30d5\u30a9\u30eb\u30c8\u30b0\u30eb\u30fc\u30d7\u540d +# {1} - \u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d2\u30c3\u30c8\u30ab\u30a6\u30f3\u30c8 +# {2} - \u30b0\u30eb\u30fc\u30d7\u30b5\u30a4\u30ba +GroupPane.headerString={0} -- {1} \u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d2\u30c3\u30c8\u6570 / {2} \u30d5\u30a1\u30a4\u30eb\u6570 +GroupPane.hederLabel.displayText=\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u30bf\u30b0\u4ed8\u3051: +# {0} - \u30d5\u30a1\u30a4\u30eb\u540d +MediaLoadTask.messageText=\u52d5\u753b\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u3067\u3059: {0} +MetaDataPane.attributeColumn.headingName=\u5c5e\u6027 +MetaDataPane.copyMenuItem.text=\u30b3\u30d4\u30fc +MetaDataPane.tableView.placeholder=\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e\u80a2\u3066\u8a73\u7d30\u3092\u3053\u3053\u306b\u8868\u793a\u3057\u307e\u3059\u3002 +MetaDataPane.titledPane.displayName=\u8a73\u7d30 +MetaDataPane.valueColumn.headingName=\u5024 +# {0} - \u30d5\u30a1\u30a4\u30ebID\u756a\u53f7 +# {1} - \u30d5\u30a1\u30a4\u30ebID\u6570 +SlideShowView.supplementalText=\u30b0\u30eb\u30fc\u30d7\u306e {0} / {1} diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/Bundle_ja.properties b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/Bundle_ja.properties index 659092aa6d..dbba4fabe0 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/Bundle_ja.properties +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/Bundle_ja.properties @@ -1,10 +1,11 @@ -GroupComparators.groupName=\u30B0\u30EB\u30FC\u30D7\u540D -GroupComparators.groupSize=\u30B0\u30EB\u30FC\u30D7\u30B5\u30A4\u30BA -GroupComparators.hitCount=\u30D2\u30C3\u30C8\u30AB\u30A6\u30F3\u30C8 -GroupComparators.hitDensity=\u30D2\u30C3\u30C8\u5BC6\u5EA6 -GroupComparators.uncategorizedCount=\u30AB\u30C6\u30B4\u30E9\u30A4\u30BA\u3055\u308C\u3066\u3044\u306A\u3044\u30AB\u30A6\u30F3\u30C8 -GroupTree.displayName.allGroups=\u5168\u3066\u306E\u30B0\u30EB\u30FC\u30D7 -HashHitGroupList.displayName.onlyHashHits=\u30CF\u30C3\u30B7\u30E5\u30D2\u30C3\u30C8\u306E\u307F -NavPanel.ascRadio.text=\u30A2\u30BB\u30F3\u30C9\uFF08\u4E0B\u304B\u3089\u4E0A\u3078\uFF09 -NavPanel.descRadio.text=\u30C7\u30A3\u30BB\u30F3\u30C9\uFF08\u4E0A\u304B\u3089\u4E0B\u3078\uFF09 -NavPanel.sortByBoxLabel.text=\u30BD\u30FC\u30C8\u30D0\u30A4\uFF1A \ No newline at end of file +GroupComparators.groupName=\u30b0\u30eb\u30fc\u30d7\u540d +GroupComparators.groupSize=\u30b0\u30eb\u30fc\u30d7\u30b5\u30a4\u30ba +GroupComparators.hitCount=\u30d2\u30c3\u30c8\u30ab\u30a6\u30f3\u30c8 +GroupComparators.hitDensity=\u30d2\u30c3\u30c8\u306e\u5bc6\u5ea6 +GroupComparators.uncategorizedCount=\u5206\u985e\u3055\u308c\u3066\u3044\u306a\u3044\u30ab\u30a6\u30f3\u30c8 +GroupTree.displayName.allGroups=\u3059\u3079\u3066\u306e\u30b0\u30eb\u30fc\u30d7 +HashHitGroupList.displayName.onlyHashHits=\u30cf\u30c3\u30b7\u30e5\u30d2\u30c3\u30c8\u6570\u306e\u307f +NavPanel.ascRadio.text=\u6607\u9806 +NavPanel.descRadio.text=\u964d\u9806 +NavPanel.placeHolder.text=\u30b0\u30eb\u30fc\u30d7\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +NavPanel.sortByBoxLabel.text=\u6b21\u3067\u30bd\u30fc\u30c8: diff --git a/InternalPythonModules/android/fbmessenger.py b/InternalPythonModules/android/fbmessenger.py index 3a678f16d0..e3514e2b17 100644 --- a/InternalPythonModules/android/fbmessenger.py +++ b/InternalPythonModules/android/fbmessenger.py @@ -17,6 +17,11 @@ See the License for the specific language governing permissions and limitations under the License. """ +import json +import traceback +import general +import ast + from java.io import File from java.lang import Class from java.lang import ClassNotFoundException @@ -43,12 +48,12 @@ from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments +from org.sleuthkit.datamodel.blackboardutils import URLAttachment +from org.sleuthkit.datamodel.blackboardutils import FileAttachment from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection - -import json -import traceback -import general +from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CallMediaType class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): @@ -94,6 +99,7 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): * have no text, * admin_text_thread_rtc_event has the specific event "group-call-started", "group-call_ended" + --- A pending_send_media_attachment - a JSON structure that has details of attachments that may or may not have been sent. --- A admin_text_thread_rtc_event column - has specific text events such as- "one-on-one-call-ended" --- A thread_key column - identifies the message thread --- A timestamp_ms column - date/time message was sent @@ -181,15 +187,305 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): contactsDb.close() - - ## Adds a recipient to given list - def addRecipientToList(self, user_key, recipientList): + ## Extracts recipeint id from 'user_key' column and adds recipient to given list, + ## if the recipeint id is not the same as sender id + def addRecipientToList(self, user_key, senderId, recipientList): if user_key is not None: - recipientId = user_key.replace('FACEBOOK:', '') - recipientList.append(recipientId) + recipientId = user_key.replace('FACEBOOK:', '') + if recipientId != senderId: + recipientList.append(recipientId) + + + ## Extracts sender id from the json in 'sender' column. + def getSenderIdFromJson(self, senderJsonStr): + senderId = None; + if senderJsonStr is not None: + sender_dict = json.loads(senderJsonStr) + senderId = sender_dict['user_key'] + senderId = senderId.replace('FACEBOOK:', '') - ## Analyze messages - def analyzeMessages(self, dataSource, fileManager, context): + return senderId + + ## determines communication direction by comparing senderId with selfAccountId + def deduceDirectionFromSenderId(self, senderId): + direction = CommunicationDirection.UNKNOWN + if senderId is not None: + if senderId == self.selfAccountId: + direction = CommunicationDirection.OUTGOING + else: + direction = CommunicationDirection.INCOMING + return direction + + ## Get the arrayList from the json passed in + def getJPGListFromJson(self, jpgJson): + jpgArray = ArrayList() + # The urls attachment will come across as unicode unless we use ast.literal_eval to change it to a dictionary + jpgDict = ast.literal_eval(jpgJson) + for jpgPreview in jpgDict.iterkeys(): + # Need to use ast.literal_eval so that the string can be converted to a dictionary + jpgUrlDict = ast.literal_eval(jpgDict[jpgPreview]) + jpgArray.add(URLAttachment(jpgUrlDict["src"])) + return jpgArray + + ## Analyzes messages + def analyzeMessages(self, threadsDb, threadsDBHelper): + try: + + ## Messages are found in the messages table. + ## This query filters messages by msg_type to only get actual user created conversation messages (msg_type 0). + ## The participant ids can be found in the thread_participants table. + ## Participant names are found in thread_users table. + ## Joining these tables produces multiple rows per message, one row for each recipient. + ## The result set is processed to collect the multiple recipients for a given message. + sqlString = """ + SELECT msg_id, text, sender, timestamp_ms, msg_type, messages.thread_key as thread_key, + snippet, thread_participants.user_key as user_key, thread_users.name as name, + attachments, pending_send_media_attachment + FROM messages + JOIN thread_participants ON messages.thread_key = thread_participants.thread_key + JOIN thread_users ON thread_participants.user_key = thread_users.user_key + WHERE msg_type = 0 + ORDER BY msg_id + """ + + messagesResultSet = threadsDb.runQuery(sqlString) + if messagesResultSet is not None: + oldMsgId = None + + direction = CommunicationDirection.UNKNOWN + fromId = None + recipientIdsList = None + timeStamp = -1 + msgText = "" + threadId = "" + messageAttachments = None + currentCase = Case.getCurrentCaseThrows() + + while messagesResultSet.next(): + msgId = messagesResultSet.getString("msg_id") + + # new msg begins when msgId changes + if msgId != oldMsgId: + # Create message artifact with collected attributes + if oldMsgId is not None: + messageArtifact = threadsDBHelper.addMessage( + self._MESSAGE_TYPE, + direction, + fromId, + recipientIdsList, + timeStamp, + MessageReadStatus.UNKNOWN, + "", # subject + msgText, + threadId) + + if (messageAttachments is not None): + threadsDBHelper.addAttachments(messageArtifact, messageAttachments) + messageAttachments = None + + oldMsgId = msgId + + # New message - collect all attributes + recipientIdsList = [] + + ## get sender id by parsing JSON in sender column + fromId = self.getSenderIdFromJson(messagesResultSet.getString("sender")) + direction = self.deduceDirectionFromSenderId(fromId) + + # Get recipient and add to list + self.addRecipientToList(messagesResultSet.getString("user_key"), fromId, + recipientIdsList) + + timeStamp = messagesResultSet.getLong("timestamp_ms") / 1000 + + # Get msg text + # Sometimes there may not be an explict msg text, + # but an app generated snippet instead + msgText = messagesResultSet.getString("text") + if not msgText: + msgText = messagesResultSet.getString("snippet") + + # Get attachments and pending attachments if they exist + attachment = messagesResultSet.getString("attachments") + pendingAttachment = messagesResultSet.getString("pending_send_media_attachment") + + urlAttachments = ArrayList() + fileAttachments = ArrayList() + + if ((attachment is not None) or (pendingAttachment is not None)): + if (attachment is not None): + attachmentDict = json.loads(attachment)[0] + if (attachmentDict["mime_type"] == "image/jpeg"): + urls = attachmentDict.get("urls", None) + if (urls is not None): + urlAttachments = self.getJPGListFromJson(urls) + + elif (attachmentDict["mime_type"] == "video/mp4"): + # filename does not have an associated path with it so it will be ignored + + urls = attachmentDict.get("urls", None) + if (urls is not None): + urlAttachments = self.getJPGListFromJson(urls) + + video_data_url = attachmentDict.get("video_data_url", None) + if (video_data_url is not None): + urlAttachments.add(URLAttachment(video_data_url)) + video_data_thumbnail_url = attachmentDict.get("video_data_thumbnail_url", None) + + if (video_data_thumbnail_url is not None): + urlAttachments.add(URLAttachment(video_data_thumbnail_url)) + elif (attachmentDict["mime_type"] == "audio/mpeg"): + audioUri = attachmentDict.get("audio_uri", None) + if (audioUri is None or audioUri == ""): + continue + else: + fileAttachments.add(FileAttachment(currentCase.getSleuthkitCase(), threadsDb.getDBFile().getDataSource(), audioUri.replace("file://",""))) + + else: + self._logger.log(Level.INFO, "Attachment type not handled: " + attachmentDict["mime_type"]) + + if (pendingAttachment is not None): + pendingAttachmentDict = json.loads(pendingAttachment)[0] + pendingAttachmentUri = pendingAttachmentDict.get("uri", None) + if (pendingAttachmentUri is not None): + fileAttachments.add(FileAttachment(currentCase.getSleuthkitCase(), threadsDb.getDBFile().getDataSource(), pendingAttachmentUri.replace("file://",""))) + + messageAttachments = MessageAttachments(fileAttachments, urlAttachments) + + threadId = messagesResultSet.getString("thread_key") + + else: # same msgId as last, just collect recipient from current row + self.addRecipientToList(messagesResultSet.getString("user_key"), fromId, + recipientIdsList) + + + # at the end of the loop, add last message + messageArtifact = threadsDBHelper.addMessage( + self._MESSAGE_TYPE, + direction, + fromId, + recipientIdsList, + timeStamp, + MessageReadStatus.UNKNOWN, + "", # subject + msgText, + threadId) + + except SQLException as ex: + self._logger.log(Level.WARNING, "Error processing query result for FB Messenger messages.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Failed to add FB Messenger message artifacts.", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except BlackboardException as ex: + self._logger.log(Level.WARNING, "Failed to post artifacts.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + + ## Analyzes call logs + def analyzeCallLogs(self, threadsDb, threadsDBHelper): + try: + + ## Call logs are found in the messages table. + ## msg_type indicates type of call: + ## 9: one to one calls + ## 203: group call + ## 1-to-1 calls only have a call_ended record. + ## group calls have a call_started_record as well as call_ended recorded, with *different* message ids. + ## all the data we need can be found in the call_ended record. + + sqlString = """ + SELECT msg_id, text, sender, timestamp_ms, msg_type, admin_text_thread_rtc_event, + generic_admin_message_extensible_data, + messages.thread_key as thread_key, + thread_participants.user_key as user_key, + thread_users.name as name + FROM messages + JOIN thread_participants ON messages.thread_key = thread_participants.thread_key + JOIN thread_users ON thread_participants.user_key = thread_users.user_key + WHERE msg_type = 9 OR (msg_type = 203 AND admin_text_thread_rtc_event = 'group_call_ended') + ORDER BY msg_id + """ + + messagesResultSet = threadsDb.runQuery(sqlString) + if messagesResultSet is not None: + oldMsgId = None + + direction = CommunicationDirection.UNKNOWN + callerId = None + calleeIdsList = None + startTimeStamp = -1 + endTimeStamp = -1 + duration = 0 + mediaType = CallMediaType.AUDIO + + while messagesResultSet.next(): + msgId = messagesResultSet.getString("msg_id") + + # new call begins when msgId changes + if msgId != oldMsgId: + # Create call log artifact with collected attributes + if oldMsgId is not None: + messageArtifact = threadsDBHelper.addCalllog( + direction, + callerId, + calleeIdsList, + startTimeStamp, + endTimeStamp, + mediaType ) + + oldMsgId = msgId + + # New message - collect all attributes + calleeIdsList = [] + + ## get caller id by parsing JSON in sender column + callerId = self.getSenderIdFromJson(messagesResultSet.getString("sender")) + direction = self.deduceDirectionFromSenderId(callerId) + + # Get recipient and add to list + self.addRecipientToList(messagesResultSet.getString("user_key"), callerId, + calleeIdsList) + + # the timestamp from call ended msg is used as end timestamp + endTimeStamp = messagesResultSet.getLong("timestamp_ms") / 1000 + + # parse the generic_admin_message_extensible_data JSON to extract the duration and video fields + adminDataJsonStr = messagesResultSet.getString("generic_admin_message_extensible_data") + if adminDataJsonStr is not None: + adminData_dict = json.loads(adminDataJsonStr) + duration = adminData_dict['call_duration'] # call duration in seconds + isVideo = adminData_dict['video'] + if isVideo: + mediaType = CallMediaType.VIDEO + + startTimeStamp = endTimeStamp - duration + + else: # same msgId as last, just collect callee from current row + self.addRecipientToList(messagesResultSet.getString("user_key"), callerId, + calleeIdsList) + + # at the end of the loop, add last message + messageArtifact = threadsDBHelper.addCalllog( + direction, + callerId, + calleeIdsList, + startTimeStamp, + endTimeStamp, + mediaType ) + + except SQLException as ex: + self._logger.log(Level.WARNING, "Error processing query result for FB Messenger call logs.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Failed to add FB Messenger call log artifacts.", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except BlackboardException as ex: + self._logger.log(Level.WARNING, "Failed to post FB Messenger call log artifacts.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + + + ## Analyze messages and call log threads + def analyzeMessagesAndCallLogs(self, dataSource, fileManager, context): threadsDbs = AppSQLiteDB.findAppDatabases(dataSource, "threads_db2", True, self._FB_MESSENGER_PACKAGE_NAME) for threadsDb in threadsDbs: try: @@ -202,113 +498,12 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): self._MODULE_NAME, threadsDb.getDBFile(), Account.Type.FACEBOOK) - ## Messages are found in the messages table. - ## This query filters messages by msg_type to only get actual user created conversation messages (msg_type 0). - ## The participant ids can be found in the thread_participants table. - ## Participant names are found in thread_users table. - ## Joining these tables produces multiple rows per message, one row for each recipient. - ## The result set is processed to collect the multiple recipients for a given message. - sqlString = """ - SELECT msg_id, text, sender, timestamp_ms, msg_type, messages.thread_key as thread_key, - snippet, thread_participants.user_key as user_key, thread_users.name as name - FROM messages - JOIN thread_participants ON messages.thread_key = thread_participants.thread_key - JOIN thread_users ON thread_participants.user_key = thread_users.user_key - WHERE msg_type = 0 - ORDER BY msg_id - """ - - messagesResultSet = threadsDb.runQuery(sqlString) - if messagesResultSet is not None: - oldMsgId = None - - direction = CommunicationDirection.UNKNOWN - fromId = None - recipientIdsList = None - timeStamp = -1 - msgText = "" - threadId = "" - - while messagesResultSet.next(): - msgId = messagesResultSet.getString("msg_id") - - # new msg begins when msgId changes - if msgId != oldMsgId: - # Create message artifact with collected attributes - if oldMsgId is not None: - messageArtifact = threadsDBHelper.addMessage( - self._MESSAGE_TYPE, - direction, - fromId, - recipientIdsList, - timeStamp, - MessageReadStatus.UNKNOWN, - "", # subject - msgText, - threadId) - - oldMsgId = msgId - - # New message - collect all attributes - recipientIdsList = [] - - ## get sender id by parsing JSON in sender column - senderJsonStr = messagesResultSet.getString("sender") - if senderJsonStr is not None: - sender_dict = json.loads(senderJsonStr) - senderId = sender_dict['user_key'] - senderId = senderId.replace('FACEBOOK:', '') - senderName = sender_dict['name'] - fromId = senderId - if senderId == self.selfAccountId: - direction = CommunicationDirection.OUTGOING - else: - direction = CommunicationDirection.INCOMING - - - # Get recipient and add to list - self.addRecipientToList(messagesResultSet.getString("user_key"), - recipientIdsList) - - timeStamp = messagesResultSet.getLong("timestamp_ms") / 1000 - - # Get msg text - # Sometimes there may not be an explict msg text, - # but an app generated snippet instead - msgText = messagesResultSet.getString("text") - if not msgText: - msgText = messagesResultSet.getString("snippet") - - # TBD: get attachment - - threadId = messagesResultSet.getString("thread_key") - - else: # same msgId as last, just collect recipient from current row - self.addRecipientToList(messagesResultSet.getString("user_key"), - recipientIdsList) - - - # at the end of the loop, add last message - messageArtifact = threadsDBHelper.addMessage( - self._MESSAGE_TYPE, - direction, - fromId, - recipientIdsList, - timeStamp, - MessageReadStatus.UNKNOWN, - "", # subject - msgText, - threadId) + self.analyzeMessages(threadsDb, threadsDBHelper) + self.analyzeCallLogs(threadsDb, threadsDBHelper) - except SQLException as ex: - self._logger.log(Level.WARNING, "Error processing query result for FB Messenger messages.", ex) - self._logger.log(Level.WARNING, traceback.format_exc()) except TskCoreException as ex: - self._logger.log(Level.SEVERE, "Failed to add FB Messenger message artifacts.", ex) + self._logger.log(Level.SEVERE, "Failed to to create CommunicationArtifactsHelper for FB Messenger.", ex) self._logger.log(Level.SEVERE, traceback.format_exc()) - except BlackboardException as ex: - self._logger.log(Level.WARNING, "Failed to post artifacts.", ex) - self._logger.log(Level.WARNING, traceback.format_exc()) finally: threadsDb.close() @@ -321,6 +516,6 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): return self.analyzeContacts(dataSource, fileManager, context) - self.analyzeMessages(dataSource, fileManager, context) + self.analyzeMessagesAndCallLogs(dataSource, fileManager, context) diff --git a/InternalPythonModules/android/imo.py b/InternalPythonModules/android/imo.py index 25dd750c1e..5a1bd89778 100644 --- a/InternalPythonModules/android/imo.py +++ b/InternalPythonModules/android/imo.py @@ -43,10 +43,14 @@ from org.sleuthkit.datamodel import Content from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.datamodel import Account +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import URLAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection +import json import traceback import general @@ -66,6 +70,8 @@ class IMOAnalyzer(general.AndroidComponentAnalyzer): -- A messages table which stores the message details --- sender/receiver buid, timestamp, message_type (1: incoming, 0: outgoing), message_read... --- 'imdata' column stores a json structure with all the message details, including attachments + ---- attachment file path may be specified in local_path or original_path. Original path, if available is a better candidate. + ---- For sent files, files seem to get uploaded to IMO Servers. There is no URL available in the imdata though. """ @@ -156,7 +162,7 @@ class IMOAnalyzer(general.AndroidComponentAnalyzer): msgReadStatus = MessageReadStatus.UNKNOWN timeStamp = messagesResultSet.getLong("timestamp") / 1000000000 - + msgBody = messagesResultSet.getString("last_message") messageArtifact = friendsDBHelper.addMessage( self._MESSAGE_TYPE, @@ -166,12 +172,34 @@ class IMOAnalyzer(general.AndroidComponentAnalyzer): timeStamp, msgReadStatus, "", # subject - messagesResultSet.getString("last_message"), + msgBody, "") # thread id - - # TBD: parse the imdata JSON structure to figure out if there is an attachment. - # If one exists, add the attachment as a derived file and a child of the message artifact. + + # Parse the imdata JSON structure to check if there is an attachment. + # If one exists, create an attachment and add to the message. + fileAttachments = ArrayList() + urlAttachments = ArrayList() + + imdataJsonStr = messagesResultSet.getString("imdata") + if imdataJsonStr is not None: + imdata_dict = json.loads(imdataJsonStr) + + # set to none if the key doesn't exist in the dict + attachmentOriginalPath = imdata_dict.get('original_path', None) + attachmentLocalPath = imdata_dict.get('local_path', None) + if attachmentOriginalPath: + attachmentPath = attachmentOriginalPath + else: + attachmentPath = attachmentLocalPath + + if attachmentPath: + # Create a file attachment with given path + fileAttachment = FileAttachment(current_case.getSleuthkitCase(), friendsDb.getDBFile().getDataSource(), attachmentPath) + fileAttachments.add(fileAttachment) + + msgAttachments = MessageAttachments(fileAttachments, []) + attachmentArtifact = friendsDBHelper.addAttachments(messageArtifact, msgAttachments) except SQLException as ex: self._logger.log(Level.WARNING, "Error processing query result for IMO friends", ex) diff --git a/InternalPythonModules/android/line.py b/InternalPythonModules/android/line.py index 6edcf158fa..80e9a51b1d 100644 --- a/InternalPythonModules/android/line.py +++ b/InternalPythonModules/android/line.py @@ -43,6 +43,8 @@ from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.autopsy.casemodule import NoCurrentCaseException from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection from TskContactsParser import TskContactsParser @@ -117,7 +119,7 @@ class LineAnalyzer(general.AndroidComponentAnalyzer): current_case.getSleuthkitCase(), self._PARSER_NAME, contact_and_message_db.getDBFile(), Account.Type.LINE) self.parse_contacts(contact_and_message_db, helper) - self.parse_messages(contact_and_message_db, helper) + self.parse_messages(contact_and_message_db, helper, current_case) for calllog_db in calllog_dbs: current_case = Case.getCurrentCaseThrows() @@ -195,22 +197,28 @@ class LineAnalyzer(general.AndroidComponentAnalyzer): "Error posting Line calllog artifacts to blackboard.", ex) self._logger.log(Level.WARNING, traceback.format_exc()) - def parse_messages(self, messages_db, helper): + def parse_messages(self, messages_db, helper, current_case): try: messages_parser = LineMessagesParser(messages_db) while messages_parser.next(): - helper.addMessage( - messages_parser.get_message_type(), - messages_parser.get_message_direction(), - messages_parser.get_phone_number_from(), - messages_parser.get_phone_number_to(), - messages_parser.get_message_date_time(), - messages_parser.get_message_read_status(), - messages_parser.get_message_subject(), - messages_parser.get_message_text(), - messages_parser.get_thread_id() - ) + message_artifact = helper.addMessage( + messages_parser.get_message_type(), + messages_parser.get_message_direction(), + messages_parser.get_phone_number_from(), + messages_parser.get_phone_number_to(), + messages_parser.get_message_date_time(), + messages_parser.get_message_read_status(), + messages_parser.get_message_subject(), + messages_parser.get_message_text(), + messages_parser.get_thread_id() + ) + if (messages_parser.get_file_attachment() is not None): + file_attachments = ArrayList() + file_attachments.add(FileAttachment(current_case.getSleuthkitCase(), messages_db.getDBFile().getDataSource(), messages_parser.get_file_attachment())) + message_attachments = MessageAttachments(file_attachments, []) + helper.addAttachments(message_artifact, message_attachments) + messages_parser.close() except SQLException as ex: self._logger.log(Level.WARNING, "Error parsing the Line App Database for messages.", ex) @@ -357,7 +365,7 @@ class LineMessagesParser(TskMessagesParser): messages.created_time, messages.attachement_type, messages.attachement_local_uri, - messages.status + messages.status FROM (SELECT id, Group_concat(M.m_id) AS members FROM membership AS M @@ -391,11 +399,6 @@ class LineMessagesParser(TskMessagesParser): def get_message_text(self): content = self.result_set.getString("content") - attachment_uri = self.result_set.getString("attachement_local_uri") - if attachment_uri is not None and content is not None: - return general.appendAttachmentList(content, [attachment_uri]) - elif attachment_uri is not None and content is None: - return general.appendAttachmentList("", [attachment_uri]) return content def get_message_direction(self): @@ -426,3 +429,14 @@ class LineMessagesParser(TskMessagesParser): if members is not None: return self.result_set.getString("id") return super(LineMessagesParser, self).get_thread_id() + + def get_file_attachment(self): + if (self.result_set.getString("attachement_local_uri") is None): + return None + # If "content:" in the beginning of the string we cannot determine at this point where a file resides. Ignoring for + # now unless data can be obtained to determine where the file may reside. + elif ("content:" in self.result_set.getString("attachement_local_uri")): + return None + else: + return self.result_set.getString("attachement_local_uri") + \ No newline at end of file diff --git a/InternalPythonModules/android/shareit.py b/InternalPythonModules/android/shareit.py index 523155a78d..c80dbaec10 100644 --- a/InternalPythonModules/android/shareit.py +++ b/InternalPythonModules/android/shareit.py @@ -43,6 +43,8 @@ from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection @@ -96,6 +98,7 @@ class ShareItAnalyzer(general.AndroidComponentAnalyzer): direction = "" fromId = None toId = None + fileAttachments = ArrayList() if (historyResultSet.getInt("history_type") == 1): direction = CommunicationDirection.INCOMING @@ -104,10 +107,6 @@ class ShareItAnalyzer(general.AndroidComponentAnalyzer): direction = CommunicationDirection.OUTGOING toId = historyResultSet.getString("device_id") - msgBody = "" # there is no body. - attachments = [historyResultSet.getString("file_path")] - msgBody = general.appendAttachmentList(msgBody, attachments) - timeStamp = historyResultSet.getLong("timestamp") / 1000 messageArtifact = historyDbHelper.addMessage( self._MESSAGE_TYPE, @@ -117,10 +116,14 @@ class ShareItAnalyzer(general.AndroidComponentAnalyzer): timeStamp, MessageReadStatus.UNKNOWN, None, # subject - msgBody, + None, # message text None ) # thread id - # TBD: add the file as attachment ?? + # add the file as attachment + fileAttachments.add(FileAttachment(current_case.getSleuthkitCase(), historyDb.getDBFile().getDataSource(), historyResultSet.getString("file_path"))) + messageAttachments = MessageAttachments(fileAttachments, []) + historyDbHelper.addAttachments(messageArtifact, messageAttachments) + except SQLException as ex: self._logger.log(Level.WARNING, "Error processing query result for ShareIt history.", ex) diff --git a/InternalPythonModules/android/skype.py b/InternalPythonModules/android/skype.py index d8b79ac7fe..721fc92421 100644 --- a/InternalPythonModules/android/skype.py +++ b/InternalPythonModules/android/skype.py @@ -46,6 +46,8 @@ from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from TskMessagesParser import TskMessagesParser from TskContactsParser import TskContactsParser from TskCallLogsParser import TskCallLogsParser @@ -137,7 +139,7 @@ class SkypeAnalyzer(general.AndroidComponentAnalyzer): ) self.parse_contacts(skype_db, helper) self.parse_calllogs(skype_db, helper) - self.parse_messages(skype_db, helper) + self.parse_messages(skype_db, helper, current_case) except NoCurrentCaseException as ex: self._logger.log(Level.WARNING, "No case currently open.", ex) self._logger.log(Level.WARNING, traceback.format_exc()) @@ -209,23 +211,30 @@ class SkypeAnalyzer(general.AndroidComponentAnalyzer): "Failed to post call log artifact to the blackboard", ex) self._logger.log(Level.WARNING, traceback.format_exc()) - def parse_messages(self, skype_db, helper): + def parse_messages(self, skype_db, helper, current_case): #Query for messages and iterate row by row adding #each message artifact try: messages_parser = SkypeMessagesParser(skype_db) while messages_parser.next(): - helper.addMessage( - messages_parser.get_message_type(), - messages_parser.get_message_direction(), - messages_parser.get_phone_number_from(), - messages_parser.get_phone_number_to(), - messages_parser.get_message_date_time(), - messages_parser.get_message_read_status(), - messages_parser.get_message_subject(), - messages_parser.get_message_text(), - messages_parser.get_thread_id() - ) + message_artifact = helper.addMessage( + messages_parser.get_message_type(), + messages_parser.get_message_direction(), + messages_parser.get_phone_number_from(), + messages_parser.get_phone_number_to(), + messages_parser.get_message_date_time(), + messages_parser.get_message_read_status(), + messages_parser.get_message_subject(), + messages_parser.get_message_text(), + messages_parser.get_thread_id() + ) + + if (messages_parser.get_file_attachment() is not None): + file_attachments = ArrayList() + file_attachments.add(FileAttachment(current_case.getSleuthkitCase(), skype_db.getDBFile().getDataSource(), messages_parser.get_file_attachment())) + message_attachments = MessageAttachments(file_attachments, []) + helper.addAttachments(message_artifact, message_attachments) + messages_parser.close() except SQLException as ex: #Error parsing Skype db @@ -425,12 +434,6 @@ class SkypeMessagesParser(TskMessagesParser): content = self.result_set.getString("content") if content is not None: - file_path = self.result_set.getString("device_gallery_path") - - #if a file name and file path are associated with a message, append it - if file_path is not None: - return general.appendAttachmentList(content, [file_path]) - return content return super(SkypeMessagesParser, self).get_message_text() @@ -440,3 +443,11 @@ class SkypeMessagesParser(TskMessagesParser): if group_ids is not None: return self.result_set.getString("conversation_id") return super(SkypeMessagesParser, self).get_thread_id() + + + def get_file_attachment(self): + if (self.result_set.getString("device_gallery_path") is None): + return None + else: + return self.result_set.getString("device_gallery_path") + diff --git a/InternalPythonModules/android/textnow.py b/InternalPythonModules/android/textnow.py index 1890c7ae42..071cdcb137 100644 --- a/InternalPythonModules/android/textnow.py +++ b/InternalPythonModules/android/textnow.py @@ -44,6 +44,8 @@ from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.autopsy.casemodule import NoCurrentCaseException from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection @@ -93,7 +95,7 @@ class TextNowAnalyzer(general.AndroidComponentAnalyzer): ) self.parse_contacts(textnow_db, helper) self.parse_calllogs(textnow_db, helper) - self.parse_messages(textnow_db, helper) + self.parse_messages(textnow_db, helper, current_case) except NoCurrentCaseException as ex: self._logger.log(Level.WARNING, "No case currently open.", ex) self._logger.log(Level.WARNING, traceback.format_exc()) @@ -159,23 +161,30 @@ class TextNowAnalyzer(general.AndroidComponentAnalyzer): "Error posting TextNow call log artifact to the blackboard", ex) self._logger.log(Level.WARNING, traceback.format_exc()) - def parse_messages(self, textnow_db, helper): + def parse_messages(self, textnow_db, helper, current_case): #Query for messages and iterate row by row adding #each message artifact try: messages_parser = TextNowMessagesParser(textnow_db) while messages_parser.next(): - helper.addMessage( - messages_parser.get_message_type(), - messages_parser.get_message_direction(), - messages_parser.get_phone_number_from(), - messages_parser.get_phone_number_to(), - messages_parser.get_message_date_time(), - messages_parser.get_message_read_status(), - messages_parser.get_message_subject(), - messages_parser.get_message_text(), - messages_parser.get_thread_id() - ) + message_artifact = helper.addMessage( + messages_parser.get_message_type(), + messages_parser.get_message_direction(), + messages_parser.get_phone_number_from(), + messages_parser.get_phone_number_to(), + messages_parser.get_message_date_time(), + messages_parser.get_message_read_status(), + messages_parser.get_message_subject(), + messages_parser.get_message_text(), + messages_parser.get_thread_id() + ) + if (len(messages_parser.get_file_attachment()) > 0): + file_attachments = ArrayList() + self._logger.log(Level.INFO, "SHow Attachment ==> " + str(len(messages_parser.get_file_attachment())) + " <> " + str(messages_parser.get_file_attachment())) + file_attachments.add(FileAttachment(current_case.getSleuthkitCase(), textnow_db.getDBFile().getDataSource(), messages_parser.get_file_attachment())) + message_attachments = MessageAttachments(file_attachments, []) + helper.addAttachments(message_artifact, message_attachments) + messages_parser.close() except SQLException as ex: #Error parsing TextNow db @@ -364,9 +373,6 @@ class TextNowMessagesParser(TskMessagesParser): def get_message_text(self): text = self.result_set.getString("message_text") - attachment = self.result_set.getString("attach") - if attachment != "": - text = general.appendAttachmentList(text, [attachment]) return text def get_thread_id(self): @@ -374,3 +380,9 @@ class TextNowMessagesParser(TskMessagesParser): if thread_id is None: return super(TextNowMessagesParser, self).get_thread_id() return thread_id + + def get_file_attachment(self): + attachment = self.result_set.getString("attach") + if attachment is None: + return None + return self.result_set.getString("attach") diff --git a/InternalPythonModules/android/viber.py b/InternalPythonModules/android/viber.py index f922b3508e..4f4f8dd3f8 100644 --- a/InternalPythonModules/android/viber.py +++ b/InternalPythonModules/android/viber.py @@ -43,6 +43,8 @@ from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.autopsy.casemodule import NoCurrentCaseException from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection from TskMessagesParser import TskMessagesParser @@ -99,7 +101,7 @@ class ViberAnalyzer(general.AndroidComponentAnalyzer): helper = CommunicationArtifactsHelper( current_case.getSleuthkitCase(), self._PARSER_NAME, message_db.getDBFile(), Account.Type.VIBER) - self.parse_messages(message_db, helper) + self.parse_messages(message_db, helper, current_case) except NoCurrentCaseException as ex: self._logger.log(Level.WARNING, "No case currently open.", ex) @@ -161,21 +163,26 @@ class ViberAnalyzer(general.AndroidComponentAnalyzer): self._logger.log(Level.WARNING, traceback.format_exc()) - def parse_messages(self, messages_db, helper): + def parse_messages(self, messages_db, helper, current_case): try: messages_parser = ViberMessagesParser(messages_db) while messages_parser.next(): - helper.addMessage( - messages_parser.get_message_type(), - messages_parser.get_message_direction(), - messages_parser.get_phone_number_from(), - messages_parser.get_phone_number_to(), - messages_parser.get_message_date_time(), - messages_parser.get_message_read_status(), - messages_parser.get_message_subject(), - messages_parser.get_message_text(), - messages_parser.get_thread_id() - ) + message_artifact = helper.addMessage( + messages_parser.get_message_type(), + messages_parser.get_message_direction(), + messages_parser.get_phone_number_from(), + messages_parser.get_phone_number_to(), + messages_parser.get_message_date_time(), + messages_parser.get_message_read_status(), + messages_parser.get_message_subject(), + messages_parser.get_message_text(), + messages_parser.get_thread_id() + ) + if (messages_parser.get_file_attachment() is not None): + file_attachments = ArrayList() + file_attachments.add(FileAttachment(current_case.getSleuthkitCase(), messages_db.getDBFile().getDataSource(), messages_parser.get_file_attachment())) + message_attachments = MessageAttachments(file_attachments, []) + helper.addAttachments(message_artifact, message_attachments) messages_parser.close() except SQLException as ex: self._logger.log(Level.WARNING, "Error querying the viber database for messages.", ex) @@ -304,7 +311,8 @@ class ViberMessagesParser(TskMessagesParser): M.body AS msg_content, M.send_type AS direction, M.msg_date AS msg_date, - M.unread AS read_status + M.unread AS read_status, + M.extra_uri AS file_attachment FROM (SELECT *, group_concat(TO_RESULT.number) AS recipients FROM (SELECT P._id AS FROM_ID, @@ -364,3 +372,11 @@ class ViberMessagesParser(TskMessagesParser): def get_thread_id(self): return str(self.result_set.getInt("thread_id")) + + def get_file_attachment(self): + if (self.result_set.getString("file_attachment") is None): + return None + elif ("content:" in self.result_set.getString("file_attachment")): + return self.result_set.getString("msg_content").replace("file://", "") + else: + return self.result_set.getString("file_attachment").replace("file://", "") diff --git a/InternalPythonModules/android/whatsapp.py b/InternalPythonModules/android/whatsapp.py index 438784f3e2..a91c21375b 100644 --- a/InternalPythonModules/android/whatsapp.py +++ b/InternalPythonModules/android/whatsapp.py @@ -45,6 +45,9 @@ from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import URLAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from TskMessagesParser import TskMessagesParser from TskContactsParser import TskContactsParser from TskCallLogsParser import TskCallLogsParser @@ -151,7 +154,7 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer): current_case.getSleuthkitCase(), self._PARSER_NAME, calllog_and_message_db.getDBFile(), Account.Type.WHATSAPP) self.parse_calllogs(calllog_and_message_db, helper) - self.parse_messages(dataSource, calllog_and_message_db, helper) + self.parse_messages(dataSource, calllog_and_message_db, helper, current_case) except NoCurrentCaseException as ex: #If there is no current case, bail out immediately. @@ -227,24 +230,32 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer): "Error posting calllog artifact to the blackboard.", ex) self._logger.log(Level.WARNING, traceback.format_exc()) - def parse_messages(self, dataSource, messages_db, helper): + def parse_messages(self, dataSource, messages_db, helper, current_case): try: messages_db.attachDatabase(dataSource, "wa.db", messages_db.getDBFile().getParentPath(), "wadb") messages_parser = WhatsAppMessagesParser(messages_db) while messages_parser.next(): - helper.addMessage( - messages_parser.get_message_type(), - messages_parser.get_message_direction(), - messages_parser.get_phone_number_from(), - messages_parser.get_phone_number_to(), - messages_parser.get_message_date_time(), - messages_parser.get_message_read_status(), - messages_parser.get_message_subject(), - messages_parser.get_message_text(), - messages_parser.get_thread_id() - ) + message_artifact = helper.addMessage( + messages_parser.get_message_type(), + messages_parser.get_message_direction(), + messages_parser.get_phone_number_from(), + messages_parser.get_phone_number_to(), + messages_parser.get_message_date_time(), + messages_parser.get_message_read_status(), + messages_parser.get_message_subject(), + messages_parser.get_message_text(), + messages_parser.get_thread_id() + ) + + # add attachments, if any + if (messages_parser.get_url_attachment() is not None): + url_attachments = ArrayList() + url_attachments.add(URLAttachment(messages_parser.get_url_attachment())) + message_attachments = MessageAttachments([], url_attachments) + helper.addAttachments(message_artifact, message_attachments) + messages_parser.close() except SQLException as ex: self._logger.log(Level.WARNING, "Error querying the whatsapp database for contacts.", ex) @@ -502,9 +513,6 @@ class WhatsAppMessagesParser(TskMessagesParser): message = self.result_set.getString("content") if message is None: message = super(WhatsAppMessagesParser, self).get_message_text() - attachment = self.result_set.getString("attachment") - if attachment is not None: - return general.appendAttachmentList(message, [attachment]) return message def get_thread_id(self): @@ -512,3 +520,14 @@ class WhatsAppMessagesParser(TskMessagesParser): if group is not None: return self.result_set.getString("id") return super(WhatsAppMessagesParser, self).get_thread_id() + + + def get_url_attachment(self): + attachment = self.result_set.getString("attachment") + if (attachment is None): + return None + elif (str(attachment).startswith("http:") or str(attachment).startswith("https:") ): + return attachment + else: + return None + diff --git a/InternalPythonModules/android/xender.py b/InternalPythonModules/android/xender.py index 9600788f26..e8c2b3d74f 100644 --- a/InternalPythonModules/android/xender.py +++ b/InternalPythonModules/android/xender.py @@ -43,6 +43,8 @@ from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection import traceback @@ -96,7 +98,7 @@ class XenderAnalyzer(general.AndroidComponentAnalyzer): Account.Type.XENDER) queryString = """ - SELECT f_path, f_display_name, f_size_str, f_create_time, c_direction, c_session_id, + SELECT f_path, f_display_name, f_size_str, c_start_time, c_direction, c_session_id, s_name, s_device_id, r_name, r_device_id FROM new_history """ @@ -107,6 +109,8 @@ class XenderAnalyzer(general.AndroidComponentAnalyzer): fromId = None toId = None + fileAttachments = ArrayList() + if (messagesResultSet.getInt("c_direction") == 1): direction = CommunicationDirection.OUTGOING toId = messagesResultSet.getString("r_device_id") @@ -114,11 +118,7 @@ class XenderAnalyzer(general.AndroidComponentAnalyzer): direction = CommunicationDirection.INCOMING fromId = messagesResultSet.getString("s_device_id") - msgBody = "" # there is no body. - attachments = [messagesResultSet.getString("f_path")] - msgBody = general.appendAttachmentList(msgBody, attachments) - - timeStamp = messagesResultSet.getLong("f_create_time") / 1000 + timeStamp = messagesResultSet.getLong("c_start_time") / 1000 messageArtifact = transactionDbHelper.addMessage( self._MESSAGE_TYPE, direction, @@ -127,10 +127,13 @@ class XenderAnalyzer(general.AndroidComponentAnalyzer): timeStamp, MessageReadStatus.UNKNOWN, None, # subject - msgBody, + None, # message text messagesResultSet.getString("c_session_id") ) - # TBD: add the file as attachment ?? + # add the file as attachment + fileAttachments.add(FileAttachment(current_case.getSleuthkitCase(), transactionDb.getDBFile().getDataSource(), messagesResultSet.getString("f_path"))) + messageAttachments = MessageAttachments(fileAttachments, []) + transactionDbHelper.addAttachments(messageArtifact, messageAttachments) except SQLException as ex: self._logger.log(Level.WARNING, "Error processing query result for profiles.", ex) diff --git a/InternalPythonModules/android/zapya.py b/InternalPythonModules/android/zapya.py index 6accd02eea..8843e772c7 100644 --- a/InternalPythonModules/android/zapya.py +++ b/InternalPythonModules/android/zapya.py @@ -43,6 +43,8 @@ from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection @@ -88,6 +90,7 @@ class ZapyaAnalyzer(general.AndroidComponentAnalyzer): direction = CommunicationDirection.UNKNOWN fromId = None toId = None + fileAttachments = ArrayList() if (transfersResultSet.getInt("direction") == 1): direction = CommunicationDirection.OUTGOING @@ -95,10 +98,6 @@ class ZapyaAnalyzer(general.AndroidComponentAnalyzer): else: direction = CommunicationDirection.INCOMING fromId = transfersResultSet.getString("device") - - msgBody = "" # there is no body. - attachments = [transfersResultSet.getString("path")] - msgBody = general.appendAttachmentList(msgBody, attachments) timeStamp = transfersResultSet.getLong("createtime") / 1000 messageArtifact = transferDbHelper.addMessage( @@ -109,10 +108,13 @@ class ZapyaAnalyzer(general.AndroidComponentAnalyzer): timeStamp, MessageReadStatus.UNKNOWN, None, # subject - msgBody, + None, # message Text None ) # thread id - # TBD: add the file as attachment ?? + # add the file as attachment + fileAttachments.add(FileAttachment(current_case.getSleuthkitCase(), transferDb.getDBFile().getDataSource(), transfersResultSet.getString("path"))) + messageAttachments = MessageAttachments(fileAttachments, []) + transferDbHelper.addAttachments(messageArtifact, messageAttachments) except SQLException as ex: self._logger.log(Level.WARNING, "Error processing query result for transfer.", ex) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index df3db1bf73..06e35314ce 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -298,6 +298,7 @@ GlobalEditListPanel.editWordButton.text=Edit Keyword SolrSearchService.ServiceName=Solr Keyword Search Service SolrSearchService.IndexReadOnlyDialog.title=Text Index Is Read-Only SolrSearchService.IndexReadOnlyDialog.msg=The text index for this case is read-only.
    You will be able to see existing keyword search results and perform exact match and substring match keyword searches,
    but you will not be able to add new text to the index or perform regex searches. You may instead open the case
    with your previous version of this application. +SolrSearchService.DeleteDataSource.msg=Error Deleting Solr data for data source id {0} ExtractedContentPanel.jLabel1.text=Text Source: ExtractedContentPanel.pagePreviousButton.actionCommand=pagePreviousButton ExtractedContentPanel.pagePreviousButton.text= diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED index 5cfd965ac8..d2fe0dd2db 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED @@ -357,6 +357,7 @@ SolrSearchService.indexingError=Unable to index blackboard artifact. SolrSearchService.ServiceName=Solr Keyword Search Service SolrSearchService.IndexReadOnlyDialog.title=Text Index Is Read-Only SolrSearchService.IndexReadOnlyDialog.msg=The text index for this case is read-only.
    You will be able to see existing keyword search results and perform exact match and substring match keyword searches,
    but you will not be able to add new text to the index or perform regex searches. You may instead open the case
    with your previous version of this application. +SolrSearchService.DeleteDataSource.msg=Error Deleting Solr data for data source id {0} ExtractedContentPanel.jLabel1.text=Text Source: ExtractedContentPanel.pagePreviousButton.actionCommand=pagePreviousButton ExtractedContentPanel.pagePreviousButton.text= diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties index 88f7abd38b..adf1364da2 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties @@ -1,136 +1,187 @@ +AccountsText.creditCardNumber=\u30af\u30ec\u30b8\u30c3\u30c8\u30ab\u30fc\u30c9\u756a\u53f7 +AccountsText.creditCardNumbers=\u30af\u30ec\u30b8\u30c3\u30c8\u30ab\u30fc\u30c9\u756a\u53f7 +AccountsText.nextItem.exception.msg=\u6b21\u306e\u9805\u76ee\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +AccountsText.nextPage.exception.msg=\u6b21\u306e\u30da\u30fc\u30b8\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +AccountsText.previousItem.exception.msg=\u524d\u306e\u9805\u76ee\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +AccountsText.previousPage.exception.msg=\u524d\u306e\u30da\u30fc\u30b8\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +CannotRunFileTypeDetection=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u691c\u51fa\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 +DropdownListSearchPanel.selected=\u30a2\u30c9\u30db\u30c3\u30af\u691c\u7d22\u30bd\u30fc\u30b9\u30d5\u30a3\u30eb\u30bf\u30fc\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u3059 +DropdownSingleTermSearchPanel.selected=\u30a2\u30c9\u30db\u30c3\u30af\u691c\u7d22\u30bd\u30fc\u30b9\u30d5\u30a3\u30eb\u30bf\u30fc\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u3059 +DropdownSingleTermSearchPanel.warning.text=\u5883\u754c\u6587\u5b57 ^ \u3068 $ \u304c\u5358\u8a9e\u9818\u57df\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093\u3002[ \\.,] \u306a\u3069\u306e\u660e\u793a\u7684\u306a\u5883\u754c\u6587\u5b57\u30ea\u30b9\u30c8\u3068\u306e\u7f6e\u63db\u3092\n\u691c\u8a0e\u3057\u3066\u304f\u3060\u3055\u3044 +DropdownSingleTermSearchPanel.warning.title=\u8b66\u544a +ExtractedContentPanel.setMarkup.panelTxt=\u30c6\u30ad\u30b9\u30c8\u3092\u8aad\u307f\u8fbc\u3093\u3067\u3044\u307e\u3059...\u304a\u5f85\u3061\u304f\u3060\u3055\u3044 +# {0} - \u30b3\u30f3\u30c6\u30f3\u30c4\u540d +ExtractedContentPanel.SetMarkup.progress.loading={0} \u306e\u30c6\u30ad\u30b9\u30c8\u3092\u8aad\u307f\u8fbc\u3093\u3067\u3044\u307e\u3059 +GlobalEditListPanel.editKeyword.title=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u7de8\u96c6 +GlobalEditListPanel.warning.text=\u5883\u754c\u6587\u5b57 ^ \u3068 $ \u304c\u5358\u8a9e\u9818\u57df\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093\u3002[ \\.,] \u306a\u3069\u306e\u660e\u793a\u7684\u306a\u5883\u754c\u6587\u5b57\u30ea\u30b9\u30c8\u3068\u306e\u7f6e\u63db\u3092\n\u691c\u8a0e\u3057\u3066\u304f\u3060\u3055\u3044 +GlobalEditListPanel.warning.title=\u8b66\u544a +IndexedText.errorMessage.errorGettingText=Error \u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +IndexedText.warningMessage.knownFile=\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u306f\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb(MD5\u30cf\u30c3\u30b7\u30e5\u30d9\u30fc\u30b9)\u3067\u3042\u308b\u305f\u3081\u3001\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +IndexedText.warningMessage.noTextAvailable=\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u306b\u306f\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +KeywordSearchGlobalSettingsPanel.Title=\u30ad\u30fc\u30ef\u30fc\u30c9\u4e00\u62ec\u691c\u7d22\u8a2d\u5b9a +KeywordSearchIngestModule.init.badInitMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d0\u30fc\u304c\u6b63\u3057\u304f\u521d\u671f\u5316\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 +# {0} - Solr\u306b\u63a5\u7d9a\u3055\u308c\u3066\u3044\u306a\u3044\u7406\u7531 +KeywordSearchIngestModule.init.exception.errConnToSolr.msg=Error connecting to SOLR\u30b5\u30fc\u30d0\u30fc\u306b\u63a5\u7d9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}\u3002 +# {0} - Solr\u304c\u8d77\u52d5\u3057\u306a\u3044\u7406\u7531 +KeywordSearchIngestModule.init.tryStopSolrMsg={0}
    Java Solr\u30d7\u30ed\u30bb\u30b9\u304c\u5b58\u5728\u3059\u308b\u5834\u5408\u306f\u505c\u6b62\u3092\u8a66\u307f\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u518d\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +KeywordSearchIngestModule.metadataTitle=\u30e1\u30bf\u30c7\u30fc\u30bf +KeywordSearchIngestModule.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +KeywordSearchIngestModule.startUp.noOpenCore.msg=\u7d22\u5f15\u3092\u958b\u3051\u306a\u304b\u3063\u305f\u304b\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +# {0} - \u30b9\u30ad\u30fc\u30de\u30d0\u30fc\u30b8\u30e7\u30f3\u756a\u53f7 +KeywordSearchIngestModule.startupException.indexSchemaNotSupported=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u306e\u30b9\u30ad\u30fc\u30de\u30d0\u30fc\u30b8\u30e7\u30f3 {0} \u3067\u306e\u30c6\u30ad\u30b9\u30c8\u8ffd\u52a0\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u306a\u304f\u306a\u308a\u307e\u3057\u305f\u3002 +# {0} - Solr\u30d0\u30fc\u30b8\u30e7\u30f3\u756a\u53f7 +KeywordSearchIngestModule.startupException.indexSolrVersionNotSupported=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u306eSolr\u30d0\u30fc\u30b8\u30e7\u30f3 {0} \u3067\u306e\u30c6\u30ad\u30b9\u30c8\u8ffd\u52a0\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u306a\u304f\u306a\u308a\u307e\u3057\u305f\u3002 +KeywordSearchIngestModule.startupMessage.failedToGetIndexSchema=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u306e\u30b9\u30ad\u30fc\u30de\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=\u8a72\u5f53\u3059\u308b\u7d50\u679c\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +KeywordSearchResultFactory.query.exception.msg=\u30af\u30a8\u30ea\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f OpenIDE-Module-Display-Category=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb -OpenIDE-Module-Long-Description=\ - \u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\n\n\ - \u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u306e\u30c7\u30a3\u30b9\u30af\u30a4\u30e1\u30fc\u30b8\u306b\u3042\u308b\u30d5\u30a1\u30a4\u30eb\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3057\u307e\u3059\u3002\n\ - \u305d\u3057\u3066\u3001\u4e00\u3064\u4ee5\u4e0a\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\uff08\u5358\u8a9e\u3084\u6b63\u898f\u8868\u73fe\u3092\u542b\u3080\uff09\u3092\u5229\u7528\u3057\u3001\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u5b9a\u671f\u7684\u306b\u691c\u7d22\u3057\u3001\u7d50\u679c\u3092\u8868\u793a\u3057\u307e\u3059\u3002\n\n\ - \u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306b\u306f\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u8a2d\u5b9a\u3001\u53f3\u4e0a\u9685\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30d0\u30fc\u3001\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u306e\u7d50\u679c\u3092\u30cf\u30a4\u30e9\u30a4\u30c8\u3057\u3066\u8868\u793a\u3059\u308b\u62bd\u51fa\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8\u30d3\u30e5\u30fc\u30a2\u3068\u691c\u7d22\u7d50\u679c\u30d3\u30e5\u30fc\u30a2\u306a\u3069\u3001\u30e1\u30a4\u30f3GUI\u3068\u9023\u643a\u3059\u308b\u8ffd\u52a0\u306e\u30c4\u30fc\u30eb\u304c\u542b\u307e\u308c\u307e\u3059\u3002 -OpenIDE-Module-Name=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 + +OpenIDE-Module-Long-Description=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3002\n\n\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u306b\u30c7\u30a3\u30b9\u30af\u30a4\u30e1\u30fc\u30b8\u3067\u898b\u3064\u304b\u3063\u305f\u30c6\u30ad\u30b9\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3057\u307e\u3059\u3002\n\u6b21\u306b\u30011\u3064\u4ee5\u4e0a\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8(\u7d14\u7c8b\u8a9e\u304a\u3088\u3073/\u307e\u305f\u306f\u6b63\u898f\u8868\u73fe\u3092\u542b\u3080)\u3067\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u4e0a\u3067\u5b9a\u671f\u7684\u306b\u691c\u7d22\u3092\u5b9f\u65bd\u3057\u3001\u7d50\u679c\u3092\u63b2\u8f09\u3057\u307e\u3059\u3002\n\n\\u30e2\u30b8\u30e5\u30fc\u30eb\u306b\u306f\u3001\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u69cb\u6210\u3001\u53f3\u4e0a\u7aef\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30d0\u30fc\u3001\u62bd\u51fa\u30c6\u30ad\u30b9\u30c8\u30d3\u30e5\u30fc\u30ef\u30fc\u3001\u8a72\u5f53\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u30cf\u30a4\u30e9\u30a4\u30c8\u8868\u793a\u3059\u308b\u691c\u7d22\u7d50\u679c\u30d3\u30e5\u30fc\u30ef\u30fc\u306a\u3069\u3001\u30e1\u30a4\u30f3GUI\u306b\u7d71\u5408\u3055\u308c\u305f\u8ffd\u52a0\u306e\u30c4\u30fc\u30eb\u3082\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002 +OpenIDE-Module-Name=KeywordSearch +OptionsCategory_Name_KeywordSearchOptions=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 +OptionsCategory_Keywords_KeywordSearchOptions=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 ListBundleName=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 -ListBundleConfig=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u8a2d\u5b9a -ExtractedContentPanel.hitLabel.text=\u30da\u30fc\u30b8\u5185\u306e\u4e00\u81f4\uff1a +ListBundleConfig=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u69cb\u6210 +ExtractedContentPanel.hitLabel.text=\u30da\u30fc\u30b8\u4e0a\u306e\u4e00\u81f4\u3059\u308b\u7d50\u679c: ExtractedContentPanel.hitCountLabel.text=- -ExtractedContentPanel.hitOfLabel.text=of +ExtractedContentPanel.hitOfLabel.text=/ ExtractedContentPanel.hitTotalLabel.text=- -ExtractedContentPanel.hitButtonsLabel.text=\u4e00\u81f4 +ExtractedContentPanel.hitButtonsLabel.text=\u4e00\u81f4\u3059\u308b\u7d50\u679c +ExtractedContentPanel.hitPreviousButton.text= +ExtractedContentPanel.hitNextButton.text= ExtractedContentPanel.copyMenuItem.text=\u30b3\u30d4\u30fc ExtractedContentPanel.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e KeywordSearchEditListPanel.saveListButton.text=\u30ea\u30b9\u30c8\u3092\u30b3\u30d4\u30fc -KeywordSearchEditListPanel.addWordButton.text=\u8ffd\u52a0 +KeywordSearchEditListPanel.addWordField.text= +KeywordSearchEditListPanel.addWordButton.text=\u65b0\u898f\u30ad\u30fc\u30ef\u30fc\u30c9 KeywordSearchEditListPanel.chRegex.text=\u6b63\u898f\u8868\u73fe -KeywordSearchEditListPanel.deleteWordButton.text=\u9078\u629e\u3057\u305f\u3082\u306e\u3092\u524a\u9664 -KeywordSearchEditListPanel.cutMenuItem.text=\u30ab\u30c3\u30c8 +KeywordSearchEditListPanel.deleteWordButton.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u524a\u9664 +KeywordSearchEditListPanel.cutMenuItem.text=\u5207\u308a\u53d6\u308a KeywordSearchEditListPanel.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e KeywordSearchEditListPanel.pasteMenuItem.text=\u8cbc\u308a\u4ed8\u3051 KeywordSearchEditListPanel.copyMenuItem.text=\u30b3\u30d4\u30fc KeywordSearchEditListPanel.exportButton.text=\u30ea\u30b9\u30c8\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 KeywordSearchEditListPanel.deleteListButton.text=\u30ea\u30b9\u30c8\u3092\u524a\u9664 +KeywordSearchEditListPanel.emptyKeyword.text=\u7a7a(\u672a\u5165\u529b)\u306e\u72b6\u614b\u306e\u30ad\u30fc\u30ef\u30fc\u30c9 +KeywordSearchEditListPanel.errorAddingKeywords.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u306e\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f KeywordSearchListsManagementPanel.newListButton.text=\u65b0\u898f\u30ea\u30b9\u30c8 KeywordSearchListsManagementPanel.importButton.text=\u30ea\u30b9\u30c8\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 KeywordSearchListsViewerPanel.searchAddButton.text=\u691c\u7d22 KeywordSearchListsViewerPanel.manageListsButton.text=\u30ea\u30b9\u30c8\u3092\u7ba1\u7406 -KeywordSearchListsViewerPanel.ingestIndexLabel.text=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\uff1a -KeywordSearchEditListPanel.ingestMessagesCheckbox.text=\u30d2\u30c3\u30c8\u6bce\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30a4\u30f3\u30dc\u30c3\u30af\u30b9\u3078\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u4fe1 -KeywordSearchEditListPanel.ingestMessagesCheckbox.toolTipText=\u3053\u306e\u30ea\u30b9\u30c8\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u304c\u691c\u7d22\u306b\u30d2\u30c3\u30c8\u3057\u305f\u5834\u5408\u3001\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306b\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u30a4\u30f3\u30dc\u30c3\u30af\u30b9\u306b\u9001\u4fe1 +KeywordSearchListsViewerPanel.ingestIndexLabel.text=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb: +ExtractedContentPanel.hitLabel.toolTipText= +KeywordSearchEditListPanel.ingestMessagesCheckbox.text=\u30d2\u30c3\u30c8\u3054\u3068\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u53d7\u4fe1\u7bb1\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u4fe1 +KeywordSearchEditListPanel.ingestMessagesCheckbox.toolTipText=\u3053\u306e\u30ea\u30b9\u30c8\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u3067\u30d2\u30c3\u30c8\u304c\u767a\u751f\u3057\u305f\u3068\u304d\u306b\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306b\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u4fe1 KeywordSearchEditListPanel.keywordOptionsLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30aa\u30d7\u30b7\u30e7\u30f3 KeywordSearchEditListPanel.listOptionsLabel.text=\u30ea\u30b9\u30c8\u30aa\u30d7\u30b7\u30e7\u30f3 -KeywordSearchListsManagementPanel.keywordListsLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\uff1a -KeywordSearchEditListPanel.keywordsLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9\uff1a -OpenIDE-Module-Short-Description=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3001\u62bd\u51fa\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8\u30d3\u30e5\u30fc\u30a2\u3001\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30c4\u30fc\u30eb -KeywordSearchListsViewerPanel.manageListsButton.toolTipText=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3001\u30ea\u30b9\u30c8\u306e\u8a2d\u5b9a\u3068\u95a2\u9023\u3059\u308b\u30ad\u30fc\u30ef\u30fc\u30c9\u306e\u7ba1\u7406\u3002\u3053\u306e\u8a2d\u5b9a\u306f\u5168\u3066\u306e\u30b1\u30fc\u30b9\u306b\u9069\u7528\u3055\u308c\u307e\u3059\u3002 +KeywordSearchListsManagementPanel.keywordListsLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8: +KeywordSearchEditListPanel.keywordsLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9: +OpenIDE-Module-Short-Description=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3001\u62bd\u51fa\u30c6\u30ad\u30b9\u30c8\u30d3\u30e5\u30fc\u30ef\u30fc\u304a\u3088\u3073\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30c4\u30fc\u30eb +KeywordSearchListsViewerPanel.manageListsButton.toolTipText=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3001\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u306e\u8a2d\u5b9a\u3001\u95a2\u9023\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u7ba1\u7406\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u306f\u3059\u3079\u3066\u306e\u30b1\u30fc\u30b9\u3067\u5171\u6709\u3055\u308c\u307e\u3059\u3002 AbstractKeywordSearchPerformer.search.dialogErrorHeader=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a8\u30e9\u30fc -AbstractKeywordSearchPerformer.search.searchIngestInProgressTitle=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u4e2d -AbstractKeywordSearchPerformer.search.ingestInProgressBody=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u4e2d
    \u5168\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u304c\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u691c\u7d22\u7d50\u679c\u304c\u4e0d\u5b8c\u5168\u306b\u306a\u308b\u5834\u5408\u304c\u3042\u308a\u307e\u3059\u3002
    \u3053\u306e\u691c\u7d22\u3092\u5b9f\u884c\u3057\u307e\u3059\u304b\uff1f -AbstractKeywordSearchPerformer.search.emptyKeywordErrorBody=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u304c\u7a7a\u767d\u3067\u3059\u3002\u6700\u4f4e\uff11\u3064\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u30ea\u30b9\u30c8\u306b\u8ffd\u52a0\u3057\u3066\u4e0b\u3055\u3044\u3002 -AbstractKeywordSearchPerformer.search.noFilesInIdxMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306b\u307e\u3060\u30d5\u30a1\u30a4\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\u3002
    \u3057\u3070\u3089\u304f\u3057\u3066\u304b\u3089\u518d\u5ea6\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306f{0}\u5206\u3054\u3068\u306b\u66f4\u65b0\u3055\u308c\u307e\u3059\u3002 -AbstractKeywordSearchPerformer.search.noFilesIdxdMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\u3002
    \u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u6709\u52b9\u5316\u3057\u3066\u30a4\u30e1\u30fc\u30b8\u3092\u518d\u5ea6\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3002 -ExtractedContentPanel.setMarkup.panelTxt=\u30c6\u30ad\u30b9\u30c8\u30ed\u30fc\u30c9\u4e2d...\u3057\u3070\u3089\u304f\u304a\u5f85\u3061\u304f\u3060\u3055\u3044\u3002 -ExtractedContentViewer.toolTip=\u30d5\u30a1\u30a4\u30eb\u3084\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u7d50\u679c\u304b\u3089\u62bd\u51fa\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8\u3092\u8868\u793a\u3002\u3053\u306e\u30d3\u30e5\u30fc\u30a2\u3092\u6709\u52b9\u5316\u3059\u308b\u306b\u306f\u3001\u30d5\u30a1\u30a4\u30eb\u306b\u5bfe\u3057\u3066\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 -ExtractedContentViewer.getTitle=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8 -ExtractedContentViewer.getSolrContent.noTxtYetMsg=

    {0}\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306b\u30c6\u30ad\u30b9\u30c8\u304c\u3042\u308a\u307e\u305b\u3093\u3002
    \u30c6\u30ad\u30b9\u30c8\u304c\u7121\u3044\u304b\u3001\u307e\u3060\u89e3\u6790\u3055\u308c\u3066\u3044\u306a\u3044\u304b\u3001\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u304c\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306b\u6709\u52b9\u5316\u3055\u308c\u3066\u3044\u306a\u304b\u3063\u305f\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002

    +AbstractKeywordSearchPerformer.search.invalidSyntaxHeader=\u7121\u52b9\u306a\u30af\u30a8\u30ea\u69cb\u6587\u3067\u3059\u3002\u3053\u308c\u304c\u6b63\u898f\u8868\u73fe\u306e\u5834\u5408\u306f\u3001Java\u3067\u4e8b\u524d\u5b9a\u7fa9\u6e08\u307f\u306e\u6587\u5b57\u30af\u30e9\u30b9\u3068POSIX\u6587\u5b57\u30af\u30e9\u30b9\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u307e\u305b\u3093\u3002 +AbstractKeywordSearchPerformer.search.searchIngestInProgressTitle=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059 +AbstractKeywordSearchPerformer.search.ingestInProgressBody=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u73fe\u5728\u5b9f\u884c\u4e2d\u3067\u3059\u3002
    \u3059\u3079\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u304c\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u306a\u304b\u3063\u305f\u305f\u3081\u3001\u3053\u306e\u691c\u7d22\u306f\u4e0d\u5b8c\u5168\u306a\u7d50\u679c\u3092\u751f\u6210\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002
    \u305d\u308c\u3067\u3082\u3053\u306e\u691c\u7d22\u3092\u7d9a\u884c\u3057\u307e\u3059\u304b? +AbstractKeywordSearchPerformer.search.emptyKeywordErrorBody=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u304c\u7a7a(\u672a\u5165\u529b)\u3067\u3059\u3002\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u30ea\u30b9\u30c8\u306b\u8ffd\u52a0\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +AbstractKeywordSearchPerformer.search.noFilesInIdxMsg=\u307e\u3060\u30d5\u30a1\u30a4\u30eb\u304c\u7d22\u5f15\u306b\u542b\u307e\u308c\u3066\u3044\u307e\u305b\u3093\u3002
    \u5f8c\u3067\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 \u7d22\u5f15\u306f {0} \u5206\u3054\u3068\u306b\u66f4\u65b0\u3055\u308c\u307e\u3059\u3002 +AbstractKeywordSearchPerformer.search.noFilesIdxdMsg=\u30d5\u30a1\u30a4\u30eb\u304c\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002
    \u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u6709\u52b9\u306b\u3057\u305f\u72b6\u614b\u3067\u30a4\u30e1\u30fc\u30b8\u3092\u518d\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +ExtractedContentViewer.toolTip=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u62bd\u51fa\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8\u3068\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u7d50\u679c\u3092\u8868\u793a\u3057\u307e\u3059\u3002\u3053\u306e\u30d3\u30e5\u30fc\u30ef\u30fc\u3092\u6709\u52b9\u5316\u3059\u308b\u306b\u306f\u3001\u30d5\u30a1\u30a4\u30eb\u306b\u5bfe\u3057\u3066\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +ExtractedContentViewer.getTitle=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8 HighlightedMatchesSource.toString=\u691c\u7d22\u7d50\u679c -Installer.reportPortError=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u30b5\u30fc\u30d0\u30fc\u30dd\u30fc\u30c8 {0} \u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002\u4f7f\u7528\u3057\u3066\u3044\u308b\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u304c {1} \u3092\u30d6\u30ed\u30c3\u30af\u3057\u3066\u3044\u306a\u3044\u304b\u78ba\u8a8d\u3057\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30e6\u30fc\u30b6\u30fc\u30d5\u30a9\u30eb\u30c0\u30fc\u5185\u306e{3}\u30d7\u30ed\u30d1\u30c6\u30a3\u30d5\u30a1\u30a4\u30eb\u306e{2}\u3092\u5909\u66f4\u3059\u308b\u691c\u8a0e\u3092\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3082\u3057\u4ed6\u306e\u51e6\u7406\u304c\u554f\u984c\u306e\u539f\u56e0\u3067\u3042\u308c\u3070\u3001\u30b7\u30b9\u30c6\u30e0\u3092\u518d\u8d77\u52d5\u3057\u3066\u4e0b\u3055\u3044\u3002 -Installer.reportStopPortError=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u30b5\u30fc\u30d0\u30fc\u30b9\u30c8\u30c3\u30d7\u30dd\u30fc\u30c8 {0} \u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30e6\u30fc\u30b6\u30fc\u30d5\u30a9\u30eb\u30c0\u30fc\u5185\u306e{3}\u30d7\u30ed\u30d1\u30c6\u30a3\u30d5\u30a1\u30a4\u30eb\u306e{2}\u3092\u5909\u66f4\u3059\u308b\u691c\u8a0e\u3092\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -Installer.errorInitKsmMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u8d77\u52d5\u30a8\u30e9\u30fc -Installer.reportInitError=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u30b5\u30fc\u30d0\u30fc\u30dd\u30fc\u30c8 {0} \u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002\u4f7f\u7528\u3057\u3066\u3044\u308b\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u304c {1} \u3092\u30d6\u30ed\u30c3\u30af\u3057\u3066\u3044\u306a\u3044\u304b\u78ba\u8a8d\u3057\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30e6\u30fc\u30b6\u30fc\u30d5\u30a9\u30eb\u30c0\u30fc\u5185\u306e{3}\u30d7\u30ed\u30d1\u30c6\u30a3\u30d5\u30a1\u30a4\u30eb\u306e{2}\u3092\u5909\u66f4\u3059\u308b\u691c\u8a0e\u3092\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3082\u3057\u4ed6\u306e\u51e6\u7406\u304c\u554f\u984c\u306e\u539f\u56e0\u3067\u3042\u308c\u3070\u3001\u30b7\u30b9\u30c6\u30e0\u3092\u518d\u8d77\u52d5\u3057\u3066\u4e0b\u3055\u3044\u3002 +Installer.reportPortError=\u7d22\u5f15\u751f\u6210\u30b5\u30fc\u30d0\u30fc\u30dd\u30fc\u30c8 {0} \u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 \u304a\u4f7f\u3044\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u304c {1} \u3092\u30d6\u30ed\u30c3\u30af\u3057\u3066\u3044\u306a\u3044\u304b\u3069\u3046\u304b\u78ba\u8a8d\u3057\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30e6\u30fc\u30b6\u30fc\u30d5\u30a9\u30eb\u30c0\u30fc\u5185\u306e {3} \u30d7\u30ed\u30d1\u30c6\u30a3\u30d5\u30a1\u30a4\u30eb\u3067 {2} \u3092\u5909\u66f4\u3059\u308b\u3053\u3068\u3092\u691c\u8a0e\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u5225\u306e\u30d7\u30ed\u30bb\u30b9\u304c\u77db\u76fe\u3092\u5f15\u304d\u8d77\u3053\u3057\u3066\u3044\u305f\u5834\u5408\u306f\u3001\u305d\u306e\u5f8c\u306b\u30b7\u30b9\u30c6\u30e0\u306e\u518d\u8d77\u52d5\u3092\u8a66\u307f\u3066\u304f\u3060\u3055\u3044\u3002 +Installer.reportStopPortError=\u7d22\u5f15\u751f\u6210\u30b5\u30fc\u30d0\u30fc\u505c\u6b62\u30dd\u30fc\u30c8 {0} \u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30e6\u30fc\u30b6\u30fc\u30d5\u30a9\u30eb\u30c0\u30fc\u5185\u306e {2} \u30d7\u30ed\u30d1\u30c6\u30a3\u30d5\u30a1\u30a4\u30eb\u3067 {1} \u3092\u5909\u66f4\u3059\u308b\u3053\u3068\u3092\u691c\u8a0e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +Installer.errorInitKsmMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u521d\u671f\u5316\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Installer.reportInitError=\u7d22\u5f15\u751f\u6210\u30b5\u30fc\u30d0\u30fc\u30dd\u30fc\u30c8 {0} \u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 \u304a\u4f7f\u3044\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u304c {1} \u3092\u30d6\u30ed\u30c3\u30af\u3057\u3066\u3044\u306a\u3044\u304b\u3069\u3046\u304b\u78ba\u8a8d\u3057\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30e6\u30fc\u30b6\u30fc\u30d5\u30a9\u30eb\u30c0\u30fc\u5185\u306e {3} \u30d7\u30ed\u30d1\u30c6\u30a3\u30d5\u30a1\u30a4\u30eb\u3067 {2} \u3092\u5909\u66f4\u3059\u308b\u3053\u3068\u3092\u691c\u8a0e\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u5225\u306e\u30d7\u30ed\u30bb\u30b9\u304c\u77db\u76fe\u3092\u5f15\u304d\u8d77\u3053\u3057\u3066\u3044\u305f\u5834\u5408\u306f\u3001\u305d\u306e\u5f8c\u306b\u30b7\u30b9\u30c6\u30e0\u306e\u518d\u8d77\u52d5\u3092\u8a66\u307f\u3066\u304f\u3060\u3055\u3044\u3002 KeywordSearchConfigurationPanel.customizeComponents.listTabTitle=\u30ea\u30b9\u30c8 -KeywordSearchConfigurationPanel.customizeComponents.stringExtTitle=\u30b9\u30c8\u30ea\u30f3\u30b0\u62bd\u51fa -KeywordSearchConfigurationPanel.customizeComponents.genTabTitle=\u4e00\u822c -KeywordSearchConfigurationPanel.customizeComponents.listLabToolTip=\u30ea\u30b9\u30c8\u8a2d\u5b9a -KeywordSearchConfigurationPanel.customizeComponents.stringExtToolTip=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u30b9\u30c8\u30ea\u30f3\u30b0\u62bd\u51fa\u8a2d\u5b9a -KeywordSearchConfigurationPanel.customizeComponents.genTabToolTip=\u4e00\u822c\u8a2d\u5b9a +KeywordSearchConfigurationPanel.customizeComponents.stringExtTitle=\u6587\u5b57\u5217\u62bd\u51fa +KeywordSearchConfigurationPanel.customizeComponents.genTabTitle=\u5168\u822c +KeywordSearchConfigurationPanel.customizeComponents.listLabToolTip=\u30ea\u30b9\u30c8\u69cb\u6210 +KeywordSearchConfigurationPanel.customizeComponents.stringExtToolTip=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u6587\u5b57\u5217\u62bd\u51fa\u69cb\u6210 +KeywordSearchConfigurationPanel.customizeComponents.genTabToolTip=\u69cb\u6210\u5168\u822c KeywordSearchConfigurationPanel1.customizeComponents.title=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u524a\u9664 -KeywordSearchConfigurationPanel1.customizeComponents.body=\u5168\u3066\u306e\u30b1\u30fc\u30b9\u306b\u304a\u3051\u308b\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u524a\u9664\u3057\u307e\u3059\u3002\u3053\u306e\u524a\u9664\u3092\u5b9f\u884c\u3057\u307e\u3059\u304b\uff1f -KeywordSearchConfigurationPanel1.customizeComponents.keywordListEmptyErr=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u304c\u7a7a\u767d\u306a\u306e\u3067\u3001\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093 -KeywordSearch.newKwListTitle=\u65b0\u898f\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u540d\uff1a -KeywordSearchConfigurationPanel1.customizeComponents.noOwDefaultMsg=\u30c7\u30d5\u30a9\u30eb\u30c8\u30ea\u30b9\u30c8\u306f\u4e0a\u66f8\u304d\u3067\u304d\u307e\u305b\u3093 -KeywordSearchConfigurationPanel1.customizeComponents.kwListExistMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 <{0}> \u306f\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b\uff1f -KeywordSearchConfigurationPanel1.customizeComponents.kwListSavedMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 <{0}> \u4fdd\u5b58\u3055\u308c\u307e\u3057\u305f -KeywordSearchEditListPanel.customizeComponents.kwReToolTip=\u30ad\u30fc\u30ef\u30fc\u30c9\u306f\u6b63\u7fa9\u8868\u73fe\u3067\u3059 -KeywordSearchEditListPanel.customizeComponents.addWordToolTip=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30ea\u30b9\u30c8\u306b\u65b0\u3057\u3044\u5358\u8a9e\u3092\u8ffd\u52a0 -KeywordSearchEditListPanel.customizeComponents.enterNewWordToolTip=\u691c\u7d22\u3059\u308b\u306e\u306b\u65b0\u898f\u5358\u8a9e\u307e\u305f\u306f\u6b63\u898f\u8868\u73fe\u3092\u5165\u529b -KeywordSearchEditListPanel.customizeComponents.exportToFile=\u65e2\u5b58\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u30d5\u30a1\u30a4\u30eb\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 -KeywordSearchEditListPanel.customizeComponents.saveCurrentWIthNewNameToolTip=\u65e2\u5b58\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u306b\u540d\u524d\u3092\u4ed8\u3051\u3066\u4fdd\u5b58 -KeywordSearchEditListPanel.customizeComponents.removeSelectedMsg=\u9078\u629e\u3057\u305f\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u30ea\u30b9\u30c8\u304b\u3089\u524a\u9664 -KeywordSearchEditListPanel.newKwTitle=\u65b0\u898f\u30ad\u30fc\u30ef\u30fc\u30c9\u30a8\u30f3\u30c8\u30ea\u30fc -KeywordSearchEditListPanel.addWordButtonAction.kwAlreadyExistsMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u306f\u65e2\u306b\u30ea\u30b9\u30c8\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 -KeywordSearchEditListPanel.invalidKwMsg=\u7121\u52b9\u306a\u30ad\u30fc\u30ef\u30fc\u30c9\u30d1\u30bf\u30fc\u30f3\u3002\u5358\u8a9e\u3082\u3057\u304f\u306f\u6b63\u3057\u3044\u6b63\u898f\u8868\u73fe\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -KeywordSearchEditListPanel.removeKwMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u524a\u9664 -KeywordSearchEditListPanel.deleteWordButtonActionPerformed.delConfirmMsg=\u5168\u3066\u306e\u30b1\u30fc\u30b9\u306b\u304a\u3051\u308b\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u524a\u9664\u3057\u307e\u3059\u3002\u3053\u306e\u524a\u9664\u3092\u5b9f\u884c\u3057\u307e\u3059\u304b\uff1f -KeywordSearchEditListPanel.exportButtonActionPerformed.fileFilterLabel=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8XML\u30d5\u30a1\u30a4\u30eb -KeywordSearchEditListPanel.exportButtonActionPerformed.fileExistPrompt=\ {0} \u30d5\u30a1\u30a4\u30eb\u306f\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b\uff1f -KeywordSearchEditListPanel.exportButtonActionPerformed.kwListExportedMsg=\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u305f\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 +KeywordSearchConfigurationPanel1.customizeComponents.body=\u3053\u308c\u306b\u3088\u308a\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u304c\u4e00\u62ec\u524a\u9664\u3055\u308c\u307e\u3059(\u3059\u3079\u3066\u306e\u30b1\u30fc\u30b9)\u3002\u524a\u9664\u3092\u7d9a\u884c\u3057\u307e\u3059\u304b? +KeywordSearchConfigurationPanel1.customizeComponents.keywordListEmptyErr=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u304c\u7a7a(\u672a\u5165\u529b)\u306e\u305f\u3081\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093 +KeywordSearch.newKwListTitle=\u65b0\u898f\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u540d: +KeywordSearch.openCore.notification.msg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u7d22\u5f15\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f +KeywordSearch.closeCore.notification.msg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u7d22\u5f15\u3092\u9589\u3058\u3066\u3044\u308b\u9593\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +KeywordSearchConfigurationPanel1.customizeComponents.noOwDefaultMsg=\u30c7\u30d5\u30a9\u30eb\u30c8\u30ea\u30b9\u30c8\u3092\u4e0a\u66f8\u304d\u3067\u304d\u307e\u305b\u3093 +KeywordSearchConfigurationPanel1.customizeComponents.kwListExistMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 <{0}> \u304c\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b? +KeywordSearchConfigurationPanel1.customizeComponents.kwListSavedMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 <{0}> \u3092\u4fdd\u5b58\u3057\u307e\u3057\u305f +KeywordSearchEditListPanel.customizeComponents.kwReToolTip=\u30ad\u30fc\u30ef\u30fc\u30c9\u306f\u6b63\u898f\u8868\u73fe\u3067\u3059 +KeywordSearchEditListPanel.customizeComponents.addWordToolTip=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30ea\u30b9\u30c8\u306b\u65b0\u898f\u7528\u8a9e\u3092\u8ffd\u52a0 +KeywordSearchEditListPanel.customizeComponents.enterNewWordToolTip=\u65b0\u898f\u7528\u8a9e\u307e\u305f\u306f\u6b63\u898f\u8868\u73fe\u3092\u5165\u529b\u3057\u3066\u691c\u7d22 +KeywordSearchEditListPanel.customizeComponents.exportToFile=\u73fe\u5728\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u30d5\u30a1\u30a4\u30eb\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +KeywordSearchEditListPanel.customizeComponents.saveCurrentWIthNewNameToolTip=\u73fe\u5728\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u5225\u540d\u3067\u4fdd\u5b58\u3059\u308b +KeywordSearchEditListPanel.customizeComponents.removeSelectedMsg=\u30ea\u30b9\u30c8\u304b\u3089\u9078\u629e\u3057\u305f\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u524a\u9664 +KeywordSearchEditListPanel.newKwTitle=\u65b0\u898f\u30ad\u30fc\u30ef\u30fc\u30c9\u5165\u529b +KeywordSearchEditListPanel.addWordButtonAction.kwAlreadyExistsMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u306f\u3059\u3067\u306b\u30ea\u30b9\u30c8\u306b\u5b58\u5728\u3057\u307e\u3059\u3002 +KeywordSearchEditListPanel.invalidKwMsg=\u7121\u52b9\u306a\u30ad\u30fc\u30ef\u30fc\u30c9\u30d1\u30bf\u30fc\u30f3\u3067\u3059\u3002 \u5358\u8a9e\u307e\u305f\u306f\u6b63\u3057\u3044\u6b63\u898f\u8868\u73fe\u30d1\u30bf\u30fc\u30f3\u3092\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +KeywordSearchEditListPanel.removeKwMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u306e\u524a\u9664 +KeywordSearchEditListPanel.deleteWordButtonActionPerformed.delConfirmMsg=\u3053\u308c\u306b\u3088\u308a\u30ad\u30fc\u30ef\u30fc\u30c9\u304c\u30ea\u30b9\u30c8\u304b\u3089\u4e00\u62ec\u524a\u9664\u3055\u308c\u307e\u3059(\u3059\u3079\u3066\u306e\u30b1\u30fc\u30b9)\u3002\u7d9a\u884c\u3057\u307e\u3059\u304b? +KeywordSearchEditListPanel.exportButtonActionPerformed.fileFilterLabel=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u306eXML\u30d5\u30a1\u30a4\u30eb +KeywordSearchEditListPanel.exportButtonActionPerformed.fileExistPrompt=\u30d5\u30a1\u30a4\u30eb {0} \u304c\u5b58\u5728\u3057\u307e\u3059\u3002\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b? +KeywordSearchEditListPanel.exportButtonActionPerformed.kwListExportedMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3057\u307e\u3057\u305f KeywordSearchEditListPanel.kwColName=\u30ad\u30fc\u30ef\u30fc\u30c9 -KeywordSearchEditListPanel.exportButtonActionPerformed.regExColName=\u6b63\u898f\u8868\u73fe -KeywordSearchFilterNode.getFileActions.openExternViewActLbl=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u3067\u958b\u304f -KeywordSearchFilterNode.getFileActions.searchSameMd5=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 +KeywordSearchEditListPanel.addKeyword.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30ea\u30b9\u30c8\u306b\u65b0\u898f\u7528\u8a9e\u3092\u8ffd\u52a0: +KeywordSearchEditListPanel.addKeyword.title=\u65b0\u898f\u30ad\u30fc\u30ef\u30fc\u30c9 +KeywordSearchFilterNode.getFileActions.openExternViewActLbl=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f Ctrl+E +KeywordSearchFilterNode.getFileActions.searchSameMd5=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 KeywordSearchFilterNode.getFileActions.viewInNewWinActionLbl=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a -KeywordSearchIngestModule.init.badInitMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d0\u30fc\u304c\u6b63\u3057\u304f\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 KeywordSearchIngestModule.init.noKwInLstMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u306b\u30ad\u30fc\u30ef\u30fc\u30c9\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -KeywordSearchIngestModule.init.onlyIdxKwSkipMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3060\u3051\u5b9f\u884c\u3055\u308c\u3001\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u306f\u30b9\u30ad\u30c3\u30d7\u3055\u308c\u307e\u3059\u3002\uff08\u300c\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 - \u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u8ffd\u52a0\u300d\u3092\u4f7f\u7528\u3057\u3001\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u8ffd\u52a0\u3059\u308b\u306e\u306f\u53ef\u80fd\u3067\u3059\u3002\uff09 -KeywordSearchIngestModule.postIndexSummary.knowFileHeaderLbl=\u65e2\u77e5\u30bf\u30a4\u30d7\u304c\u3042\u308b\u30d5\u30a1\u30a4\u30eb -KeywordSearchIngestModule.postIndexSummary.fileGenStringsHead=\u4e00\u822c\u7684\u306a\u30b9\u30c8\u30ea\u30f3\u30b0\u304c\u62bd\u51fa\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb -KeywordSearchIngestModule.postIndexSummary.mdOnlyLbl=\u30e1\u30bf\u30c7\u30fc\u30bf\u306e\u307f\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u307e\u3057\u305f -KeywordSearchIngestModule.postIndexSummary.idxErrLbl=\u30a8\u30e9\u30fc\uff08\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u30a8\u30e9\u30fc\uff09 -KeywordSearchIngestModule.postIndexSummary.errTxtLbl=\u30a8\u30e9\u30fc\uff08\u30c6\u30ad\u30b9\u30c8\u62bd\u51fa\uff09 -KeywordSearchIngestModule.postIndexSummary.errIoLbl=\u30a8\u30e9\u30fc\uff08I/O\uff09 -KeywordSearchIngestModule.postIndexSummary.kwIdxResultsLbl=\u30ad\u30fc\u30ef\u30fc\u30c9\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u7d50\u679c -KeywordSearchIngestModule.postIndexSummary.kwIdxErrsTitle=\u30ad\u30fc\u30ef\u30fc\u30c9\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u30a8\u30e9\u30fc -KeywordSearchIngestModule.postIndexSummary.kwIdxErrMsgFiles=\u30ad\u30fc\u30ef\u30fc\u30c9\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u30b5\u30fc\u30d3\u30b9\u304c{0}\u30d5\u30a1\u30a4\u30eb\u3092\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -KeywordSearchIngestModule.postIndexSummary.kwIdxWarnMsgTitle=\u30ad\u30fc\u30ef\u30fc\u30c9\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u8b66\u544a -KeywordSearchIngestModule.postIndexSummary.idxErrReadFilesMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u30b5\u30fc\u30d3\u30b9\u304c\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3080\u969b\u3084\u30c6\u30ad\u30b9\u30c8\u3092\u62bd\u51fa\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u539f\u56e0\u306f\u7834\u640d\u3057\u305f\u30e1\u30c7\u30a3\u30a2\u3084\u30d5\u30a1\u30a4\u30eb\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -KeywordSearchListsViewerPanel.initIngest.addIngestTitle=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u8ffd\u52a0 -KeywordSearchListsViewerPanel.initIngest.addIngestMsg=\u8ffd\u52a0\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u9078\u629e\u3067\u304d\u307e\u3059
    \u305d\u3057\u3066\u5b9f\u884c\u4e2d\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u8ffd\u52a0\u3067\u304d\u307e\u3059
    \u6b21\u56de\u306e\u30d5\u30a1\u30a4\u30eb\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u518d\u69cb\u7bc9\u306e\u3068\u304d\u306b\u9078\u629e\u3055\u308c\u305f\u30ea\u30b9\u30c8\u3082\u691c\u7d22\u3055\u308c\u307e\u3059\u3002 +KeywordSearchIngestModule.init.onlyIdxKwSkipMsg=\u7d22\u5f15\u751f\u6210\u306e\u307f\u304c\u5b9f\u884c\u3055\u308c\u3001\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u306f\u30b9\u30ad\u30c3\u30d7\u3055\u308c\u307e\u3059(\u305d\u308c\u3067\u3082 [\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 - \u8ffd\u52a0\u3057\u3066\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8] \u3092\u7528\u3044\u3066\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u8ffd\u52a0\u3067\u304d\u307e\u3059)\u3002 +KeywordSearchIngestModule.doInBackGround.displayName=\u30ad\u30fc\u30ef\u30fc\u30c9\u5b9a\u671f\u691c\u7d22 +KeywordSearchIngestModule.doInBackGround.finalizeMsg=\u78ba\u5b9a +KeywordSearchIngestModule.doInBackGround.pendingMsg=(\u4fdd\u7559\u4e2d) +RawText.FileText=\u30d5\u30a1\u30a4\u30eb\u30c6\u30ad\u30b9\u30c8 +RawText.ResultText=\u7d50\u679c\u30c6\u30ad\u30b9\u30c8 +SearchRunner.doInBackGround.cancelMsg=(\u53d6\u308a\u6d88\u3057\u4e2d...) +KeywordSearchIngestModule.postIndexSummary.knowFileHeaderLbl=\u65e2\u77e5\u306e\u30bf\u30a4\u30d7\u306e\u30d5\u30a1\u30a4\u30eb +KeywordSearchIngestModule.postIndexSummary.fileGenStringsHead=\u4e00\u822c\u7684\u306a\u6587\u5b57\u5217\u304c\u62bd\u51fa\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb +KeywordSearchIngestModule.postIndexSummary.mdOnlyLbl=\u30e1\u30bf\u30c7\u30fc\u30bf\u306e\u307f\u3092\u62bd\u51fa\u3057\u307e\u3057\u305f +KeywordSearchIngestModule.postIndexSummary.idxErrLbl=\u30a8\u30e9\u30fc(\u30a4\u30f3\u30c7\u30af\u30b5\u30fc) +KeywordSearchIngestModule.postIndexSummary.errTxtLbl=\u30a8\u30e9\u30fc(\u30c6\u30ad\u30b9\u30c8\u62bd\u51fa) +KeywordSearchIngestModule.postIndexSummary.errIoLbl=\u30a8\u30e9\u30fc(\u5165\u51fa\u529b) +KeywordSearchIngestModule.postIndexSummary.kwIdxResultsLbl=\u30ad\u30fc\u30ef\u30fc\u30c9\u7d22\u5f15\u751f\u6210\u7d50\u679c +KeywordSearchIngestModule.postIndexSummary.kwIdxErrsTitle=\u30ad\u30fc\u30ef\u30fc\u30c9\u7d22\u5f15\u751f\u6210\u30a8\u30e9\u30fc +KeywordSearchIngestModule.postIndexSummary.kwIdxErrMsgFiles= {0} \u30d5\u30a1\u30a4\u30eb\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306b\u30ad\u30fc\u30ef\u30fc\u30c9\u7d22\u5f15\u30b5\u30fc\u30d3\u30b9\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +KeywordSearchIngestModule.postIndexSummary.kwIdxWarnMsgTitle=\u30ad\u30fc\u30ef\u30fc\u30c9\u7d22\u5f15\u751f\u6210\u8b66\u544a +KeywordSearchIngestModule.postIndexSummary.idxErrReadFilesMsg=\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u304a\u3088\u3073\u30c6\u30ad\u30b9\u30c8\u62bd\u51fa\u4e2d\u306b\u30ad\u30fc\u30ef\u30fc\u30c9\u7d22\u5f15\u30b5\u30fc\u30d3\u30b9\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u7834\u640d\u3057\u305f\u30e1\u30c7\u30a3\u30a2\u307e\u305f\u306f\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u306e\u3082\u306e\u3067\u3042\u3063\u305f\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +KeywordSearchListsViewerPanel.initIngest.addIngestTitle=\u8ffd\u52a0\u3057\u3066\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 +KeywordSearchListsViewerPanel.initIngest.addIngestMsg=\u8ffd\u52a0\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u9078\u629e
    \u3057\u3001\u9032\u884c\u4e2d\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u30a8\u30f3\u30ad\u30e5\u30fc\u3067\u304d\u307e\u3059\u3002
    \u9078\u629e\u3057\u305f\u30ea\u30b9\u30c8\u306f\u6b21\u56de\u306e\u30d5\u30a1\u30a4\u30eb\u7d22\u5f15\u518d\u69cb\u7bc9\u6642\u306b\u691c\u7d22\u3055\u308c\u307e\u3059\u3002 KeywordSearchListsViewerPanel.initIngest.searchIngestTitle=\u691c\u7d22 -KeywordSearchListsViewerPanel.initIngest.addIdxSearchMsg=\u9078\u629e\u3057\u305f\u30ea\u30b9\u30c8\u5185\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u5185\u3067\u691c\u7d22 -KeywordSearchListsViewerPanel.initIngest.ongoingIngestMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\uff1a {0} \uff08\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306f\u5b9f\u884c\u4e2d\uff09 -KeywordSearchListsViewerPanel.initIngest.fileIndexCtMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\uff1a {0} +KeywordSearchListsViewerPanel.initIngest.addIdxSearchMsg=\u9078\u629e\u3057\u305f\u30ea\u30b9\u30c8\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 +KeywordSearchListsViewerPanel.initIngest.ongoingIngestMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb: {0} (\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306e\u9032\u884c\u4e2d) +KeywordSearchListsViewerPanel.initIngest.fileIndexCtMsg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb: {0} KeywordSearch.selectedColLbl=\u9078\u629e\u6e08\u307f KeywordSearch.nameColLbl=\u540d\u524d -KeywordSearch.regExColLbl=\u6b63\u898f\u8868\u73fe +KeywordSearch.typeColLbl=\u30ad\u30fc\u30ef\u30fc\u30c9\u30bf\u30a4\u30d7 KeywordSearchQueryManager.execute.exeWinTitle=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 {0} - {1} KeywordSearch.newKeywordListMsg=\u65b0\u898f\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 -KeywordSearch.importListFileDialogMsg={0}\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u30a4\u30f3\u30dd\u30fc\u30c8\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +KeywordSearch.importListFileDialogMsg=\u30d5\u30a1\u30a4\u30eb {0} \u304b\u3089\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u30a4\u30f3\u30dd\u30fc\u30c8\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f KeywordSearch.yesOwMsg=\u306f\u3044\u3001\u4e0a\u66f8\u304d\u3057\u307e\u3059 KeywordSearch.noSkipMsg=\u3044\u3044\u3048\u3001\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059 -KeywordSearch.cancelImportMsg=\u30a4\u30f3\u30dd\u30fc\u30c8\u3092\u30ad\u30e3\u30f3\u30bb\u30eb -KeywordSearch.overwriteListPrompt=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 <{0}> \u306f\u30ed\u30fc\u30ab\u30eb\u306b\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b\uff1f -KeywordSearch.importOwConflict=\u30ea\u30b9\u30c8\u30a4\u30f3\u30dd\u30fc\u30c8\u306e\u554f\u984c -KeywordSearch.kwListFailImportMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u304c\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +KeywordSearch.cancelImportMsg=\u30a4\u30f3\u30dd\u30fc\u30c8\u3092\u53d6\u308a\u6d88\u3059 +KeywordSearch.overwriteListPrompt=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 <{0}> \u306f\u30ed\u30fc\u30ab\u30eb\u306b\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b? +KeywordSearch.importOwConflict=\u30ea\u30b9\u30c8\u306e\u30a4\u30f3\u30dd\u30fc\u30c8\u6642\u306e\u77db\u76fe +KeywordSearch.kwListFailImportMsg=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u304c\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f KeywordSearchListsManagementPanel.fileExtensionFilterLbl=Autopsy\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb(xml) -KeywordSearch.listImportFeatureTitle=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u30a4\u30f3\u30dd\u30fc\u30c8 +KeywordSearchListsManagementPanel.fileExtensionFilterLb2=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb(txt)\u3092\u5305\u542b +KeywordSearch.listImportFeatureTitle=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u306e\u30a4\u30f3\u30dd\u30fc\u30c8 KeywordSearchIngestModule.moduleName=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 +KeywordSearchIngestModule.moduleDescription=\u30ea\u30b9\u30c8\u5185\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u3068\u6b63\u898f\u8868\u73fe\u3067\u30d5\u30a1\u30a4\u30eb\u7d22\u5f15\u751f\u6210\u3068\u5b9a\u671f\u691c\u7d22\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002 +DropdownSearchPanel.keywordTextField.text= +KeywordSearchPanel.searchDropButton.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 +DropdownSearchPanel.exactRadioButton.text=\u5b8c\u5168\u4e00\u81f4 +DropdownSearchPanel.substringRadioButton.text=\u90e8\u5206\u4e00\u81f4 +DropdownSearchPanel.regexRadioButton.text=\u6b63\u898f\u8868\u73fe +DropdownSearchPanel.searchButton.text=\u691c\u7d22 +DropdownSearchPanel.cutMenuItem.text=\u5207\u308a\u53d6\u308a DropdownSearchPanel.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e DropdownSearchPanel.pasteMenuItem.text=\u8cbc\u308a\u4ed8\u3051 DropdownSearchPanel.copyMenuItem.text=\u30b3\u30d4\u30fc -DropdownSearchPanel.cutMenuItem.text=\u30ab\u30c3\u30c8 -KeywordSearchIngestModule.moduleDescription=\u30ea\u30b9\u30c8\u5185\u306e\u30ad\u30fc\u30ef\u30fc\u30c9\u304a\u3088\u3073\u6b63\u898f\u8868\u73fe\u3092\u4f7f\u3044\u3001\u30d5\u30a1\u30a4\u30eb\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u304a\u3088\u3073\u5b9a\u671f\u7684\u306a\u691c\u7d22\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002 -OptionsCategory_Name_KeywordSearchOptions=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 -OptionsCategory_Keywords_KeywordSearchOptions=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 -AbstractFileStringContentStream.getSize.exception.msg=\u30b9\u30c8\u30ea\u30f3\u30b0\u5168\u4f53\u304c\u5909\u63db\u3055\u308c\u306a\u3051\u308c\u3070\u3001\u5909\u63db\u3055\u308c\u305f\u30b9\u30c8\u30ea\u30f3\u30b0\u5185\u306e\u30ad\u30e3\u30e9\u30af\u30bf\u30fc\u6570\u306f\u4e0d\u660e\u3067\u3059\u3002 -AbstractFileStringContentStream.getSrcInfo.text=\u30d5\u30a1\u30a4\u30eb\uff1a{0} -ByteContentStream.getSrcInfo.text=\u30d5\u30a1\u30a4\u30eb\uff1a{0} -ExtractedContentPanel.SetMarkup.progress.loading=\u30c6\u30ad\u30b9\u30c8\u3092\u8aad\u307f\u8fbc\u307f\u4e2d -ExtractedContentPanel.SetMarkup.progress.displayName=\u30c6\u30ad\u30b9\u30c8\u3092\u8aad\u307f\u8fbc\u307f\u4e2d +AbstractFileStringContentStream.getSize.exception.msg=\u6587\u5b57\u5217\u5168\u4f53\u304c\u5909\u63db\u3055\u308c\u308b\u307e\u3067\u3001\u5909\u63db\u3055\u308c\u305f\u6587\u5b57\u5217\u306e\u6587\u5b57\u6570\u306f\u308f\u304b\u308a\u307e\u305b\u3093 +AbstractFileStringContentStream.getSrcInfo.text=\u30d5\u30a1\u30a4\u30eb:{0} +ByteContentStream.getSrcInfo.text=\u30d5\u30a1\u30a4\u30eb:{0} ExtractedContentViewer.nextPage.exception.msg=\u6b21\u306e\u30da\u30fc\u30b8\u304c\u3042\u308a\u307e\u305b\u3093\u3002 ExtractedContentViewer.previousPage.exception.msg=\u524d\u306e\u30da\u30fc\u30b8\u304c\u3042\u308a\u307e\u305b\u3093\u3002 ExtractedContentViewer.hasNextItem.exception.msg=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u691c\u7d22\u53ef\u80fd\u306a\u30bd\u30fc\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 @@ -138,147 +189,188 @@ ExtractedContentViewer.hasPreviousItem.exception.msg=\u30b5\u30dd\u30fc\u30c8\u3 ExtractedContentViewer.nextItem.exception.msg=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u691c\u7d22\u53ef\u80fd\u306a\u30bd\u30fc\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 ExtractedContentViewer.previousItem.exception.msg=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u691c\u7d22\u53ef\u80fd\u306a\u30bd\u30fc\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 ExtractedContentViewer.currentItem.exception.msg=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u691c\u7d22\u53ef\u80fd\u306a\u30bd\u30fc\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002 -HighlightedMatchesSource.nextPage.exception.msg=\u6b21\u306e\u30da\u30fc\u30b8\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -HighlightedMatchesSource.previousPage.exception.msg=\u524d\u306e\u30da\u30fc\u30b8\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -HighlightedMatchesSource.nextItem.exception.msg=\u6b21\u306e\u30a2\u30a4\u30c6\u30e0\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -HighlightedMatchesSource.previousItem.exception.msg=\u524d\u306e\u30a2\u30a4\u30c6\u30e0\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -Ingester.ingest.exception.unknownImgId.msg=\u6b21\u306e\u30d5\u30a1\u30a4\u30eb\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u3066\u3044\u307e\u3059\u3002\u30d5\u30a1\u30a4\u30eb\uff1a{0}\u306e\u4e0d\u660e\u306a\u30a4\u30e1\u30fc\u30b8ID -Ingester.ingest.exception.cantReadStream.msg=\u30b3\u30f3\u30c6\u30f3\u30c4\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u8aad\u307f\u53d6\u308c\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} -Ingester.ingest.exception.err.msg=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306e\u30a8\u30e9\u30fc\uff1a{0} -Ingester.ingestExtract.exception.solrTimeout.msg=\u6b21\u306eID\u306b\u5bfe\u3059\u308b\u3001Solr\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u30ea\u30af\u30a8\u30b9\u30c8\u306f\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f\uff1a{0}, \u540d\u524d\: {1} -Ingester.ingestExtract.exception.probPostToSolr.msg=Solr\u306b\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u30dd\u30b9\u30c8\u3059\u308b\u306e\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002ID\uff1a{0}, \u540d\u524d\: {1} -Ingester.UpReqestTask.run.exception.sorlNotAvail.msg=Solr\u30b3\u30a2\u304c\u5229\u7528\u4e0d\u53ef\u3067\u3059\u3002\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3067\u304d\u307e\u305b\u3093\u3002 -Ingester.UpRequestTask.run.exception.probReadFile.msg=\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u53d6\u308a\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -Ingester.UpRequestTask.run.exception.solrProb.msg=Solr\u306b\u554f\u984c\u304c\u3042\u308a\u307e\u3059 -Ingester.UpRequestTask.run.exception.probPostToSolr.msg=\u30d5\u30a1\u30a4\u30eb\u30b3\u30f3\u30c6\u30f3\u30c4\u3092Solr\u306b\u8f09\u305b\u308b\u306e\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002SolrException\u30a8\u30e9\u30fc\u30b3\u30fc\u30c9\uff1a{0} -Ingester.FscContentStream.getSrcInfo=\u30d5\u30a1\u30a4\u30eb\uff1a{0} +Ingester.ingest.exception.unknownImgId.msg=\u30d5\u30a1\u30a4\u30eb\u306e\u7d22\u5f15\u751f\u6210\u304c\u30b9\u30ad\u30c3\u30d7\u3055\u308c\u307e\u3059\u3002\u672a\u77e5\u306e\u30a4\u30e1\u30fc\u30b8ID\u3067\u3059\u3002\u5bfe\u8c61\u30d5\u30a1\u30a4\u30eb: {0} +Ingester.ingest.exception.cantReadStream.msg=\u6b21\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f: {0} +Ingester.ingest.exception.err.msg=\u6b21\u306e\u6587\u66f8\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +Ingester.ingestExtract.exception.solrTimeout.msg=Solr\u7d22\u5f15\u751f\u6210\u30ea\u30af\u30a8\u30b9\u30c8\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3002\u5bfe\u8c61ID: {0}\u3001\u540d\u524d: {1} +Ingester.ingestExtract.exception.probPostToSolr.msg=Solr\u306b\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u63b2\u8f09\u4e2d\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u5bfe\u8c61ID: {0}\u3001\u540d\u524d: {1} +Ingester.UpReqestTask.run.exception.sorlNotAvail.msg=\u5229\u7528\u53ef\u80fd\u306aSolr\u30b3\u30a2\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093 +Ingester.UpRequestTask.run.exception.probReadFile.msg=\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Ingester.UpRequestTask.run.exception.solrProb.msg=Solr\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Ingester.UpRequestTask.run.exception.probPostToSolr.msg=Solr\u306b\u30d5\u30a1\u30a4\u30eb\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u63b2\u8f09\u4e2d\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002SolrException\u30a8\u30e9\u30fc\u30b3\u30fc\u30c9: {0} +Ingester.FscContentStream.getSrcInfo=\u30d5\u30a1\u30a4\u30eb:{0} Ingester.FscContentStream.getReader=\u307e\u3060\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 -Ingester.NullContentStream.getSrcInfo.text=\u30d5\u30a1\u30a4\u30eb\uff1a{0} +Ingester.NullContentStream.getSrcInfo.text=\u30d5\u30a1\u30a4\u30eb:{0} Ingester.NullContentStream.getReader=\u307e\u3060\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 KeywordSearch.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -KeywordSearch.fireNumIdxFileChg.moduleErr.msg=KeywordSearch\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -KeywordSearchIngestModule.init.exception.errConnToSolr.msg=Solr\u30b5\u30fc\u30d0\u3078\u63a5\u7d9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a{0} +KeywordSearch.fireNumIdxFileChg.moduleErr.msg=KeywordSearch\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 KeywordSearchListsEncase.save.exception.msg=\u307e\u3060\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 KeywordSearchListsEncase.save2.exception.msg=\u307e\u3060\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 -KeywordSearchListsEncase.encaseMetaType.exception.msg=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044EncaseMetaType\uff1a{0} +KeywordSearchListsEncase.encaseMetaType.exception.msg=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044EncaseMetaType: {0} KeywordSearchListsManagementPanel.getColName.text=\u540d\u524d KeywordSearchListsManagementPanel.setValueAt.exception.msg=\u30bb\u30eb\u306e\u7de8\u96c6\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093 KeywordSearchOptionsPanelController.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -KeywordSearchOptionsPanelController.moduleErr.msg1=KeywordSearchOptionsPanelController\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -KeywordSearchOptionsPanelController.moduleErr.msg2=KeywordSearchOptionsPanelController\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 +KeywordSearchOptionsPanelController.moduleErr.msg1=KeywordSearchOptionsPanelController\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +KeywordSearchOptionsPanelController.moduleErr.msg2=KeywordSearchOptionsPanelController\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 KeywordSearchQueryManager.pathText.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 -KeywordSearchResultFactory.progress.saving=\u7d50\u679c\u3092\u4fdd\u5b58\u4e2d\uff1a{0} -KeywordSearchSettings.moduleName.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 +KeywordSearchResultFactory.progress.saving=\u6b21\u306e\u7d50\u679c\u3092\u4fdd\u5b58: {0} +KeywordSearchSettings.moduleName.text=KeywordSearch KeywordSearchSettings.properties_options.text={0}_\u30aa\u30d7\u30b7\u30e7\u30f3 KeywordSearchSettings.propertiesNSRL.text={0}_NSRL -KeywordSearchSettings.propertiesScripts.text={0}_\u30b9\u30af\u30ea\u30d7\u30c8 -NoOpenCoreException.err.noOpenSorlCore.msg=\u73fe\u5728\u958b\u3044\u3066\u3044\u308bSolr\u30b3\u30a2\u304c\u3042\u308a\u307e\u305b\u3093\u3002 -Server.start.exception.cantStartSolr.msg=Solr\u30b5\u30fc\u30d0\u30d7\u30ed\u30bb\u30b9\u3092\u958b\u59cb\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -Server.start.exception.cantStartSolr.msg2=Solr\u30b5\u30fc\u30d0\u30d7\u30ed\u30bb\u30b9\u3092\u958b\u59cb\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -Server.isRunning.exception.errCheckSolrRunning.msg=Solr\u30b5\u30fc\u30d0\u306e\u7a3c\u50cd\u72b6\u614b\u3092\u78ba\u8a8d\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -Server.isRunning.exception.errCheckSolrRunning.msg2=Solr\u30b5\u30fc\u30d0\u304c\u7a3c\u50cd\u3057\u3066\u3044\u308b\u304b\u78ba\u8a8d\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -Server.openCore.exception.alreadyOpen.msg=\u3059\u3067\u306b\u958b\u3044\u3066\u3044\u308b\u30b3\u30a2\u3067\u3059\uff01\u307e\u305a\u78ba\u5b9f\u306b\u30b3\u30a2\u3092\u9589\u3058\u3066\u4e0b\u3055\u3044\u3002 -Server.queryNumIdxFiles.exception.msg=\u8907\u6570\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306b\u5bfe\u3057\u3066\u306e\u30af\u30a8\u30ea\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -Server.queryNumIdxChunks.exception.msg=\u8907\u6570\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30c1\u30e3\u30f3\u30af\u306b\u5bfe\u3057\u3066\u306e\u30af\u30a8\u30ea\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -Server.queryNumIdxDocs.exception.msg=\u8907\u6570\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306b\u5bfe\u3057\u3066\u306e\u30af\u30a8\u30ea\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -Server.queryIsIdxd.exception.msg=\u30b3\u30f3\u30c6\u30f3\u30c4\u304c\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u304b\u78ba\u8a8d\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -Server.queryNumFileChunks.exception.msg=\u30d5\u30a1\u30a4\u30eb\u30c1\u30e3\u30f3\u30af\u306e\u6570\u3092\u78ba\u8a8d\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -Server.query.exception.msg=\u30af\u30a8\u30ea\u3092\u5b9f\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a{0} -Server.query2.exception.msg=\u30af\u30a8\u30ea\uff1a{0}\u3092\u5b9f\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -Server.queryTerms.exception.msg=Terms\u30af\u30a8\u30ea\: {0}\u3092\u5b9f\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -Server.openCore.exception.msg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d3\u30b9\u304c\u307e\u3060\u7a3c\u50cd\u3057\u3066\u3044\u307e\u305b\u3093 -Server.openCore.exception.cantOpen.msg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u4f5c\u6210\u307e\u305f\u306f\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f +KeywordSearchSettings.propertiesScripts.text={0}_\u6587\u5b57\u5217 +NoOpenCoreException.err.noOpenSorlCore.msg=\u73fe\u5728\u958b\u3044\u3066\u3044\u308bSolr\u306e\u30b3\u30a2\u306f\u3042\u308a\u307e\u305b\u3093\u3002 +SearchRunner.query.exception.msg=\u6b21\u306e\u30af\u30a8\u30ea\u3092\u5b9f\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: +# {0} - \u30b3\u30a2\u540d +Server.deleteCore.exception.msg=Solr \u30b3\u30a2 {0} \u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +Server.start.exception.cantStartSolr.msg=Solr\u30b5\u30fc\u30d0\u30fc\u30d7\u30ed\u30bb\u30b9\u3092\u958b\u59cb\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +Server.start.exception.cantStartSolr.msg2=Solr\u30b5\u30fc\u30d0\u30fc\u30d7\u30ed\u30bb\u30b9\u3092\u958b\u59cb\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +Server.isRunning.exception.errCheckSolrRunning.msg=Solr\u30b5\u30fc\u30d0\u30fc\u304c\u5b9f\u884c\u4e2d\u304b\u3069\u3046\u304b\u3092\u78ba\u8a8d\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Server.isRunning.exception.errCheckSolrRunning.msg2=Solr\u30b5\u30fc\u30d0\u30fc\u304c\u5b9f\u884c\u4e2d\u304b\u3069\u3046\u304b\u3092\u78ba\u8a8d\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Server.openCore.exception.alreadyOpen.msg=\u958b\u3044\u3066\u3044\u308bSolr\u306e\u30b3\u30a2\u304c\u3059\u3067\u306b\u3042\u308a\u307e\u3059\u3002\u6700\u521d\u306b\u30b3\u30a2\u3092\u660e\u793a\u7684\u306b\u9589\u3058\u3066\u304f\u3060\u3055\u3044\u3002 +Server.queryNumIdxFiles.exception.msg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u6570\u3092\u30af\u30a8\u30ea\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Server.queryNumIdxChunks.exception.msg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30c1\u30e3\u30f3\u30af\u6570\u3092\u30af\u30a8\u30ea\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Server.queryNumIdxDocs.exception.msg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u6587\u66f8\u6570\u3092\u30af\u30a8\u30ea\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Server.queryIsIdxd.exception.msg=\u30b3\u30f3\u30c6\u30f3\u30c4\u304c\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u304b\u3069\u3046\u304b\u3092\u78ba\u8a8d\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Server.queryNumFileChunks.exception.msg=\u30d5\u30a1\u30a4\u30eb\u30c1\u30e3\u30f3\u30af\u6570\u306e\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Server.query.exception.msg=\u6b21\u306e\u30af\u30a8\u30ea\u306e\u5b9f\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +Server.query2.exception.msg=\u6b21\u306e\u30af\u30a8\u30ea\u306e\u5b9f\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +Server.queryTerms.exception.msg=\u6b21\u306eterms(\u6574\u6570/\u6587\u5b57\u5217/\u914d\u5217)\u30af\u30a8\u30ea\u306e\u5b9f\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} +Server.connect.exception.msg=\u6b21\u306eSolr\u30b5\u30fc\u30d0\u30fc\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +Server.openCore.exception.msg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d3\u30b9\u306f\u307e\u3060\u5b9f\u884c\u3057\u3066\u3044\u307e\u305b\u3093 +Server.openCore.exception.cantOpen.msg=\u7d22\u5f15\u3092\u4f5c\u6210\u307e\u305f\u306f\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f +Server.openCore.exception.noIndexDir.msg=\u7d22\u5f15\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u4f5c\u6210\u3067\u304d\u306a\u304b\u3063\u305f\u304b\u3001\u898b\u3064\u304b\u308a\u307e\u305b\u3093 Server.request.exception.exception.msg=Solr\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u767a\u884c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -Server.commit.exception.msg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u30b3\u30df\u30c3\u30c8\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -Server.addDoc.exception.msg=\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30cf\u30f3\u30c9\u30e9\u30fc\u3092\u4f7f\u7528\u3057\u307e\u3057\u305f\u304c\u3001\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306b\u6b21\u306e\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} -Server.close.exception.msg=\u30b3\u30a2\u3092\u9589\u3058\u308c\u307e\u305b\u3093 -Server.close.exception.msg2=\u30b3\u30a2\u3092\u9589\u3058\u308c\u307e\u305b\u3093 -Server.solrServerNoPortException.msg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u306b\u4f7f\u7528\u3057\u3066\u3044\u308b\u30b5\u30fc\u30d0\u306f\u30dd\u30fc\u30c8{0}\u306b\u30d0\u30a4\u30f3\u30c9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30dd\u30fc\u30c8\u306f\u4f7f\u7528\u4e0d\u53ef\u3067\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8{1}\u30dd\u30fc\u30c8\u306e\u5909\u66f4\u3092\u691c\u8a0e\u3057\u3066\u4e0b\u3055\u3044\u3002 -KeywordSearchIngestModule.doInBackGround.displayName=\u5b9a\u671f\u7684\u306a\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 -KeywordSearchIngestModule.doInBackGround.finalizeMsg=- \u6700\u7d42\u51e6\u7406\u4e2d -KeywordSearchIngestModule.doInBackGround.pendingMsg=\uff08\u30da\u30f3\u30c7\u30a3\u30f3\u30b0\uff09 -SearchRunner.doInBackGround.cancelMsg=\uff08\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026\uff09 -Server.addDoc.exception.msg2=\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30cf\u30f3\u30c9\u30e9\u30fc\u3092\u4f7f\u7528\u3057\u307e\u3057\u305f\u304c\u3001\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306b\u6b21\u306e\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} +Server.commit.exception.msg=\u7d22\u5f15\u3092\u78ba\u5b9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +Server.addDoc.exception.msg=\u66f4\u65b0\u30cf\u30f3\u30c9\u30e9\u30fc\u7d4c\u7531\u3067\u6b21\u306e\u6587\u66f8\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +Server.addDoc.exception.msg2=\u66f4\u65b0\u30cf\u30f3\u30c9\u30e9\u30fc\u7d4c\u7531\u3067\u6b21\u306e\u6587\u66f8\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +Server.close.exception.msg=\u30b3\u30a2\u3092\u9589\u3058\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093 +Server.close.exception.msg2=\u30b3\u30a2\u3092\u9589\u3058\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093 +Server.solrServerNoPortException.msg=\u7d22\u5f15\u751f\u6210\u30b5\u30fc\u30d0\u30fc\u306f\u30dd\u30fc\u30c8 {0} \u306b\u30d0\u30a4\u30f3\u30c9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u306e {1} \u30dd\u30fc\u30c8\u306b\u5909\u66f4\u3059\u308b\u3053\u3068\u3092\u691c\u8a0e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 KeywordSearchJobSettingsPanel.keywordSearchEncodings.text=- +KeywordSearchJobSettingsPanel.languagesValLabel.toolTipText= KeywordSearchJobSettingsPanel.languagesValLabel.text=- -KeywordSearchJobSettingsPanel.encodingsLabel.text=\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\uff1a -KeywordSearchJobSettingsPanel.titleLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u306b\u6709\u52b9\u306b\u3059\u308b\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u9078\u629e\uff1a -KeywordSearchJobSettingsPanel.languagesLabel.toolTipText=\u4e0d\u660e\u306a\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u304b\u3089\u306e\u30b9\u30c8\u30ea\u30f3\u30b0\u62bd\u51fa\u3092\u6709\u52b9\u306b\u3057\u305f\u30b9\u30af\u30ea\u30d7\u30c8\u3002\u30a2\u30c9\u30d0\u30f3\u30b9\u8a2d\u5b9a\u304b\u3089\u5909\u66f4\u304c\u53ef\u80fd\u3067\u3059\u3002 -KeywordSearchJobSettingsPanel.languagesLabel.text=\u4e0d\u660e\u306a\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u304b\u3089\u306e\u30b9\u30c8\u30ea\u30f3\u30b0\u62bd\u51fa\u3092\u6709\u52b9\u306b\u3057\u305f\u30b9\u30af\u30ea\u30d7\u30c8\uff1a -KeywordSearchGlobalLanguageSettingsPanel.enableUTF8Checkbox.text=UTF8\u30c6\u30ad\u30b9\u30c8\u62bd\u51fa\u306e\u6709\u52b9\u5316 -KeywordSearchGlobalLanguageSettingsPanel.ingestSettingsLabel.text=\u4e0d\u660e\u306a\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u304b\u3089\u306e\u30b9\u30c8\u30ea\u30f3\u30b0\u62bd\u51fa\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u8a2d\u5b9a\uff08\u5909\u66f4\u306f\u6b21\u56de\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304b\u3089\u6709\u52b9\uff09\uff1a -KeywordSearchGlobalLanguageSettingsPanel.enableUTF16Checkbox.text=UTF16LE\u3068UTF16BE\u30b9\u30c8\u30ea\u30f3\u30b0\u62bd\u51fa\u306e\u6709\u52b9\u5316 -KeywordSearchGlobalLanguageSettingsPanel.languagesLabel.text=\u6709\u52b9\u306a\u30b9\u30af\u30ea\u30d7\u30c8\uff08\u8a00\u8a9e\uff09\uff1a -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.toolTipText=\uff12\uff10\u5206\uff08\u6700\u77ed\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593\uff09 -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.text=\uff12\uff10\u5206\uff08\u6700\u3082\u9045\u3044\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u3001\u6700\u77ed\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593\uff09 -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.toolTipText=\uff11\uff10\u5206\uff08\u30c7\u30d5\u30a9\u30eb\u30c8\u3088\u308a\u5168\u4f53\u7684\u306b\u901f\u3044\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593\uff09 -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.text=\uff11\uff10\u5206\uff08\u3088\u308a\u9045\u3044\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u3001\u3088\u308a\u901f\u3044\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593\uff09 -KeywordSearchGlobalSearchSettingsPanel.frequencyLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306e\u7d50\u679c\u66f4\u65b0\u306e\u983b\u5ea6\uff1a -KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.toolTipText=Hash DB\u30b5\u30fc\u30d3\u30b9\u3092\u4e8b\u524d\u306b\u5b9f\u884c\u3059\u308b\u304b\u3001\u6b21\u56de\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 -KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306bNSRL\u306e\u30d5\u30a1\u30a4\u30eb\uff08\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\uff09\u3092\u30ad\u30fc\u30ef\u30fc\u30c9\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306b\u8ffd\u52a0\u3057\u306a\u3044 +KeywordSearchJobSettingsPanel.encodingsLabel.text=\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0: +KeywordSearchJobSettingsPanel.titleLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306b\u6709\u52b9\u5316\u3059\u308b\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u9078\u629e: +KeywordSearchJobSettingsPanel.languagesLabel.toolTipText=\u672a\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u304b\u3089\u306e\u6587\u5b57\u5217\u62bd\u51fa\u306e\u305f\u3081\u306b\u6709\u52b9\u5316\u3055\u308c\u305f\u6587\u5b57\u5217\u3067\u3059\u3002\u8a73\u7d30\u8a2d\u5b9a\u3067\u5909\u66f4\u3092\u884c\u3048\u307e\u3059\u3002 +KeywordSearchJobSettingsPanel.languagesLabel.text=\u672a\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u304b\u3089\u306e\u6587\u5b57\u5217\u62bd\u51fa\u306e\u305f\u3081\u306b\u6709\u52b9\u5316\u3055\u308c\u305f\u6587\u5b57\u5217\u3067\u3059\u3002 +KeywordSearchGlobalLanguageSettingsPanel.enableUTF8Checkbox.text=UTF8\u30c6\u30ad\u30b9\u30c8\u62bd\u51fa\u3092\u6709\u52b9\u5316\u3059\u308b +KeywordSearchGlobalLanguageSettingsPanel.ingestSettingsLabel.text=\u672a\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u304b\u3089\u306e\u6587\u5b57\u5217\u62bd\u51fa\u306e\u305f\u3081\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u8a2d\u5b9a(\u5909\u66f4\u306f\u6b21\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3067\u6709\u52b9\u306b\u306a\u308a\u307e\u3059): +KeywordSearchGlobalLanguageSettingsPanel.enableUTF16Checkbox.text=UTF16LE\u304a\u3088\u3073UTF16BE\u6587\u5b57\u5217\u62bd\u51fa\u3092\u6709\u52b9\u5316 +KeywordSearchGlobalLanguageSettingsPanel.enableOcrCheckbox.text=\u5149\u5b66\u6587\u5b57\u8a8d\u8b58(OCR)\u3092\u6709\u52b9\u5316 +KeywordSearchGlobalLanguageSettingsPanel.languagesLabel.text=\u6709\u52b9\u5316\u3055\u308c\u305f\u6587\u5b57\u5217(\u8a00\u8a9e): +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.toolTipText=20\u5206(\u6700\u901f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593) +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.text=20\u5206(\u6700\u9045\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u3001\u6700\u901f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8) +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.toolTipText=10\u5206(\u30c7\u30d5\u30a9\u30eb\u30c8\u3088\u308a\u3082\u901f\u3044\u7dcf\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593) +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.text=10\u5206(\u3088\u308a\u9045\u3044\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u3001\u3088\u308a\u901f\u3044\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8) +KeywordSearchGlobalSearchSettingsPanel.frequencyLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306e\u7d50\u679c\u66f4\u65b0\u983b\u5ea6: +KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.toolTipText=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30b5\u30fc\u30d3\u30b9\u306b\u4ee5\u524d\u306b\u5b9f\u884c\u6e08\u307f\u3067\u3042\u308b\u3053\u3068\u3001\u307e\u305f\u306f\u6b21\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u9078\u629e\u3055\u308c\u308b\u3053\u3068\u3092\u8981\u6c42\u3057\u307e\u3059\u3002 +KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306bNSRL(\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb)\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u30ad\u30fc\u30ef\u30fc\u30c9\u306b\u8ffd\u52a0\u3057\u306a\u3044\u3067\u304f\u3060\u3055\u3044 KeywordSearchGlobalSearchSettingsPanel.informationLabel.text=\u60c5\u5831 KeywordSearchGlobalSearchSettingsPanel.settingsLabel.text=\u8a2d\u5b9a -KeywordSearchGlobalSearchSettingsPanel.filesIndexedValue.text=- -KeywordSearchGlobalSearchSettingsPanel.filesIndexedLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5185\u306e\u30d5\u30a1\u30a4\u30eb\uff1a -KeywordSearchGlobalSearchSettingsPanel.chunksValLabel.text=- -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.toolTipText=\uff11\u5206\uff08\u5168\u4f53\u7684\u306a\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593\u304c\u9577\u304f\u306a\u308a\u307e\u3059\uff09 -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.text_1=\uff11\u5206\uff08\u3088\u308a\u901f\u3044\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u3001\u6700\u3082\u9577\u3044\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593\uff09 -KeywordSearchGlobalSearchSettingsPanel.chunksLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5185\u306e\u30c1\u30e3\u30f3\u30af\uff1a -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.toolTipText=\uff15\u5206\uff08\u5168\u4f53\u7684\u306a\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593\u304c\u9577\u304f\u306a\u308a\u307e\u3059\uff09 -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.text=\uff15\u5206\uff08\u30c7\u30d5\u30a9\u30eb\u30c8\uff09 -DropdownSearchPanel.substringRadioButton.text=\u30b5\u30d6\u30b9\u30c8\u30ea\u30f3\u30b0\u4e00\u81f4 -AbstractFileTikaTextExtract.index.exception.tikaParse.msg=\u4f8b\u5916\uff1a\u30d5\u30a1\u30a4\u30eb\uff1a{0}, {1}\u306eApache Tika\u30d1\u30fc\u30b9\u30bf\u30b9\u30af\u5b9f\u884c\u4e2d\u306e\u4e88\u671f\u305b\u306c\u4f8b\u5916 -AbstractFileTikaTextExtract.index.tikaParseTimeout.text=\u4f8b\u5916\uff1a\u30b3\u30f3\u30c6\u30f3\u30c4\uff1a{0}, {1}\u306eApache Tika\u30d1\u30fc\u30b9\u306e\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8 -DropdownSearchPanel.exactRadioButton.text=\u5b8c\u5168\u4e00\u81f4 -DropdownSearchPanel.regexRadioButton.text=\u6b63\u898f\u8868\u73fe -DropdownSearchPanel.searchButton.text=\u691c\u7d22 -KeywordSearchEditListPanel.exportButtonAction.featureName.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 -KeywordSearchGlobalListSettingsPanel.component.featureName.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u4fdd\u5b58 -KeywordSearchGlobalSearchSettingsPanel.showSnippetsCB.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d7\u30ec\u30d3\u30e5\u30fc\u3092\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u7d50\u679c\u306b\u8868\u793a\uff08\u691c\u7d22\u6642\u9593\u304c\u9577\u304f\u306a\u308a\u307e\u3059\uff09 -KeywordSearchIngestModule.fileThLbl=\u30d5\u30a1\u30a4\u30eb -KeywordSearchIngestModule.kwHitLbl=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d2\u30c3\u30c8\uff1a +KeywordSearchGlobalSearchSettingsPanel.filesIndexedValue.text=0 +KeywordSearchGlobalSearchSettingsPanel.filesIndexedLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u7d22\u5f15\u5185\u306e\u30d5\u30a1\u30a4\u30eb: +KeywordSearchGlobalSearchSettingsPanel.showSnippetsCB.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u7d50\u679c\u306b\u30ad\u30fc\u30ef\u30fc\u30c9\u30d7\u30ec\u30d3\u30e5\u30fc\u3092\u8868\u793a(\u691c\u7d22\u6642\u9593\u304c\u9577\u304f\u306a\u308a\u307e\u3059) +KeywordSearchGlobalSearchSettingsPanel.chunksValLabel.text=0 +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.toolTipText=1\u5206(\u7dcf\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593\u306f\u6700\u9577\u306b\u306a\u308a\u307e\u3059) +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.text_1=1\u5206(\u3088\u308a\u901f\u3044\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u3001\u6700\u9577\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8) +KeywordSearchGlobalSearchSettingsPanel.chunksLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u7d22\u5f15\u5185\u306e\u30c1\u30e3\u30f3\u30af: +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.toolTipText=5\u5206(\u7dcf\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u6642\u9593\u304c\u9577\u304f\u306a\u308a\u307e\u3059) +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.text=5\u5206(\u30c7\u30d5\u30a9\u30eb\u30c8) +KeywordSearchIngestModule.regExpHitLbl=\u6b63\u898f\u8868\u73fe\u30d2\u30c3\u30c8: +KeywordSearchIngestModule.kwHitLbl=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d2\u30c3\u30c8: KeywordSearchIngestModule.kwHitThLbl=\u30ad\u30fc\u30ef\u30fc\u30c9 -KeywordSearchIngestModule.listThLbl=\u30ea\u30b9\u30c8 KeywordSearchIngestModule.previewThLbl=\u30d7\u30ec\u30d3\u30e5\u30fc -KeywordSearchIngestModule.regExpHitLbl=\u6b63\u898f\u8868\u73fe\u30d2\u30c3\u30c8\uff1a +KeywordSearchIngestModule.fileThLbl=\u30d5\u30a1\u30a4\u30eb +KeywordSearchIngestModule.listThLbl=\u30ea\u30b9\u30c8 KeywordSearchIngestModule.regExThLbl=\u6b63\u898f\u8868\u73fe -KeywordSearchListsAbstract.addList.errMsg1.msg=KeywordSearchListsAbstract\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -KeywordSearchListsAbstract.addList.errMsg2.msg=KeywordSearchListsAbstract\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 +AbstractFileTikaTextExtract.index.tikaParseTimeout.text=\u4f8b\u5916: \u6b21\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u306eTika\u89e3\u6790\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8: {0}\u3001{1} +AbstractFileTikaTextExtract.index.exception.tikaParse.msg=\u4f8b\u5916: \u6b21\u306e\u30d5\u30a1\u30a4\u30eb\u306eTika\u89e3\u6790\u30bf\u30b9\u30af\u5b9f\u884c\u6642\u306e\u4e88\u671f\u305b\u306c\u4f8b\u5916: {0}\u3001{1} +KeywordSearchEditListPanel.exportButtonAction.featureName.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u306e\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +KeywordSearchGlobalListSettingsPanel.component.featureName.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u3092\u4fdd\u5b58 KeywordSearchListsAbstract.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc -KeywordSearchPanel.searchDropButton.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22 -SearchRunner.updateTimer.title.text=SearchRunner\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30bf\u30a4\u30de\u30fc -KeywordSearchListsAbstract.deleteList.errMsg1.msg=KeywordSearchListsAbstract\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -KeywordSearchListsAbstract.saveList.errMsg1.msg=KeywordSearchListsAbstract\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -KeywordSearchListsAbstract.saveList.errMsg2.msg=KeywordSearchListsAbstract\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -KeywordSearchListsAbstract.writeLists.errMsg1.msg=KeywordSearchListsAbstract\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -KeywordSearchListsAbstract.writeLists.errMsg2.msg=KeywordSearchListsAbstract\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 -KeywordSearchListsManagementPanel.newKeywordListDescription=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8<{0}>\u306f\u8aad\u307f\u53d6\u308a\u5c02\u7528\u30ea\u30b9\u30c8\u3068\u3057\u3066\u5b58\u5728\u3057\u307e\u3059\u3002\u30d7\u30ed\u30b0\u30e9\u30e0\u3092\u4f7f\u7528\u4e2d\u306e\u307f\u3053\u306e\u30ea\u30b9\u30c8\u3092\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b\uff1f\uff08\u3053\u306e\u5909\u66f4\u306f\u7d99\u7d9a\u3055\u308c\u307e\u305b\u3093\uff09 -KeywordSearchListsManagementPanel.newKeywordListDescription2=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8<{0}>\u306f\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b\uff1f -KeywordSearchModuleFactory.createFileIngestModule.exception.msg=\u8a2d\u5b9a\u3092\u884c\u3046\u70ba\u306e\u60f3\u5b9a\u3055\u308c\u308b\u5f15\u6570\u306finstanceof KeywordSearchJobSettings -KeywordSearchModuleFactory.getIngestJobSettingsPanel.exception.msg=\u8a2d\u5b9a\u3092\u884c\u3046\u70ba\u306e\u60f3\u5b9a\u3055\u308c\u308b\u5f15\u6570\u306finstanceof KeywordSearchJobSettings -SearchRunner.Searcher.done.err.msg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u3092\u5b9f\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.text=\u5b9a\u671f\u7684\u691c\u7d22\u7121\u3057 -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.toolTipText=\u5168\u4f53\u7684\u306b\u4e00\u756a\u901f\u3044\u3067\u3059\u304c\u3001\u51e6\u7406\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u7d50\u679c\u306f\u8868\u793a\u3055\u308c\u307e\u305b\u3093 -KeywordSearch.openCore.notification.msg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f -KeywordSearch.closeCore.notification.msg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u9589\u3058\u308b\u969b\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -KeywordSearchListsManagementPanel.fileExtensionFilterLb2=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u30a8\u30f3\u30b1\u30fc\u30b9\u3059\u308b(txt) -Server.connect.exception.msg=Solr\u30b5\u30fc\u30d0\u30fc\u3078\u306e\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f\uff1a{0} -Server.openCore.exception.noIndexDir.msg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u307e\u305f\u306f\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -KeywordSearchIngestModule.startUp.noOpenCore.msg=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u304c\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u307e\u305f\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 -SolrConnectionCheck.HostnameOrPort=hostname\u3084\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u7121\u52b9\u3067\u3059\u3002 -SolrConnectionCheck.Hostname=hostname\u304c\u7121\u52b9\u3067\u3059\u3002 -SolrConnectionCheck.Port=\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u7121\u52b9\u3067\u3059\u3002 -SolrConnectionCheck.MissingHostname=hostname\u304c\u6b20\u3051\u3066\u307e\u3059\u3002 -ExtractedContentViewer.getText.error.msg=\u30c6\u30ad\u30b9\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +KeywordSearchListsAbstract.addList.errMsg1.msg=KeywordSearchListsAbstract\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +KeywordSearchListsAbstract.addList.errMsg2.msg=KeywordSearchListsAbstract\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +SearchRunner.updateTimer.title.text=SearchRunner\u66f4\u65b0\u30bf\u30a4\u30de\u30fc +KeywordSearchListsAbstract.saveList.errMsg1.msg=KeywordSearchListsAbstract\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +KeywordSearchListsAbstract.saveList.errMsg2.msg=KeywordSearchListsAbstract\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +KeywordSearchListsAbstract.writeLists.errMsg1.msg=KeywordSearchListsAbstract\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +KeywordSearchListsAbstract.writeLists.errMsg2.msg=KeywordSearchListsAbstract\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +KeywordSearchListsAbstract.deleteList.errMsg1.msg=KeywordSearchListsAbstract\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +KeywordSearchListsManagementPanel.newKeywordListDescription=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 <{0}> \u306f\u8aad\u307f\u53d6\u308a\u5c02\u7528\u30ea\u30b9\u30c8\u3068\u3057\u3066\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u5b9f\u884c\u4e2d\u306b\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b(\u5909\u66f4\u306f\u6052\u4e45\u7684\u3067\u306f\u3042\u308a\u307e\u305b\u3093)? +KeywordSearchListsManagementPanel.newKeywordListDescription2=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8 <{0}> \u304c\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b? +KeywordSearchModuleFactory.getIngestJobSettingsPanel.exception.msg=instanceof KeywordSearchJobSettings\u306b\u306a\u308b\u3068\u4e88\u60f3\u3055\u308c\u308b\u8a2d\u5b9a\u5f15\u6570 +KeywordSearchModuleFactory.createFileIngestModule.exception.msg=instanceof KeywordSearchJobSettings\u306b\u306a\u308b\u3068\u4e88\u60f3\u3055\u308c\u308b\u8a2d\u5b9a\u5f15\u6570 +SearchRunner.Searcher.done.err.msg=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u306e\u5b9f\u884c\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.toolTipText=\u5168\u4f53\u3067\u6700\u901f\u3067\u3059\u304c\u3001\u6700\u5f8c\u307e\u3067\u7d50\u679c\u306f\u8868\u793a\u3055\u308c\u307e\u305b\u3093 +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.text=\u5b9a\u671f\u691c\u7d22\u306a\u3057 +SolrConnectionCheck.HostnameOrPort=\u7121\u52b9\u306a\u30db\u30b9\u30c8\u540d\u304a\u3088\u3073/\u307e\u305f\u306f\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u3059\u3002 +SolrConnectionCheck.Hostname=\u7121\u52b9\u306a\u30db\u30b9\u30c8\u540d\u3067\u3059\u3002 +SolrConnectionCheck.MissingHostname=\u30db\u30b9\u30c8\u540d\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002 +GlobalListsManagementPanel.newListButton.text=\u65b0\u898f\u30ea\u30b9\u30c8 +GlobalListsManagementPanel.importButton.text=\u30ea\u30b9\u30c8\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 +GlobalListsManagementPanel.keywordListsLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30ea\u30b9\u30c8: +NewKeywordPanel.regexButton.text=\u6b63\u898f\u8868\u73fe +NewKeywordPanel.exactButton.text=\u5b8c\u5168\u4e00\u81f4 +NewKeywordPanel.substringButton.text=\u90e8\u5206\u4e00\u81f4 +NewKeywordPanel.keywordTextField.text= +NewKeywordPanel.newKeywordLabel.text=\u65b0\u898f\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u5165\u529b: +AddKeywordsDialog.exactRadioButton.text=\u5b8c\u5168\u4e00\u81f4 +AddKeywordsDialog.substringRadioButton.text=\u90e8\u5206\u4e00\u81f4 +AddKeywordsDialog.regexRadioButton.text=\u6b63\u898f\u8868\u73fe +AddKeywordsDialog.keywordTypeLabel.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30bf\u30a4\u30d7\u3092\u9078\u629e: +AddKeywordsDialog.enterKeywordsLabel.text=\u6b21\u306b\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u5165\u529b(1\u884c\u306b\u3064\u304d1\u3064): +AddKeywordsDialog.pasteButton.text=\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u304b\u3089\u8cbc\u308a\u4ed8\u3051 +AddKeywordsDialog.addButton.text=OK +AddKeywordsDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057 +AddKeywordsDialog.addKeywordsTitle.text=\u65b0\u898f\u30ad\u30fc\u30ef\u30fc\u30c9 +GlobalEditListPanel.newKeywordsButton.text=\u65b0\u898f\u30ad\u30fc\u30ef\u30fc\u30c9 +GlobalEditListPanel.addKeywordResults.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u7d50\u679c\u3092\u8ffd\u52a0 +GlobalEditListPanel.keywordsAdded.text={0} \u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u6b63\u5e38\u306b\u8ffd\u52a0\u3057\u307e\u3057\u305f\u3002 +GlobalEditListPanel.keywordsAddedPlural.text={0} \u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u6b63\u5e38\u306b\u8ffd\u52a0\u3057\u307e\u3057\u305f\u3002 +GlobalEditListPanel.keywordDupesSkipped.text={0} \u30ad\u30fc\u30ef\u30fc\u30c9\u306f\u30ea\u30b9\u30c8\u306b\u3059\u3067\u306b\u5b58\u5728\u3057\u3066\u3044\u307e\u3057\u305f\u3002 +GlobalEditListPanel.keywordDupesSkippedPlural.text={0} \u30ad\u30fc\u30ef\u30fc\u30c9\u306f\u30ea\u30b9\u30c8\u306b\u3059\u3067\u306b\u5b58\u5728\u3057\u3066\u3044\u307e\u3057\u305f\u3002 +GlobalEditListPanel.keywordErrors.text={0} \u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u89e3\u6790\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u78ba\u8a8d\u3057\u3066\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 +GlobalEditListPanel.keywordErrorsPlural.text={0} \u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u89e3\u6790\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u78ba\u8a8d\u3057\u3066\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 GlobalListsManagementPanel.exportButton.text=\u30ea\u30b9\u30c8\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 GlobalListsManagementPanel.deleteListButton.text=\u30ea\u30b9\u30c8\u3092\u524a\u9664 GlobalListsManagementPanel.copyListButton.text=\u30ea\u30b9\u30c8\u3092\u30b3\u30d4\u30fc +GlobalListsManagementPanel.renameListButton.text=\u30ea\u30b9\u30c8\u540d\u3092\u7de8\u96c6 +GlobalEditListPanel.editWordButton.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u7de8\u96c6 +SolrConnectionCheck.Port=\u7121\u52b9\u306a\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u3059\u3002 +SolrSearch.checkingForLatestIndex.msg=\u6700\u65b0\u306eSolr\u3068\u30b9\u30ad\u30fc\u30de\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u306e\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u3092\u691c\u7d22\u4e2d\u3067\u3059 +SolrSearch.complete.msg=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u304c\u6b63\u5e38\u306b\u958b\u304d\u307e\u3057\u305f +SolrSearch.creatingNewIndex.msg=\u65b0\u898f\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u3092\u751f\u6210\u4e2d\u3067\u3059 +SolrSearch.findingIndexes.msg=\u65e2\u5b58\u306e\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u691c\u7d22\u4e2d\u3067\u3059 +SolrSearch.indentifyingIndex.msg=\u4f7f\u7528\u3059\u308b\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u3092\u7279\u5b9a\u4e2d\u3067\u3059 +SolrSearch.lookingForMetadata.msg=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22\u4e2d\u3067\u3059 +SolrSearch.openCore.msg=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u3092\u8d77\u52d5\u4e2d\u3067\u3059 +SolrSearch.openGiantCore.msg=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u3092\u8d77\u52d5\u4e2d\u3067\u3059\u3002\u3053\u306e\u30b1\u30fc\u30b9\u306e\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u306f\u975e\u5e38\u306b\u5927\u304d\u3044\u305f\u3081\u3001\u8d77\u52d5\u306b\u6642\u9593\u304c\u304b\u304b\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 +SolrSearch.openLargeCore.msg=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u3092\u8d77\u52d5\u4e2d\u3067\u3059\u3002\u3053\u308c\u306b\u306f\u6570\u5206\u304b\u304b\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002 +SolrSearch.readingIndexes.msg=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u3067\u3059 +# {0} - \u7d22\u5f15\u30d5\u30a9\u30eb\u30c0\u30fc\u30d1\u30b9 +SolrSearchService.exceptionMessage.failedToDeleteIndexFiles={0} \u306e\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u30d5\u30a1\u30a4\u30eb\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +SolrSearchService.exceptionMessage.noCurrentSolrCore=IndexMetadata\u306b\u306f\u73fe\u5728\u306eSolr\u306e\u30b3\u30a2\u304c\u542b\u307e\u308c\u3066\u3044\u306a\u304b\u3063\u305f\u305f\u3081\u3001\u30b1\u30fc\u30b9\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +# {0} - \u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc +SolrSearchService.exceptionMessage.noIndexMetadata=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u304b\u3089\u6b21\u306eIndexMetaData\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +SolrSearchService.ServiceName=Solr\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d3\u30b9 +SolrSearchService.IndexReadOnlyDialog.title=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u306f\u8aad\u307f\u53d6\u308a\u5c02\u7528\u3067\u3059 +SolrSearchService.IndexReadOnlyDialog.msg=\u3053\u306e\u30b1\u30fc\u30b9\u306e\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u306f\u8aad\u307f\u53d6\u308a\u5c02\u7528\u3067\u3059\u3002
    \u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u7d50\u679c\u3092\u78ba\u8a8d\u3057\u3001\u5b8c\u5168\u4e00\u81f4\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u3068\u90e8\u5206\u4e00\u81f4\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u3092\u5b9f\u884c\u3067\u304d\u307e\u3059
    \u304c\u3001\u7d22\u5f15\u306b\u65b0\u898f\u30c6\u30ad\u30b9\u30c8\u3092\u8ffd\u52a0\u307e\u305f\u306f\u6b63\u898f\u8868\u73fe\u691c\u7d22\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002\u305d\u306e\u4ee3\u308f\u308a\u306b\u3001\u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u4ee5\u524d\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u3067
    \u30b1\u30fc\u30b9\u3092\u958b\u3051\u307e\u3059\u3002 +ExtractedContentPanel.jLabel1.text=\u30c6\u30ad\u30b9\u30c8\u30bd\u30fc\u30b9: +ExtractedContentPanel.pagePreviousButton.actionCommand=pagePreviousButton +ExtractedContentPanel.pagePreviousButton.text= +ExtractedContentPanel.pageNextButton.text= ExtractedContentPanel.pageCurLabel.text=- -ExtractedContentPanel.pageOfLabel.text=of +ExtractedContentPanel.pageOfLabel.text=/ ExtractedContentPanel.pageTotalLabel.text=- ExtractedContentPanel.pageButtonsLabel.text=\u30da\u30fc\u30b8 -ExtractedContentPanel.pagesLabel.text=\u30da\u30fc\u30b8\uff1a -DropdownSingleTermSearchPanel.ingestIndexLabel.text=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\uff1a -GlobalEditListPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u3067\u3059\u3002\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 -KeywordSearchGlobalLanguageSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u3067\u3059\u3002\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 -KeywordSearchGlobalSearchSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u3067\u3059\u3002\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +ExtractedContentPanel.pagesLabel.text=\u30da\u30fc\u30b8: +DropdownSingleTermSearchPanel.dataSourceCheckBox.text=\u9078\u629e\u3057\u305f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u691c\u7d22\u3092\u5236\u9650: +DropdownListSearchPanel.dataSourceCheckBox.text=\u9078\u629e\u3057\u305f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u691c\u7d22\u3092\u5236\u9650: +DropdownSingleTermSearchPanel.ingestIndexLabel.text=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb: +DropdownSingleTermSearchPanel.jSaveSearchResults.toolTipText=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d2\u30c3\u30c8\u306b\u3088\u308b\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c\u306e\u5f62\u3067\u7d50\u679c\u3092\u4fdd\u5b58\u305b\u305a\u306b\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u3092\u5b9f\u884c +DropdownSingleTermSearchPanel.jSaveSearchResults.text=\u691c\u7d22\u7d50\u679c\u3092\u4fdd\u5b58 +DropdownListSearchPanel.jSaveSearchResults.toolTipText=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d2\u30c3\u30c8\u306b\u3088\u308b\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c\u306e\u5f62\u3067\u7d50\u679c\u3092\u4fdd\u5b58\u305b\u305a\u306b\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u3092\u5b9f\u884c +DropdownListSearchPanel.jSaveSearchResults.text=\u691c\u7d22\u7d50\u679c\u3092\u4fdd\u5b58 +GlobalEditListPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +KeywordSearchGlobalLanguageSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +KeywordSearchGlobalSearchSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index 6052e3deba..af7ce2e771 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.stream.Collectors; +import org.apache.tika.mime.MimeTypes; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; @@ -44,12 +45,12 @@ import org.sleuthkit.autopsy.ingest.IngestMessage.MessageType; import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException; -import org.sleuthkit.autopsy.keywordsearch.TextFileExtractor.TextFileExtractorException; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.autopsy.textextractors.TextExtractor; import org.sleuthkit.autopsy.textextractors.TextExtractorFactory; +import org.sleuthkit.autopsy.textextractors.TextFileExtractor; import org.sleuthkit.autopsy.textextractors.configs.ImageConfig; import org.sleuthkit.autopsy.textextractors.configs.StringsConfig; import org.sleuthkit.datamodel.AbstractFile; @@ -577,8 +578,16 @@ public final class KeywordSearchIngestModule implements FileIngestModule { TskData.TSK_DB_FILES_TYPE_ENUM aType = aFile.getType(); - // unallocated and unused blocks can only have strings extracted from them. - if ((aType.equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) || aType.equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS))) { + /** + * Extract unicode strings from unallocated and unused blocks and + * carved text files. The reason for performing string extraction + * on these is because they all may contain multiple encodings which + * can cause text to be missed by the more specialized text extractors + * used below. + */ + if ((aType.equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) + || aType.equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) + || (aType.equals(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED) && aFile.getNameExtension().equalsIgnoreCase("txt"))) { if (context.fileIngestIsCancelled()) { return; } @@ -632,7 +641,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule { if (context.fileIngestIsCancelled()) { return; } - if (fileType.equals("application/octet-stream")) { + if (fileType.equals(MimeTypes.OCTET_STREAM)) { extractStringsAndIndex(aFile); return; } @@ -657,20 +666,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule { if ((wasTextAdded == false) && (aFile.getNameExtension().equalsIgnoreCase("txt") && !(aFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED)))) { //Carved Files should be the only type of unallocated files capable of a txt extension and //should be ignored by the TextFileExtractor because they may contain more than one text encoding - try { - TextFileExtractor textFileExtractor = new TextFileExtractor(); - Reader textReader = textFileExtractor.getReader(aFile); - if (textReader == null) { - logger.log(Level.INFO, "Unable to extract with TextFileExtractor, Reader was null for file: {0}", aFile.getName()); - } else if (Ingester.getDefault().indexText(textReader, aFile.getId(), aFile.getName(), aFile, context)) { - putIngestStatus(jobId, aFile.getId(), IngestStatus.TEXT_INGESTED); - wasTextAdded = true; - } - } catch (IngesterException ex) { - logger.log(Level.WARNING, "Unable to index as unicode", ex); - } catch (TextFileExtractorException ex) { - logger.log(Level.INFO, "Could not extract text with TextFileExtractor", ex); - } + wasTextAdded = indexTextFile(aFile); } // if it wasn't supported or had an error, default to strings @@ -678,5 +674,29 @@ public final class KeywordSearchIngestModule implements FileIngestModule { extractStringsAndIndex(aFile); } } + + /** + * Adds the text file to the index given an encoding. + * Returns true if indexing was successful and false otherwise. + * + * @param aFile Text file to analyze + * @param detectedCharset the encoding of the file + */ + private boolean indexTextFile(AbstractFile aFile) { + try { + TextFileExtractor textFileExtractor = new TextFileExtractor(aFile); + Reader textReader = textFileExtractor.getReader(); + if (textReader == null) { + logger.log(Level.INFO, "Unable to extract with TextFileExtractor, Reader was null for file: {0}", aFile.getName()); + } else if (Ingester.getDefault().indexText(textReader, aFile.getId(), aFile.getName(), aFile, context)) { + textReader.close(); + putIngestStatus(jobId, aFile.getId(), IngestStatus.TEXT_INGESTED); + return true; + } + } catch (IngesterException | IOException ex) { + logger.log(Level.WARNING, "Unable to index " + aFile.getName(), ex); + } + return false; + } } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index a988417461..a4a3b581b3 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -189,16 +189,16 @@ public class Server { } }, /** - * termfreq is a function which returns the number of times the term appears. - * This is not an actual field defined in schema.xml, but can be gotten from returned documents - * in the same way as fields. + * termfreq is a function which returns the number of times the term + * appears. This is not an actual field defined in schema.xml, but can + * be gotten from returned documents in the same way as fields. */ TERMFREQ { @Override public String toString() { return "termfreq"; //NON-NLS } - } + } }; public static final String HL_ANALYZE_CHARS_UNLIMITED = "500000"; //max 1MB in a chunk. use -1 for unlimited, but -1 option may not be supported (not documented) @@ -525,7 +525,7 @@ public class Server { @Override public void run() { MessageNotifyUtil.Notify.error( - NbBundle.getMessage(this.getClass(), "Installer.errorInitKsmMsg"), + NbBundle.getMessage(this.getClass(), "Installer.errorInitKsmMsg"), Bundle.Server_status_failed_msg()); } }); @@ -890,28 +890,30 @@ public class Server { throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex); } } - + /** - * Get the host and port for a multiuser case. - * If the file solrserver.txt exists, then use the values from that file. - * Otherwise use the settings from the properties file. - * + * Get the host and port for a multiuser case. If the file solrserver.txt + * exists, then use the values from that file. Otherwise use the settings + * from the properties file. + * * @param caseDirectory Current case directory - * @return IndexingServerProperties containing the solr host/port for this case + * + * @return IndexingServerProperties containing the solr host/port for this + * case */ public static IndexingServerProperties getMultiUserServerProperties(String caseDirectory) { Path serverFilePath = Paths.get(caseDirectory, "solrserver.txt"); - if(serverFilePath.toFile().exists()){ - try{ + if (serverFilePath.toFile().exists()) { + try { List lines = Files.readAllLines(serverFilePath); - if(lines.isEmpty()) { + if (lines.isEmpty()) { logger.log(Level.SEVERE, "solrserver.txt file does not contain any data"); - } else if (! lines.get(0).contains(",")) { + } else if (!lines.get(0).contains(",")) { logger.log(Level.SEVERE, "solrserver.txt file is corrupt - could not read host/port from " + lines.get(0)); } else { String[] parts = lines.get(0).split(","); - if(parts.length != 2) { + if (parts.length != 2) { logger.log(Level.SEVERE, "solrserver.txt file is corrupt - could not read host/port from " + lines.get(0)); } else { return new IndexingServerProperties(parts[0], parts[1]); @@ -921,102 +923,104 @@ public class Server { logger.log(Level.SEVERE, "solrserver.txt file could not be read", ex); } } - + // Default back to the user preferences if the solrserver.txt file was not found or if an error occurred String host = UserPreferences.getIndexingServerHost(); String port = UserPreferences.getIndexingServerPort(); return new IndexingServerProperties(host, port); } - + /** - * Pick a solr server to use for this case and record it in the case directory. - * Looks for a file named "solrServerList.txt" in the root output directory - - * if this does not exist then no server is recorded. - * - * Format of solrServerList.txt: - * (host),(port) - * Ex: 10.1.2.34,8983 - * + * Pick a solr server to use for this case and record it in the case + * directory. Looks for a file named "solrServerList.txt" in the root output + * directory - if this does not exist then no server is recorded. + * + * Format of solrServerList.txt: (host),(port) Ex: 10.1.2.34,8983 + * * @param rootOutputDirectory * @param caseDirectoryPath - * @throws KeywordSearchModuleException + * + * @throws KeywordSearchModuleException */ public static void selectSolrServerForCase(Path rootOutputDirectory, Path caseDirectoryPath) throws KeywordSearchModuleException { // Look for the solr server list file String serverListName = "solrServerList.txt"; Path serverListPath = Paths.get(rootOutputDirectory.toString(), serverListName); - if(serverListPath.toFile().exists()){ - + if (serverListPath.toFile().exists()) { + // Read the list of solr servers List lines; - try{ + try { lines = Files.readAllLines(serverListPath); - } catch (IOException ex){ + } catch (IOException ex) { throw new KeywordSearchModuleException(serverListName + " could not be read", ex); } - + // Remove any lines that don't contain a comma (these are likely just whitespace) for (Iterator iterator = lines.iterator(); iterator.hasNext();) { String line = iterator.next(); - if (! line.contains(",")) { + if (!line.contains(",")) { // Remove the current element from the iterator and the list. iterator.remove(); } } - if(lines.isEmpty()) { + if (lines.isEmpty()) { throw new KeywordSearchModuleException(serverListName + " had no valid server information"); } - + // Choose which server to use int rnd = new Random().nextInt(lines.size()); String[] parts = lines.get(rnd).split(","); - if(parts.length != 2) { + if (parts.length != 2) { throw new KeywordSearchModuleException("Invalid server data: " + lines.get(rnd)); } - + // Split it up just to do a sanity check on the data String host = parts[0]; - String port = parts[1]; - if(host.isEmpty() || port.isEmpty()) { + String port = parts[1]; + if (host.isEmpty() || port.isEmpty()) { throw new KeywordSearchModuleException("Invalid server data: " + lines.get(rnd)); } - + // Write the server data to a file Path serverFile = Paths.get(caseDirectoryPath.toString(), "solrserver.txt"); try { caseDirectoryPath.toFile().mkdirs(); - if (! caseDirectoryPath.toFile().exists()) { + if (!caseDirectoryPath.toFile().exists()) { throw new KeywordSearchModuleException("Case directory " + caseDirectoryPath.toString() + " does not exist"); } Files.write(serverFile, lines.get(rnd).getBytes()); - } catch (IOException ex){ + } catch (IOException ex) { throw new KeywordSearchModuleException(serverFile.toString() + " could not be written", ex); } } } - + /** * Helper class to store the current server properties */ public static class IndexingServerProperties { + private final String host; private final String port; - - IndexingServerProperties (String host, String port) { + + IndexingServerProperties(String host, String port) { this.host = host; this.port = port; } /** * Get the host + * * @return host */ public String getHost() { return host; } - + /** * Get the port + * * @return port */ public String getPort() { @@ -1265,6 +1269,26 @@ public class Server { } } + /** + * Delete a data source from SOLR. + * + * @param dataSourceId to delete + * + * @throws NoOpenCoreException + */ + void deleteDataSource(Long dataSourceId) throws IOException, KeywordSearchModuleException, NoOpenCoreException, SolrServerException { + try { + currentCoreLock.writeLock().lock(); + if (null == currentCore) { + throw new NoOpenCoreException(); + } + currentCore.deleteDataSource(dataSourceId); + currentCore.commit(); + } finally { + currentCoreLock.writeLock().unlock(); + } + } + /** * Get the text contents of the given file as stored in SOLR. * @@ -1375,10 +1399,10 @@ public class Server { * @throws IOException */ void connectToSolrServer(HttpSolrServer solrServer) throws SolrServerException, IOException { - TimingMetric metric = HealthMonitor.getTimingMetric("Solr: Connectivity check"); + TimingMetric metric = HealthMonitor.getTimingMetric("Solr: Connectivity check"); CoreAdminRequest statusRequest = new CoreAdminRequest(); - statusRequest.setCoreName( null ); - statusRequest.setAction( CoreAdminParams.CoreAdminAction.STATUS ); + statusRequest.setCoreName(null); + statusRequest.setAction(CoreAdminParams.CoreAdminAction.STATUS); statusRequest.setIndexInfoNeeded(false); statusRequest.process(solrServer); HealthMonitor.submitTimingMetric(metric); @@ -1435,7 +1459,7 @@ public class Server { // the server to access a core needs to be built from a URL with the // core in it, and is only good for core-specific operations private final HttpSolrServer solrCore; - + private final int QUERY_TIMEOUT_MILLISECONDS = 86400000; // 24 Hours = 86,400,000 Milliseconds private Core(String name, CaseType caseType, Index index) { @@ -1447,7 +1471,7 @@ public class Server { //TODO test these settings // socket read timeout, make large enough so can index larger files - solrCore.setSoTimeout(QUERY_TIMEOUT_MILLISECONDS); + solrCore.setSoTimeout(QUERY_TIMEOUT_MILLISECONDS); //solrCore.setConnectionTimeout(1000); solrCore.setDefaultMaxConnectionsPerHost(32); solrCore.setMaxTotalConnections(32); @@ -1506,6 +1530,13 @@ public class Server { } } + private void deleteDataSource(Long dsObjId) throws IOException, SolrServerException { + String dataSourceId = Long.toString(dsObjId); + String deleteQuery = "image_id:" + dataSourceId; + + solrCore.deleteByQuery(deleteQuery); + } + void addDocument(SolrInputDocument doc) throws KeywordSearchModuleException { try { solrCore.add(doc); @@ -1527,7 +1558,8 @@ public class Server { * @param chunkID Chunk ID of the Solr document * * @return Text from matching Solr document (as String). Null if no - * matching Solr document found or error while getting content from Solr + * matching Solr document found or error while getting content + * from Solr */ private String getSolrContent(long contentID, int chunkID) { final SolrQuery q = new SolrQuery(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index 9efe326e8c..94dd567c5e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -60,8 +60,7 @@ import org.sleuthkit.datamodel.TskCoreException; * text indexing and search. */ @ServiceProviders(value = { - @ServiceProvider(service = KeywordSearchService.class) - , + @ServiceProvider(service = KeywordSearchService.class), @ServiceProvider(service = AutopsyService.class) }) public class SolrSearchService implements KeywordSearchService, AutopsyService { @@ -194,6 +193,26 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService { } } + /** + * Deletes a data source from Solr for a case. + * + * @param dataSourceId the id of the data source to delete. + * + * @throws + * org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException + */ + @Override + public void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { + + try { + Server ddsServer = KeywordSearch.getServer(); + ddsServer.deleteDataSource(dataSourceId); + } catch (IOException | KeywordSearchModuleException | NoOpenCoreException | SolrServerException ex) { + logger.log(Level.WARNING, NbBundle.getMessage(SolrSearchService.class, "SolrSearchService.DeleteDataSource.msg", dataSourceId), ex); + throw new KeywordSearchServiceException(NbBundle.getMessage(SolrSearchService.class, "SolrSearchService.DeleteDataSource.msg", dataSourceId), ex); + } + } + /** * Deletes Solr core for a case. * @@ -275,7 +294,7 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService { String caseDirPath = context.getCase().getCaseDirectory(); Case theCase = context.getCase(); List indexes = new ArrayList<>(); - progress.start(Bundle.SolrSearch_lookingForMetadata_msg(), totalNumProgressUnits); + progress.progress(Bundle.SolrSearch_lookingForMetadata_msg(), totalNumProgressUnits); if (IndexMetadata.isMetadataFilePresent(caseDirPath)) { try { // metadata file exists, get list of existing Solr cores for this case diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TextFileExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TextFileExtractor.java deleted file mode 100644 index 66d26a95bf..0000000000 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TextFileExtractor.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018-2019 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.keywordsearch; -import java.io.IOException; -import java.io.InputStream; -import java.io.BufferedInputStream; -import java.io.Reader; -import org.apache.tika.parser.txt.CharsetDetector; -import org.apache.tika.parser.txt.CharsetMatch; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.ReadContentInputStream; - -/** - * Extract text from .txt files - */ -final class TextFileExtractor { - - //Set a Minimum confidence value to reject matches that may not have a valid text encoding - //Values of valid text encodings were generally 100, xml code sometimes had a value around 50, - //and pictures and other files with a .txt extention were showing up with a value of 5 or less in limited testing. - //This limited information was used to select the current value as one that would filter out clearly non-text - //files while hopefully working on all files with a valid text encoding - static final private int MIN_MATCH_CONFIDENCE = 20; - - public Reader getReader(AbstractFile source) throws TextFileExtractorException { - CharsetDetector detector = new CharsetDetector(); - //wrap stream in a BufferedInputStream so that it supports the mark/reset methods necessary for the CharsetDetector - InputStream stream = new BufferedInputStream(new ReadContentInputStream(source)); - try { - detector.setText(stream); - } catch (IOException ex) { - throw new TextFileExtractorException("Unable to get string from detected text in TextFileExtractor", ex); - } - CharsetMatch match = detector.detect(); - if (match == null) { - throw new TextFileExtractorException("Unable to detect any matches using TextFileExtractor"); - } else if (match.getConfidence() < MIN_MATCH_CONFIDENCE) { - throw new TextFileExtractorException("Text does not match any character set with a high enough confidence for TextFileExtractor"); - } - - return match.getReader(); - } - - public class TextFileExtractorException extends Exception { - public TextFileExtractorException(String msg, Throwable ex) { - super(msg, ex); - } - public TextFileExtractorException(String msg) { - super(msg); - } - } -} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/BinaryCookieReader.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/BinaryCookieReader.java index ce92c2e084..1937377804 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/BinaryCookieReader.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/BinaryCookieReader.java @@ -42,6 +42,7 @@ import org.sleuthkit.autopsy.recentactivity.BinaryCookieReader.Cookie; */ final class BinaryCookieReader implements Iterable { + private static final Logger LOG = Logger.getLogger(BinaryCookieReader.class.getName()); private static final int MAGIC_SIZE = 4; private static final int SIZEOF_INT_BYTES = 4; private static final int PAGE_HEADER_VALUE = 256; @@ -53,8 +54,6 @@ final class BinaryCookieReader implements Iterable { private final int[] pageSizeArray; private final File cookieFile; - private static final Logger LOG = Logger.getLogger(BinaryCookieReader.class.getName()); - /** * The binary cookie reader encapsulates all the knowledge of how to read * the mac .binarycookie files into one class. @@ -62,7 +61,7 @@ final class BinaryCookieReader implements Iterable { */ private BinaryCookieReader(File cookieFile, int[] pageSizeArray) { this.cookieFile = cookieFile; - this.pageSizeArray = pageSizeArray; + this.pageSizeArray = pageSizeArray.clone(); } /** @@ -71,7 +70,9 @@ final class BinaryCookieReader implements Iterable { * open. * * @param cookieFile binarycookie file + * * @return An instance of the reader + * * @throws FileNotFoundException * @throws IOException */ @@ -229,6 +230,7 @@ final class BinaryCookieReader implements Iterable { * correct format by checking for the header value of 0x0100. * * @param page byte array representing a cookie page + * * @throws IOException */ CookiePage(byte[] page) throws IOException { @@ -414,7 +416,8 @@ final class BinaryCookieReader implements Iterable { * offset ending at the first null terminator found. * * @param byteArray Array of bytes - * @param offset starting offset in the array + * @param offset starting offset in the array + * * @return String with bytes converted to ascii */ private String decodeString(byte[] byteArray, int offset) { diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index 805e776717..2f7e1e164c 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -1,6 +1,7 @@ cannotBuildXmlParser=Unable to build XML parser: cannotLoadSEUQA=Unable to load Search Engine URL Query Analyzer settings file, SEUQAMappings.xml: cannotParseXml=Unable to parse XML file: +ChromeCacheExtract_adding_extracted_files_msg=Adding %d extracted files for analysis. ChromeCacheExtractor.moduleName=ChromeCacheExtractor # {0} - module name # {1} - row number diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle_ja.properties b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle_ja.properties index 47af63c1ac..e41e0ee1e3 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle_ja.properties +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle_ja.properties @@ -1,100 +1,100 @@ -OpenIDE-Module-Display-Category=\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB +OpenIDE-Module-Display-Category=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb OpenIDE-Module-Long-Description=\ - \u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB\u3002\n\n\ - \u3053\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u306F\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u4E2D\u306E\u30C7\u30A3\u30B9\u30AF\u30A4\u30E1\u30FC\u30B8\u304B\u3089\u6709\u7528\u306A\u6700\u8FD1\u306E\u30E6\u30FC\u30B6\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u3092\u62BD\u51FA\u3057\u307E\u3059\u3002\u4F8B\u3048\u3070\uFF1A\n\n-\u6700\u8FD1\u958B\u3044\u305F\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u3001\n-\u30A6\u30A7\u30D6\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\uFF08\u8A2A\u308C\u305F\u30B5\u30A4\u30C8\u3001\u4FDD\u5B58\u3055\u308C\u305FCookie\u3001\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u3055\u308C\u305F\u30B5\u30A4\u30C8\u3001\u30B5\u30FC\u30C1\u30A8\u30F3\u30B8\u30F3\u30AF\u30A8\u30EA\u3001\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\uFF09\u3001\n-\u6700\u8FD1\u63A5\u7D9A\u3057\u305F\u30C7\u30D0\u30A4\u30B9\u3001\n-\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u305F\u30D7\u30ED\u30B0\u30E9\u30E0\u3002\n\n\ - \u3053\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u306F\u73FE\u5728Windows\u306E\u30C7\u30A3\u30B9\u30AF\u30A4\u30E1\u30FC\u30B8\u3057\u304B\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u307E\u305B\u3093\u3002\n\ - \u30D7\u30E9\u30B0\u30A4\u30F3\u306FWindows\u7248\u306EAutopsy\u3092\u5229\u7528\u3059\u308B\u3068\u5168\u3066\u306E\u6A5F\u80FD\u304C\u4F7F\u3048\u307E\u3059\u3002 -OpenIDE-Module-Name=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -OpenIDE-Module-Short-Description=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u30D5\u30A1\u30A4\u30F3\u30C0\u30FC\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB + \u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3002\n\n\ + \u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306e\u30c7\u30a3\u30b9\u30af\u30a4\u30e1\u30fc\u30b8\u304b\u3089\u6709\u7528\u306a\u6700\u8fd1\u306e\u30e6\u30fc\u30b6\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3\u3092\u62bd\u51fa\u3057\u307e\u3059\u3002\u4f8b\u3048\u3070\uff1a\n\n-\u6700\u8fd1\u958b\u3044\u305f\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3001\n-\u30a6\u30a7\u30d6\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3\uff08\u8a2a\u308c\u305f\u30b5\u30a4\u30c8\u3001\u4fdd\u5b58\u3055\u308c\u305fCookie\u3001\u30d6\u30c3\u30af\u30de\u30fc\u30af\u3055\u308c\u305f\u30b5\u30a4\u30c8\u3001\u30b5\u30fc\u30c1\u30a8\u30f3\u30b8\u30f3\u30af\u30a8\u30ea\u3001\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\uff09\u3001\n-\u6700\u8fd1\u63a5\u7d9a\u3057\u305f\u30c7\u30d0\u30a4\u30b9\u3001\n-\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3055\u308c\u305f\u30d7\u30ed\u30b0\u30e9\u30e0\u3002\n\n\ + \u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u73fe\u5728Windows\u306e\u30c7\u30a3\u30b9\u30af\u30a4\u30e1\u30fc\u30b8\u3057\u304b\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u305b\u3093\u3002\n\ + \u30d7\u30e9\u30b0\u30a4\u30f3\u306fWindows\u7248\u306eAutopsy\u3092\u5229\u7528\u3059\u308b\u3068\u5168\u3066\u306e\u6a5f\u80fd\u304c\u4f7f\u3048\u307e\u3059\u3002 +OpenIDE-Module-Name=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +OpenIDE-Module-Short-Description=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3\u30d5\u30a1\u30a4\u30f3\u30c0\u30fc\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb Chrome.moduleName=Chrome -Chrome.getHistory.errMsg.errGettingFiles=Chrome\u5C65\u6B74\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Chrome.getHistory.errMsg.couldntFindAnyFiles=\u30A2\u30ED\u30B1\u30FC\u30B7\u30E7\u30F3\u3055\u308C\u305FChrome\u5C65\u6B74\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -Chrome.getHistory.errMsg.errAnalyzingFile={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Chrome.parentModuleName=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -Chrome.getBookmark.errMsg.errGettingFiles=Chrome\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Chrome.getBookmark.errMsg.errAnalyzingFile={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Chrome.getBookmark.errMsg.errAnalyzeFile={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Chrome.getBookmark.errMsg.errAnalyzingFile3={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Chrome.getBookmark.errMsg.errAnalyzingFile4={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Chrome.getCookie.errMsg.errGettingFiles=Chrome\u5C65\u6B74\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Chrome.getCookie.errMsg.errAnalyzeFile={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Chrome.getDownload.errMsg.errGettingFiles=Chrome\u5C65\u6B74\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Chrome.getDownload.errMsg.errAnalyzeFiles1={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Chrome.getLogin.errMsg.errGettingFiles=Chrome\u5C65\u6B74\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Chrome.getLogin.errMsg.errAnalyzingFiles={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Extract.dbConn.errMsg.failedToQueryDb={0}\:\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u30AF\u30A8\u30EA\u5B9F\u884C\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002 +Chrome.getHistory.errMsg.errGettingFiles=Chrome\u5c65\u6b74\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Chrome.getHistory.errMsg.couldntFindAnyFiles=\u30a2\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u3055\u308c\u305fChrome\u5c65\u6b74\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +Chrome.getHistory.errMsg.errAnalyzingFile={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Chrome.parentModuleName=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +Chrome.getBookmark.errMsg.errGettingFiles=Chrome\u30d6\u30c3\u30af\u30de\u30fc\u30af\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Chrome.getBookmark.errMsg.errAnalyzingFile={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Chrome.getBookmark.errMsg.errAnalyzeFile={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Chrome.getBookmark.errMsg.errAnalyzingFile3={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Chrome.getBookmark.errMsg.errAnalyzingFile4={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Chrome.getCookie.errMsg.errGettingFiles=Chrome\u5c65\u6b74\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Chrome.getCookie.errMsg.errAnalyzeFile={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Chrome.getDownload.errMsg.errGettingFiles=Chrome\u5c65\u6b74\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Chrome.getDownload.errMsg.errAnalyzeFiles1={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Chrome.getLogin.errMsg.errGettingFiles=Chrome\u5c65\u6b74\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Chrome.getLogin.errMsg.errAnalyzingFiles={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Extract.dbConn.errMsg.failedToQueryDb={0}\:\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30af\u30a8\u30ea\u5b9f\u884c\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 ExtractIE.moduleName.text=Internet Explorer -ExtractIE.getBookmark.errMsg.errGettingBookmarks={0}\: Internet Explorer\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -ExtractIE.parentModuleName.noSpace=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -ExtractIE.parentModuleName=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -ExtractIE.getURLFromIEBmkFile.errMsg={0}\:Internet Explorer\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u30D5\u30A1\u30A4\u30EB{1}\u3092\u30D1\u30FC\u30B9\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ExtractIE.getURLFromIEBmkFile.errMsg2={0}\:Internet Explorer\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u30D5\u30A1\u30A4\u30EB{1}\u3092\u30D1\u30FC\u30B9\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ExtractIE.getCookie.errMsg.errGettingFile={0}\:Internet Explorer\u306Ecookie\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -ExtractIE.getCookie.errMsg.errReadingIECookie={0}\:Internet Explorer\u306Ecookie{1}\u3092\u8AAD\u307F\u53D6\u308A\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -ExtractIE.getHistory.errMsg.unableToGetHist={0}\:Internet Explorer\u5C65\u6B74\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF1Apasco\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F -ExtractIE.getHistory.errMsg.errGettingHistFiles={0}\:Internet Explorer\u5C65\u6B74\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -ExtractIE.getHistory.errMsg.noHistFiles=Internet Explorer\u5C65\u6B74\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -ExtractIE.getHistory.errMsg.errWriteFile={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u66F8\u304D\u8FBC\u307F\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ExtractIE.getHistory.errMsg.errProcHist={0}\: Internet Explorer\u5C65\u6B74\u3092\u51E6\u7406\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -ExtractIE.parsePascoOutput.errMsg.notFound={0}\:Pasco\u30A2\u30A6\u30C8\u30D7\u30C3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\uFF1A{1} -ExtractIE.parsePascoOutput.errMsg.errParsing={0}\:Internet Explorer\u5C65\u6B74\u30A8\u30F3\u30C8\u30EA{1}\u3092\u30D1\u30FC\u30B9\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ExtractIE.parsePascoOutput.errMsg.errParsingEntry={0}\: Internet Explorer\u5C65\u6B74\u30A8\u30F3\u30C8\u30EA\u3092\u30D1\u30FC\u30B9\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -ExtractRegistry.moduleName.text=\u30EC\u30B8\u30B9\u30C8\u30EA -ExtractRegistry.findRegFiles.errMsg.errReadingFile=\u30EC\u30B8\u30B9\u30C8\u30EA\u30D5\u30A1\u30A4\u30EB\uFF1A{0}\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp={0}\:\u30EC\u30B8\u30B9\u30C8\u30EA\u30D5\u30A1\u30A4\u30EB{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ExtractRegistry.analyzeRegFiles.failedParsingResults={0}\:\u30EC\u30B8\u30B9\u30C8\u30EA\u30D5\u30A1\u30A4\u30EB\u7D50\u679C\u306E\u30D1\u30FC\u30B9\u306B\u5931\u6557\u3057\u307E\u3057\u305F{1} -ExtractRegistry.parentModuleName.noSpace=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -ExtractRegistry.programName=\u30EC\u30B8\u30B9\u30C8\u30EA\u30EA\u30C3\u30D1\u30FC -ExtractRegistry.analyzeRegFiles.errMsg.errReadingRegFile={0}\:\u30EC\u30B8\u30B9\u30C8\u30EA\u30D5\u30A1\u30A4\u30EB - {1}\u3092\u8AAD\u307F\u53D6\u308A\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile={0}\:\u30EC\u30B8\u30B9\u30C8\u30EA\u30D5\u30A1\u30A4\u30EB\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F -ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile2={0}\:\u30EC\u30B8\u30B9\u30C8\u30EA\u30D5\u30A1\u30A4\u30EB\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F -ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile3={0}\:\u30EC\u30B8\u30B9\u30C8\u30EA\u30D5\u30A1\u30A4\u30EB\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F -ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile4={0}\:\u30EC\u30B8\u30B9\u30C8\u30EA\u30D5\u30A1\u30A4\u30EB\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F +ExtractIE.getBookmark.errMsg.errGettingBookmarks={0}\: Internet Explorer\u30d6\u30c3\u30af\u30de\u30fc\u30af\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ExtractIE.parentModuleName.noSpace=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +ExtractIE.parentModuleName=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +ExtractIE.getURLFromIEBmkFile.errMsg={0}\:Internet Explorer\u30d6\u30c3\u30af\u30de\u30fc\u30af\u30d5\u30a1\u30a4\u30eb{1}\u3092\u30d1\u30fc\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ExtractIE.getURLFromIEBmkFile.errMsg2={0}\:Internet Explorer\u30d6\u30c3\u30af\u30de\u30fc\u30af\u30d5\u30a1\u30a4\u30eb{1}\u3092\u30d1\u30fc\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ExtractIE.getCookie.errMsg.errGettingFile={0}\:Internet Explorer\u306ecookie\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ExtractIE.getCookie.errMsg.errReadingIECookie={0}\:Internet Explorer\u306ecookie{1}\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ExtractIE.getHistory.errMsg.unableToGetHist={0}\:Internet Explorer\u5c65\u6b74\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1apasco\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f +ExtractIE.getHistory.errMsg.errGettingHistFiles={0}\:Internet Explorer\u5c65\u6b74\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ExtractIE.getHistory.errMsg.noHistFiles=Internet Explorer\u5c65\u6b74\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ExtractIE.getHistory.errMsg.errWriteFile={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u66f8\u304d\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ExtractIE.getHistory.errMsg.errProcHist={0}\: Internet Explorer\u5c65\u6b74\u3092\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ExtractIE.parsePascoOutput.errMsg.notFound={0}\:Pasco\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{1} +ExtractIE.parsePascoOutput.errMsg.errParsing={0}\:Internet Explorer\u5c65\u6b74\u30a8\u30f3\u30c8\u30ea{1}\u3092\u30d1\u30fc\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ExtractIE.parsePascoOutput.errMsg.errParsingEntry={0}\: Internet Explorer\u5c65\u6b74\u30a8\u30f3\u30c8\u30ea\u3092\u30d1\u30fc\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ExtractRegistry.moduleName.text=\u30ec\u30b8\u30b9\u30c8\u30ea +ExtractRegistry.findRegFiles.errMsg.errReadingFile=\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\uff1a{0}\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ExtractRegistry.analyzeRegFiles.failedParsingResults={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u7d50\u679c\u306e\u30d1\u30fc\u30b9\u306b\u5931\u6557\u3057\u307e\u3057\u305f{1} +ExtractRegistry.parentModuleName.noSpace=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +ExtractRegistry.programName=\u30ec\u30b8\u30b9\u30c8\u30ea\u30ea\u30c3\u30d1\u30fc +ExtractRegistry.analyzeRegFiles.errMsg.errReadingRegFile={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb - {1}\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790\u306b\u5931\u6557\u3057\u307e\u3057\u305f +ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile2={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790\u306b\u5931\u6557\u3057\u307e\u3057\u305f +ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile3={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790\u306b\u5931\u6557\u3057\u307e\u3057\u305f +ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile4={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790\u306b\u5931\u6557\u3057\u307e\u3057\u305f Firefox.moduleName=FireFox -Firefox.getHistory.errMsg.errFetchingFiles=Firefox\u306E\u30A4\u30F3\u30BF\u30FC\u30CD\u30C3\u30C8\u5C65\u6B74\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Firefox.getHistory.errMsg.noFilesFound=Firefox\u5C65\u6B74\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -Firefox.getHistory.errMsg.errAnalyzeFile={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Firefox.parentModuleName.noSpace=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -Firefox.parentModuleName=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -Firefox.getBookmark.errMsg.errFetchFiles=Firefox\u306E\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Firefox.getBookmark.errMsg.errAnalyzeFile={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u306E\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Firefox.getCookie.errMsg.errFetchFile=Firefox\u306Ecookie\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Firefox.getCookie.errMsg.errAnalyzeFile={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Firefox.getDlPre24.errMsg.errFetchFiles=Firefox\u306E\u300C\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u300D\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Firefox.getDlPre24.errMsg.errAnalyzeFiles={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Firefox.getDlPre24.errMsg.errParsingArtifacts={0}\:{1} Firefox\u30A6\u30A7\u30D6\u5C65\u6B74\u30A2\u30FC\u30C6\u30A3\u30D5\u30A1\u30AF\u30C8\u3092\u30D1\u30FC\u30B9\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Firefox.getDlV24.errMsg.errFetchFiles=Firefox\u306E\u300C\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u300D\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -Firefox.getDlV24.errMsg.errAnalyzeFile={0}\:\u30D5\u30A1\u30A4\u30EB\:{1}\u3092\u89E3\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -Firefox.getDlV24.errMsg.errParsingArtifacts={0}\:{1} Firefox\u30A6\u30A7\u30D6\u5C65\u6B74\u30A2\u30FC\u30C6\u30A3\u30D5\u30A1\u30AF\u30C8\u3092\u30D1\u30FC\u30B9\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -RAImageIngestModule.process.started={0}\u3092\u958B\u59CB\u3057\u307E\u3057\u305F -RAImageIngestModule.process.errModFailed={0}\u5931\u6557\u3057\u307E\u3057\u305F - \u8A73\u7D30\u306F\u30ED\u30B0\u3092\u78BA\u8A8D\u3057\u3066\u4E0B\u3055\u3044
    -RAImageIngestModule.process.errModErrs={0}\u306B\u30A8\u30E9\u30FC\u304C\u3042\u308A\u307E\u3057\u305F -- \u30ED\u30B0\u3092\u78BA\u8A8D\u3057\u3066\u4E0B\u3055\u3044 -RAImageIngestModule.process.errMsg.errsEncountered=

    \u89E3\u6790\u4E2D\u306B\u767A\u751F\u3057\u305F\u30A8\u30E9\u30FC\uFF1A

      -RAImageIngestModule.process.errMsgSub.oneErr=1\u3064\u306E\u30A8\u30E9\u30FC\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F -RAImageIngestModule.process.errMsgSub.nErrs={0}\u306E\u30A8\u30E9\u30FC\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F -RAImageIngestModule.process.ingestMsg.finished=\u5B8C\u4E86 {0} - {1} -RAImageIngestModule.process.errMsg.noErrs=

      \u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u305B\u3093\u3067\u3057\u305F\u3002

      -RAImageIngestModule.process.errMsgSub.noErrs=\u30A8\u30E9\u30FC\u304C\u30EC\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093 -RAImageIngestModule.process.histMsg.title=

      {0}\u306B\u95A2\u3057\u3066\u306E\u30D6\u30E9\u30A6\u30B6\u30C7\u30FC\u30BF\uFF1A

        -RAImageIngestModule.process.histMsg.found= \u898B\u3064\u304B\u308A\u307E\u3057\u305F\u3002 -RAImageIngestModule.process.histMsg.notFnd=\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -RAImageIngestModule.process.ingestMsg.results={0} - \u30D6\u30E9\u30A6\u30B6\u7D50\u679C -RAImageIngestModule.complete.errMsg.failed={0} \u5B8C\u4E86\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F - \u8A73\u7D30\u306F\u30ED\u30B0\u3092\u78BA\u8A8D\u3057\u3066\u4E0B\u3055\u3044
        -RAImageIngestModule.getName=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -RAImageIngestModule.getDesc=\u30A6\u30A7\u30D6\u30D6\u30E9\u30A6\u30B8\u30F3\u30B0\u3001\u6700\u8FD1\u958B\u3044\u305F\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u3001\u6700\u8FD1\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u305F\u30D7\u30ED\u30B0\u30E9\u30E0\u7B49\u306E\u6700\u8FD1\u306E\u30E6\u30FC\u30B6\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u3092\u62BD\u51FA\u3057\u307E\u3059\u3002 -RecentDocumentsByLnk.getRecDoc.errMsg.errGetLnkFiles={0}\:lnk\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -RecentDocumentsByLnk.getRecDoc.errParsingFile={0}\: \u6700\u8FD1\u306E\u30D5\u30A1\u30A4\u30EB{1}\u3092\u30D1\u30FC\u30B9\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -RecentDocumentsByLnk.parentModuleName.noSpace=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -RecentDocumentsByLnk.parentModuleName=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -SearchEngineURLQueryAnalyzer.moduleName.text=\u691C\u7D22\u30A8\u30F3\u30B8\u30F3 +Firefox.getHistory.errMsg.errFetchingFiles=Firefox\u306e\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u5c65\u6b74\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Firefox.getHistory.errMsg.noFilesFound=Firefox\u5c65\u6b74\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +Firefox.getHistory.errMsg.errAnalyzeFile={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Firefox.parentModuleName.noSpace=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +Firefox.parentModuleName=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +Firefox.getBookmark.errMsg.errFetchFiles=Firefox\u306e\u30d6\u30c3\u30af\u30de\u30fc\u30af\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Firefox.getBookmark.errMsg.errAnalyzeFile={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u306e\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Firefox.getCookie.errMsg.errFetchFile=Firefox\u306ecookie\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Firefox.getCookie.errMsg.errAnalyzeFile={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Firefox.getDlPre24.errMsg.errFetchFiles=Firefox\u306e\u300c\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u300d\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Firefox.getDlPre24.errMsg.errAnalyzeFiles={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Firefox.getDlPre24.errMsg.errParsingArtifacts={0}\:{1} Firefox\u30a6\u30a7\u30d6\u5c65\u6b74\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30d1\u30fc\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Firefox.getDlV24.errMsg.errFetchFiles=Firefox\u306e\u300c\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u300d\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +Firefox.getDlV24.errMsg.errAnalyzeFile={0}\:\u30d5\u30a1\u30a4\u30eb\:{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +Firefox.getDlV24.errMsg.errParsingArtifacts={0}\:{1} Firefox\u30a6\u30a7\u30d6\u5c65\u6b74\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30d1\u30fc\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +RAImageIngestModule.process.started={0}\u3092\u958b\u59cb\u3057\u307e\u3057\u305f +RAImageIngestModule.process.errModFailed={0}\u5931\u6557\u3057\u307e\u3057\u305f - \u8a73\u7d30\u306f\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044
        +RAImageIngestModule.process.errModErrs={0}\u306b\u30a8\u30e9\u30fc\u304c\u3042\u308a\u307e\u3057\u305f -- \u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044 +RAImageIngestModule.process.errMsg.errsEncountered=

        \u89e3\u6790\u4e2d\u306b\u767a\u751f\u3057\u305f\u30a8\u30e9\u30fc\uff1a

          +RAImageIngestModule.process.errMsgSub.oneErr=1\u3064\u306e\u30a8\u30e9\u30fc\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f +RAImageIngestModule.process.errMsgSub.nErrs={0}\u306e\u30a8\u30e9\u30fc\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f +RAImageIngestModule.process.ingestMsg.finished=\u5b8c\u4e86 {0} - {1} +RAImageIngestModule.process.errMsg.noErrs=

          \u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u305b\u3093\u3067\u3057\u305f\u3002

          +RAImageIngestModule.process.errMsgSub.noErrs=\u30a8\u30e9\u30fc\u304c\u30ec\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +RAImageIngestModule.process.histMsg.title=

          {0}\u306b\u95a2\u3057\u3066\u306e\u30d6\u30e9\u30a6\u30b6\u30c7\u30fc\u30bf\uff1a

            +RAImageIngestModule.process.histMsg.found= \u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002 +RAImageIngestModule.process.histMsg.notFnd=\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +RAImageIngestModule.process.ingestMsg.results={0} - \u30d6\u30e9\u30a6\u30b6\u7d50\u679c +RAImageIngestModule.complete.errMsg.failed={0} \u5b8c\u4e86\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f - \u8a73\u7d30\u306f\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044
            +RAImageIngestModule.getName=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +RAImageIngestModule.getDesc=\u30a6\u30a7\u30d6\u30d6\u30e9\u30a6\u30b8\u30f3\u30b0\u3001\u6700\u8fd1\u958b\u3044\u305f\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3001\u6700\u8fd1\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u305f\u30d7\u30ed\u30b0\u30e9\u30e0\u7b49\u306e\u6700\u8fd1\u306e\u30e6\u30fc\u30b6\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3\u3092\u62bd\u51fa\u3057\u307e\u3059\u3002 +RecentDocumentsByLnk.getRecDoc.errMsg.errGetLnkFiles={0}\:lnk\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +RecentDocumentsByLnk.getRecDoc.errParsingFile={0}\: \u6700\u8fd1\u306e\u30d5\u30a1\u30a4\u30eb{1}\u3092\u30d1\u30fc\u30b9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +RecentDocumentsByLnk.parentModuleName.noSpace=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +RecentDocumentsByLnk.parentModuleName=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +SearchEngineURLQueryAnalyzer.moduleName.text=\u691c\u7d22\u30a8\u30f3\u30b8\u30f3 SearchEngineURLQueryAnalyzer.engineName.none=\u7121\u3057 SearchEngineURLQueryAnalyzer.domainSubStr.none=\u7121\u3057 -SearchEngineURLQueryAnalyzer.toString=\u540D\u79F0\uFF1A {0}\n\ - \u30C9\u30E1\u30A4\u30F3\u30B5\u30D6\u30B9\u30C8\u30EA\u30F3\u30B0\uFF1A {1}\n\ - \u30AB\u30A6\u30F3\u30C8\uFF1A {2}\n\ - \u5206\u5272\u30C8\u30FC\u30AF\u30F3\n\ +SearchEngineURLQueryAnalyzer.toString=\u540d\u79f0\uff1a {0}\n\ + \u30c9\u30e1\u30a4\u30f3\u30b5\u30d6\u30b9\u30c8\u30ea\u30f3\u30b0\uff1a {1}\n\ + \u30ab\u30a6\u30f3\u30c8\uff1a {2}\n\ + \u5206\u5272\u30c8\u30fc\u30af\u30f3\n\ {3} -SearchEngineURLQueryAnalyzer.parentModuleName.noSpace=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -SearchEngineURLQueryAnalyzer.parentModuleName=\u6700\u8FD1\u306E\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3 -SearchEngineURLQueryAnalyzer.init.exception.msg={0}\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F -UsbDeviceIdMapper.parseAndLookup.text=\u30D7\u30ED\u30C0\u30AF\u30C8\uFF1A{0} \ No newline at end of file +SearchEngineURLQueryAnalyzer.parentModuleName.noSpace=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +SearchEngineURLQueryAnalyzer.parentModuleName=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 +SearchEngineURLQueryAnalyzer.init.exception.msg={0}\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f +UsbDeviceIdMapper.parseAndLookup.text=\u30d7\u30ed\u30c0\u30af\u30c8\uff1a{0} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java index f75132c610..49ac9911fe 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java @@ -554,22 +554,24 @@ class Chrome extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); - BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes); - if (bbart != null) { - bbartifacts.add(bbart); - } - - // find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it.. - try { - for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(fullPath), FilenameUtils.getPath(fullPath))) { - BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - downloadSourceArt.addAttributes(createDownloadSourceAttributes(result.get("url").toString())); - - bbartifacts.add(downloadSourceArt); - break; + BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes); + if (webDownloadArtifact != null) { + bbartifacts.add(webDownloadArtifact); + + // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. + try { + for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(fullPath), FilenameUtils.getPath(fullPath))) { + BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID())); + + bbartifacts.add(associatedObjectArtifact); + break; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'", fullPath), ex); //NON-NLS } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Error creating download source artifact for file '%s'", fullPath), ex); //NON-NLS } } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java index efaf8934b2..12c4208d89 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java @@ -39,6 +39,7 @@ import java.util.Map.Entry; import java.util.Optional; 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.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -276,6 +277,10 @@ final class ChromeCacheExtractor { } } + @Messages({ + "ChromeCacheExtract_adding_extracted_files_msg=Adding %d extracted files for analysis." + }) + /** * Processes a user's cache and creates corresponding artifacts and derived files. * @@ -355,6 +360,8 @@ final class ChromeCacheExtractor { cleanup(); return; } + + progressBar.progress(String.format(Bundle.ChromeCacheExtract_adding_extracted_files_msg(), derivedFiles.size())); derivedFiles.forEach((derived) -> { services.fireModuleContentEvent(new ModuleContentEvent(derived)); @@ -380,12 +387,12 @@ final class ChromeCacheExtractor { * Extracts the files if needed and adds as derived files, creates artifacts * * @param cacheEntryAddress cache entry address - * @param sourceArtifacts any source artifacts created are added to this collection + * @param associatedObjectArtifacts any associated object artifacts created are added to this collection * @param webCacheArtifacts any web cache artifacts created are added to this collection * * @return Optional derived file, is a derived file is added for the given entry */ - private List processCacheEntry(CacheAddress cacheEntryAddress, Collection sourceArtifacts, Collection webCacheArtifacts ) throws TskCoreException, IngestModuleException { + private List processCacheEntry(CacheAddress cacheEntryAddress, Collection associatedObjectArtifacts, Collection webCacheArtifacts ) throws TskCoreException, IngestModuleException { List derivedFiles = new ArrayList<>(); @@ -437,10 +444,6 @@ final class ChromeCacheExtractor { moduleName, cacheEntry.getHTTPHeaders()); - Collection sourceArtifactAttributes = new ArrayList<>(); - sourceArtifactAttributes.add(urlAttr); - sourceArtifactAttributes.add(createTimeAttr); - Collection webCacheAttributes = new ArrayList<>(); webCacheAttributes.add(urlAttr); webCacheAttributes.add(createTimeAttr); @@ -450,12 +453,7 @@ final class ChromeCacheExtractor { // add artifacts to the f_XXX file if (dataSegment.isInExternalFile() ) { try { - BlackboardArtifact sourceArtifact = cachedFileAbstractFile.get().newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - if (sourceArtifact != null) { - sourceArtifact.addAttributes(sourceArtifactAttributes); - sourceArtifacts.add(sourceArtifact); - } - + BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE); if (webCacheArtifact != null) { webCacheArtifact.addAttributes(webCacheAttributes); @@ -469,6 +467,14 @@ final class ChromeCacheExtractor { moduleName, cachedFileAbstractFile.get().getId())); webCacheArtifacts.add(webCacheArtifact); + + BlackboardArtifact associatedObjectArtifact = cachedFileAbstractFile.get().newArtifact(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + if (associatedObjectArtifact != null) { + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + moduleName, webCacheArtifact.getArtifactID())); + associatedObjectArtifacts.add(associatedObjectArtifact); + } } if (isBrotliCompressed) { @@ -497,12 +503,7 @@ final class ChromeCacheExtractor { "", TskData.EncodingType.NONE); - BlackboardArtifact sourceArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - if (sourceArtifact != null) { - sourceArtifact.addAttributes(sourceArtifactAttributes); - sourceArtifacts.add(sourceArtifact); - } - + BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE); if (webCacheArtifact != null) { webCacheArtifact.addAttributes(webCacheAttributes); @@ -516,6 +517,14 @@ final class ChromeCacheExtractor { moduleName, derivedFile.getId())); webCacheArtifacts.add(webCacheArtifact); + + BlackboardArtifact associatedObjectArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + if (associatedObjectArtifact != null) { + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + moduleName, webCacheArtifact.getArtifactID())); + associatedObjectArtifacts.add(associatedObjectArtifact); + } } if (isBrotliCompressed) { diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java index 704826047f..c11e4fde22 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java @@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.recentactivity.BinaryCookieReader.Cookie; 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; import org.xml.sax.SAXException; @@ -637,15 +638,17 @@ final class ExtractSafari extends Extract { time = date.getDate().getTime(); } - BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD); - bbart.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName())); - bbartifacts.add(bbart); + BlackboardArtifact webDownloadArtifact = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD); + webDownloadArtifact.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName())); + bbartifacts.add(webDownloadArtifact); - // find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it. + // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(path), FilenameUtils.getPath(path))) { - BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - downloadSourceArt.addAttributes(createDownloadSourceAttributes(url)); - bbartifacts.add(downloadSourceArt); + BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID())); + bbartifacts.add(associatedObjectArtifact); break; } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java index 250c55fa6e..5062806133 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java @@ -28,7 +28,6 @@ import java.util.List; import java.util.Properties; import java.util.Set; import java.util.logging.Level; -import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.NetworkUtils; @@ -36,13 +35,11 @@ import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD; import org.sleuthkit.datamodel.BlackboardAttribute; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; @@ -94,7 +91,7 @@ final class ExtractZoneIdentifier extends Extract { return; } - Collection sourceArtifacts = new ArrayList<>(); + Collection associatedObjectArtifacts = new ArrayList<>(); Collection downloadArtifacts = new ArrayList<>(); for (AbstractFile zoneFile : zoneFiles) { @@ -104,7 +101,7 @@ final class ExtractZoneIdentifier extends Extract { } try { - processZoneFile(context, dataSource, zoneFile, sourceArtifacts, downloadArtifacts, knownPathIDs); + processZoneFile(context, dataSource, zoneFile, associatedObjectArtifacts, downloadArtifacts, knownPathIDs); } catch (TskCoreException ex) { addErrorMessage(Bundle.ExtractZone_process_errMsg()); String message = String.format("Failed to process zone identifier file %s", zoneFile.getName()); //NON-NLS @@ -112,23 +109,23 @@ final class ExtractZoneIdentifier extends Extract { } } - postArtifacts(sourceArtifacts); + postArtifacts(associatedObjectArtifacts); postArtifacts(downloadArtifacts); } /** * Process a single Zone Identifier file. * - * @param context IngetJobContext - * @param dataSource Content - * @param zoneFile Zone Indentifier file - * @param sourceArtifacts List for TSK_DOWNLOAD_SOURCE artifacts - * @param downloadArtifacts List for TSK_WEB_DOWNLOAD aritfacts - * + * @param context IngetJobContext + * @param dataSource Content + * @param zoneFile Zone Indentifier file + * @param associatedObjectArtifacts List for TSK_ASSOCIATED_OBJECT artifacts + * @param downloadArtifacts List for TSK_WEB_DOWNLOAD artifacts + * * @throws TskCoreException */ private void processZoneFile(IngestJobContext context, Content dataSource, - AbstractFile zoneFile, Collection sourceArtifacts, + AbstractFile zoneFile, Collection associatedObjectArtifacts, Collection downloadArtifacts, Set knownPathIDs) throws TskCoreException { @@ -155,16 +152,16 @@ final class ExtractZoneIdentifier extends Extract { BlackboardArtifact downloadBba = createDownloadArtifact(zoneFile, zoneInfo); if (downloadBba != null) { downloadArtifacts.add(downloadBba); + // create a TSK_ASSOCIATED_OBJECT for the downloaded file, associating it with the TSK_WEB_DOWNLOAD artifact. + if (downloadFile.getArtifactsCount(TSK_ASSOCIATED_OBJECT) == 0) { + BlackboardArtifact associatedObjectBba = createAssociatedObjectArtifact(downloadFile, downloadBba); + if (associatedObjectBba != null) { + associatedObjectArtifacts.add(associatedObjectBba); + } + } } } - // check if download has a child TSK_DOWNLOAD_SOURCE artifact, if not create one - if (downloadFile.getArtifactsCount(TSK_DOWNLOAD_SOURCE) == 0) { - BlackboardArtifact sourceBba = createDownloadSourceArtifact(downloadFile, zoneInfo); - if (sourceBba != null) { - sourceArtifacts.add(sourceBba); - } - } } } @@ -203,33 +200,26 @@ final class ExtractZoneIdentifier extends Extract { } /** - * Create a Download Source Artifact for the given ZoneIdentifierInfo + * Create a Associated Object Artifact for the given ZoneIdentifierInfo * object. * * @param downloadFile AbstractFile representing the file downloaded, not - * the zone identifier file. - * @param zoneInfo Zone identifier file wrapper object + * the zone identifier file. + * @param downloadBba TSK_WEB_DOWNLOAD artifact to associate with. * - * @return TSK_DOWNLOAD_SOURCE object for given parameters + * @return TSK_ASSOCIATED_OBJECT artifact. */ - private BlackboardArtifact createDownloadSourceArtifact(AbstractFile downloadFile, ZoneIdentifierInfo zoneInfo) { + private BlackboardArtifact createAssociatedObjectArtifact(AbstractFile downloadFile, BlackboardArtifact downloadBba) { Collection bbattributes = new ArrayList<>(); bbattributes.addAll(Arrays.asList( - new BlackboardAttribute(TSK_URL, - RecentActivityExtracterModuleFactory.getModuleName(), - StringUtils.defaultString(zoneInfo.getURL(), "")), - - new BlackboardAttribute(TSK_DOMAIN, - RecentActivityExtracterModuleFactory.getModuleName(), - (zoneInfo.getURL() != null) ? NetworkUtils.extractDomain(zoneInfo.getURL()) : ""), - - new BlackboardAttribute(TSK_LOCATION, - RecentActivityExtracterModuleFactory.getModuleName(), - StringUtils.defaultString(zoneInfo.getZoneIdAsString(), "")))); //NON-NLS + new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT, + RecentActivityExtracterModuleFactory.getModuleName(), + downloadBba.getArtifactID()) + )); - return createArtifactWithAttributes(TSK_DOWNLOAD_SOURCE, downloadFile, bbattributes); + return createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, downloadFile, bbattributes); } /** diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java index dd4e70ee06..7fddeb0aa8 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java @@ -544,22 +544,25 @@ class Firefox extends Extract { domain)); //NON-NLS } - BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); - if (bbart != null) { - bbartifacts.add(bbart); - } - - // find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it. - try { - for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { - BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - downloadSourceArt.addAttributes(createDownloadSourceAttributes(source)); - bbartifacts.add(downloadSourceArt); - break; + BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); + if (webDownloadArtifact != null) { + bbartifacts.add(webDownloadArtifact); + + // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. + try { + for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { + BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID())); + + bbartifacts.add(associatedObjectArtifact); + break; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'", + downloadedFilePath), ex); //NON-NLS } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Error creating download source artifact for file '%s'", - downloadedFilePath), ex); //NON-NLS } } if (errors > 0) { @@ -681,22 +684,24 @@ class Firefox extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), domain)); //NON-NLS } - BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); - if (bbart != null) { - bbartifacts.add(bbart); - } + BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); + if (webDownloadArtifact != null) { + bbartifacts.add(webDownloadArtifact); - // find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it. - try { - for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { - BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - downloadSourceArt.addAttributes(createDownloadSourceAttributes(url)); - bbartifacts.add(downloadSourceArt); - break; + // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. + try { + for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { + BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID())); + bbartifacts.add(associatedObjectArtifact); + break; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'", + downloadedFilePath), ex); //NON-NLS } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Error creating download source artifact for file '%s'", - downloadedFilePath), ex); //NON-NLS } } if (errors > 0) { diff --git a/Running_Linux_OSX.txt b/Running_Linux_OSX.txt index 56ea6a5888..c47dc5b418 100644 --- a/Running_Linux_OSX.txt +++ b/Running_Linux_OSX.txt @@ -9,33 +9,41 @@ The following need to be done at least once. They do not need to be repeated for -- Linux: % sudo apt-get install testdisk -- OS X: % brew install testdisk -- Install a Java 8 JRE and JavaFX 8 and set JAVA_HOME. --- Linux: Any Java 8 version of OpenJDK/OpenJFX distribution should suffice. The following instructions use the Zulu Community distribution. - 1. Download a 64 bit Java 8 JRE for your specific platform from https://www.azul.com/downloads/zulu-community - 2. Install the JRE. e.g. % sudo apt install ./zulu8.40.0.25-ca-jre8.0.222-linux_amd64.deb - 3. Download a 64 bit Java 8 JavaFX for your specific platform from the same location. - 4. Extract the contents of the JavaFX archive into the folder where the JRE was installed. - e.g. % sudo tar xzf ~/Downloads/zulu8.40.0.25-ca-fx-jre8.0.222-linux_x64.tar.gz -C /usr/lib/jvm/zre-8-amd64 --strip-components=1 - +- Install the BellSoft Java 8 JRE and JavaFX 8 distribution and set JAVA_HOME. + * The BellSoft distribution bundles OpenJDK and OpenJFX. Other distributions we have tried either don't + bundle OpenJFX (AdoptOpenJDK) or don't include all necessary binaries (Amazon Corretto). +-- Linux: + 1. Install BellSoft Java 8 + % wget -q -O - https://download.bell-sw.com/pki/GPG-KEY-bellsoft | sudo apt-key add - + % echo "deb [arch=amd64] https://apt.bell-sw.com/ stable main" | sudo tee /etc/apt/sources.list.d/bellsoft.list + % sudo apt-get update + % sudo apt-get install bellsoft-java8 + 2. Set JAVA_HOME + % export JAVA_HOME=/usr/lib/jvm/bellsoft-java8-amd64 + NOTE: You may need to log out and back in again after setting JAVA_HOME before the Autopsy unix_setup.sh script can see the value. --- OS X: Any Java 8 version of OpenJDK/OpenJFX distribution should suffice. - 1. Install a 64 bit Java 8 JRE. - % brew tap adoptopenjdk/openjdk - % brew cask install adoptopenjdk8 - 2. Download a 64 bit Java 8 JavaFX for macOS from https://www.azul.com/downloads/zulu-community - 3. Extract the contents of the JavaFX archive into the folder where the JRE was installed. - e.g. % sudo tar xf ~/Downloads/zulu8.40.0.25-ca-fx-jre8.0.222-macosx_x64.tar.gz -C /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home--strip-components=1 - 4. Confirm Java 8 is being found by running 'java -version' - 5. Set JAVA_HOME environment variable to location of JRE installation. +-- OS X: + 1. Install BellSoft Java 8. + % brew tap bell-sw/liberica + % brew cask install liberica-jdk8 + 2. Set JAVA_HOME environment variable to location of JRE installation. + e.g. add the following to ~/.bashrc + export JAVA_HOME=$(/usr/libexec/java_home -v 1.8) +- Confirm your version of Java by running + % java -version + openjdk version "1.8.0.232" + OpenJDK Runtime Environment (build 1.8.0_232-BellSoft-b10) + OpenJDK 64-Bit Server VM (build 25.232-b10, mixed mode) + * Install The Sleuth Kit Java Bindings * Autopsy depends on a specific version of The Sleuth Kit. You need the Java libraries of The Sleuth Kit installed, which is not part of all packages. - Linux: Install the sleuthkit-java.deb file that you can download from github.com/sleuthkit/sleuthkit/releases. This will install libewf, etc. --- % sudo apt install ./sleuthkit-java_4.6.0-1_amd64.deb +-- % sudo apt install ./sleuthkit-java_4.7.0-1_amd64.deb - OS X: Install The Sleuth Kit from brew. -- % brew install sleuthkit @@ -55,6 +63,24 @@ Autopsy depends on a specific version of The Sleuth Kit. You need the Java libr - Run Autopsy % ./autopsy +* Troubleshooting * + +- If you see something like "Cannot create case: javafx/scene/paint/Color" it is an indication that Java FX + is not being found. + Confirm that the file $JAVA_HOME/jre/lib/ext/jfxrt.jar exists. If it does not exist, return to the Java + setup steps above. +- If you see something like "An illegal reflective access operation has occurred" it is an indication that + the wrong version of Java is being used to run Autopsy. + Check the version of Java reported in the ~/.autopsy/dev/var/log/messages.log file. It should contain lines that looks like: + Java; VM; Vendor = 1.8.0_232; OpenJDK 64-Bit Server V 25.232-b10; BellSoft + Runtime = OpenJDK Runtime Environment 1.8.0_232-BellSoft-b10 + Java Home = /usr/lib/jvm/bellsoft-java8-amd64/jre + + If your messages.log file indicates that Java 8 is not being used: + (a) confirm that you have a version of Java 8 installed and + (b) confirm that your JAVA_HOME environment variable is set correctly: + % echo $JAVA_HOME + * Limitations (Updated May 2018) * - Timeline does not work on OS X - Video thumbnails are not generated (need to get a consistent version of OpenCV) diff --git a/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/Bundle_ja.properties b/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/Bundle_ja.properties index 2f387da587..c4ba8d47fc 100644 --- a/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/Bundle_ja.properties +++ b/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/Bundle_ja.properties @@ -1,13 +1,13 @@ -OpenIDE-Module-Name=Scalpel\u30AB\u30FC\u30D0 -OpenIDE-Module-Display-Category=\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB +OpenIDE-Module-Name=Scalpel\u30ab\u30fc\u30d0 +OpenIDE-Module-Display-Category=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb OpenIDE-Module-Long-Description=\ - \u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u3092\u5B9F\u884C\u3059\u308B\u969B\u306B\u672A\u5272\u308A\u5F53\u3066\u9818\u57DF\u304B\u3089\u30D5\u30A1\u30A4\u30EB\u3092\u5207\u308A\u51FA\u3057\u307E\u3059\u3002\n\ - \u5207\u308A\u51FA\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u306F\u518D\u5EA6\u89E3\u6790\u3055\u308C\u3001\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30C4\u30EA\u30FC\u306B\u8868\u793A\u3055\u308C\u307E\u3059\u3002 -OpenIDE-Module-Short-Description=\u672A\u5272\u308A\u5F53\u3066\u9818\u57DF\u304B\u3089\u30D5\u30A1\u30A4\u30EB\u3092\u5207\u308A\u51FA\u3057\u307E\u3059 -ScalpelCarverIngestModule.moduleName=Scalpel\u30AB\u30FC\u30D0 -ScalpelCarverIngestModule.moduleDesc.text=\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u3092\u5B9F\u884C\u3059\u308B\u969B\u306B\u672A\u5272\u308A\u5F53\u3066\u9818\u57DF\u304B\u3089\u30D5\u30A1\u30A4\u30EB\u3092\u5207\u308A\u51FA\u3057\u307E\u3059\u3002\n\ -\u5207\u308A\u51FA\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u306F\u518D\u5EA6\u89E3\u6790\u3055\u308C\u3001\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30C4\u30EA\u30FC\u306B\u8868\u793A\u3055\u308C\u307E\u3059\u3002 -ScalpelCarverIngestModule.startUp.exception.msg1=\u73FE\u5728\u3001Scalpel\u30AB\u30FC\u30D3\u30F3\u30B0\u30E2\u30B8\u30E5\u30FC\u30EB\u306FWindows OS\u4EE5\u5916\u3068\u306F\u4E92\u63DB\u6027\u304C\u3042\u308A\u307E\u305B\u3093\u3002 -ScalpelCarverIngestModule.startUp.exception.msg2=Scalpel\u30AB\u30FC\u30D0\u306E\u521D\u671F\u5316\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002 -ScalpelCarverIngestModule.startUp.exception.msg3=Scalpel\u30E2\u30B8\u30E5\u30FC\u30EB\u306E\u30A2\u30A6\u30C8\u30D7\u30C3\u30C8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -ScalpelCarverIngestModule.startUp.exception.msg4=Scalpel\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u3078\u306E\u30D1\u30B9\u304C\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 \ No newline at end of file + \u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u3059\u308b\u969b\u306b\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u304b\u3089\u30d5\u30a1\u30a4\u30eb\u3092\u5207\u308a\u51fa\u3057\u307e\u3059\u3002\n\ + \u5207\u308a\u51fa\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306f\u518d\u5ea6\u89e3\u6790\u3055\u308c\u3001\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c4\u30ea\u30fc\u306b\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +OpenIDE-Module-Short-Description=\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u304b\u3089\u30d5\u30a1\u30a4\u30eb\u3092\u5207\u308a\u51fa\u3057\u307e\u3059 +ScalpelCarverIngestModule.moduleName=Scalpel\u30ab\u30fc\u30d0 +ScalpelCarverIngestModule.moduleDesc.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u3059\u308b\u969b\u306b\u672a\u5272\u308a\u5f53\u3066\u9818\u57df\u304b\u3089\u30d5\u30a1\u30a4\u30eb\u3092\u5207\u308a\u51fa\u3057\u307e\u3059\u3002\n\ +\u5207\u308a\u51fa\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306f\u518d\u5ea6\u89e3\u6790\u3055\u308c\u3001\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c4\u30ea\u30fc\u306b\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +ScalpelCarverIngestModule.startUp.exception.msg1=\u73fe\u5728\u3001Scalpel\u30ab\u30fc\u30d3\u30f3\u30b0\u30e2\u30b8\u30e5\u30fc\u30eb\u306fWindows OS\u4ee5\u5916\u3068\u306f\u4e92\u63db\u6027\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +ScalpelCarverIngestModule.startUp.exception.msg2=Scalpel\u30ab\u30fc\u30d0\u306e\u521d\u671f\u5316\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +ScalpelCarverIngestModule.startUp.exception.msg3=Scalpel\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ScalpelCarverIngestModule.startUp.exception.msg4=Scalpel\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3078\u306e\u30d1\u30b9\u304c\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 diff --git a/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/jni/Bundle_ja.properties b/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/jni/Bundle_ja.properties index cab0908ab7..5dd426932e 100644 --- a/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/jni/Bundle_ja.properties +++ b/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/jni/Bundle_ja.properties @@ -1,8 +1,8 @@ -ScalpelCarver.loadLib.errMsg.cannotLoadLib=\u4F7F\u7528\u3055\u308C\u3066\u3044\u308B\u74B0\u5883\u306B\u30E9\u30A4\u30D6\u30E9\u30EA{0}\u3092\u30ED\u30FC\u30C9\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F -ScalpelCarver.loadLib.errMsg.cannotLoadLib2=\u4F7F\u7528\u3055\u308C\u3066\u3044\u308B\u74B0\u5883\u306B\u30E9\u30A4\u30D6\u30E9\u30EA{0}\u3092\u30ED\u30FC\u30C9\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F -ScalpelCarver.carve.exception.libNotInit=Scalpel\u30E9\u30A4\u30D6\u30E9\u30EA\u306F\u5B8C\u5168\u306B\u521D\u671F\u5316\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002 -ScalpelCarver.carve.exception.invalidArgs=Scalpel\u30AB\u30FC\u30D3\u30F3\u30B0\u306B\u7121\u52B9\u306A\u30A2\u30FC\u30AE\u30E5\u30E1\u30F3\u30C8\u3002 -ScalpelCarver.carve.exception.cannotReadConfig=libscalpel\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\: {0}\u3092\u8AAD\u307F\u53D6\u308C\u307E\u305B\u3093\u3067\u3057\u305F -ScalpelCarver.carve.exception.cannotWriteConfig=libscalpel\u30A2\u30A6\u30C8\u30D7\u30C3\u30C8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\: {0}\u306B\u66F8\u304D\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F -ScalpelOutputParser.outputStart.text=\u4E0B\u8A18\u306E\u30D5\u30A1\u30A4\u30EB\u304C\u5207\u308A\u51FA\u3055\u308C\u307E\u3057\u305F\uFF1A -ScalpelOutputParser.toString.text=CarvedFileMeta'{'\u30D5\u30A1\u30A4\u30EB\u540D\={0}, \u30B9\u30BF\u30FC\u30C8\: {1}, \u30B5\u30A4\u30BA\: {2}'}' \ No newline at end of file +ScalpelCarver.loadLib.errMsg.cannotLoadLib=\u4f7f\u7528\u3055\u308c\u3066\u3044\u308b\u74b0\u5883\u306b\u30e9\u30a4\u30d6\u30e9\u30ea{0}\u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +ScalpelCarver.loadLib.errMsg.cannotLoadLib2=\u4f7f\u7528\u3055\u308c\u3066\u3044\u308b\u74b0\u5883\u306b\u30e9\u30a4\u30d6\u30e9\u30ea{0}\u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +ScalpelCarver.carve.exception.libNotInit=Scalpel\u30e9\u30a4\u30d6\u30e9\u30ea\u306f\u5b8c\u5168\u306b\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +ScalpelCarver.carve.exception.invalidArgs=Scalpel\u30ab\u30fc\u30d3\u30f3\u30b0\u306b\u7121\u52b9\u306a\u30a2\u30fc\u30ae\u30e5\u30e1\u30f3\u30c8\u3002 +ScalpelCarver.carve.exception.cannotReadConfig=libscalpel\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\: {0}\u3092\u8aad\u307f\u53d6\u308c\u307e\u305b\u3093\u3067\u3057\u305f +ScalpelCarver.carve.exception.cannotWriteConfig=libscalpel\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\: {0}\u306b\u66f8\u304d\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f +ScalpelOutputParser.outputStart.text=\u4e0b\u8a18\u306e\u30d5\u30a1\u30a4\u30eb\u304c\u5207\u308a\u51fa\u3055\u308c\u307e\u3057\u305f\uff1a +ScalpelOutputParser.toString.text=CarvedFileMeta'{'\u30d5\u30a1\u30a4\u30eb\u540d\={0}, \u30b9\u30bf\u30fc\u30c8\: {1}, \u30b5\u30a4\u30ba\: {2}'}' diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 4fd4fb33d8..b20ccf5912 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Fri, 04 Oct 2019 14:30:10 -0400 +#Tue, 12 Nov 2019 17:21:46 -0500 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 17f4cb7436..998d3f715c 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Fri, 04 Oct 2019 14:30:10 -0400 +#Tue, 12 Nov 2019 17:21:46 -0500 CTL_MainWindow_Title=Autopsy 4.13.0 CTL_MainWindow_Title_No_Project=Autopsy 4.13.0 diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index b640c41549..d73868792a 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -772,6 +772,9 @@ INPUT = main.dox \ regressionTesting.dox \ native_libs.dox \ modDevPython.dox \ + modFileIngestTutorial.dox \ + modDSIngestTutorial.dox \ + modReportModuleTutorial.dox \ debugTsk.dox \ ../../Core/src \ ../../CoreLibs/src \ @@ -867,7 +870,7 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = . +IMAGE_PATH = images/ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program diff --git a/docs/doxygen/footer.html b/docs/doxygen/footer.html index ac0c0a8d1c..8bf1577a45 100644 --- a/docs/doxygen/footer.html +++ b/docs/doxygen/footer.html @@ -1,5 +1,5 @@
            -

            Copyright © 2012-2018 Basis Technology. Generated on: $date
            +

            Copyright © 2012-2019 Basis Technology. Generated on: $date
            This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.

            diff --git a/docs/doxygen/images/bigAndRoundFiles.png b/docs/doxygen/images/bigAndRoundFiles.png new file mode 100644 index 0000000000..fa6430e7d8 Binary files /dev/null and b/docs/doxygen/images/bigAndRoundFiles.png differ diff --git a/docs/doxygen/images/demoScript_folder.png b/docs/doxygen/images/demoScript_folder.png new file mode 100644 index 0000000000..01d7d16f4c Binary files /dev/null and b/docs/doxygen/images/demoScript_folder.png differ diff --git a/docs/doxygen/images/ingest-modules.PNG b/docs/doxygen/images/ingest-modules.PNG new file mode 100644 index 0000000000..9941a0dfb3 Binary files /dev/null and b/docs/doxygen/images/ingest-modules.PNG differ diff --git a/docs/doxygen/images/reports_select.png b/docs/doxygen/images/reports_select.png new file mode 100644 index 0000000000..fe4f1f10bd Binary files /dev/null and b/docs/doxygen/images/reports_select.png differ diff --git a/docs/doxygen/viewer_image.JPG b/docs/doxygen/images/viewer_image.JPG old mode 100755 new mode 100644 similarity index 100% rename from docs/doxygen/viewer_image.JPG rename to docs/doxygen/images/viewer_image.JPG diff --git a/docs/doxygen/main.dox b/docs/doxygen/main.dox index 22bcd097ad..230be1fd03 100644 --- a/docs/doxygen/main.dox +++ b/docs/doxygen/main.dox @@ -9,9 +9,9 @@ If these pages don't answer your question, then send the question to the Writing Python or Java Modules

    If you want to write Java or Python modules, then there are some tutorials and detailed pages in this document. The Python tutorials include: -- File Ingest Modules: http://www.basistech.com/python-autopsy-module-tutorial-1-the-file-ingest-module/ -- Data Source Ingest Modules: http://www.basistech.com/python-autopsy-module-tutorial-2-the-data-source-ingest-module/ -- Report Modules: http://www.basistech.com/python-autopsy-module-tutorial-3-the-report-module/ +- File Ingest Modules: \subpage mod_python_file_ingest_tutorial_page +- Data Source Ingest Modules: \subpage mod_python_ds_ingest_tutorial_page +- Report Modules: \subpage mod_python_report_tutorial_page This document contains the following pages: - \subpage platform_page diff --git a/docs/doxygen/modDSIngestTutorial.dox b/docs/doxygen/modDSIngestTutorial.dox new file mode 100644 index 0000000000..c38f3d5814 --- /dev/null +++ b/docs/doxygen/modDSIngestTutorial.dox @@ -0,0 +1,171 @@ +/*! \page mod_python_ds_ingest_tutorial_page Python Tutorial #2: Writing a Data Source Ingest Module + + +In the \ref mod_python_file_ingest_tutorial_page "first tutorial" we built a basic Python Autopsy module that looked for big and round files. In this tutorial we're going to make two data source ingest modules. The first focuses on finding SQLite databases and parsing them, and the second focuses on running a command line tool on a disk image. + +The main difference from the first tutorial, which focused on file ingest modules, is that these are data source ingest modules. Data source-ingest modules are given a reference to a data source and the module needs to find the files to analyze, whereas file-level ingest modules are given a reference to each file in the data source. + +\section python_tutorial2_assumptions Assumptions + +This post assumes you've read the \ref mod_python_file_ingest_tutorial_page "first tutorial". That means that you should know why it is better to write an Autopsy module versus a stand-alone tool, and what you need to set up (Autopsy installed, text editor, etc.). You may also recall the limitations (and benefits) of data source ingest modules. The most notable difference between them is that data source-ingest modules may not have access to carved files or files that are inside of ZIP files. For our example in this post, we are looking for a SQLite database with a specific name, and it will not be inside of a ZIP file, so data source ingest modules are the most efficient and will get us results faster. + +The other assumption is that you know something about SQL queries. We have some example queries below and we don't go into detail about how they work. + +\section python_tutorial2_getting_started Getting Started + +\subsection python_tutorial2_folder Making Your Module Folder +We'll start by making our module folder. As we learned in the \ref mod_python_file_ingest_tutorial_page "first tutorial", every Python module in Autopsy gets its own folder. To find out where you should put your Python module, launch Autopsy and choose the Tools->Python Plugins menu item. That will open a subfolder in your AppData folder, such as "C:\Users\JDoe\AppData\Roaming\Autopsy\python_modules". + +Make a folder inside of there to store your module. Call it "DemoScript2". Copy the dataSourcengestModule.py sample file from github into the this new folder and rename it to FindContactsDb.py. + +\subsection python_tutorial2_script Writing The Script + +We are going to write a script that: +
      +
    • Queries the backend database for files of a given name
    • +
    • Opens the database
    • +
    • Queries data from the database and makes an artifact for each row
    • +
    + +Open the FindContactsDb.py script in your favorite text editor. The sample Autopsy Python modules all have TODO entries in them to let you know what you should change. The below steps jump from one TODO to the next. +
      +
    1. Factory Class Name: The first thing to do is rename the sample class name from "SampleJythonDataSourceIngestModuleFactory" to "ContactsDbIngestModuleFactory". In the sample module, there are several uses of this class name, so you should search and replace for these strings.
    2. +
    3. Name and Description: The next TODO entries are for names and descriptions. These are shown to users. For this example, we'll name it "Contacts Db Analyzer". The description can be anything you want. Note that Autopsy requires that modules have unique names, so don't make it too generic.
    4. +
    5. Ingest Module Class Name: The next thing to do is rename the ingest module class from "SampleJythonDataSourceIngestModule" to "ContactsDbIngestModule". Our usual naming convention is that this class is the same as the factory class with "Factory" removed from the end. There are a couple of places where this name is used, so do a search and replace in your code.
    6. +
    7. startUp() method: The startUp() method is where each module initializes. For our example, we don't need to do anything special in here except save a reference to the passed in context object. This is used later on to see if the module has been cancelled.
    8. +
    9. process() method: This is where we do our analysis and we'll focus on this more in the next section.
    10. +
    + +That's it. In the file-level ingest module, we had a shutdown() method, but we do not need that with data source-level ingest modules. When their process method is finished, it can shut itself down. The process() method will be called only once. + +\subsection python_tutorial2_process The process() Method + +The process method in a data source-level ingest module is passed in reference to the data source as a Content object and a Progress Bar class to update our progress.

    +

    For this tutorial, you can start by deleting the contents of the existing process() method in the sample module. The full source code is linked to at the end of this blog and shows more detail about a fully fledged module. We'll just cover the analytics in the blog.

    + +\subsubsection python_tutorial2_getting_files Getting Files +Because data source-level ingest modules are not passed in specific files to analyze, nearly all of these types of modules will need to use the org.sleuthkit.autopsy.casemodule.services.FileManager service to find relevant files. Check out the methods on that class to see the different ways that you can find files. + +NOTE: See the \ref python_tutorial2_running_exes section for an example of when you simply want to run a command line tool on a disk image instead of querying for files to analyze. + +For our example, we want to find all files named "contacts.db". The org.sleuthkit.autopsy.casemodule.services.FileManager class contains several findFiles() methods to help. You can search for all files with a given name or files with a given name in a particular folder. You can also use SQL syntax to match file patterns, such as "%.jpg" to find all files with a JPEG extension. + +Our example needs these two lines to get the FileManager for the current case and to find the files. +\verbatim +fileManager = Case.getCurrentCase().getServices().getFileManager() +files = fileManager.findFiles(dataSource, "contacts.db")\endverbatim + +findFiles() returns a list of AbstractFile objects. This gives you access to the file's metadata and content. + +For our example, we are going to open these SQLite files. That means that we need to save them to disk. This is less than ideal because it wastes time writing the data to disk and then reading it back in, but it is the only option with many libraries. If you are doing some other type analysis on the content, then you do not need to write it to disk. You can read directly from the AbstractFile (see the sample modules for specific code to do this). + +The org.sleuthkit.autopsy.datamodel.ContentUtils class provides a utility to save file content to disk. We'll make a path in the temp folder of our case directory. To prevent naming collisions, we'll name the file based on its unique ID. The following two lines save the file to lclDbPath. + +\verbatim +lclDbPath = os.path.join(Case.getCurrentCase().getTempDirectory(), str(file.getId()) + ".db") +ContentUtils.writeToFile(file, File(lclDbPath))\endverbatim + +\subsubsection python_tutorial2_analyzing_sqlite Analyzing SQLite +Next, we need to open the SQLite database. We are going to use the Java JDBC infrastructure for this. JDBC is Java's generic way of dealing with different types of databases. To open the database, we do this: +\verbatim +Class.forName("org.sqlite.JDBC").newInstance() +dbConn = DriverManager.getConnection("jdbc:sqlite:%s" % lclDbPath)\endverbatim + +With our connection in hand, we can do some queries. In our sample database, we have a single table named "contacts", which has columns for name, email, and phone. We first start by querying for all rows in our simple table: +\verbatim +stmt = dbConn.createStatement() +resultSet = stmt.executeQuery("SELECT * FROM contacts")\endverbatim + +For each row, we are going to get the values for the name, e-mail, and phone number and make a TSK_CONTACT artifact. Recall from the first tutorial that posting artifacts to the blackboard allows modules to communicate with each other and also allows you to easily display data to the user. The TSK_CONTACT artifact is for storing contact information. + +The basic approach in our example is to make an artifact of a given type (TSK_CONTACT) and have it be associated with the database it came from. We then make attributes for the name, email, and phone. The following code does this for each row in the database: +\verbatim +while resultSet.next(): + + # Make an artifact on the blackboard and give it attributes + art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) + + name = resultSet.getString("name") + art.addAttribute(BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON.getTypeID(), + ContactsDbIngestModuleFactory.moduleName, name)) + + email = resultSet.getString("email") + art.addAttribute(BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getTypeID(), + ContactsDbIngestModuleFactory.moduleName, email)) + + phone = resultSet.getString("phone") + art.addAttribute(BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), + ContactsDbIngestModuleFactory.moduleName, phone))\endverbatim + +That's it. We've just found the databases, queried them, and made artifacts for the user to see. There are some final things though. First, we should fire off an event so that the UI updates and refreshes with the new artifacts. We can fire just one event after each database is parsed (or you could fire one for each artifact - it's up to you). + +\verbatim +IngestServices.getInstance().fireModuleDataEvent( + ModuleDataEvent(ContactsDbIngestModuleFactory.moduleName, + BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT, None))\endverbatim + +And the final thing is to clean up. We should close the database connections and delete our temporary file. +\verbatim +stmt.close() +dbConn.close() +os.remove(lclDbPath)\endverbatim + +\subsection python_tutorial2_niceties Niceties + +Data source-level ingest modules can run for quite some time. Therefore, data source-level ingest modules should do some additional things that file-level ingest modules do not need to. +
      +
    • Progress bars: Each data source-level ingest module will have its own progress bar in the lower right. A reference to it is passed into the process() method. You should update it to provide user feedback.
    • +
    • Cancellation: A user could cancel ingest while your module is running. You should periodically check if that occurred so that you can bail out as soon as possible. You can do that with a check of: +\verbatim if self.context.isJobCancelled():\endverbatim
    • +
    + +\subsection python_tutorial2_tips Debugging and Development Tips + +You can find the full file along with a small sample database on github. To use the database, add it as a logical file and run your module on it. + +Whenever you have syntax errors or other errors in your script, you will get some form of dialog from Autopsy when you try to run ingest modules. If that happens, fix the problem and run ingest modules again. You don't need to restart Autopsy each time! + +The sample module has some log statements in there to help debug what is going on since we don't know of better ways to debug the scripts while running in Autopsy. + +\section python_tutorial2_running_exes Running Executables +While the above example outlined using the FileManager to find files to analyze, the other common use of data source-level ingest modules is to wrap a command line tool that takes a disk image as input. A sample program (RunExe.py) that does that can be found on github. I'll cover the big topics of that program in this section. There are more details in the script about error checking and such. + +\subsection python_tutorial2_finding_exe Finding The Executable + +To write this kind of data source-level ingest module, put the executable in your module's folder (the DemoScript2 folder we previously made). Use "__file__" to get the path to where your script is and then use some os.path methods to get to the executable in the same folder. +\verbatim +path_to_exe = os.path.join(os.path.dirname(os.path.abspath(__file__)), "img_stat.exe")\endverbatim + +In our sample program, we do this and verify we can find it in the startup() method so that if we don't, then ingest never starts. + +\subsection python_tutorial2_running_the_exe Running The Executable + +Data sources can be disk images, but they can also be a folder of files. We only want to run our executable on a disk image. So, verify that: +\verbatim +if not isinstance(dataSource, Image): + self.log(Level.INFO, "Ignoring data source. Not an image") + return IngestModule.ProcessResult.OK \endverbatim + +You can get the path to the disk image using dataSource.getPaths(). + +Once you have the EXE and the disk image, you can use the various subprocess methods to run them. + +\subsection python_tutorial2_showing_results Showing the User Results + +After the command line tool runs, you have the option of either showing the user the raw output of the tool or parsing it into individual artifacts. Refer to previous sections of this tutorial and the previous tutorial for making artifacts. If you want to simply show the user the output of the tool, then save the output to the Reports folder in the Case directory: +\verbatim +reportPath = os.path.join(Case.getCurrentCase().getCaseDirectory(), + "Reports", "img_stat-" + str(dataSource.getId()) + ".txt") \endverbatim + +Then you can add the report to the case so that it shows up in the tree in the main UI panel. +\verbatim Case.getCurrentCase().addReport(reportPath, "Run EXE", "img_stat output")\endverbatim + +\section python_tutorial2_conclusion Conclusion + +Data source-level ingest modules allow you to query for a subset of files by name or to run on an entire disk image. This tutorial has shown an example of both use cases and shown how to use SQLite in Jython. + + +*/ \ No newline at end of file diff --git a/docs/doxygen/modDevPython.dox b/docs/doxygen/modDevPython.dox index 8878d97cdb..85b420a4be 100644 --- a/docs/doxygen/modDevPython.dox +++ b/docs/doxygen/modDevPython.dox @@ -15,11 +15,10 @@ Using it is very easy though in Autopsy and it allows you to access all of the J To develop a module, you should follow this section to get your environment setup and then read the later sections on the different types of modules. -There are also a set of tutorials that Basis Technology published on their blog. While not as thorough as this documentation, they are an easy introduction to the general ideas. - -- File Ingest Modules: http://www.basistech.com/python-autopsy-module-tutorial-1-the-file-ingest-module/ -- Data Source Ingest Modules: http://www.basistech.com/python-autopsy-module-tutorial-2-the-data-source-ingest-module/ -- Report Modules: http://www.basistech.com/python-autopsy-module-tutorial-3-the-report-module/ +There are also a set of tutorials that provide an easy introduction to the general ideas. +- File Ingest Modules: \subpage mod_python_file_ingest_tutorial_page +- Data Source Ingest Modules: \subpage mod_python_ds_ingest_tutorial_page +- Report Modules: \subpage mod_python_report_tutorial_page \section mod_dev_py_setup Basic Setup diff --git a/docs/doxygen/modFileIngestTutorial.dox b/docs/doxygen/modFileIngestTutorial.dox new file mode 100644 index 0000000000..7873513a4f --- /dev/null +++ b/docs/doxygen/modFileIngestTutorial.dox @@ -0,0 +1,154 @@ +/*! \page mod_python_file_ingest_tutorial_page Python Tutorial #1: Writing a File Ingest Module + + +\section python_tutorial1_why Why Write a File Ingest Module? +
      +
    • Autopsy hides the fact that a file is coming from a file system, was carved, was from inside of a ZIP file, or was part of a local file. So, you don't need to spend time supporting all of the ways that your user may want to get data to you. You just need to worry about analyzing the content.
    • +
    • Autopsy displays files automatically and can include them in reports if you use standard blackboard artifacts (described later). That means you don't need to worry about UIs and reports.
    • +
    • Autopsy gives you access to results from other modules. So, you can build on top of their results instead of duplicating them.
    • +
    + +\section python_tutorial1_ingest_modules Ingest Modules + +For our first example, we're going to write an ingest module. Ingest modules in Autopsy run on the data sources that are added to a case. When you add a disk image (or local drive or logical folder) in Autopsy, you'll be presented with a list of modules to run (such as hash lookup and keyword search). + +\image html ingest-modules.PNG + +Those are all ingest modules. We're going to write one of those. There are two types of ingest modules that we can build: +
      +
    • File Ingest Modules are the easiest to write. During their lifetime, they will get passed in each file in the data source. This includes files that are found via carving or inside of ZIP files (if those modules are also enabled).
    • +
    • Data Source Ingest Modules require slightly more work because you have to query the database for the files of interest. If you only care about a small number of files, know their name, and know they won't be inside of ZIP files, then these are your best bet.
    • +
    + +For this first tutorial, we're going to write a file ingest module. The \ref mod_python_ds_ingest_tutorial_page "second tutorial" will focus on data source ingest modules. Regardless of the type of ingest module you are writing, you will need to work with two classes: +
      +
    • The factory class provides Autopsy with module information such as display name and version. It also creates instances of ingest modules as needed.
    • +
    • The ingest module class will do the actual analysis. One of these will be created per thread. For file ingest modules, Autopsy will typically create two or more of these at a time so that it can analyze files in parallel. If you keep things simple, and don't use static variables, then you don't have to think about anything multithreaded.
    • +
    + +\section python_tutorial1_getting_started Getting Started + +To write your first file ingest module, you'll need: +
      +
    • An installed copy of Autopsy available from SleuthKit
    • +
    • A text editor.
    • +
    • A copy of the sample file ingest module from Github
    • +
    + +Some other general notes are that you will be writing in Jython, which converts Python-looking code into Java. It has some limitations, including: +
      +
    • You can't use Python 3 (you are limited to Python 2.7)
    • +
    • You can't use libraries that use native code
    • +
    + +But, Jython will give you access to all of the Java classes and services that Autopsy provides. So, if you want to stray from this example, then refer to the Developer docs on what classes and methods you have access to. The comments in the sample file will identify what type of object is being passed in along with a URL to its documentation. + +\subsection python_tutorial1_folder Making Your Module Folder + +Every Python module in Autopsy gets its own folder. This reduces naming collisions between modules. To find out where you should put your Python module, launch Autopsy and choose the Tools -> Python Plugins menu item. That will open a folder in your AppData folder, such as "C:\Users\JDoe\AppData\Roaming\Autopsy\python_modules". + +

    Make a folder inside of there to store your module. Call it "DemoScript". Copy the fileIngestModule.py sample file listed above into the this new folder and rename it to FindBigRoundFiles.py. Your folder should look like this: + +\image html demoScript_folder.png + +\subsection python_tutorial1_writing Writing the Script + +We are going to write a script that flags any file that is larger than 10MB and whose size is a multiple of 4096. We'll call these big and round files. This kind of technique could be useful for finding encrypted files. An additional check would be for entropy of the file, but we'll keep the example simple. + +Open the FindBigRoundFiles.py file in your favorite python text editor. The sample Autopsy Python modules all have TODO entries in them to let you know what you should change. The below steps jump from one TODO to the next. +

      +
    1. Factory Class Name: The first thing to do is rename the sample class name from "SampleJythonFileIngestModuleFactory" to "FindBigRoundFilesIngestModuleFactory". In the sample module, there are several uses of this class name, so you should search and replace for these strings.
    2. +
    3. Name and Description: The next TODO entries are for names and descriptions. These are shown to users. For this example, we'll name it "Big and Round File Finder". The description can be anything you want. Note that Autopsy requires that modules have unique names, so don't make it too generic.
    4. +
    5. Ingest Module Class Name: The next thing to do is rename the ingest module class from "SampleJythonFileIngestModule" to "FindBigRoundFilesIngestModule". Our usual naming convention is that this class is the same as the factory class with "Factory" removed from the end.
    6. +
    7. startUp() method: The startUp() method is where each module initializes. For our example, we don't need to do anything special in here. Typically though, this is where you want to do stuff that could fail because throwing an exception here causes the entire ingest to stop.
    8. +
    9. process() method: This is where we do our analysis. The sample module is well documented with what it does. It ignores non-files, looks at the file name, and makes a blackboard artifact for ".txt" files. There are also a bunch of other things that it does to show examples for easy copy and pasting, but we don't need them in our module. We'll cover what goes into this method in the next section.
    10. +
    11. shutdown() method: The shutDown() method either frees resources that were allocated or sends summary messages. For our module, it will do nothing.
    12. +
    + +\subsection python_tutorial1_process The process() Method + +The process() method is passed in a reference to an AbstractFile Object. With this, you have access to all of a file's contents and metadata. We want to flag files that are larger than 10MB and that are a multiple of 4096 bytes. The following code does that: + +\verbatim if ((file.getSize() > 10485760) and ((file.getSize() % 4096) == 0)): +\endverbatim + +Now that we have found the files, we want to do something with them. In our situation, we just want to alert the user to them. We do this by making an "Interesting Item" blackboard artifact. The Blackboard is where ingest modules can communicate with each other and with the Autopsy GUI. The blackboard has a set of artifacts on it and each artifact:

    +
      +
    • Has a type
    • +
    • Is associated with a file
    • +
    • Has one or more attributes. Attributes are simply name and value pairs.
    • +
    + +For our example, we are going to make an artifact of type "TSK_INTERESTING_FILE" whenever we find a big and round file. These are one of the most generic artifact types and are simply a way of alerting the user that a file is interesting for some reason. Once you make the artifact, it will be shown in the UI. The below code makes an artifact for the file and puts it into the set of "Big and Round Files". You can create whatever set names you want. The Autopsy GUI organizes Interesting Files by their set name. +\verbatim + art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT) + att = BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), + FindBigRoundFilesIngestModuleFactory.moduleName, "Big and Round Files") + art.addAttribute(att)\endverbatim + +The above code adds the artifact and a single attribute to the blackboard in the embedded database, but it does not notify other modules or the UI. The UI will eventually refresh, but it is faster to fire an event with this: +\verbatim + IngestServices.getInstance().fireModuleDataEvent( + ModuleDataEvent(FindBigRoundFilesIngestModuleFactory.moduleName, + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, None))\endverbatim + +That's it. Your process() method should look something like this: +\verbatim + def process(self, file): + + # Skip non-files + + if ((file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) or + + (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) or + + (file.isFile() == False)): + + return IngestModule.ProcessResult.OK + + + + # Look for files bigger than 10MB that are a multiple of 4096 + + if ((file.getSize() > 10485760) and ((file.getSize() % 4096) == 0)): + + + + # Make an artifact on the blackboard. TSK_INTERESTING_FILE_HIT is a generic type of + + # artifact. Refer to the developer docs for other examples. + + art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT) + + att = BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), + + FindBigRoundFilesIngestModuleFactory.moduleName, "Big and Round Files") + + art.addAttribute(att) + + + + # 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)) + + + + return IngestModule.ProcessResult.OK\endverbatim + +Save this file and run the module on some of your data. If you have any big and round files, you should see an entry under the "Interesting Items" node in the tree. + +\image html bigAndRoundFiles.png + +\subsection python_tutorial1_debug Debugging and Development Tips + +Whenever you have syntax errors or other errors in your script, you will get some form of dialog from Autopsy when you try to run ingest modules. If that happens, fix the problem and run ingest modules again. You don't need to restart Autopsy each time! + +The sample module has some log statements in there to help debug what is going on since we don't know of better ways to debug the scripts while running in Autopsy. + + +*/ diff --git a/docs/doxygen/modReportModuleTutorial.dox b/docs/doxygen/modReportModuleTutorial.dox new file mode 100644 index 0000000000..26cb14564e --- /dev/null +++ b/docs/doxygen/modReportModuleTutorial.dox @@ -0,0 +1,123 @@ +/*! \page mod_python_report_tutorial_page Python Tutorial #3: Writing a Report Module + +In our last two tutorials, we built a Python Autopsy \ref mod_python_file_ingest_tutorial_page "file ingest modules" and \ref mod_python_ds_ingest_tutorial_page "data source ingest modules" that analyzed the data sources as they were added to cases. In our third post, we're going to make an entirely different kind of module, a report module. + +Report modules are typically run after the user has completed their analysis. Autopsy comes with report modules to generate HTML, Excel, KML, and other types of reports. We're going to make a report module that outputs data in CSV. + +Like in the second tutorial, we are going to assume that you've read at least the \ref mod_python_file_ingest_tutorial_page "first tutorial" to know how to get your environment set up. As a reminder, Python modules in Autopsy are written in Jython and have access to all of the Java classes (which is why we have links to Java documentation below). + +\section python_tutorial3_report_modules Report Modules + +Autopsy report modules are often run after the user has run some ingest modules, reviewed the results, and tagged some files of interest. The user will be given a list of report modules to choose from. + +\image html reports_select.png + +The main reasons for writing an Autopsy report module are: +
      +
    • You need the results in a custom output format, such as XML or JSON.
    • +
    • You want to upload results to a central location.
    • +
    • You want to perform additional analysis after all ingest modules have run. While the modules have the word "report" in them, there is no actual requirement that they produce a report or export data. The module can simply perform data analysis and post artifacts to the blackboard like ingest modules do.
    • +
    + +As we dive into the details, you will notice that the report module API is fairly generic. This is because reports are created at a case level, not a data source level. So, when a user chooses to run a report module, all Autopsy does is tell it to run and gives it a path to a directory to store its results in. The report module can store whatever it wants in the directory. + +Note that if you look at the \ref mod_report_page "full developer docs", there are other report module types that are supported in Java. These are not supported though in Python. + +\subsection python_tutorial3_getting_content Getting Content + +With report modules, it is up to you to find the content that you want to include in your report or analysis. Generally, you will want to access some or all of the files, tagged files, or blackboard artifacts. As you may recall from the previous tutorials, blackboard artifacts are how ingest modules in Autopsy store their results so that they can be shown in the UI, used by other modules, and included in the final report. In this tutorial, we will introduce the SleuthkitCase class, which we generally don't introduce to module writers because it has lots of methods, many of which are low-level, and there are other classes, such as FileManager, that are more focused and easier to use. + +\subsubsection python_tutorial3_getting_files Getting Files + +You have three choices for getting files to report on. You can use the FileManager, which we used in \ref mod_python_ds_ingest_tutorial_page "the last Data Source-level Ingest Module tutorial". The only change is that you will need to call it multiple times, one for each data source in the case. You will have code that looks something like this: +\verbatim +dataSources = Case.getCurrentCase().getDataSources() +fileManager = Case.getCurrentCase().getServices().getFileManager() + +for dataSource in dataSources: + files = fileManager.findFiles(dataSource, "%.txt")\endverbatim + +Another approach is to use the SleuthkitCase.findAllFilesWhere() method that allows you to specify a SQL query. To use this method, you must know the schema of the database (which makes this a bit more challenging, but more powerful). The schema is defined on the wiki. + +Usually, you just need to focus on the tsk_files table. You may run into memory problems and you can also use SleuthkitCase.findAllFileIdsWhere() to get just the IDs and then call SleuthkitCase.getAbstractFileById() to get files as needed. + +A third approach is to call org.sleuthkit.autopsy.casemodule.Case.getDataSources(), and then recursively call getChildren() on each Content object. This will traverse all of the folders and files in the case. This is the most memory efficient, but also more complex to code. + +\subsubsection python_tutorial3_getting_artifacts Getting Blackboard Artifacts + +The blackboard is where modules store their analysis results. If you want to include them in your report, then there are several methods that you could use. If you want all artifacts of a given type, then you can use SleuthkitCase.getBlackboardArtifacts(). There are many variations of this method that take different arguments. Look at them to find the one that is most convenient for you. + +\subsubsection python_tutorial3_getting_tags Getting Tagged Files or Artifacts + +If you want to find files or artifacts that are tagged, then you can use the org.sleuthkit.autopsy.casemodule.services.TagsManager. It has methods to get all tags of a given name, such as org.sleuthkit.autopsy.casemodule.services.TagsManager.getContentTagsByTagName(). + +\section python_tutorial3_getting_started Getting Started + +\subsection python_tutorial3_making_the_folder Making the Folder + +We'll start by making our module folder. As we learned in \ref mod_python_file_ingest_tutorial_page "the first tutorial", every Python module in Autopsy gets its own folder. To find out where you should put your Python module, launch Autopsy and choose the Tools->Python Plugins menu item. That will open a subfolder in your AppData folder, such as "C:\Users\JDoe\AppData\Roaming\Autopsy\python_modules". + +Make a folder inside of there to store your module. Call it "DemoScript3". Copy the reportmodule.py sample file into the this new folder and rename it to CSVReport.py. + +\subsection python_tutorial3_writing_script Writing the Script + +We are going to write a script that makes some basic CSV output: file name and MD5 hash. Open the CSVReport.py file in your favorite Python text editor. The sample Autopsy Python modules all have TODO entries in them to let you know what you should change. The below steps jump from one TODO to the next. + +
      +
    1. Factory Class Name: The first thing to do is rename the sample class name from "SampleGeneralReportModule" to "CSVReportModule". In the sample module, there are several uses of this class name, so you should search and replace for these strings.
    2. +
    3. Name and Description: The next TODO entries are for names and descriptions. These are shown to users. For this example, we'll name it "CSV Hash Report Module". The description can be anything you want. Note that Autopsy requires that modules have unique names, so don't make it too generic.
    4. +
    5. Relative File Path: The next step is to specify the filename that your module is going to use for the report. Autopsy will later provide you with a folder name to save your report in. If you have multiple file names, then pick the main one. This path will be shown to the user after the report has been generated so that they can open it. For this example, we'll call it "hashes.csv" in the getRelativeFilePath() method.
    6. +
    7. generateReport() Method: This method is what is called when the user wants to run the module. It gets passed in the base directory to store the results in and a progress bar. It is responsible for making the report and calling Case.addReport() so that it will be shown in the tree. We'll cover the details of this method in a later section.
    8. +
    + +\subsection python_tutorial3_generate_report The generateReport() method + +The generateReport() method is where the work is done. The baseReportDir argument is a string for the base directory to store results in. The progressBar argument is a org.sleuthkit.autopsy.report.ReportProgressPanel +that shows the user progress while making long reports and to make the progress bar red if an error occurs. + +We'll use one of the basic ideas from the sample, so you can copy and paste from that as you see fit to make this method. Our general approach is going to be this: +
      +
    1. Open the CSV file.
    2. +
    3. Query for all files.
    4. +
    5. Cycle through each of the files and print a line of text.
    6. +
    7. Add the report to the Case database.
    8. +
    + +To focus on the essential code, we'll skip the progress bar details. However, the final solution that we'll link to at the end contains the progress bar code. + +To open the report file in the right folder, we'll need a line such as this: +\verbatim +fileName = os.path.join(baseReportDir, self.getRelativeFilePath()) +report = open(fileName, 'w')\endverbatim + +Next we need to query for the files. In our case, we want all of the files, but can skip the directories. We'll use lines such as this to get the current case and then call the SleuthkitCase.findAllFilesWhere() method. +\verbatim +sleuthkitCase = Case.getCurrentCase().getSleuthkitCase() +files = sleuthkitCase.findAllFilesWhere("NOT meta_type = " + + str(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()))\endverbatim + +Now, we want to print a line for each file. To do this, you'll need something like: +\verbatim +for file in files: + md5 = file.getMd5Hash() + + if md5 is None: + md5 = "" + + report.write(file.getParentPath() + file.getName() + "," + md5 + "n")\endverbatim + +Note that the file will only have an MD5 value if the Hash Lookup ingest module was run on the data source. + +Lastly, we want to add the report to the case database so that the user can later find it from the tree and we want to report that we completed successfully. +\verbatim +Case.getCurrentCase().addReport(fileName, self.moduleName, "Hashes CSV") +progressBar.complete(ReportStatus.COMPLETE)\endverbatim + +That's it. The final code can be found on github. + +\subsection python_tutorial3_conclusions Conclusions + +In this tutorial, we made a basic report module that creates a custom CSV file. The most challenging part of writing a report module is knowing how to get all of the data that you need. Hopefully, the \ref python_tutorial3_getting_content section above covered what you need, but if not, then go on the Sleuthkit forum and we'll try to point you in the right direction.

    + + +*/ \ No newline at end of file diff --git a/ruleset.xml b/ruleset.xml index 5e8d384232..468f95087e 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -140,7 +140,6 @@ - diff --git a/thirdparty/rr-full/plugins/itempos.pl b/thirdparty/rr-full/plugins/itempos.pl index 8f5d4562b8..cbb6d445d8 100644 --- a/thirdparty/rr-full/plugins/itempos.pl +++ b/thirdparty/rr-full/plugins/itempos.pl @@ -1,10 +1,8 @@ #----------------------------------------------------------- -# shellbags.pl -# RR plugin to parse (Vista, Win7/Win2008R2) shell bags -# +# itempos.pl +# # History: -# 20130514 - added checking of ShellNoRoam\Bags subkeys for WinXP -# 20120814 - created +# 20191111 - Added default value to $jmp if $item{extver} cannot be determined. # # References # http://c0nn3ct0r.blogspot.com/2011/11/windows-shellbag-forensics.html @@ -21,8 +19,7 @@ # provided, Andrew Case for posting the Registry Decoder code, and Kevin # Moore for writing the shell bag parser for Registry Decoder, as well as # assistance with some parsing. -# -# License: GPL v3 +# # copyright 2013 Quantum Analytics Research, LLC # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- @@ -228,7 +225,6 @@ sub parseFolderItem { my $str = ""; while($tag) { my $s = substr($data,$ofs_shortname + $cnt,1); - return %item unless (defined $s); if ($s =~ m/\x00/ && ((($cnt + 1) % 2) == 0)) { $tag = 0; } @@ -244,9 +240,7 @@ sub parseFolderItem { $tag = 1; $cnt = 0; while ($tag) { - my $s = substr($data,$ofs + $cnt,2); - return %item unless (defined $s); - if (unpack("v",$s) == 0xbeef) { + if (unpack("v",substr($data,$ofs + $cnt,2)) == 0xbeef) { $tag = 0; } else { @@ -272,7 +266,9 @@ sub parseFolderItem { elsif ($item{extver} == 0x08) { $jmp = 30; } - else {} + else { + $jmp = 34; + } $ofs += $jmp; diff --git a/thirdparty/sevenzip/sevenzipjbinding-AllPlatforms.jar b/thirdparty/sevenzip/sevenzipjbinding-AllPlatforms.jar old mode 100755 new mode 100644 index 7a42df8aaf..3b9ab96e1c Binary files a/thirdparty/sevenzip/sevenzipjbinding-AllPlatforms.jar and b/thirdparty/sevenzip/sevenzipjbinding-AllPlatforms.jar differ diff --git a/thirdparty/sevenzip/sevenzipjbinding.jar b/thirdparty/sevenzip/sevenzipjbinding.jar old mode 100755 new mode 100644 index 2181a7c2b7..808cbda71e Binary files a/thirdparty/sevenzip/sevenzipjbinding.jar and b/thirdparty/sevenzip/sevenzipjbinding.jar differ diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle_ja.properties b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle_ja.properties index bf1cb7ba93..1c54f49f57 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle_ja.properties +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle_ja.properties @@ -1,27 +1,27 @@ -OpenIDE-Module-Display-Category=\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB +OpenIDE-Module-Display-Category=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb OpenIDE-Module-Long-Description=\ \ - Email\u30D1\u30FC\u30B5\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB\u3002\n\n\ - \u3053\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u306FMBOX\u3068PST e-mail\u30D5\u30A1\u30A4\u30EB\u3092\u62BD\u51FA\u3057\u3001Blackboard\u306B\u8F09\u305B\u307E\u3059\u3002 \n\ - Thunderbird\u306EMBOX\u30D5\u30A1\u30A4\u30EB\u306E\u30D5\u30A9\u30EB\u30C0\u69CB\u9020\u3092\u628A\u63E1\u3057\u3066\u3044\u307E\u3059\u3002 -OpenIDE-Module-Name=Email\u30D1\u30FC\u30B5 -OpenIDE-Module-Short-Description=MOBX\u3068PST\u30D5\u30A1\u30A4\u30EB\u3092\u30D1\u30FC\u30B9\u3057\u307E\u3059 -MboxParser.parse.errMsg.failedToReadFile=\u30C7\u30A3\u30B9\u30AF\u304B\u3089mbox\u30D5\u30A1\u30A4\u30EB\u3092\u8AAD\u307F\u53D6\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -MboxParser.parse.errMsg.couldntFindCharset=\u9069\u5207\u306A\u6587\u5B57\u30BB\u30C3\u30C8\u30A8\u30F3\u30B3\u30FC\u30C0\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -MboxParser.parse.errMsg.failedToParseNMsgs={0}\u500B\u306EEmail\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u62BD\u51FA\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002 -MboxParser.handleAttch.errMsg.failedToCreateOnDisk=MBOX\u306E\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u3092\u30C7\u30A3\u30B9\u30AF\: {0}\u3078\u62BD\u51FA\u3059\u308B\u306E\u306B\u5931\u6557\u3057\u307E\u3057\u305F -MboxParser.handleAttch.failedWriteToDisk=\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u3092\u30C7\u30A3\u30B9\u30AF\: {0}\u3078\u62BD\u51FA\u3059\u308B\u306E\u306B\u5931\u6557\u3057\u307E\u3057\u305F -PstParser.parse.errMsg.failedToParseNMsgs={0}\u500B\u306EEmail\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u62BD\u51FA\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002 -PstParser.extractAttch.errMsg.failedToExtractToDisk=PST\u306E\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u3092\u30C7\u30A3\u30B9\u30AF\: {0}\u3078\u62BD\u51FA\u3059\u308B\u306E\u306B\u5931\u6557\u3057\u307E\u3057\u305F -ThunderbirdMboxFileIngestModule.moduleName=Email\u30D1\u30FC\u30B5 -ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace=\u30C7\u30A3\u30B9\u30AF\u9818\u57DF\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002\u30D1\u30FC\u30B9\u3059\u308B\u70BA\u306B{0}\u3092\u30B3\u30D4\u30FC\u3067\u304D\u307E\u305B\u3093\u3002 -ThunderbirdMboxFileIngestModule.encryptionFileLevel=\u30D5\u30A1\u30A4\u30EB\u30EC\u30D9\u30EB\u6697\u53F7\u5316 -ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg={0}\u306E\u51E6\u7406\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ThunderbirdMboxFileIngestModule.processPst.errProcFile.details=Outlook 2003\u304A\u3088\u3073\u305D\u308C\u4EE5\u964D\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u304B\u3089\u306E\u30D5\u30A1\u30A4\u30EB\u3057\u304B\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002 -ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg2={0}\u306E\u51E6\u7406\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg={0}\u306E\u51E6\u7406\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ThunderbirdMboxFileIngestModule.processMBox.errProfFile.details=\u30C7\u30A3\u30B9\u30AF\u9818\u57DF\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002\u30D1\u30FC\u30B9\u3059\u308B\u70BA\u306B\u30D5\u30A1\u30A4\u30EB\u3092\u30B3\u30D4\u30FC\u3067\u304D\u307E\u305B\u3093\u3002 -ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2={0}\u306E\u51E6\u7406\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ThunderbirdMboxFileIngestModule.getDesc.text=\u3053\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u306Fmbox\u304A\u3088\u3073pst/ost\u30D5\u30A1\u30A4\u30EB\u3092\u691C\u51FA\u3001\u30D1\u30FC\u30B9\u3057\u3001blackboard\u306EEmail\u30A2\u30FC\u30C6\u30A3\u30D5\u30A1\u30AF\u30C8\u306B\u7D50\u679C\u3092\u6295\u5165\u3057\u307E\u3059\u3002 -ThunderbirdMboxFileIngestModule.handleAttch.errMsg={0}\u306E\u51E6\u7406\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F -ThunderbirdMboxFileIngestModule.handleAttch.errMsg.details={0}\u306E\u540D\u79F0\u3092\u6301\u3064\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u3092\u30B1\u30FC\u30B9\u306B\u8FFD\u52A0\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -ThunderbirdMboxFileIngestModule.notAvail=\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093 \ No newline at end of file + Email\u30d1\u30fc\u30b5\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u3002\n\n\ + \u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306fMBOX\u3068PST e-mail\u30d5\u30a1\u30a4\u30eb\u3092\u62bd\u51fa\u3057\u3001Blackboard\u306b\u8f09\u305b\u307e\u3059\u3002 \n\ + Thunderbird\u306eMBOX\u30d5\u30a1\u30a4\u30eb\u306e\u30d5\u30a9\u30eb\u30c0\u69cb\u9020\u3092\u628a\u63e1\u3057\u3066\u3044\u307e\u3059\u3002 +OpenIDE-Module-Name=Email\u30d1\u30fc\u30b5 +OpenIDE-Module-Short-Description=MOBX\u3068PST\u30d5\u30a1\u30a4\u30eb\u3092\u30d1\u30fc\u30b9\u3057\u307e\u3059 +MboxParser.parse.errMsg.failedToReadFile=\u30c7\u30a3\u30b9\u30af\u304b\u3089mbox\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u53d6\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +MboxParser.parse.errMsg.couldntFindCharset=\u9069\u5207\u306a\u6587\u5b57\u30bb\u30c3\u30c8\u30a8\u30f3\u30b3\u30fc\u30c0\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +MboxParser.parse.errMsg.failedToParseNMsgs={0}\u500b\u306eEmail\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u62bd\u51fa\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 +MboxParser.handleAttch.errMsg.failedToCreateOnDisk=MBOX\u306e\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb\u3092\u30c7\u30a3\u30b9\u30af\: {0}\u3078\u62bd\u51fa\u3059\u308b\u306e\u306b\u5931\u6557\u3057\u307e\u3057\u305f +MboxParser.handleAttch.failedWriteToDisk=\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb\u3092\u30c7\u30a3\u30b9\u30af\: {0}\u3078\u62bd\u51fa\u3059\u308b\u306e\u306b\u5931\u6557\u3057\u307e\u3057\u305f +PstParser.parse.errMsg.failedToParseNMsgs={0}\u500b\u306eEmail\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u62bd\u51fa\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 +PstParser.extractAttch.errMsg.failedToExtractToDisk=PST\u306e\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb\u3092\u30c7\u30a3\u30b9\u30af\: {0}\u3078\u62bd\u51fa\u3059\u308b\u306e\u306b\u5931\u6557\u3057\u307e\u3057\u305f +ThunderbirdMboxFileIngestModule.moduleName=Email\u30d1\u30fc\u30b5 +ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace=\u30c7\u30a3\u30b9\u30af\u9818\u57df\u304c\u4e0d\u8db3\u3057\u3066\u3044\u307e\u3059\u3002\u30d1\u30fc\u30b9\u3059\u308b\u70ba\u306b{0}\u3092\u30b3\u30d4\u30fc\u3067\u304d\u307e\u305b\u3093\u3002 +ThunderbirdMboxFileIngestModule.encryptionFileLevel=\u30d5\u30a1\u30a4\u30eb\u30ec\u30d9\u30eb\u6697\u53f7\u5316 +ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg={0}\u306e\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ThunderbirdMboxFileIngestModule.processPst.errProcFile.details=Outlook 2003\u304a\u3088\u3073\u305d\u308c\u4ee5\u964d\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u304b\u3089\u306e\u30d5\u30a1\u30a4\u30eb\u3057\u304b\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg2={0}\u306e\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg={0}\u306e\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ThunderbirdMboxFileIngestModule.processMBox.errProfFile.details=\u30c7\u30a3\u30b9\u30af\u9818\u57df\u304c\u4e0d\u8db3\u3057\u3066\u3044\u307e\u3059\u3002\u30d1\u30fc\u30b9\u3059\u308b\u70ba\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u30b3\u30d4\u30fc\u3067\u304d\u307e\u305b\u3093\u3002 +ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2={0}\u306e\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ThunderbirdMboxFileIngestModule.getDesc.text=\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306fmbox\u304a\u3088\u3073pst/ost\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u51fa\u3001\u30d1\u30fc\u30b9\u3057\u3001blackboard\u306eEmail\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u306b\u7d50\u679c\u3092\u6295\u5165\u3057\u307e\u3059\u3002 +ThunderbirdMboxFileIngestModule.handleAttch.errMsg={0}\u306e\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +ThunderbirdMboxFileIngestModule.handleAttch.errMsg.details={0}\u306e\u540d\u79f0\u3092\u6301\u3064\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb\u3092\u30b1\u30fc\u30b9\u306b\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +ThunderbirdMboxFileIngestModule.notAvail=\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093 diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessage.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessage.java index 40f2fc0933..09a6637e6e 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessage.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessage.java @@ -83,7 +83,7 @@ class EmailMessage { void setSubject(String subject) { if (subject != null) { this.subject = subject; - if(subject.matches("^[R|r][E|e].*?:.*")) { + if (subject.matches("^[R|r][E|e].*?:.*")) { this.simplifiedSubject = subject.replaceAll("[R|r][E|e].*?:", "").trim(); replySubject = true; } else { @@ -93,19 +93,19 @@ class EmailMessage { this.simplifiedSubject = ""; } } - + /** * Returns the orginal subject with the "RE:" stripped off". - * + * * @return Message subject with the "RE" stripped off */ String getSimplifiedSubject() { return simplifiedSubject; } - + /** * Returns whether or not the message subject started with "RE:" - * + * * @return true if the original subject started with RE otherwise false. */ boolean isReplySubject() { @@ -121,6 +121,7 @@ class EmailMessage { this.headers = headers; } } + String getTextBody() { return textBody; } @@ -211,75 +212,80 @@ class EmailMessage { this.localPath = localPath; } } - + /** - * Returns the value of the Message-ID header field of this message or - * empty string if it is not present. - * + * Returns the value of the Message-ID header field of this message or empty + * string if it is not present. + * * @return the identifier of this message. */ String getMessageID() { return messageID; } - + /** * Sets the identifier of this message. - * + * * @param messageID identifer of this message */ void setMessageID(String messageID) { - this.messageID = messageID; + if (messageID != null) { + this.messageID = messageID; + } else { + this.messageID = ""; + } } - + /** - * Returns the messageID of the parent message or empty String if not present. - * + * Returns the messageID of the parent message or empty String if not + * present. + * * @return the idenifier of the message parent */ String getInReplyToID() { return inReplyToID; } - + /** * Sets the messageID of the parent message. - * + * * @param inReplyToID messageID of the parent message. */ void setInReplyToID(String inReplyToID) { this.inReplyToID = inReplyToID; } - + /** - * Returns a list of Message-IDs listing the parent, grandparent, - * great-grandparent, and so on, of this message. - * + * Returns a list of Message-IDs listing the parent, grandparent, + * great-grandparent, and so on, of this message. + * * @return The reference list or empty string if none is available. */ List getReferences() { return references; } - + /** * Set the list of reference message-IDs from the email message header. - * - * @param references + * + * @param references */ void setReferences(List references) { this.references = references; } - + /** * Sets the ThreadID of this message. - * + * * @param threadID - the thread ID to set */ void setMessageThreadID(String threadID) { this.messageThreadID = threadID; } - + /** * Returns the ThreadID for this message. - * + * * @return - the message thread ID or "" is non is available */ String getMessageThreadID() { @@ -308,7 +314,7 @@ class EmailMessage { private long aTime = 0L; private long mTime = 0L; - + private TskData.EncodingType encodingType = TskData.EncodingType.NONE; String getName() { @@ -394,14 +400,14 @@ class EmailMessage { this.mTime = mTime.getTime() / 1000; } } - - void setEncodingType(TskData.EncodingType encodingType){ + + void setEncodingType(TskData.EncodingType encodingType) { this.encodingType = encodingType; } - - TskData.EncodingType getEncodingType(){ + + TskData.EncodingType getEncodingType() { return encodingType; } - + } } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index 5c42269a8a..f1260f269f 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -503,6 +503,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { DerivedFile df = fileManager.addDerivedFile(filename, relPath, size, cTime, crTime, aTime, mTime, true, messageArtifact, "", EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType); + + associateAttachmentWithMesssge(messageArtifact, df); + files.add(df); } catch (TskCoreException ex) { postErrorMessage( @@ -516,6 +519,19 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { return files; } + /** + * Creates a TSK_ASSOCIATED_OBJECT artifact between the attachment file and + * the message artifact. + */ + private BlackboardArtifact associateAttachmentWithMesssge(BlackboardArtifact message, AbstractFile attachedFile) throws TskCoreException { + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, EmailParserModuleFactory.getModuleName(), message.getArtifactID())); + + BlackboardArtifact bba = attachedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + bba.addAttributes(attributes); //write out to bb + return bba; + } + /** * Finds and returns a set of unique email addresses found in the input string * diff --git a/travis_build.sh b/travis_build.sh new file mode 100755 index 0000000000..f7b7cfb72d --- /dev/null +++ b/travis_build.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e +echo "Building TSK..." +cd sleuthkit/sleuthkit +./bootstrap && ./configure --prefix=/usr && make +pushd bindings/java && ant -q dist-PostgreSQL && popd + +echo "Building Autopsy..." && echo -en 'travis_fold:start:script.build\\r' +cd $TRAVIS_BUILD_DIR/ +ant build +echo -en 'travis_fold:end:script.build\\r'