diff --git a/.travis.yml b/.travis.yml index 9200839ce7..75597cee85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,11 +19,11 @@ install: - sh travis_build.sh script: - set -e - - echo "building autopsy..." && echo -en 'travis_fold:start:script.build\\r' + - echo "Building Autopsy..." && echo -en 'travis_fold:start:script.build\\r' - cd $TRAVIS_BUILD_DIR/ - - ant -q build + - ant build - echo -en 'travis_fold:end:script.build\\r' - - echo "testing autopsy..." && echo -en 'travis_fold:start:script.tests\\r' + - echo "Testing Autopsy..." && echo -en 'travis_fold:start:script.tests\\r' - cd Core/ - xvfb-run ant -q test - echo -en 'travis_fold:end:script.tests\\r' diff --git a/Core/ivy.xml b/Core/ivy.xml index 5949f5a777..0088cc75ba 100644 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -35,6 +35,11 @@ + + + + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index b18bef9b6a..4df5159bd6 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -1,26 +1,59 @@ file.reference.activemq-all-5.11.1.jar=release/modules/ext/activemq-all-5.11.1.jar +file.reference.apache-mime4j-core-0.8.1.jar=release/modules/ext/apache-mime4j-core-0.8.1.jar +file.reference.apache-mime4j-dom-0.8.1.jar=release/modules/ext/apache-mime4j-dom-0.8.1.jar +file.reference.asm-5.0.4.jar=release/modules/ext/asm-5.0.4.jar +file.reference.bcmail-jdk15on-1.54.jar=release/modules/ext/bcmail-jdk15on-1.54.jar +file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar +file.reference.boilerpipe-1.1.0.jar=release/modules/ext/boilerpipe-1.1.0.jar file.reference.c3p0-0.9.5.jar=release/modules/ext/c3p0-0.9.5.jar +file.reference.cdm-4.5.5.jar=release/modules/ext/cdm-4.5.5.jar +file.reference.commons-codec-1.6.jar=release/modules/ext/commons-codec-1.6.jar file.reference.commons-compress-1.14.jar=release/modules/ext/commons-compress-1.14.jar -file.reference.commons-dbcp2-2.1.1.jar=release\\modules\\ext\\commons-dbcp2-2.1.1.jar -file.reference.commons-pool2-2.4.2.jar=release\\modules\\ext\\commons-pool2-2.4.2.jar +file.reference.commons-dbcp2-2.1.1.jar=release/modules/ext/commons-dbcp2-2.1.1.jar +file.reference.commons-io-2.5.jar=release/modules/ext/commons-io-2.5.jar +file.reference.commons-pool2-2.4.2.jar=release/modules/ext/commons-pool2-2.4.2.jar file.reference.dd-plist-1.20.jar=release/modules/ext/dd-plist-1.20.jar +file.reference.geoapi-3.0.0.jar=release/modules/ext/geoapi-3.0.0.jar +file.reference.grib-4.5.5.jar=release/modules/ext/grib-4.5.5.jar +file.reference.gson-2.8.1.jar=release/modules/ext/gson-2.8.1.jar +file.reference.httpservices-4.5.5.jar=release/modules/ext/httpservices-4.5.5.jar +file.reference.isoparser-1.1.18.jar=release/modules/ext/isoparser-1.1.18.jar +file.reference.jackcess-2.2.0.jar=release/modules/ext/jackcess-2.2.0.jar +file.reference.jackcess-encrypt-2.1.4.jar=release/modules/ext/jackcess-encrypt-2.1.4.jar +file.reference.java-libpst-0.8.1.jar=release/modules/ext/java-libpst-0.8.1.jar +file.reference.jcl-over-slf4j-1.7.24.jar=release/modules/ext/jcl-over-slf4j-1.7.24.jar file.reference.jackson-core-2.9.7.jar=release/modules/ext/jackson-core-2.9.7.jar file.reference.jdom-2.0.5-contrib.jar=release/modules/ext/jdom-2.0.5-contrib.jar file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar +file.reference.jericho-html-3.3.jar=release/modules/ext/jericho-html-3.3.jar file.reference.jgraphx-v3.8.0.jar=release/modules/ext/jgraphx-v3.8.0.jar +file.reference.jhighlight-1.0.2.jar=release/modules/ext/jhighlight-1.0.2.jar +file.reference.jmatio-1.2.jar=release/modules/ext/jmatio-1.2.jar +file.reference.json-1.8.jar=release/modules/ext/json-1.8.jar +file.reference.json-simple-1.1.1.jar=release/modules/ext/json-simple-1.1.1.jar file.reference.jsoup-1.10.3.jar=release/modules/ext/jsoup-1.10.3.jar +file.reference.jul-to-slf4j-1.7.24.jar=release/modules/ext/jul-to-slf4j-1.7.24.jar +file.reference.juniversalchardet-1.0.3.jar=release/modules/ext/juniversalchardet-1.0.3.jar +file.reference.junrar-0.7.jar=release/modules/ext/junrar-0.7.jar file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone-2.7.0.jar file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar file.reference.metadata-extractor-2.10.1.jar=release/modules/ext/metadata-extractor-2.10.1.jar +file.reference.netcdf4-4.5.5.jar=release/modules/ext/netcdf4-4.5.5.jar +file.reference.opennlp-tools-1.8.3.jar=release/modules/ext/opennlp-tools-1.8.3.jar +file.reference.poi-3.17.jar=release/modules/ext/poi-3.17.jar +file.reference.poi-ooxml-3.17.jar=release/modules/ext/poi-ooxml-3.17.jar +file.reference.poi-scratchpad-3.17.jar=release/modules/ext/poi-scratchpad-3.17.jar file.reference.postgresql-9.4.1211.jre7.jar=release/modules/ext/postgresql-9.4.1211.jre7.jar file.reference.Rejistry-1.0-SNAPSHOT.jar=release/modules/ext/Rejistry-1.0-SNAPSHOT.jar +file.reference.rome-1.5.1.jar=release/modules/ext/rome-1.5.1.jar file.reference.sevenzipjbinding-AllPlatforms.jar=release/modules/ext/sevenzipjbinding-AllPlatforms.jar file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar -file.reference.sqlite-jdbc-3.8.11.jar=release\\modules\\ext\\sqlite-jdbc-3.8.11.jar +file.reference.sis-metadata-0.6.jar=release/modules/ext/sis-metadata-0.6.jar +file.reference.sis-netcdf-0.6.jar=release/modules/ext/sis-netcdf-0.6.jar +file.reference.sis-utility-0.6.jar=release/modules/ext/sis-utility-0.6.jar +file.reference.slf4j-api-1.7.24.jar=release/modules/ext/slf4j-api-1.7.24.jar +file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar file.reference.StixLib.jar=release/modules/ext/StixLib.jar -file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar -file.reference.jackcess-2.2.0.jar=release/modules/ext/jackcess-2.2.0.jar -file.reference.jackcess-encrypt-2.1.4.jar=release/modules/ext/jackcess-encrypt-2.1.4.jar file.reference.jempbox-1.8.13.jar=release/modules/ext/jempbox-1.8.13.jar file.reference.javax.ws.rs-api-2.0.1.jar=release/modules/ext/javax.ws.rs-api-2.0.1.jar file.reference.cxf-core-3.0.16.jar=release/modules/ext/cxf-core-3.0.16.jar @@ -31,11 +64,14 @@ file.reference.fontbox-2.0.8.jar=release/modules/ext/fontbox-2.0.8.jar file.reference.pdfbox-2.0.8.jar=release/modules/ext/pdfbox-2.0.8.jar file.reference.pdfbox-tools-2.0.8.jar=release/modules/ext/pdfbox-tools-2.0.8.jar file.reference.sleuthkit-postgresql-4.6.4.jar=release/modules/ext/sleuthkit-postgresql-4.6.4.jar +file.reference.tagsoup-1.2.1.jar=release/modules/ext/tagsoup-1.2.1.jar file.reference.tika-core-1.17.jar=release/modules/ext/tika-core-1.17.jar file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar +file.reference.vorbis-java-core-0.8.jar=release/modules/ext/vorbis-java-core-0.8.jar +file.reference.vorbis-java-tika-0.8.jar=release/modules/ext/vorbis-java-tika-0.8.jar file.reference.xmpcore-5.1.3.jar=release/modules/ext/xmpcore-5.1.3.jar file.reference.xz-1.6.jar=release/modules/ext/xz-1.6.jar file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index d6f3562663..0e8b544024 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -338,81 +338,59 @@ org.sleuthkit.autopsy.modules.vmextractor org.sleuthkit.autopsy.progress org.sleuthkit.autopsy.report + org.sleuthkit.autopsy.textextractors + org.sleuthkit.autopsy.textextractors.extractionconfigs org.sleuthkit.autopsy.texttranslation org.sleuthkit.datamodel + + ext/apache-mime4j-dom-0.8.1.jar + release/modules/ext/apache-mime4j-dom-0.8.1.jar + ext/jackcess-2.2.0.jar release/modules/ext/jackcess-2.2.0.jar - ext/zookeeper-3.4.6.jar - release/modules/ext/zookeeper-3.4.6.jar + ext/jericho-html-3.3.jar + release/modules/ext/jericho-html-3.3.jar - ext/jdom-2.0.5.jar - release/modules/ext/jdom-2.0.5.jar + ext/cdm-4.5.5.jar + release/modules/ext/cdm-4.5.5.jar - ext/cxf-rt-transports-http-3.0.16.jar - release/modules/ext/cxf-rt-transports-http-3.0.16.jar + ext/httpservices-4.5.5.jar + release/modules/ext/httpservices-4.5.5.jar ext/commons-validator-1.6.jar release/modules/ext/commons-validator-1.6.jar - - ext/curator-framework-2.8.0.jar - release/modules/ext/curator-framework-2.8.0.jar - - - ext/bcprov-jdk15on-1.54.jar - release/modules/ext/bcprov-jdk15on-1.54.jar - ext/commons-compress-1.14.jar release/modules/ext/commons-compress-1.14.jar - ext/fontbox-2.0.8.jar - release/modules/ext/fontbox-2.0.8.jar + ext/geoapi-3.0.0.jar + release/modules/ext/geoapi-3.0.0.jar - ext/commons-dbcp2-2.1.1.jar - release\modules\ext\commons-dbcp2-2.1.1.jar - - - ext/jgraphx-v3.8.0.jar - release/modules/ext/jgraphx-v3.8.0.jar - - - ext/jython-standalone-2.7.0.jar - release/modules/ext/jython-standalone-2.7.0.jar + ext/boilerpipe-1.1.0.jar + release/modules/ext/boilerpipe-1.1.0.jar ext/sevenzipjbinding.jar release/modules/ext/sevenzipjbinding.jar - ext/sleuthkit-postgresql-4.6.4.jar - release/modules/ext/sleuthkit-postgresql-4.6.4.jar + ext/bcmail-jdk15on-1.54.jar + release/modules/ext/bcmail-jdk15on-1.54.jar ext/mchange-commons-java-0.2.9.jar release/modules/ext/mchange-commons-java-0.2.9.jar - - ext/cxf-core-3.0.16.jar - release/modules/ext/cxf-core-3.0.16.jar - - - ext/javax.ws.rs-api-2.0.1.jar - release/modules/ext/javax.ws.rs-api-2.0.1.jar - - - ext/postgresql-9.4.1211.jre7.jar - release/modules/ext/postgresql-9.4.1211.jre7.jar - ext/curator-recipes-2.8.0.jar release/modules/ext/curator-recipes-2.8.0.jar @@ -421,6 +399,14 @@ ext/metadata-extractor-2.10.1.jar release/modules/ext/metadata-extractor-2.10.1.jar + + ext/apache-mime4j-core-0.8.1.jar + release/modules/ext/apache-mime4j-core-0.8.1.jar + + + ext/tagsoup-1.2.1.jar + release/modules/ext/tagsoup-1.2.1.jar + ext/tika-core-1.17.jar release/modules/ext/tika-core-1.17.jar @@ -429,45 +415,37 @@ ext/StixLib.jar release/modules/ext/StixLib.jar - - ext/curator-client-2.8.0.jar - release/modules/ext/curator-client-2.8.0.jar - - - ext/jackson-core-2.9.7.jar - release/modules/ext/jackson-core-2.9.7.jar - - - ext/cxf-rt-frontend-jaxrs-3.0.16.jar - release/modules/ext/cxf-rt-frontend-jaxrs-3.0.16.jar - ext/pdfbox-tools-2.0.8.jar release/modules/ext/pdfbox-tools-2.0.8.jar + + ext/asm-5.0.4.jar + release/modules/ext/asm-5.0.4.jar + + + ext/jcl-over-slf4j-1.7.24.jar + release/modules/ext/jcl-over-slf4j-1.7.24.jar + ext/tika-parsers-1.17.jar release/modules/ext/tika-parsers-1.17.jar ext/sqlite-jdbc-3.8.11.jar - release\modules\ext\sqlite-jdbc-3.8.11.jar + release/modules/ext/sqlite-jdbc-3.8.11.jar - ext/activemq-all-5.11.1.jar - release/modules/ext/activemq-all-5.11.1.jar + ext/json-simple-1.1.1.jar + release/modules/ext/json-simple-1.1.1.jar - ext/xz-1.6.jar - release/modules/ext/xz-1.6.jar + ext/sis-utility-0.6.jar + release/modules/ext/sis-utility-0.6.jar - ext/Rejistry-1.0-SNAPSHOT.jar - release/modules/ext/Rejistry-1.0-SNAPSHOT.jar - - - ext/dd-plist-1.20.jar - release/modules/ext/dd-plist-1.20.jar + ext/jhighlight-1.0.2.jar + release/modules/ext/jhighlight-1.0.2.jar ext/jempbox-1.8.13.jar @@ -477,21 +455,9 @@ ext/cxf-rt-rs-client-3.0.16.jar release/modules/ext/cxf-rt-rs-client-3.0.16.jar - - ext/sevenzipjbinding-AllPlatforms.jar - release/modules/ext/sevenzipjbinding-AllPlatforms.jar - ext/commons-pool2-2.4.2.jar - release\modules\ext\commons-pool2-2.4.2.jar - - - ext/jackcess-encrypt-2.1.4.jar - release/modules/ext/jackcess-encrypt-2.1.4.jar - - - ext/jsoup-1.10.3.jar - release/modules/ext/jsoup-1.10.3.jar + release/modules/ext/commons-pool2-2.4.2.jar ext/jdom-2.0.5-contrib.jar @@ -513,6 +479,190 @@ ext/xmpcore-5.1.3.jar release/modules/ext/xmpcore-5.1.3.jar + + ext/zookeeper-3.4.6.jar + release/modules/ext/zookeeper-3.4.6.jar + + + ext/jdom-2.0.5.jar + release/modules/ext/jdom-2.0.5.jar + + + ext/cxf-rt-transports-http-3.0.16.jar + release/modules/ext/cxf-rt-transports-http-3.0.16.jar + + + ext/sis-metadata-0.6.jar + release/modules/ext/sis-metadata-0.6.jar + + + ext/isoparser-1.1.18.jar + release/modules/ext/isoparser-1.1.18.jar + + + ext/sleuthkit-postgresql-4.6.4.jar + release/modules/ext/sleuthkit-postgresql-4.6.4.jar + + + ext/vorbis-java-core-0.8.jar + release/modules/ext/vorbis-java-core-0.8.jar + + + ext/commons-codec-1.6.jar + release/modules/ext/commons-codec-1.6.jar + + + ext/netcdf4-4.5.5.jar + release/modules/ext/netcdf4-4.5.5.jar + + + ext/slf4j-api-1.7.24.jar + release/modules/ext/slf4j-api-1.7.24.jar + + + ext/java-libpst-0.8.1.jar + release/modules/ext/java-libpst-0.8.1.jar + + + ext/jul-to-slf4j-1.7.24.jar + release/modules/ext/jul-to-slf4j-1.7.24.jar + + + ext/gson-2.8.1.jar + release/modules/ext/gson-2.8.1.jar + + + ext/poi-3.17.jar + release/modules/ext/poi-3.17.jar + + + ext/poi-scratchpad-3.17.jar + release/modules/ext/poi-scratchpad-3.17.jar + + + ext/sis-netcdf-0.6.jar + release/modules/ext/sis-netcdf-0.6.jar + + + ext/commons-io-2.5.jar + release/modules/ext/commons-io-2.5.jar + + + ext/curator-framework-2.8.0.jar + release/modules/ext/curator-framework-2.8.0.jar + + + ext/bcprov-jdk15on-1.54.jar + release/modules/ext/bcprov-jdk15on-1.54.jar + + + ext/fontbox-2.0.8.jar + release/modules/ext/fontbox-2.0.8.jar + + + ext/commons-dbcp2-2.1.1.jar + release/modules/ext/commons-dbcp2-2.1.1.jar + + + ext/jgraphx-v3.8.0.jar + release/modules/ext/jgraphx-v3.8.0.jar + + + ext/juniversalchardet-1.0.3.jar + release/modules/ext/juniversalchardet-1.0.3.jar + + + ext/jython-standalone-2.7.0.jar + release/modules/ext/jython-standalone-2.7.0.jar + + + ext/jackcess-encrypt-2.1.4.jar + release/modules/ext/jackcess-encrypt-2.1.4.jar + + + ext/cxf-core-3.0.16.jar + release/modules/ext/cxf-core-3.0.16.jar + + + ext/javax.ws.rs-api-2.0.1.jar + release/modules/ext/javax.ws.rs-api-2.0.1.jar + + + ext/opennlp-tools-1.8.3.jar + release/modules/ext/opennlp-tools-1.8.3.jar + + + ext/junrar-0.7.jar + release/modules/ext/junrar-0.7.jar + + + ext/postgresql-9.4.1211.jre7.jar + release/modules/ext/postgresql-9.4.1211.jre7.jar + + + ext/poi-ooxml-3.17.jar + release/modules/ext/poi-ooxml-3.17.jar + + + ext/curator-client-2.8.0.jar + release/modules/ext/curator-client-2.8.0.jar + + + ext/jackson-core-2.9.7.jar + release/modules/ext/jackson-core-2.9.7.jar + + + ext/cxf-rt-frontend-jaxrs-3.0.16.jar + release/modules/ext/cxf-rt-frontend-jaxrs-3.0.16.jar + + + ext/grib-4.5.5.jar + release/modules/ext/grib-4.5.5.jar + + + ext/jackson-core-2.9.2.jar + release/modules/ext/jackson-core-2.9.2.jar + + + ext/activemq-all-5.11.1.jar + release/modules/ext/activemq-all-5.11.1.jar + + + ext/xz-1.6.jar + release/modules/ext/xz-1.6.jar + + + ext/Rejistry-1.0-SNAPSHOT.jar + release/modules/ext/Rejistry-1.0-SNAPSHOT.jar + + + ext/dd-plist-1.20.jar + release/modules/ext/dd-plist-1.20.jar + + + ext/rome-1.5.1.jar + release/modules/ext/rome-1.5.1.jar + + + ext/sevenzipjbinding-AllPlatforms.jar + release/modules/ext/sevenzipjbinding-AllPlatforms.jar + + + ext/jmatio-1.2.jar + release/modules/ext/jmatio-1.2.jar + + + ext/jsoup-1.10.3.jar + release/modules/ext/jsoup-1.10.3.jar + + + ext/vorbis-java-tika-0.8.jar + release/modules/ext/vorbis-java-tika-0.8.jar + + + ext/json-1.8.jar + release/modules/ext/json-1.8.jar + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.form index 6a1bd5465e..8e17a6a606 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.form @@ -31,14 +31,6 @@ - - - - - - - - @@ -47,14 +39,22 @@ - - - - - - + + + + + + - + + + + + + + + + @@ -96,7 +96,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index 61c0e84f9f..d9b68ece9d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -205,12 +205,6 @@ public class ImageFilePanel extends JPanel implements DocumentListener { .addComponent(browseButton) .addGap(2, 2, 2)) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pathLabel) - .addComponent(errorLabel) - .addComponent(noFatOrphansCheckbox)) - .addGap(0, 0, Short.MAX_VALUE)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(sha256HashLabel) .addComponent(sha1HashLabel) @@ -218,13 +212,19 @@ public class ImageFilePanel extends JPanel implements DocumentListener { .addComponent(sectorSizeLabel) .addComponent(timeZoneLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(sha256HashTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 455, Short.MAX_VALUE) + .addComponent(sha1HashTextField) + .addComponent(md5HashTextField) + .addComponent(sectorSizeComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(timeZoneComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(sectorSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 85, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(md5HashTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 231, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(sha1HashTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 287, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(sha256HashTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 455, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(11, Short.MAX_VALUE)) + .addComponent(pathLabel) + .addComponent(errorLabel) + .addComponent(noFatOrphansCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 262, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 325, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -258,7 +258,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener { .addComponent(sha256HashLabel)) .addGap(18, 18, 18) .addComponent(errorLabel) - .addContainerGap(45, Short.MAX_VALUE)) + .addContainerGap(23, Short.MAX_VALUE)) ); }// //GEN-END:initComponents diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java index b049613f33..79dcf21f64 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java @@ -122,20 +122,8 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { */ boolean isCentralRepoNode() { return (originalCorrelationInstance != null); - } - - /** - * Uses the saved instance plus type and value to make a new CorrelationAttribute. - * Should only be called if isCentralRepoNode() is true. - * @return the newly created CorrelationAttribute - */ - CorrelationAttributeInstance getCorrelationAttribute() throws EamDbException { - if (! isCentralRepoNode() ) { - throw new EamDbException("Can not create CorrelationAttribute for non central repo node"); - } - return originalCorrelationInstance; - } - + } + /** * Get the case name * @return the case name @@ -206,7 +194,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * @return the original abstract file */ AbstractFile getAbstractFile() throws EamDbException { - if (originalCorrelationInstance == null) { + if (originalAbstractFile == null) { throw new EamDbException("AbstractFile is null"); } return originalAbstractFile; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index c61e19f79e..27c6407b5d 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -58,8 +58,8 @@ abstract class AbstractSqlEamDb implements EamDb { private final static Logger logger = Logger.getLogger(AbstractSqlEamDb.class.getName()); static final String SCHEMA_MAJOR_VERSION_KEY = "SCHEMA_VERSION"; static final String SCHEMA_MINOR_VERSION_KEY = "SCHEMA_MINOR_VERSION"; - static final String CREATED_SCHEMA_MAJOR_VERSION_KEY = "CREATED_SCHEMA_MAJOR_VERSION"; - static final String CREATED_SCHEMA_MINOR_VERSION_KEY = "CREATED_SCHEMA_MINOR_VERSION"; + static final String CREATION_SCHEMA_MAJOR_VERSION_KEY = "CREATION_SCHEMA_MAJOR_VERSION"; + static final String CREATION_SCHEMA_MINOR_VERSION_KEY = "CREATION_SCHEMA_MINOR_VERSION"; static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION = new CaseDbSchemaVersionNumber(1, 2); protected final List defaultCorrelationTypes; @@ -3184,27 +3184,41 @@ abstract class AbstractSqlEamDb implements EamDb { statement = conn.createStatement(); int minorVersion = 0; - resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='SCHEMA_MINOR_VERSION'"); + String minorVersionStr = null; + resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='" + AbstractSqlEamDb.SCHEMA_MINOR_VERSION_KEY + "'"); if (resultSet.next()) { - String minorVersionStr = resultSet.getString("value"); + minorVersionStr = resultSet.getString("value"); try { minorVersion = Integer.parseInt(minorVersionStr); } catch (NumberFormatException ex) { throw new EamDbException("Bad value for schema minor version (" + minorVersionStr + ") - database is corrupt", ex); } + } else { + throw new EamDbException("Failed to read schema minor version from db_info table"); } int majorVersion = 0; - resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='SCHEMA_VERSION'"); + String majorVersionStr = null; + resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='" + AbstractSqlEamDb.SCHEMA_MAJOR_VERSION_KEY + "'"); if (resultSet.next()) { - String majorVersionStr = resultSet.getString("value"); + majorVersionStr = resultSet.getString("value"); try { majorVersion = Integer.parseInt(majorVersionStr); } catch (NumberFormatException ex) { throw new EamDbException("Bad value for schema version (" + majorVersionStr + ") - database is corrupt", ex); } + } else { + throw new EamDbException("Failed to read schema major version from db_info table"); } + /* + * IMPORTANT: The code that follows had a bug in it prior to Autopsy + * 4.10.0. The consequence of the bug is that the schema version + * number is always reset to 1.0 or 1.1 if a Central Repository is + * opened by an Autopsy 4.9.1 or earlier client. To cope with this, + * there is an effort in updates to 1.2 and greater to not retry + * schema updates that may already have been done once. + */ CaseDbSchemaVersionNumber dbSchemaVersion = new CaseDbSchemaVersionNumber(majorVersion, minorVersion); if (dbSchemaVersion.equals(CURRENT_DB_SCHEMA_VERSION)) { logger.log(Level.INFO, "Central Repository is up to date"); @@ -3215,7 +3229,11 @@ abstract class AbstractSqlEamDb implements EamDb { return; } - // Update from 1.0 to 1.1 + EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.getSelectedPlatform(); + + /* + * Update to 1.1 + */ if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 1)) < 0) { statement.execute("ALTER TABLE reference_sets ADD COLUMN known_status INTEGER;"); //NON-NLS statement.execute("ALTER TABLE reference_sets ADD COLUMN read_only BOOLEAN;"); //NON-NLS @@ -3226,9 +3244,11 @@ abstract class AbstractSqlEamDb implements EamDb { // regardless of whether this succeeds. EamDbUtil.insertDefaultOrganization(conn); } - //Update to 1.2 + + /* + * Update to 1.2 + */ if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) { - EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.getSelectedPlatform(); final String addIntegerColumnTemplate = "ALTER TABLE %s ADD COLUMN %s INTEGER;"; //NON-NLS final String addSsidTableTemplate; final String addCaseIdIndexTemplate; @@ -3362,26 +3382,50 @@ abstract class AbstractSqlEamDb implements EamDb { } /* - * Put values into the db_info table indicating that the - * creation schema version is not known. + * Drop the db_info table and add it back in with the name + * column having a UNIQUE constraint. The name column could now + * be used as the primary key, but the essentially useless id + * column is retained for the sake of backwards compatibility. + * Note that the creation schema version number is set to 0.0 + * to indicate that it is unknown. */ - statement.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATED_SCHEMA_MAJOR_VERSION_KEY + "', '0')"); - statement.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATED_SCHEMA_MINOR_VERSION_KEY + "', '0')"); - - } - if (!updateSchemaVersion(conn)) { - throw new EamDbException("Error updating schema version"); + String creationMajorVer; + resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name = '" + AbstractSqlEamDb.CREATION_SCHEMA_MAJOR_VERSION_KEY + "'"); + if (resultSet.next()) { + creationMajorVer = resultSet.getString("value"); + } else { + creationMajorVer = "0"; + } + String creationMinorVer; + resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name = '" + AbstractSqlEamDb.CREATION_SCHEMA_MINOR_VERSION_KEY + "'"); + if (resultSet.next()) { + creationMinorVer = resultSet.getString("value"); + } else { + creationMinorVer = "0"; + } + statement.execute("DROP TABLE db_info"); + if (selectedPlatform == EamDbPlatformEnum.POSTGRESQL) { + statement.execute("CREATE TABLE db_info (id SERIAL, name TEXT UNIQUE NOT NULL, value TEXT NOT NULL)"); + } else { + statement.execute("CREATE TABLE db_info (id INTEGER PRIMARY KEY, name TEXT UNIQUE NOT NULL, value TEXT NOT NULL)"); + } + statement.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.SCHEMA_MAJOR_VERSION_KEY + "','" + majorVersionStr + "')"); + statement.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.SCHEMA_MINOR_VERSION_KEY + "','" + minorVersionStr + "')"); + statement.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATION_SCHEMA_MAJOR_VERSION_KEY + "','" + creationMajorVer + "')"); + statement.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATION_SCHEMA_MINOR_VERSION_KEY + "','" + creationMinorVer + "')"); } + updateSchemaVersion(conn); conn.commit(); - logger.log(Level.INFO, "Central Repository upgraded to version " + CURRENT_DB_SCHEMA_VERSION); + logger.log(Level.INFO, String.format("Central Repository schema updated to version %s", CURRENT_DB_SCHEMA_VERSION)); + } catch (SQLException | EamDbException ex) { try { if (conn != null) { conn.rollback(); } } catch (SQLException ex2) { - logger.log(Level.SEVERE, "Database rollback failed", ex2); + logger.log(Level.SEVERE, String.format("Central Repository rollback of failed schema update to %s failed", CURRENT_DB_SCHEMA_VERSION), ex2); } throw ex; } finally { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java old mode 100644 new mode 100755 diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java index 3129d1222e..6e629b52e2 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java @@ -126,42 +126,17 @@ public class EamDbUtil { } /** - * Store the schema version into the db_info table. - * - * This should be called immediately following the database schema being - * loaded. + * Writes the current schema version into the database. * * @param conn Open connection to use. * - * @return true on success, else false + * @throws SQLException If there is an error doing the update. */ - static boolean updateSchemaVersion(Connection conn) { - - Statement statement; - ResultSet resultSet; - try { - statement = conn.createStatement(); - resultSet = statement.executeQuery("SELECT id FROM db_info WHERE name='SCHEMA_VERSION'"); - if (resultSet.next()) { - int id = resultSet.getInt("id"); - statement.execute("UPDATE db_info SET value=" + CURRENT_DB_SCHEMA_VERSION.getMajor() + " WHERE id=" + id); - } else { - statement.execute("INSERT INTO db_info (name, value) VALUES ('SCHEMA_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')"); - } - - resultSet = statement.executeQuery("SELECT id FROM db_info WHERE name='SCHEMA_MINOR_VERSION'"); - if (resultSet.next()) { - int id = resultSet.getInt("id"); - statement.execute("UPDATE db_info SET value=" + CURRENT_DB_SCHEMA_VERSION.getMinor() + " WHERE id=" + id); - } else { - statement.execute("INSERT INTO db_info (name, value) VALUES ('SCHEMA_MINOR_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "')"); - } - } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Error adding schema version to db_info.", ex); - return false; + static void updateSchemaVersion(Connection conn) throws SQLException { + try (Statement statement = conn.createStatement()) { + statement.execute("UPDATE db_info SET value = '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "' WHERE name = '" + AbstractSqlEamDb.SCHEMA_MAJOR_VERSION_KEY + "'"); + statement.execute("UPDATE db_info SET value = '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "' WHERE name = '" + AbstractSqlEamDb.SCHEMA_MINOR_VERSION_KEY + "'"); } - - return true; } /** @@ -191,9 +166,9 @@ public class EamDbUtil { } /** - * Upgrade the current central reposity to the newest version. If the upgrade - * fails, the central repository will be disabled and the current settings - * will be cleared. + * Upgrade the current Central Reposity schema to the newest version. If the + * upgrade fails, the Central Repository will be disabled and the current + * settings will be cleared. * * @return true if the upgrade succeeds, false otherwise. */ @@ -201,7 +176,7 @@ public class EamDbUtil { if (!EamDb.isEnabled()) { return true; } - + CoordinationService.Lock lock = null; try { EamDb db = EamDb.getInstance(); @@ -217,23 +192,23 @@ public class EamDbUtil { LOGGER.log(Level.SEVERE, "Error updating central repository", ex); // Disable the central repo and clear the current settings. - try{ + try { if (null != EamDb.getInstance()) { EamDb.getInstance().shutdownConnections(); } - } catch (EamDbException ex2){ + } catch (EamDbException ex2) { LOGGER.log(Level.SEVERE, "Error shutting down central repo connection pool", ex); - } + } setUseCentralRepo(false); EamDbPlatformEnum.setSelectedPlatform(EamDbPlatformEnum.DISABLED.name()); EamDbPlatformEnum.saveSelectedPlatform(); - + return false; } finally { - if(lock != null){ - try{ + if (lock != null) { + try { lock.release(); - } catch (CoordinationServiceException ex){ + } catch (CoordinationServiceException ex) { LOGGER.log(Level.SEVERE, "Error releasing database lock", ex); } } @@ -254,6 +229,7 @@ public class EamDbUtil { * Check whether the given org is the default organization. * * @param org + * * @return true if it is the default org, false otherwise */ public static boolean isDefaultOrg(EamOrganization org) { @@ -264,6 +240,7 @@ public class EamDbUtil { * Add the default organization to the database * * @param conn + * * @return true if successful, false otherwise */ static boolean insertDefaultOrganization(Connection conn) { @@ -294,7 +271,7 @@ public class EamDbUtil { * If the Central Repos use has been enabled. * * @return true if the Central Repo may be configured, false if it should - * not be able to be + * not be able to be */ public static boolean useCentralRepo() { return Boolean.parseBoolean(ModuleSettings.getConfigSetting(CENTRAL_REPO_NAME, CENTRAL_REPO_USE_KEY)); @@ -305,7 +282,7 @@ public class EamDbUtil { * configured. * * @param centralRepoCheckBoxIsSelected - true if the central repo can be - * used + * used */ public static void setUseCentralRepo(boolean centralRepoCheckBoxIsSelected) { ModuleSettings.setConfigSetting(CENTRAL_REPO_NAME, CENTRAL_REPO_USE_KEY, Boolean.toString(centralRepoCheckBoxIsSelected)); @@ -364,7 +341,7 @@ public class EamDbUtil { * Close the prepared statement. * * @param preparedStatement The prepared statement to be closed. - * + * * @deprecated Use closeStatement() instead. * * @throws EamDbException diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java old mode 100644 new mode 100755 diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java index fa1fe9207b..14a5719f52 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java @@ -406,13 +406,6 @@ public final class PostgresEamDbSettings { String instancesKnownStatusIdx = getAddKnownStatusIndexTemplate(); String instancesObjectIdIdx = getAddObjectIdIndexTemplate(); - StringBuilder createDbInfoTable = new StringBuilder(); - createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info ("); - createDbInfoTable.append("id SERIAL PRIMARY KEY NOT NULL,"); - createDbInfoTable.append("name text NOT NULL,"); - createDbInfoTable.append("value text NOT NULL"); - createDbInfoTable.append(")"); - // NOTE: the db_info table currenly only has 1 row, so having an index // provides no benefit. Connection conn = null; @@ -438,11 +431,16 @@ public final class PostgresEamDbSettings { stmt.execute(createCorrelationTypesTable.toString()); - stmt.execute(createDbInfoTable.toString()); + /* + * Note that the essentially useless id column in the following + * table is required for backwards compatibility. Otherwise, the + * name column could be the primary key. + */ + stmt.execute("CREATE TABLE db_info (id SERIAL, name TEXT UNIQUE NOT NULL, value TEXT NOT NULL)"); stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.SCHEMA_MAJOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')"); stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.SCHEMA_MINOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "')"); - stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATED_SCHEMA_MAJOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')"); - stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATED_SCHEMA_MINOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "')"); + stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATION_SCHEMA_MAJOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')"); + stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATION_SCHEMA_MINOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "')"); // Create a separate instance and reference table for each correlation type List DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes(); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java index 8ebf70b14e..4851e51b51 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java @@ -291,7 +291,7 @@ public final class SqliteEamDbSettings { createDataSourcesTable.append("datasource_obj_id integer,"); createDataSourcesTable.append("md5 text DEFAULT NULL,"); createDataSourcesTable.append("sha1 text DEFAULT NULL,"); - createDataSourcesTable.append("sha256 text DEFAULT NULL,"); + createDataSourcesTable.append("sha256 text DEFAULT NULL,"); createDataSourcesTable.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,"); createDataSourcesTable.append("CONSTRAINT datasource_unique UNIQUE (case_id, device_id, name)"); createDataSourcesTable.append(")"); @@ -348,13 +348,6 @@ public final class SqliteEamDbSettings { String instancesValueIdx = getAddValueIndexTemplate(); String instancesKnownStatusIdx = getAddKnownStatusIndexTemplate(); String instancesObjectIdIdx = getAddObjectIdIndexTemplate(); - - StringBuilder createDbInfoTable = new StringBuilder(); - createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info ("); - createDbInfoTable.append("id integer primary key NOT NULL,"); - createDbInfoTable.append("name text NOT NULL,"); - createDbInfoTable.append("value text NOT NULL"); - createDbInfoTable.append(")"); // NOTE: the db_info table currenly only has 1 row, so having an index // provides no benefit. @@ -387,11 +380,16 @@ public final class SqliteEamDbSettings { stmt.execute(createCorrelationTypesTable.toString()); - stmt.execute(createDbInfoTable.toString()); + /* + * Note that the essentially useless id column in the following + * table is required for backwards compatibility. Otherwise, the + * name column could be the primary key. + */ + stmt.execute("CREATE TABLE db_info (id INTEGER PRIMARY KEY, name TEXT UNIQUE NOT NULL, value TEXT NOT NULL)"); stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.SCHEMA_MAJOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')"); stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.SCHEMA_MINOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "')"); - stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATED_SCHEMA_MAJOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')"); - stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATED_SCHEMA_MINOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "')"); + stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATION_SCHEMA_MAJOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')"); + stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATION_SCHEMA_MINOR_VERSION_KEY + "', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "')"); // Create a separate instance and reference table for each artifact type List DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes(); @@ -427,7 +425,7 @@ public final class SqliteEamDbSettings { } return true; } - + /** * Get the template String for creating a new _instances table in a Sqlite * central repository. %s will exist in the template where the name of the @@ -511,8 +509,8 @@ public final class SqliteEamDbSettings { * instance table. %s will exist in the template where the name of the new * table will be addedd. * - * @return a String which is a template for adding an index to the file_obj_id - * column of a _instances table + * @return a String which is a template for adding an index to the + * file_obj_id column of a _instances table */ static String getAddObjectIdIndexTemplate() { // Each "%s" will be replaced with the relevant TYPE_instances table name. diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index 9a5a4f47d6..87d824ed0f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -58,23 +58,24 @@ import org.sleuthkit.datamodel.TskCoreException; */ @NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Correlation Engine"}) public class IngestEventsListener { - + private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName()); private static final String MODULE_NAME = Bundle.IngestEventsListener_ingestmodule_name(); - + final Collection recentlyAddedCeArtifacts = new LinkedHashSet<>(); private static int correlationModuleInstanceCount; private static boolean flagNotableItems; private static boolean flagSeenDevices; + private static boolean createCrProperties; private final ExecutorService jobProcessingExecutor; private static final String INGEST_EVENT_THREAD_NAME = "Ingest-Event-Listener-%d"; private final PropertyChangeListener pcl1 = new IngestModuleEventListener(); private final PropertyChangeListener pcl2 = new IngestJobEventListener(); - + IngestEventsListener() { jobProcessingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(INGEST_EVENT_THREAD_NAME).build()); } - + void shutdown() { ThreadUtils.shutDownTaskExecutor(jobProcessingExecutor); } @@ -149,6 +150,15 @@ public class IngestEventsListener { return flagSeenDevices; } + /** + * Are correlation properties being created + * + * @return True if creating correlation properties; otherwise false. + */ + public synchronized static boolean shouldCreateCrProperties() { + return createCrProperties; + } + /** * Configure the listener to flag notable items or not. * @@ -166,11 +176,20 @@ public class IngestEventsListener { public synchronized static void setFlagSeenDevices(boolean value) { flagSeenDevices = value; } - + + /** + * Configure the listener to create correlation properties + * + * @param value True to create properties; otherwise false. + */ + public synchronized static void setCreateCrProperties(boolean value) { + createCrProperties = value; + } + @NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)", "IngestEventsListener.prevCaseComment.text=Previous Case: "}) static private void postCorrelatedBadArtifactToBlackboard(BlackboardArtifact bbArtifact, List caseDisplayNames) { - + Collection attributes = Arrays.asList( new BlackboardAttribute( TSK_SET_NAME, MODULE_NAME, @@ -204,17 +223,17 @@ public class IngestEventsListener { bbArtifact.getArtifactID())); postArtifactToBlackboard(bbArtifact, attributes); } - + private static void postArtifactToBlackboard(BlackboardArtifact bbArtifact, Collection attributes) { try { - SleuthkitCase tskCase = bbArtifact.getSleuthkitCase(); - AbstractFile abstractFile = bbArtifact.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID()); + AbstractFile abstractFile = tskCase.getAbstractFileById(bbArtifact.getObjectID()); Blackboard blackboard = tskCase.getBlackboard(); // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_ARTIFACT_HIT, attributes)) { BlackboardArtifact tifArtifact = abstractFile.newArtifact(TSK_INTERESTING_ARTIFACT_HIT); tifArtifact.addAttributes(attributes); + try { // index the artifact for keyword search blackboard.postArtifact(tifArtifact, MODULE_NAME); @@ -228,9 +247,9 @@ public class IngestEventsListener { LOGGER.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS } } - + private class IngestModuleEventListener implements PropertyChangeListener { - + @Override public void propertyChange(PropertyChangeEvent evt) { //if ingest is running we want there to check if there is a Correlation Engine module running @@ -249,16 +268,17 @@ public class IngestEventsListener { //if ingest isn't running create the interesting items otherwise use the ingest module setting to determine if we create interesting items boolean flagNotable = !IngestManager.getInstance().isIngestRunning() || isFlagNotableItems(); boolean flagPrevious = !IngestManager.getInstance().isIngestRunning() || isFlagSeenDevices(); - jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, flagNotable, flagPrevious)); + boolean createAttributes = !IngestManager.getInstance().isIngestRunning() || shouldCreateCrProperties(); + jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, flagNotable, flagPrevious, createAttributes)); break; } } } } } - + private class IngestJobEventListener implements PropertyChangeListener { - + @Override public void propertyChange(PropertyChangeEvent evt) { switch (IngestManager.IngestJobEvent.valueOf(evt.getPropertyName())) { @@ -268,11 +288,11 @@ public class IngestEventsListener { } } } - + } - + private final class AnalysisCompleteTask implements Runnable { - + @Override public void run() { // clear the tracker to reduce memory usage @@ -282,21 +302,23 @@ public class IngestEventsListener { //else another instance of the Correlation Engine Module is still being run. } // DATA_SOURCE_ANALYSIS_COMPLETED } - + private final class DataAddedTask implements Runnable { - + private final EamDb dbManager; private final PropertyChangeEvent event; private final boolean flagNotableItemsEnabled; private final boolean flagPreviousItemsEnabled; - - private DataAddedTask(EamDb db, PropertyChangeEvent evt, boolean flagNotableItemsEnabled, boolean flagPreviousItemsEnabled) { - dbManager = db; - event = evt; + private final boolean createCorrelationAttributes; + + private DataAddedTask(EamDb db, PropertyChangeEvent evt, boolean flagNotableItemsEnabled, boolean flagPreviousItemsEnabled, boolean createCorrelationAttributes) { + this.dbManager = db; + this.event = evt; this.flagNotableItemsEnabled = flagNotableItemsEnabled; this.flagPreviousItemsEnabled = flagPreviousItemsEnabled; + this.createCorrelationAttributes = createCorrelationAttributes; } - + @Override public void run() { if (!EamDb.isEnabled()) { @@ -308,7 +330,7 @@ public class IngestEventsListener { return; } List eamArtifacts = new ArrayList<>(); - + for (BlackboardArtifact bbArtifact : bbArtifacts) { // eamArtifact will be null OR a EamArtifact containing one EamArtifactInstance. List convertedArtifacts = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, true); @@ -347,7 +369,9 @@ public class IngestEventsListener { LOGGER.log(Level.INFO, String.format("Unable to flag notable item: %s.", eamArtifact.toString()), ex); } } - eamArtifacts.add(eamArtifact); + if (createCorrelationAttributes) { + eamArtifacts.add(eamArtifact); + } } } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error counting notable artifacts.", ex); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties index f99db1edb6..8b12289af0 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties @@ -1,3 +1,4 @@ IngestSettingsPanel.ingestSettingsLabel.text=Ingest Settings IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text=Flag previously seen devices +IngestSettingsPanel.createCorrelationPropertiesCheckbox.text=Save items to the Central Repository diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java index a43f4029ab..800e7eef5e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.centralrepository.ingestmodule; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.logging.Level; @@ -48,8 +49,12 @@ import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT; 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; @@ -61,8 +66,11 @@ import org.sleuthkit.datamodel.TskData; "CentralRepoIngestModule.prevCaseComment.text=Previous Case: "}) final class CentralRepoIngestModule implements FileIngestModule { + private static final String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName(); + static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = true; static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = true; + static final boolean DEFAULT_CREATE_CR_PROPERTIES = true; private final static Logger logger = Logger.getLogger(CentralRepoIngestModule.class.getName()); private final IngestServices services = IngestServices.getInstance(); @@ -75,6 +83,7 @@ final class CentralRepoIngestModule implements FileIngestModule { private final boolean flagTaggedNotableItems; private final boolean flagPreviouslySeenDevices; private Blackboard blackboard; + private final boolean createCorrelationProperties; /** * Instantiate the Correlation Engine ingest module. @@ -84,6 +93,7 @@ final class CentralRepoIngestModule implements FileIngestModule { CentralRepoIngestModule(IngestSettings settings) { flagTaggedNotableItems = settings.isFlagTaggedNotableItems(); flagPreviouslySeenDevices = settings.isFlagPreviousDevices(); + createCorrelationProperties = settings.shouldCreateCorrelationProperties(); } @Override @@ -153,27 +163,28 @@ final class CentralRepoIngestModule implements FileIngestModule { } } - // insert this file into the central repository - try { - CorrelationAttributeInstance cefi = new CorrelationAttributeInstance( - filesType, - md5, - eamCase, - eamDataSource, - abstractFile.getParentPath() + abstractFile.getName(), - null, - TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database. - , - abstractFile.getId()); - dbManager.addAttributeInstanceBulk(cefi); - } catch (EamDbException ex) { - logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS - return ProcessResult.ERROR; - } catch (CorrelationAttributeNormalizationException ex) { - logger.log(Level.INFO, "Error adding artifact to bulk artifacts.", ex); // NON-NLS - return ProcessResult.ERROR; + // insert this file into the central repository + if (createCorrelationProperties) { + try { + CorrelationAttributeInstance cefi = new CorrelationAttributeInstance( + filesType, + md5, + eamCase, + eamDataSource, + abstractFile.getParentPath() + abstractFile.getName(), + null, + TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database. + , + abstractFile.getId()); + dbManager.addAttributeInstanceBulk(cefi); + } catch (EamDbException ex) { + logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS + return ProcessResult.ERROR; + } catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.INFO, "Error adding artifact to bulk artifacts.", ex); // NON-NLS + return ProcessResult.ERROR; + } } - return ProcessResult.OK; } @@ -236,6 +247,9 @@ final class CentralRepoIngestModule implements FileIngestModule { if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.isFlagSeenDevices()) { IngestEventsListener.setFlagSeenDevices(flagPreviouslySeenDevices); } + if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.shouldCreateCrProperties()) { + IngestEventsListener.setCreateCrProperties(createCorrelationProperties); + } if (EamDb.isEnabled() == false) { /* @@ -330,17 +344,18 @@ final class CentralRepoIngestModule implements FileIngestModule { */ private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List caseDisplayNames) { - String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName(); - - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, - Bundle.CentralRepoIngestModule_prevTaggedSet_text())); - attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, - Bundle.CentralRepoIngestModule_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", "")))); + Collection attributes = Arrays.asList( + new BlackboardAttribute( + TSK_SET_NAME, MODULE_NAME, + Bundle.CentralRepoIngestModule_prevTaggedSet_text()), + new BlackboardAttribute( + TSK_COMMENT, MODULE_NAME, + Bundle.CentralRepoIngestModule_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",")))); try { + // Create artifact if it doesn't already exist. - if (!blackboard.artifactExists(abstractFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, attributes)) { - BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); + if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_FILE_HIT, attributes)) { + BlackboardArtifact tifArtifact = abstractFile.newArtifact(TSK_INTERESTING_FILE_HIT); tifArtifact.addAttributes(attributes); try { // index the artifact for keyword search diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettings.java index 74ad3537d8..454a2c3628 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettings.java @@ -27,8 +27,9 @@ final class IngestSettings implements IngestModuleIngestJobSettings { private static final long serialVersionUID = 1L; - private boolean flagTaggedNotableItems; - private boolean flagPreviousDevices; + private final boolean flagTaggedNotableItems; + private final boolean flagPreviousDevices; + private final boolean createCorrelationProperties; /** * Instantiate the ingest job settings with default values. @@ -36,17 +37,22 @@ final class IngestSettings implements IngestModuleIngestJobSettings { IngestSettings() { this.flagTaggedNotableItems = CentralRepoIngestModule.DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS; this.flagPreviousDevices = CentralRepoIngestModule.DEFAULT_FLAG_PREVIOUS_DEVICES; + this.createCorrelationProperties = CentralRepoIngestModule.DEFAULT_CREATE_CR_PROPERTIES; } /** * Instantiate the ingest job settings. * - * @param flagTaggedNotableItems Flag previously tagged notable items. - * @param flagPreviousDevices Flag devices which exist already in the Central Repository + * @param flagTaggedNotableItems Flag previously tagged notable items. + * @param flagPreviousDevices Flag devices which exist already in + * the Central Repository + * @param createCorrelationProperties Create correlation properties in the + * central repository */ - IngestSettings(boolean flagTaggedNotableItems, boolean flagPreviousDevices) { + IngestSettings(boolean flagTaggedNotableItems, boolean flagPreviousDevices, boolean createCorrelationProperties) { this.flagTaggedNotableItems = flagTaggedNotableItems; this.flagPreviousDevices = flagPreviousDevices; + this.createCorrelationProperties = createCorrelationProperties; } @Override @@ -71,4 +77,13 @@ final class IngestSettings implements IngestModuleIngestJobSettings { boolean isFlagPreviousDevices() { return flagPreviousDevices; } + + /** + * Should correlation properties be created + * + * @return True if creating; otherwise false. + */ + boolean shouldCreateCorrelationProperties() { + return createCorrelationProperties; + } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.form index 3c2fddca0f..82383f135f 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.form @@ -22,9 +22,10 @@ - - - + + + + @@ -37,11 +38,13 @@ - + + + - + @@ -71,5 +74,12 @@ + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java index 159f925355..6438b399e7 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java @@ -43,11 +43,12 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { private void customizeComponents(IngestSettings settings) { flagTaggedNotableItemsCheckbox.setSelected(settings.isFlagTaggedNotableItems()); flagPreviouslySeenDevicesCheckbox.setSelected(settings.isFlagPreviousDevices()); + createCorrelationPropertiesCheckbox.setSelected(settings.shouldCreateCorrelationProperties()); } @Override public IngestModuleIngestJobSettings getSettings() { - return new IngestSettings(flagTaggedNotableItemsCheckbox.isSelected(), flagPreviouslySeenDevicesCheckbox.isSelected()); + return new IngestSettings(flagTaggedNotableItemsCheckbox.isSelected(), flagPreviouslySeenDevicesCheckbox.isSelected(), createCorrelationPropertiesCheckbox.isSelected()); } /** @@ -62,6 +63,7 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { ingestSettingsLabel = new javax.swing.JLabel(); flagTaggedNotableItemsCheckbox = new javax.swing.JCheckBox(); flagPreviouslySeenDevicesCheckbox = new javax.swing.JCheckBox(); + createCorrelationPropertiesCheckbox = new javax.swing.JCheckBox(); ingestSettingsLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(ingestSettingsLabel, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.ingestSettingsLabel.text")); // NOI18N @@ -70,6 +72,8 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { org.openide.awt.Mnemonics.setLocalizedText(flagPreviouslySeenDevicesCheckbox, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(createCorrelationPropertiesCheckbox, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.createCorrelationPropertiesCheckbox.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -80,9 +84,10 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { .addComponent(ingestSettingsLabel) .addGroup(layout.createSequentialGroup() .addGap(10, 10, 10) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(flagPreviouslySeenDevicesCheckbox) - .addComponent(flagTaggedNotableItemsCheckbox)))) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(flagTaggedNotableItemsCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(flagPreviouslySeenDevicesCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(createCorrelationPropertiesCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) .addContainerGap(65, Short.MAX_VALUE)) ); layout.setVerticalGroup( @@ -90,15 +95,18 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(ingestSettingsLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGap(9, 9, 9) + .addComponent(createCorrelationPropertiesCheckbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(flagTaggedNotableItemsCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(flagPreviouslySeenDevicesCheckbox) - .addContainerGap(222, Short.MAX_VALUE)) + .addContainerGap(197, Short.MAX_VALUE)) ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox createCorrelationPropertiesCheckbox; private javax.swing.JCheckBox flagPreviouslySeenDevicesCheckbox; private javax.swing.JCheckBox flagTaggedNotableItemsCheckbox; private javax.swing.JLabel ingestSettingsLabel; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java index 7f4d6605ab..adf8576b03 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java @@ -1,16 +1,16 @@ /* - * + * * Autopsy Forensic Browser - * + * * Copyright 2018 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. @@ -47,11 +47,12 @@ public abstract class AbstractCommonAttributeInstance { * Create a leaf node for attributes found in files in the current case db. * * @param abstractFileReference file from which the common attribute was - * found - * @param cachedFiles storage for abstract files which have been used - * already so we can avoid extra roundtrips to the case db - * @param dataSource datasource where this attribute appears - * @param caseName case where this attribute appears + * found + * @param cachedFiles storage for abstract files which have been + * used already so we can avoid extra + * roundtrips to the case db + * @param dataSource datasource where this attribute appears + * @param caseName case where this attribute appears */ AbstractCommonAttributeInstance(Long abstractFileReference, String dataSource, String caseName) { this.abstractFileObjectId = abstractFileReference; @@ -64,17 +65,19 @@ public abstract class AbstractCommonAttributeInstance { * available in the current data case. * * @param cachedFiles storage for abstract files which have been used - * already so we can avoid extra roundtrips to the case db + * already so we can avoid extra roundtrips to the case + * db */ AbstractCommonAttributeInstance() { this.abstractFileObjectId = -1L; this.caseName = ""; this.dataSource = ""; } - + /** * Get the type of common attribute. - * @return + * + * @return */ public abstract CorrelationAttributeInstance.Type getCorrelationAttributeInstanceType(); @@ -83,8 +86,8 @@ public abstract class AbstractCommonAttributeInstance { * CaseDB. * * @return AbstractFile corresponding to this common attribute or null if it - * cannot be found (for example, in the event that this is a central repo - * file) + * cannot be found (for example, in the event that this is a central + * repo file) */ abstract AbstractFile getAbstractFile(); @@ -143,25 +146,38 @@ public abstract class AbstractCommonAttributeInstance { * Otherwise, we will get an InterCaseCommonAttributeInstanceNode which * supports only baseline functionality. * - * @param attributeInstance common file attribute instance form the central - * repo - * @param abstractFile an AbstractFile from which the attribute instance was - * found - applies to CaseDbCommonAttributeInstance only - * @param currentCaseName + * @param attribute common file attribute instance form the central + * repo + * @param abstractFile an AbstractFile from which the attribute instance + * was found - applies to + * CaseDbCommonAttributeInstance only + * @param currentCaseName the name of the current case + * @param nodeType the type of the node to create for determining + * which columns to add + * * @return the appropriate leaf node for the results tree + * * @throws TskCoreException */ - static DisplayableItemNode createNode(CorrelationAttributeInstance attribute, AbstractFile abstractFile, String currentCaseName) throws TskCoreException { + static DisplayableItemNode createNode(CorrelationAttributeInstance attribute, AbstractFile abstractFile, String currentCaseName, NODE_TYPE nodeType) throws TskCoreException { DisplayableItemNode leafNode; if (abstractFile == null) { - leafNode = new CentralRepoCommonAttributeInstanceNode(attribute); + leafNode = new CentralRepoCommonAttributeInstanceNode(attribute, nodeType); } else { final String abstractFileDataSourceName = abstractFile.getDataSource().getName(); - leafNode = new CaseDBCommonAttributeInstanceNode(abstractFile, currentCaseName, abstractFileDataSourceName); + leafNode = new CaseDBCommonAttributeInstanceNode(abstractFile, currentCaseName, abstractFileDataSourceName, attribute.getCorrelationValue(), nodeType); } - return leafNode; } + + /** + * Enum for type of results being displayed in nodes. + */ + public enum NODE_TYPE { + //depending on the type of results displayed different columns will be necessary to display complete information + CASE_NODE, + COUNT_NODE; + } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java index 1ef14d9ede..3b2947dba9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java @@ -34,7 +34,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Prototype for an object which finds files with common attributes. Subclass - * this and implement findMatches in order + * this and implement findMatchesByCount in order */ public abstract class AbstractCommonAttributeSearcher { @@ -50,7 +50,7 @@ public abstract class AbstractCommonAttributeSearcher { /** * Implement this to search for files with common attributes. Creates an - * object (CommonAttributeSearchResults) which contains all of the + * object (CommonAttributeCountSearchResults) which contains all of the * information required to display a tree view in the UI. The view will * contain 3 layers: a top level node, indicating the number matches each of * it's children possess, a mid level node indicating the matched attribute, @@ -62,7 +62,25 @@ public abstract class AbstractCommonAttributeSearcher { * @throws SQLException * @throws EamDbException */ - public abstract CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException; + public abstract CommonAttributeCountSearchResults findMatchesByCount() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException; + + /** + * Implement this to search for files with common attributes. Creates an + * object (CommonAttributeCountSearchResults) which contains all of the + * information required to display a tree view in the UI. The view will + * contain 3 layers: a top level node, indicating the name of the case the + * results were found in, a mid level node indicating what data source the + * match was found in, and a bottom level which shows the files the match + * was found in + * + * @return An object containing the results of the search + * + * @throws TskCoreException + * @throws NoCurrentCaseException + * @throws SQLException + * @throws EamDbException + */ + public abstract CommonAttributeCaseSearchResults findMatchesByCase() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException; /** * Implement this to create a descriptive string for the tab which will diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java index a4a4387ed0..0628087c94 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java @@ -52,11 +52,19 @@ public class AllInterCaseCommonAttributeSearcher extends InterCaseCommonAttribut } @Override - public CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + public CommonAttributeCountSearchResults findMatchesByCount() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(corAttrType); - Map interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase()); + Map interCaseCommonFiles = eamDbAttrInst.findInterCaseValuesByCount(Case.getCurrentCase()); Set mimeTypesToFilterOn = getMimeTypesToFilterOn(); - return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType, mimeTypesToFilterOn); + return new CommonAttributeCountSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType, mimeTypesToFilterOn); + } + + @Override + public CommonAttributeCaseSearchResults findMatchesByCase() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(corAttrType); + Map> interCaseCommonFiles = eamDbAttrInst.findInterCaseValuesByCase(Case.getCurrentCase()); + Set mimeTypesToFilterOn = getMimeTypesToFilterOn(); + return new CommonAttributeCaseSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType, mimeTypesToFilterOn); } @NbBundle.Messages({ diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties index c87a97a803..cf530cf013 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties @@ -40,3 +40,9 @@ CommonAttributePanel.commonItemSearchDescription.text=Find items that exis CommonAttributePanel.scopeLabel.text=Scope of Search: InterCasePanel.correlationComboBoxLabel.text=Property Type to Match: CommonAttributePanel.percentageThresholdInputBox.text=20 +CommonAttributePanel.resultsDisplayLabel.text_2=Display results organized by: +CommonAttributePanel.organizeByCaseRadio.text=Case +CommonAttributePanel.organizeByCountRadio.text=Number of occurrences +CommonAttributePanel.caseResultsRadioButton.text=Case +CommonAttributePanel.countResultsRadioButton.text=Number of occurrences +CommonAttributePanel.displayResultsLabel.text_2=Display results organized by: diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java index 7bf6801c6b..e8dc6e03ad 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java @@ -1,16 +1,16 @@ /* - * + * * Autopsy Forensic Browser - * + * * Copyright 2018 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. @@ -31,30 +31,33 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Encapsulates data required to instantiate a FileInstanceNode for an instance in the CaseDB + * Encapsulates data required to instantiate a FileInstanceNode for + * an instance in the CaseDB */ final public class CaseDBCommonAttributeInstance extends AbstractCommonAttributeInstance { - + private static final Logger LOGGER = Logger.getLogger(CaseDBCommonAttributeInstance.class.getName()); - - + private final String value; + /** * Create meta data required to find an abstract file and build a * FileInstanceNode. * - * @param objectId id of abstract file to find + * @param objectId id of abstract file to find * @param dataSourceName name of datasource where the object is found + * @param value the correlation value which was found */ - CaseDBCommonAttributeInstance(Long abstractFileReference, String dataSource, String caseName) { + CaseDBCommonAttributeInstance(Long abstractFileReference, String dataSource, String caseName, String value) { super(abstractFileReference, dataSource, caseName); + this.value = value; } @Override public DisplayableItemNode[] generateNodes() { - final CaseDBCommonAttributeInstanceNode intraCaseCommonAttributeInstanceNode = new CaseDBCommonAttributeInstanceNode(this.getAbstractFile(), this.getCaseName(), this.getDataSource()); + final CaseDBCommonAttributeInstanceNode intraCaseCommonAttributeInstanceNode = new CaseDBCommonAttributeInstanceNode(this.getAbstractFile(), this.getCaseName(), this.getDataSource(), this.value, NODE_TYPE.COUNT_NODE); return Arrays.asList(intraCaseCommonAttributeInstanceNode).toArray(new DisplayableItemNode[1]); } - + @Override AbstractFile getAbstractFile() { diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java index 504aa45b97..ac9cb360aa 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java @@ -37,20 +37,26 @@ public class CaseDBCommonAttributeInstanceNode extends FileNode { private final String caseName; private final String dataSource; + private final String value; + private final AbstractCommonAttributeInstance.NODE_TYPE nodeType; /** * Create a node which can be used in a multilayer tree table and is based * on an AbstractFile. * - * @param fsContent the file which is being represented by this node - * @param caseName the name of the case + * @param fsContent the file which is being represented by this node + * @param caseName the name of the case * @param dataSource the datasource which contains the file - * + * @param value the value that the correlation attribute was matched on + * @param nodeType the type of node to display columns for + * */ - public CaseDBCommonAttributeInstanceNode(AbstractFile fsContent, String caseName, String dataSource) { + public CaseDBCommonAttributeInstanceNode(AbstractFile fsContent, String caseName, String dataSource, String value, AbstractCommonAttributeInstance.NODE_TYPE nodeType) { super(fsContent, false); this.caseName = caseName; this.dataSource = dataSource; + this.nodeType = nodeType; + this.value = value; } @Override @@ -77,22 +83,27 @@ public class CaseDBCommonAttributeInstanceNode extends FileNode { Sheet sheet = super.createSheet(); Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); 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.mimeType"))); - - for(Property p : sheetSet.getProperties()) { - if(!keepProps.contains(p.getName())){ + 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.mimeType"))); + + for (Property p : sheetSet.getProperties()) { + if (!keepProps.contains(p.getName())) { sheetSet.remove(p.getName()); } } final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText(); - - sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource())); - sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName)); - + //add different columns for complete information depending on how nodes are structured in results + if (nodeType == AbstractCommonAttributeInstance.NODE_TYPE.COUNT_NODE) { + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath())); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource())); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl(), NO_DESCR, caseName)); + } else if (nodeType == AbstractCommonAttributeInstance.NODE_TYPE.CASE_NODE) { + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_localPath(), Bundle.CommonFilesSearchResultsViewerTable_localPath(), NO_DESCR, this.getContent().getParentPath())); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_valueColLbl(), Bundle.CommonFilesSearchResultsViewerTable_valueColLbl(), NO_DESCR, this.value)); + } return sheet; } -} \ No newline at end of file +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java index 212178a57a..cfcc40e603 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java @@ -43,13 +43,15 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr private static final Logger LOGGER = Logger.getLogger(CentralRepoCommonAttributeInstance.class.getName()); private final Integer crFileId; + private final NODE_TYPE nodeType; private CorrelationAttributeInstance currentAttribute; private final CorrelationAttributeInstance.Type correlationType; - CentralRepoCommonAttributeInstance(Integer attrInstId, CorrelationAttributeInstance.Type correlationType) { + CentralRepoCommonAttributeInstance(Integer attrInstId, CorrelationAttributeInstance.Type correlationType, NODE_TYPE nodeType) { super(); this.crFileId = attrInstId; this.correlationType = correlationType; + this.nodeType = nodeType; } @Override @@ -75,7 +77,6 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr // Only attempt to make the abstract file if the attribute is from the current case if (currentCase.getName().equals(currentAttributeInstance.getCorrelationCase().getCaseUUID())) { - SleuthkitCase tskDb = currentCase.getSleuthkitCase(); // Find the correct data source @@ -96,8 +97,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr parentPath += File.separator; } parentPath = parentPath.replace("\\", "/"); - - final String whereClause = String.format("lower(name) = '%s' AND md5 = '%s' AND lower(parent_path) = '%s' AND data_source_obj_id = %s", fileName, currentAttribute.getCorrelationValue(), parentPath, dataSource.get().getId()); + final String whereClause = String.format("lower(name) = '%s' AND lower(parent_path) = '%s' AND data_source_obj_id = %s", fileName, parentPath, dataSource.get().getId()); List potentialAbstractFiles = tskDb.findAllFilesWhere(whereClause); if (potentialAbstractFiles.isEmpty()) { @@ -127,7 +127,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr String currCaseDbName = Case.getCurrentCase().getDisplayName(); try { AbstractFile abstractFileForAttributeInstance = this.getAbstractFile(); - DisplayableItemNode generatedInstNode = AbstractCommonAttributeInstance.createNode(currentAttribute, abstractFileForAttributeInstance, currCaseDbName); + DisplayableItemNode generatedInstNode = AbstractCommonAttributeInstance.createNode(currentAttribute, abstractFileForAttributeInstance, currCaseDbName, nodeType); attrInstNodeList.add(generatedInstNode); } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, String.format("Unable to get DataSource for record with md5: %s. Node not created.", new Object[]{currentAttribute.getCorrelationValue()}), ex); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstanceNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstanceNode.java index e0b8e928d7..8ec5ad3a3b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstanceNode.java @@ -1,16 +1,16 @@ /* - * + * * Autopsy Forensic Browser - * + * * Copyright 2018 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. @@ -26,40 +26,45 @@ import java.util.List; import javax.swing.Action; import org.openide.nodes.Children; import org.openide.nodes.Sheet; +import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.NodeProperty; /** - * Used by the Common Files search feature to encapsulate instances of a given - * MD5s matched in the search. These nodes will be children of Md5Nodes. - * - * Use this type for files which are not in the current case, but from the - * Central Repo. Contrast with SleuthkitCase which should be used + * Used by the Common Files search feature to encapsulate instances of a given + * MD5s matched in the search. These nodes will be children of + * Md5Nodes. + * + * Use this type for files which are not in the current case, but from the + * Central Repo. Contrast with SleuthkitCase which should be used * when the FileInstance was found in the case presently open in Autopsy. */ public class CentralRepoCommonAttributeInstanceNode extends DisplayableItemNode { private final CorrelationAttributeInstance crFile; - - CentralRepoCommonAttributeInstanceNode(CorrelationAttributeInstance content) { + private final AbstractCommonAttributeInstance.NODE_TYPE nodeType; + + CentralRepoCommonAttributeInstanceNode(CorrelationAttributeInstance content, AbstractCommonAttributeInstance.NODE_TYPE nodeType) { super(Children.LEAF, Lookups.fixed(content)); this.crFile = content; this.setDisplayName(new File(this.crFile.getFilePath()).getName()); + this.nodeType = nodeType; } - - public CorrelationAttributeInstance getCorrelationAttributeInstance(){ + + public CorrelationAttributeInstance getCorrelationAttributeInstance() { return this.crFile; } - + @Override - public Action[] getActions(boolean context){ + public Action[] getActions(boolean context) { List actionsList = new ArrayList<>(); - + actionsList.addAll(Arrays.asList(super.getActions(true))); - + return actionsList.toArray(new Action[actionsList.size()]); } @@ -79,19 +84,19 @@ public class CentralRepoCommonAttributeInstanceNode extends DisplayableItemNode // of this type and they will need to provide the same key return CaseDBCommonAttributeInstanceNode.class.getName(); } - + @Override - protected Sheet createSheet(){ + protected Sheet createSheet() { Sheet sheet = new Sheet(); Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); - - if(sheetSet == null){ + + if (sheetSet == null) { sheetSet = Sheet.createPropertiesSet(); sheet.put(sheetSet); } - + final CorrelationAttributeInstance centralRepoFile = this.getCorrelationAttributeInstance(); - + final String fullPath = centralRepoFile.getFilePath(); final File file = new File(fullPath); @@ -99,18 +104,24 @@ public class CentralRepoCommonAttributeInstanceNode extends DisplayableItemNode final String name = file.getName(); final String parent = file.getParent(); + final String value = centralRepoFile.getCorrelationValue(); final String dataSourceName = centralRepoFile.getCorrelationDataSource().getName(); - - final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText(); - - sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, name)); - sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, parent)); - sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, "")); - sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, dataSourceName)); - sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, "")); - sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName)); - return sheet; + final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText(); + + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), NO_DESCR, name)); + //add different columns for complete information depending on how nodes are structured in results + if (nodeType == AbstractCommonAttributeInstance.NODE_TYPE.COUNT_NODE) { + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, parent)); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, dataSourceName)); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl(), NO_DESCR, caseName)); + } else if (nodeType == AbstractCommonAttributeInstance.NODE_TYPE.CASE_NODE) { + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_localPath(), Bundle.CommonFilesSearchResultsViewerTable_localPath(), NO_DESCR, parent)); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_valueColLbl(), Bundle.CommonFilesSearchResultsViewerTable_valueColLbl(), NO_DESCR, value)); + } + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), NO_DESCR, "")); + + return sheet; } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeCaseSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeCaseSearchResults.java new file mode 100644 index 0000000000..66876c190a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeCaseSearchResults.java @@ -0,0 +1,266 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 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.commonfilesearch; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.logging.Level; +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.CorrelationAttributeNormalizationException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * Stores the results from the various types of common attribute searching + * Stores results based on how they are currently displayed in the UI + */ +final public class CommonAttributeCaseSearchResults { + + private static final Logger LOGGER = Logger.getLogger(CommonAttributeCaseSearchResults.class.getName()); + + // maps instance count to list of attribute values. + private final Map> caseNameToDataSources; + + /** + * Create a values object which can be handed off to the node factories. + * + * @param metadata list of CommonAttributeValue indexed by case + * name + * @param percentageThreshold threshold to filter out files which are too + * common, value of 0 is disabled + * @param resultType The type of Correlation Attribute being + * searched for + * @param mimeTypesToFilterOn Set of mime types to include for intercase + * searches + */ + CommonAttributeCaseSearchResults(Map> metadata, int percentageThreshold, CorrelationAttributeInstance.Type resultType, Set mimeTypesToFilterOn) { + this.caseNameToDataSources = filterMetadata(metadata, percentageThreshold, resultType.getId(), mimeTypesToFilterOn); + } + + /** + * Create a values object which can be handed off to the node factories. + * + * @param metadata Map of Datasources and their + * commonAttributeValueLists indexed by case name + * @param percentageThreshold threshold to filter out files which are too + * common, value of 0 is disabled + */ + CommonAttributeCaseSearchResults(Map> metadata, int percentageThreshold) { + this.caseNameToDataSources = filterMetadata(metadata, percentageThreshold, CorrelationAttributeInstance.FILES_TYPE_ID, new HashSet<>()); + } + + /** + * Find the child node whose children have the specified case name. + * + * This is a convenience method - you can also iterate over + * getValues(). + * + * @param caseName caseNameKey + * + * @return list of values which represent matches + */ + Map getAttributeValuesForCaseName(String caseName) { + return this.caseNameToDataSources.get(caseName); + } + + /** + * Get an unmodifiable collection of values, indexed by case name, which + * represents the common attributes found in the search. + * + * @return map of cases to data sources and their list of matches + */ + public Map> getMetadata() { + return Collections.unmodifiableMap(this.caseNameToDataSources); + } + + /** + * Get an unmodifiable collection of values, indexed by case name, which + * represents the common attributes found in the search. + * + * Remove results which are not found in the portion of available data + * sources described by maximumPercentageThreshold. + * + * @param metadata the unfiltered metadata + * @param percentageThreshold the percentage threshold that a file should + * not be more common than + * @param resultTypeId the ID of the result type contained in the + * metadata + * @param mimeTypesToFilterOn the mimetypes to include in our results + * + * @return metadata + */ + private Map> filterMetadata(Map> metadata, int percentageThreshold, int resultTypeId, Set mimeTypesToFilterOn) { + try { + final String currentCaseName; + try { + currentCaseName = Case.getCurrentCaseThrows().getDisplayName(); + } catch (NoCurrentCaseException ex) { + throw new EamDbException("Unable to get current case while performing filtering", ex); + } + Map currentCaseDataSourceMap = metadata.get(currentCaseName); + if (currentCaseDataSourceMap == null) { + throw new EamDbException("No data for current case found in results, indicating there are no results and nothing will be filtered"); + } + CorrelationAttributeInstance.Type attributeType = CorrelationAttributeInstance + .getDefaultCorrelationTypes() + .stream() + .filter(filterType -> filterType.getId() == resultTypeId) + .findFirst().get(); + //Call countUniqueDataSources once to reduce the number of DB queries needed to get the frequencyPercentage + Double uniqueCaseDataSourceTuples = EamDb.getInstance().getCountUniqueDataSources().doubleValue(); + Map> filteredCaseNameToDataSourcesTree = new HashMap<>(); + Map valuesToKeepCurrentCase = getValuesToKeepFromCurrentCase(currentCaseDataSourceMap, attributeType, percentageThreshold, uniqueCaseDataSourceTuples, mimeTypesToFilterOn); + for (Entry> mapOfDataSources : Collections.unmodifiableMap(metadata).entrySet()) { + if (!mapOfDataSources.getKey().equals(currentCaseName)) { + //rebuild the metadata structure with items from the current case substituted for their matches in other cases results we want to filter out removed + Map newTreeForCase = createTreeForCase(valuesToKeepCurrentCase, mapOfDataSources.getValue()); + filteredCaseNameToDataSourcesTree.put(mapOfDataSources.getKey(), newTreeForCase); + } + } + return filteredCaseNameToDataSourcesTree; + } catch (EamDbException ex) { + LOGGER.log(Level.INFO, "Unable to perform filtering returning unfiltered result set", ex); + return metadata; + } + + } + + /** + * Get the values from the results for the current case + * + * @param dataSourceToValueList the map of datasources to their + * CommonAttributeValueLists for the + * current case + * @param attributeType the result type contained in the + * metadata + * @param maximumPercentageThreshold the percentage threshold that a file + * should not be more common than + * @param uniqueCaseDataSourceTuples the number of unique data sources in + * the CR + * @param mimeTypesToFilterOn the mimetypes to include in our results + * + * @return a map of correlation value to CommonAttributeValue for results + * from the current case + * + * @throws EamDbException + */ + private Map getValuesToKeepFromCurrentCase(Map dataSourceToValueList, CorrelationAttributeInstance.Type attributeType, int maximumPercentageThreshold, Double uniqueCaseDataSourceTuples, Set mimeTypesToFilterOn) throws EamDbException { + Map valuesToKeep = new HashMap<>(); + Set valuesToRemove = new HashSet<>(); + for (Entry mapOfValueLists : Collections.unmodifiableMap(dataSourceToValueList).entrySet()) { + for (CommonAttributeValue value : mapOfValueLists.getValue().getDelayedMetadataList()) { + if (valuesToRemove.contains(value.getValue())) { + //do nothing this value will not be added + } else if (filterValue(attributeType, value, maximumPercentageThreshold, uniqueCaseDataSourceTuples, mimeTypesToFilterOn)) { + valuesToRemove.add(value.getValue()); + } else { + valuesToKeep.put(value.getValue(), value); + } + } + } + return valuesToKeep; + } + + /** + * Create a new map representing the portion of the tree for a single case + * + * @param valuesToKeepCurrentCase a map of correlation value to + * CommonAttributeValue for results from the + * current case to substitute in + * @param dataSourceToValueList the reslts for a single case which need to + * be filtered + * + * @return the modified results for the case + * + * @throws EamDbException + */ + private Map createTreeForCase(Map valuesToKeepCurrentCase, Map dataSourceToValueList) throws EamDbException { + Map treeForCase = new HashMap<>(); + for (Entry mapOfValueLists : Collections.unmodifiableMap(dataSourceToValueList).entrySet()) { + for (CommonAttributeValue value : mapOfValueLists.getValue().getDelayedMetadataList()) { + if (valuesToKeepCurrentCase.containsKey(value.getValue())) { + if (!treeForCase.containsKey(mapOfValueLists.getKey())) { + treeForCase.put(mapOfValueLists.getKey(), new CommonAttributeValueList()); + } + treeForCase.get(mapOfValueLists.getKey()).addMetadataToList(valuesToKeepCurrentCase.get(value.getValue())); + } + } + } + return treeForCase; + } + + /** + * Determine if a value should be included in the results displayed to the + * user + * + * @param attributeType the result type contained in the + * metadata + * @param value the correlationAttributeValue we are + * evaluating + * @param maximumPercentageThreshold the percentage threshold that a file + * should not be more common than + * @param uniqueCaseDataSourceTuples the number of unique data sources in + * the CR + * @param mimeTypesToInclude the mimetypes to include in our results + * + * @return true if the value should be filtered and removed from what is + * shown to the user, false if the value should not be removed and + * the user will see it as a result + * + * @throws EamDbException + */ + private boolean filterValue(CorrelationAttributeInstance.Type attributeType, CommonAttributeValue value, int maximumPercentageThreshold, Double uniqueCaseDataSourceTuples, Set mimeTypesToInclude) throws EamDbException { + //Intracase common attribute searches will have been created with an empty mimeTypesToInclude list + //because when performing intra case search this filtering will have been done during the query of the case database + if (!mimeTypesToInclude.isEmpty()) { //only do the mime type filtering when mime types aren't empty + for (AbstractCommonAttributeInstance commonAttr : value.getInstances()) { + AbstractFile abstractFile = commonAttr.getAbstractFile(); + if (abstractFile != null) { + String mimeType = abstractFile.getMIMEType(); + if (mimeType != null && !mimeTypesToInclude.contains(mimeType)) { + return true; + } + } + } + } + if (maximumPercentageThreshold != 0) { //only do the frequency filtering when a max % was set + try { + Double uniqueTypeValueTuples = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue( + attributeType, value.getValue()).doubleValue(); + Double commonalityPercentage = uniqueTypeValueTuples / uniqueCaseDataSourceTuples * 100; + int frequencyPercentage = commonalityPercentage.intValue(); + if (frequencyPercentage > maximumPercentageThreshold) { + return true; + } + } catch (CorrelationAttributeNormalizationException ex) { + LOGGER.log(Level.WARNING, "Unable to determine frequency percentage attribute - frequency filter may not be accurate for these results.", ex); + } + } + return false; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeCountSearchResults.java similarity index 92% rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeCountSearchResults.java index d1d13da176..17e029c400 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeCountSearchResults.java @@ -39,9 +39,9 @@ import org.sleuthkit.datamodel.AbstractFile; * Stores the results from the various types of common attribute searching * Stores results based on how they are currently displayed in the UI */ -final public class CommonAttributeSearchResults { +final public class CommonAttributeCountSearchResults { - private static final Logger LOGGER = Logger.getLogger(CommonAttributeSearchResults.class.getName()); + private static final Logger LOGGER = Logger.getLogger(CommonAttributeCountSearchResults.class.getName()); // maps instance count to list of attribute values. private final Map instanceCountToAttributeValues; @@ -61,7 +61,7 @@ final public class CommonAttributeSearchResults { * @param mimeTypesToFilterOn Set of mime types to include for intercase * searches */ - CommonAttributeSearchResults(Map metadata, int percentageThreshold, CorrelationAttributeInstance.Type resultType, Set mimeTypesToFilterOn) { + CommonAttributeCountSearchResults(Map metadata, int percentageThreshold, CorrelationAttributeInstance.Type resultType, Set mimeTypesToFilterOn) { //wrap in a new object in case any client code has used an unmodifiable collection this.instanceCountToAttributeValues = new HashMap<>(metadata); this.percentageThreshold = percentageThreshold; @@ -77,7 +77,7 @@ final public class CommonAttributeSearchResults { * @param percentageThreshold threshold to filter out files which are too * common, value of 0 is disabled */ - CommonAttributeSearchResults(Map metadata, int percentageThreshold) { + CommonAttributeCountSearchResults(Map metadata, int percentageThreshold) { //wrap in a new object in case any client code has used an unmodifiable collection this.instanceCountToAttributeValues = new HashMap<>(metadata); this.percentageThreshold = percentageThreshold; @@ -106,12 +106,17 @@ final public class CommonAttributeSearchResults { * * @return map of sizes of children to list of matches */ - public Map getMetadata() throws EamDbException { - if (this.percentageThreshold == 0 && mimeTypesToInclude.isEmpty()) { - return Collections.unmodifiableMap(this.instanceCountToAttributeValues); - } else { - return this.getMetadata(this.percentageThreshold); - } + public Map getMetadata() { + return Collections.unmodifiableMap(this.instanceCountToAttributeValues); + } + + /** + * Filter the results based on the criteria the user specified + * + * @throws EamDbException + */ + public void filterMetadata() throws EamDbException { + filterMetadata(this.percentageThreshold); } /** @@ -124,7 +129,7 @@ final public class CommonAttributeSearchResults { * * @return metadata */ - private Map getMetadata(int maximumPercentageThreshold) throws EamDbException { + private void filterMetadata(int maximumPercentageThreshold) throws EamDbException { CorrelationAttributeInstance.Type attributeType = CorrelationAttributeInstance .getDefaultCorrelationTypes() .stream() @@ -164,7 +169,7 @@ final public class CommonAttributeSearchResults { //value will be removed as the mime type existed and was not in the set to be included //because value is removed this value does not need to be checked further mimeTypeToRemove = true; - break; + break; } } if (mimeTypeToRemove) { @@ -193,7 +198,6 @@ final public class CommonAttributeSearchResults { } } } - for (Entry> valuesToRemove : itemsToRemove.entrySet()) { final Integer key = valuesToRemove.getKey(); final List values = valuesToRemove.getValue(); @@ -207,8 +211,6 @@ final public class CommonAttributeSearchResults { } } } - - return Collections.unmodifiableMap(this.instanceCountToAttributeValues); } /** diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index 9274e9ba29..04df198c0f 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -4,6 +4,8 @@ + + @@ -27,7 +29,7 @@ - + @@ -37,6 +39,9 @@ + + + @@ -94,6 +99,22 @@ + + + + + + + + + + + + + + + + @@ -115,9 +136,15 @@ - + + + + + + + - + @@ -187,7 +214,7 @@ - + @@ -256,6 +283,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index bec93f8778..0bad76d40d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -81,7 +81,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer private final UserInputErrorManager errorManager; private int percentageThresholdValue = 20; - + private final IntraCasePanel intraCasePanel; private final InterCasePanel interCasePanel; @@ -97,17 +97,17 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer super(WindowManager.getDefault().getMainWindow(), Bundle.CommonAttributePanel_frame_title(), true); initComponents(); this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - + interCasePanel = new InterCasePanel(); interCasePanel.setVisible(true); interCasePanel.setSize((int) containerPanel.getPreferredSize().getWidth(), (int) containerPanel.getPreferredSize().getHeight()); - + intraCasePanel = new IntraCasePanel(); intraCasePanel.setVisible(true); intraCasePanel.setSize((int) containerPanel.getPreferredSize().getWidth(), (int) containerPanel.getPreferredSize().getHeight()); - + this.setupDataSources(); - + if (CommonAttributePanel.isEamDbAvailableForIntercaseSearch()) { this.setupCases(); this.interCasePanel.setupCorrelationTypeFilter(); @@ -220,14 +220,14 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer "CommonAttributePanel.search.done.interupted=Something went wrong finding common properties.", "CommonAttributePanel.search.done.sqlException=Unable to query db for properties or data sources.", "CommonAttributePanel.search.done.noResults=No results found."}) - private void search() { - new SwingWorker() { + private void searchByCount() { + new SwingWorker() { private String tabTitle; private ProgressHandle progress; @Override - protected CommonAttributeSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + protected CommonAttributeCountSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { progress = ProgressHandle.createHandle(Bundle.CommonAttributePanel_search_done_searchProgressGathering()); progress.start(); progress.switchToIndeterminate(); @@ -236,7 +236,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer Integer caseId = interCasePanel.getSelectedCaseId(); AbstractCommonAttributeSearcher builder; - CommonAttributeSearchResults metadata; + CommonAttributeCountSearchResults metadata; boolean filterByMedia = false; boolean filterByDocuments = false; @@ -276,7 +276,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer } } - metadata = builder.findMatches(); + metadata = builder.findMatchesByCount(); this.tabTitle = builder.getTabTitle(); return metadata; } @@ -285,13 +285,8 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer protected void done() { try { super.done(); - CommonAttributeSearchResults metadata = this.get(); - boolean noKeysExist = true; - try { - noKeysExist = metadata.getMetadata().keySet().isEmpty(); - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Unable to get keys from metadata", ex); - } + CommonAttributeCountSearchResults metadata = this.get(); + boolean noKeysExist = metadata.getMetadata().keySet().isEmpty(); if (noKeysExist) { Node commonFilesNode = new TableFilterNode(new EmptyNode(Bundle.CommonAttributePanel_search_done_noResults()), true); progress.setDisplayName(Bundle.CommonAttributePanel_search_done_searchProgressDisplay()); @@ -335,6 +330,108 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer }.execute(); } + /** + * Perform the common attribute search. + */ + private void searchByCase() { + new SwingWorker() { + private String tabTitle; + private ProgressHandle progress; + + @Override + protected CommonAttributeCaseSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + progress = ProgressHandle.createHandle(Bundle.CommonAttributePanel_search_done_searchProgressGathering()); + progress.start(); + progress.switchToIndeterminate(); + Long dataSourceId = intraCasePanel.getSelectedDataSourceId(); + Integer caseId = interCasePanel.getSelectedCaseId(); + AbstractCommonAttributeSearcher builder; + CommonAttributeCaseSearchResults metadata; + boolean filterByMedia = false; + boolean filterByDocuments = false; + int percentageThreshold = CommonAttributePanel.this.percentageThresholdValue; + if (!CommonAttributePanel.this.percentageThresholdCheck.isSelected()) { + //0 has the effect of disabling the feature + percentageThreshold = 0; + } + if (CommonAttributePanel.this.interCaseRadio.isSelected()) { + CorrelationAttributeInstance.Type corType = interCasePanel.getSelectedCorrelationType(); + if (interCasePanel.fileCategoriesButtonIsSelected()) { + filterByMedia = interCasePanel.pictureVideoCheckboxIsSelected(); + filterByDocuments = interCasePanel.documentsCheckboxIsSelected(); + } + if (corType == null) { + corType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0); + } + if (caseId == InterCasePanel.NO_CASE_SELECTED) { + builder = new AllInterCaseCommonAttributeSearcher(filterByMedia, filterByDocuments, corType, percentageThreshold); + } else { + builder = new SingleInterCaseCommonAttributeSearcher(caseId, filterByMedia, filterByDocuments, corType, percentageThreshold); + } + } else { + if (intraCasePanel.fileCategoriesButtonIsSelected()) { + filterByMedia = intraCasePanel.pictureVideoCheckboxIsSelected(); + filterByDocuments = intraCasePanel.documentsCheckboxIsSelected(); + } + if (Objects.equals(dataSourceId, CommonAttributePanel.NO_DATA_SOURCE_SELECTED)) { + builder = new AllIntraCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); + } else { + builder = new SingleIntraCaseCommonAttributeSearcher(dataSourceId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); + } + } + metadata = builder.findMatchesByCase(); + this.tabTitle = builder.getTabTitle(); + return metadata; + } + + @Override + protected void done() { + try { + super.done(); + CommonAttributeCaseSearchResults metadata = this.get(); + if (metadata.getMetadata().keySet().isEmpty()) { + Node commonFilesNode = new TableFilterNode(new EmptyNode(Bundle.CommonAttributePanel_search_done_noResults()), true); + progress.setDisplayName(Bundle.CommonAttributePanel_search_done_searchProgressDisplay()); + DataResultTopComponent.createInstance(tabTitle, Bundle.CommonAttributePanel_search_results_pathText(), commonFilesNode, 1); + } else { + // -3969 + Node commonFilesNode = new CommonAttributeSearchResultRootNode(metadata); + DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonAttributePanel.this)); + TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode, 3); + DataResultViewerTable table = new CommonAttributesSearchResultsViewerTable(); + Collection viewers = new ArrayList<>(1); + viewers.add(table); + progress.setDisplayName(Bundle.CommonAttributePanel_search_done_searchProgressDisplay()); + //0 passed as arguement due to JIRA-4502 ensuring the value is never displayed JIRA-TODO + DataResultTopComponent.createInstance(tabTitle, Bundle.CommonAttributePanel_search_results_pathText(), tableFilterWithDescendantsNode, 0, viewers); + } + } catch (InterruptedException ex) { + LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files", ex); + MessageNotifyUtil.Message.error(Bundle.CommonAttributePanel_search_done_interupted()); + } catch (ExecutionException ex) { + String errorMessage; + Throwable inner = ex.getCause(); + if (inner instanceof TskCoreException) { + LOGGER.log(Level.SEVERE, "Failed to load files from database.", ex); + errorMessage = Bundle.CommonAttributePanel_search_done_tskCoreException(); + } else if (inner instanceof NoCurrentCaseException) { + LOGGER.log(Level.SEVERE, "Current case has been closed.", ex); + errorMessage = Bundle.CommonAttributePanel_search_done_noCurrentCaseException(); + } else if (inner instanceof SQLException) { + LOGGER.log(Level.SEVERE, "Unable to query db for files.", ex); + errorMessage = Bundle.CommonAttributePanel_search_done_sqlException(); + } else { + LOGGER.log(Level.SEVERE, "Unexpected exception while running Common Files Search.", ex); + errorMessage = Bundle.CommonAttributePanel_search_done_exception(); + } + MessageNotifyUtil.Message.error(errorMessage); + } finally { + progress.finish(); + } + } + }.execute(); + } + /** * Sets up the data sources dropdown and returns the data sources map for * future usage. @@ -424,15 +521,18 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer } }.execute(); } - + /** * Display the provided panel inside the container panel. - * + * * @param panel The panel to be shown. */ private void switchInnerPanel(JPanel panel) { containerPanel.removeAll(); containerPanel.add(panel); + caseResultsRadioButton.setVisible(this.interCaseRadio.isSelected()); + countResultsRadioButton.setVisible(this.interCaseRadio.isSelected()); + displayResultsLabel.setVisible(this.interCaseRadio.isSelected()); this.revalidate(); this.repaint(); } @@ -519,6 +619,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer private void initComponents() { interIntraButtonGroup = new javax.swing.ButtonGroup(); + displayResultsButtonGroup = new javax.swing.ButtonGroup(); jPanel1 = new javax.swing.JPanel(); commonItemSearchDescription = new javax.swing.JLabel(); scopeLabel = new javax.swing.JLabel(); @@ -531,6 +632,9 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer dataSourcesLabel = new javax.swing.JLabel(); errorText = new javax.swing.JLabel(); searchButton = new javax.swing.JButton(); + caseResultsRadioButton = new javax.swing.JRadioButton(); + countResultsRadioButton = new javax.swing.JRadioButton(); + displayResultsLabel = new javax.swing.JLabel(); setMinimumSize(new java.awt.Dimension(450, 570)); setResizable(false); @@ -541,6 +645,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer }); jPanel1.setMaximumSize(null); + jPanel1.setPreferredSize(new java.awt.Dimension(450, 646)); jPanel1.setRequestFocusEnabled(false); org.openide.awt.Mnemonics.setLocalizedText(commonItemSearchDescription, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonItemSearchDescription.text")); // NOI18N @@ -577,7 +682,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer ); containerPanelLayout.setVerticalGroup( containerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 330, Short.MAX_VALUE) + .addGap(0, 326, Short.MAX_VALUE) ); org.openide.awt.Mnemonics.setLocalizedText(percentageThresholdCheck, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdCheck.text_1_1")); // NOI18N @@ -610,6 +715,20 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer } }); + displayResultsButtonGroup.add(caseResultsRadioButton); + caseResultsRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(caseResultsRadioButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.caseResultsRadioButton.text")); // NOI18N + caseResultsRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + caseResultsRadioButtonActionPerformed(evt); + } + }); + + displayResultsButtonGroup.add(countResultsRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(countResultsRadioButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.countResultsRadioButton.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(displayResultsLabel, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.displayResultsLabel.text_2")); // NOI18N + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( @@ -648,6 +767,17 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer .addGap(20, 20, 20) .addComponent(interCaseRadio, javax.swing.GroupLayout.PREFERRED_SIZE, 383, javax.swing.GroupLayout.PREFERRED_SIZE)))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(displayResultsLabel)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(30, 30, 30) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(caseResultsRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 410, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(countResultsRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addGap(10, 10, 10)) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -667,9 +797,15 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer .addComponent(percentageThresholdCheck) .addComponent(percentageThresholdInputBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(percentageThresholdTextTwo)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(displayResultsLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(caseResultsRadioButton) + .addGap(0, 0, 0) + .addComponent(countResultsRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(dataSourcesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(searchButton) .addComponent(errorText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) @@ -706,6 +842,10 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer this.dispose(); }//GEN-LAST:event_searchButtonActionPerformed + private void caseResultsRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_caseResultsRadioButtonActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_caseResultsRadioButtonActionPerformed + /** * If the settings reflect that a inter-case search is being performed, * checks that the data sources in the current case have been processed with @@ -789,7 +929,11 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer performSearch = DialogDisplayer.getDefault().notify(descriptor) == NotifyDescriptor.YES_OPTION; } if (performSearch) { - search(); + if (interCaseRadio.isSelected() && caseResultsRadioButton.isSelected()) { + searchByCase(); + } else { + searchByCount(); + } } } catch (InterruptedException | ExecutionException ex) { LOGGER.log(Level.SEVERE, "Unexpected exception while looking for common properties", ex); //NON-NLS @@ -868,9 +1012,13 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer } // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JRadioButton caseResultsRadioButton; private javax.swing.JLabel commonItemSearchDescription; private javax.swing.JPanel containerPanel; + private javax.swing.JRadioButton countResultsRadioButton; private javax.swing.JLabel dataSourcesLabel; + private javax.swing.ButtonGroup displayResultsButtonGroup; + private javax.swing.JLabel displayResultsLabel; private javax.swing.JLabel errorText; private javax.swing.JRadioButton interCaseRadio; private javax.swing.ButtonGroup interIntraButtonGroup; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java index 3aede081f5..f24f249255 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java @@ -19,28 +19,30 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.util.List; -import java.util.logging.Level; +import java.util.Map; import java.util.logging.Logger; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; /** - * Top-level node to store common file search results. Current structure is: - * - node for number of matches - * -- node for MD5/commmon attribute - * --- node for instance. + * Top-level node to store common file search results. Current structure is: - + * node for number of matches -- node for MD5/commmon attribute --- node for + * instance. */ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNode { - CommonAttributeSearchResultRootNode(CommonAttributeSearchResults metadataList) { + CommonAttributeSearchResultRootNode(CommonAttributeCountSearchResults metadataList) { super(Children.create(new InstanceCountNodeFactory(metadataList), true)); } + CommonAttributeSearchResultRootNode(CommonAttributeCaseSearchResults metadataList) { + super(Children.create(new InstanceCaseNodeFactory(metadataList), true)); + } + @NbBundle.Messages({ "CommonFilesNode.getName.text=Common Files"}) @Override @@ -62,39 +64,70 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo public String getItemType() { return getClass().getName(); } - + /** * Used to generate InstanceCountNodes. */ - static class InstanceCountNodeFactory extends ChildFactory{ + static class InstanceCountNodeFactory extends ChildFactory { private static final Logger LOGGER = Logger.getLogger(InstanceCountNodeFactory.class.getName()); - - private final CommonAttributeSearchResults searchResults; - + + private final CommonAttributeCountSearchResults searchResults; + /** - * Build a factory which converts a CommonAttributeSearchResults - * object into DisplayableItemNodes. - * @param searchResults + * Build a factory which converts a + * CommonAttributeCountSearchResults object into + * DisplayableItemNodes. + * + * @param searchResults */ - InstanceCountNodeFactory(CommonAttributeSearchResults searchResults){ + InstanceCountNodeFactory(CommonAttributeCountSearchResults searchResults) { this.searchResults = searchResults; } @Override protected boolean createKeys(List list) { - try { - list.addAll(this.searchResults.getMetadata().keySet()); - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Unable to create keys.", ex); - } + list.addAll(this.searchResults.getMetadata().keySet()); return true; } - + @Override - protected Node createNodeForKey(Integer instanceCount){ - CommonAttributeValueList attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount); + protected Node createNodeForKey(Integer instanceCount) { + CommonAttributeValueList attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount); return new InstanceCountNode(instanceCount, attributeValues); } } + + /** + * Used to generate InstanceCaseNodes. + */ + static class InstanceCaseNodeFactory extends ChildFactory { + + private static final Logger LOGGER = Logger.getLogger(InstanceCaseNodeFactory.class.getName()); + + private final CommonAttributeCaseSearchResults searchResults; + + /** + * Build a factory which converts a + * CommonAttributeCaseSearchResults object into + * DisplayableItemNodes. + * + * @param searchResults + */ + InstanceCaseNodeFactory(CommonAttributeCaseSearchResults searchResults) { + this.searchResults = searchResults; + } + + @Override + protected boolean createKeys(List list) { + list.addAll(this.searchResults.getMetadata().keySet()); + return true; + } + + @Override + protected Node createNodeForKey(String caseName) { + Map dataSourceNameToInstances = this.searchResults.getAttributeValuesForCaseName(caseName); + return new InstanceCaseNode(caseName, dataSourceNameToInstances); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java index 967a205a73..254ff26971 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java @@ -1,16 +1,16 @@ /* - * + * * Autopsy Forensic Browser - * + * * Copyright 2018 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. @@ -28,33 +28,27 @@ import java.util.Set; import java.util.stream.Collectors; /** - * Defines a value that was in the common file search results - * as well as information about its instances. + * Defines a value that was in the common file search results as well as + * information about its instances. */ final public class CommonAttributeValue { - private final String md5; + private final String value; private final List fileInstances; - CommonAttributeValue(String md5, List fileInstances) { - this.md5 = md5; - this.fileInstances = fileInstances; - - } - - CommonAttributeValue(String md5) { - this.md5 = md5; + CommonAttributeValue(String value) { + this.value = value; this.fileInstances = new ArrayList<>(); } public String getValue() { - return this.md5; + return this.value; } /** * concatenate cases this value was seen into a single string - * - * @return + * + * @return */ public String getCases() { return this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", ")); @@ -65,7 +59,7 @@ final public class CommonAttributeValue { for (AbstractCommonAttributeInstance data : this.fileInstances) { sources.add(data.getDataSource()); } - + return String.join(", ", sources); } @@ -78,7 +72,7 @@ final public class CommonAttributeValue { } /** - * How many distinct file instances exist for the MD5 represented by this + * How many distinct file instances exist for the value represented by this * object? * * @return number of instances diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java index 219072fa2a..03011ead6d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java @@ -1,16 +1,16 @@ /* - * + * * Autopsy Forensic Browser - * + * * Copyright 2018 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. @@ -25,13 +25,14 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.NodeProperty; /** - * Represents the layer in the tree for the value (such as MD5) that was in multiple places. - * Children are instances of that value. + * Represents the layer in the tree for the value (such as MD5) that was in + * multiple places. Children are instances of that value. */ public class CommonAttributeValueNode extends DisplayableItemNode { @@ -45,36 +46,37 @@ public class CommonAttributeValueNode extends DisplayableItemNode { }) /** * Create a Match node whose children will all have this object in common. + * * @param data the common feature, and the children */ public CommonAttributeValueNode(CommonAttributeValue data) { super(Children.create( new FileInstanceNodeFactory(data), true)); - this.commonFileCount = data.getInstanceCount(); this.cases = data.getCases(); // @@ We seem to be doing this string concat twice. We also do it in getDataSources() this.dataSources = String.join(", ", data.getDataSources()); this.value = data.getValue(); - this.setDisplayName(String.format(Bundle.CommonAttributeValueNode_CommonAttributeValueNode_format(), this.value)); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS } /** - * How many files are in common? This will be the number of children. + * How many files are in common? This will be the number of children. + * * @return int */ int getCommonFileCount() { return this.commonFileCount; } - - String getCases(){ + + String getCases() { return this.cases; } /** * Datasources where these matches occur. + * * @return string delimited list of sources */ String getDataSources() { @@ -82,14 +84,17 @@ public class CommonAttributeValueNode extends DisplayableItemNode { } /** - * MD5 which is common to these matches - * @return string md5 hash + * Value which is common to these matches + * + * @return string the the value which is correlated on */ public String getValue() { return this.value; } - @NbBundle.Messages({"Md5Node.createSheet.noDescription= "}) + @NbBundle.Messages({ + "ValueNode.createSheet.noDescription= " + }) @Override protected Sheet createSheet() { Sheet sheet = new Sheet(); @@ -99,16 +104,14 @@ public class CommonAttributeValueNode extends DisplayableItemNode { sheet.put(sheetSet); } - final String NO_DESCR = Bundle.Md5Node_createSheet_noDescription(); - sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, "")); + final String NO_DESCR = Bundle.ValueNode_createSheet_noDescription(); + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), NO_DESCR, "")); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, "")); - sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, "")); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSources())); return sheet; } - @Override public T accept(DisplayableItemNodeVisitor visitor) { return visitor.visit(this); @@ -141,12 +144,11 @@ public class CommonAttributeValueNode extends DisplayableItemNode { list.addAll(this.descendants.getInstances()); return true; } - + @Override protected Node[] createNodesForKey(AbstractCommonAttributeInstance searchResult) { return searchResult.generateNodes(); } - } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java index 92d4b1a2d9..5fb0906ac7 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java @@ -29,6 +29,7 @@ import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; +import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; /** * DataResultViewerTable which overrides the default column header @@ -48,14 +49,17 @@ public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTa static { Map map = new HashMap<>(); - map.put(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), 260); + map.put(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), 260); + map.put(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"), 20); + map.put(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"), 20); + map.put(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"), 20); map.put(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), 65); map.put(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), 300); - map.put(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), 200); + map.put(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl(), 200); + map.put(Bundle.CommonFilesSearchResultsViewerTable_localPath(), 200); + map.put(Bundle.CommonFilesSearchResultsViewerTable_valueColLbl(), 200); map.put(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), 200); - map.put(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), 100); - map.put(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), 130); - map.put(Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), 300); + map.put(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), 130); COLUMN_WIDTHS = Collections.unmodifiableMap(map); } @@ -75,14 +79,12 @@ public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTa @NbBundle.Messages({ "CommonFilesSearchResultsViewerTable.noDescText= ", - "CommonFilesSearchResultsViewerTable.filesColLbl=Files", "CommonFilesSearchResultsViewerTable.instancesColLbl=Instances", + "CommonFilesSearchResultsViewerTable.localPath=Parent Path in Current Case", "CommonFilesSearchResultsViewerTable.pathColLbl=Parent Path", - "CommonFilesSearchResultsViewerTable.hashsetHitsColLbl=Hash Set Hits", - "CommonFilesSearchResultsViewerTable.caseColLbl1=Case", + "CommonFilesSearchResultsViewerTable.caseColLbl=Case", + "CommonFilesSearchResultsViewerTable.valueColLbl=Value", "CommonFilesSearchResultsViewerTable.dataSourceColLbl=Data Source", - "CommonFilesSearchResultsViewerTable.mimeTypeColLbl=MIME Type", - "CommonFilesSearchResultsViewerTable.tagsColLbl1=Tags" }) @Override protected void setColumnWidths() { @@ -99,7 +101,7 @@ public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTa if (defaultWidth == null) { column.setPreferredWidth(DEFAULT_WIDTH); - LOGGER.log(Level.SEVERE, String.format("Tried to set width on a column not supported by the CommonFilesSearchResultsViewerTable: %s", headerValue)); + LOGGER.log(Level.WARNING, String.format("Tried to set width on a column not supported by the CommonAttributesSearchResultsViewerTable: %s", headerValue)); } else { column.setPreferredWidth(defaultWidth); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCaseNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCaseNode.java new file mode 100644 index 0000000000..92429437c1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCaseNode.java @@ -0,0 +1,149 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 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.commonfilesearch; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.openide.nodes.ChildFactory; +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.DisplayableItemNode; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; +import org.sleuthkit.autopsy.datamodel.NodeProperty; + +/** + * Node used to group results by case. + */ +public final class InstanceCaseNode extends DisplayableItemNode { + + private static final Logger logger = Logger.getLogger(InstanceCaseNode.class.getName()); + + final private String caseName; + final private Map dataSourceToValueList; + + /** + * Create a node with all instances for the given case, and the given + * selection of metadata. + * + * @param caseName the name of the case + * @param attributeValues the map of data sources to + * CommonAttributeValueLists to be included + */ + public InstanceCaseNode(String caseName, Map attributeValues) { + super(Children.create(new CommonAttributeDataSourceNodeFactory(attributeValues), true)); + this.caseName = caseName; + this.dataSourceToValueList = attributeValues; + this.setDisplayName(this.caseName); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/briefcase.png"); //NON-NLS + } + + /** + * Name of the case all child nodes are contained in. + * + * @return String case name + */ + String getCaseName() { + return this.caseName; + } + + /** + * Creates the Children of this node. By doing this here instead of in the + * constructor, lazy creation of the Children is made possible. + */ + void createChildren() { + setChildren(Children.create(new CommonAttributeDataSourceNodeFactory(dataSourceToValueList), true)); + } + + /** + * Get a list of metadata for the datasources which are children of this + * object. + * + * @return List + */ + Map getDataSourceToValueList() { + return this.dataSourceToValueList; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public String getItemType() { + return getClass().getName(); + } + + @Override + protected Sheet createSheet() { + Sheet sheet = new Sheet(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + if (sheetSet == null) { + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + final String NO_DESCR = Bundle.InstanceCountNode_createSheet_noDescription(); + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_localPath(), Bundle.CommonFilesSearchResultsViewerTable_localPath(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_valueColLbl(), Bundle.CommonFilesSearchResultsViewerTable_valueColLbl(), NO_DESCR, "")); + return sheet; + } + + /** + * ChildFactory which builds InstanceDataSourceNode from the metadata data + * sources. + */ + static class CommonAttributeDataSourceNodeFactory extends ChildFactory { + + /** + * Map of data sources, each of which is a parent node matching a case + * name, containing children FileNodes. + */ + private final Map metadata; + + CommonAttributeDataSourceNodeFactory(Map attributeValues) { + this.metadata = new HashMap<>(); + this.metadata.putAll(attributeValues); + } + + @Override + protected boolean createKeys(List list) { + list.addAll(this.metadata.keySet()); + return true; + } + + @Override + protected Node createNodeForKey(String dataSourceName) { + return new InstanceDataSourceNode(dataSourceName, this.metadata.get(dataSourceName)); + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java index 93de3267d0..4b555bd40b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java @@ -28,7 +28,9 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.NodeProperty; @@ -117,14 +119,17 @@ public final class InstanceCountNode extends DisplayableItemNode { } final String NO_DESCR = Bundle.InstanceCountNode_createSheet_noDescription(); - sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"), NO_DESCR, "")); + if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) { + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"), NO_DESCR, "")); + } sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), NO_DESCR, this.getInstanceCount())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, "")); - sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl(), NO_DESCR, "")); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, "")); - sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, "")); - sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, "")); - sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), NO_DESCR, "")); return sheet; } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNodeTreeExpansionListener.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNodeTreeExpansionListener.java index 3de4d44b7f..f016cc3c8f 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNodeTreeExpansionListener.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNodeTreeExpansionListener.java @@ -47,6 +47,10 @@ final class InstanceCountNodeTreeExpansionListener implements TreeExpansionListe if (instanceCountNode != null) { instanceCountNode.createChildren(); } + final InstanceDataSourceNode instanceDataSourceNode = dataResultFilterNode.getLookup().lookup(InstanceDataSourceNode.class); + if (instanceDataSourceNode != null) { + instanceDataSourceNode.createChildren(); + } } } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceDataSourceNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceDataSourceNode.java new file mode 100644 index 0000000000..4d2a3fda33 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceDataSourceNode.java @@ -0,0 +1,138 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 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.commonfilesearch; + +import java.util.List; +import org.openide.nodes.ChildFactory; +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.DisplayableItemNode; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; + +import org.sleuthkit.autopsy.datamodel.NodeProperty; + +/** + * Node used to group results by data source. + */ +public final class InstanceDataSourceNode extends DisplayableItemNode { + + private static final Logger logger = Logger.getLogger(InstanceDataSourceNode.class.getName()); + + final private String dataSourceName; + final private CommonAttributeValueList dataSourceToValueList; + + /** + * Create a node with all instances for the given data source, and the given + * selection of metadata. + * + * @param dataSourceName the name of the dataSource + * @param attributeValues the commonAttributeValueList containing the + * results + */ + public InstanceDataSourceNode(String dataSourceName, CommonAttributeValueList attributeValues) { + super(Children.create(new FileInstanceNodeFactory(attributeValues), true)); + + this.dataSourceName = dataSourceName; + this.dataSourceToValueList = attributeValues; + + this.setDisplayName(this.dataSourceName); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png"); //NON-NLS + } + + /** + * Get the name of the data source + * + * @return String data source name + */ + String getDatasourceName() { + return this.dataSourceName; + } + + /** + * Creates the Children of this node. By doing this here instead of in the + * constructor, lazy creation of the Children is made possible. + */ + void createChildren() { + dataSourceToValueList.displayDelayedMetadata(); + setChildren(Children.create(new FileInstanceNodeFactory(dataSourceToValueList), true)); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public String getItemType() { + return getClass().getName(); + } + + @Override + protected Sheet createSheet() { + Sheet sheet = new Sheet(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + if (sheetSet == null) { + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + final String NO_DESCR = Bundle.InstanceCountNode_createSheet_noDescription(); + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_localPath(), Bundle.CommonFilesSearchResultsViewerTable_localPath(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_valueColLbl(), Bundle.CommonFilesSearchResultsViewerTable_valueColLbl(), NO_DESCR, "")); + return sheet; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + /** + * ChildFactory which builds DisplayableItem from the metadata data + * sources. + */ + static class FileInstanceNodeFactory extends ChildFactory { + + private final CommonAttributeValueList descendants; + + FileInstanceNodeFactory(CommonAttributeValueList descendants) { + this.descendants = descendants; + } + + @Override + protected boolean createKeys(List list) { + for (CommonAttributeValue value : descendants.getDelayedMetadataList()) { + list.addAll(value.getInstances()); + } + return true; + } + + @Override + protected Node[] createNodesForKey(AbstractCommonAttributeInstance searchResult) { + return searchResult.generateNodes(); + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java index 56e28da3dc..c36e9da084 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java @@ -19,10 +19,10 @@ */ package org.sleuthkit.autopsy.commonfilesearch; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; /** * Provides logic for selecting common files from all data sources and all cases diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java index 1a71e50bc9..173a472f1f 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java @@ -34,12 +34,13 @@ 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.commonfilesearch.AbstractCommonAttributeInstance.NODE_TYPE; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.HashUtility; /** - * Used to process and return CorrelationCase md5s from the EamDB for + * Used to process and return CorrelationCase values from the EamDB for * CommonFilesSearch. */ final class InterCaseSearchResultsProcessor { @@ -109,8 +110,8 @@ final class InterCaseSearchResultsProcessor { try { InterCaseCommonAttributeRowCallback instancetableCallback = new InterCaseCommonAttributeRowCallback(); - EamDb DbManager = EamDb.getInstance(); - DbManager.processInstanceTableWhere(correlationType, String.format("id = %s", attrbuteId), instancetableCallback); + EamDb dbManager = EamDb.getInstance(); + dbManager.processInstanceTableWhere(correlationType, String.format("id = %s", attrbuteId), instancetableCallback); return instancetableCallback.getCorrelationAttribute(); @@ -121,20 +122,49 @@ final class InterCaseSearchResultsProcessor { return null; } + /** + * Given the current case, fins all intercase common files from the EamDb + * and builds maps of case name to maps of data source name to + * CommonAttributeValueList. + * + * @param currentCase The current TSK Case. + * + * @return map of Case name to Maps of Datasources and their + * CommonAttributeValueLists + */ + Map> findInterCaseValuesByCase(Case currentCase) { + try { + InterCaseByCaseCallback instancetableCallback = new InterCaseByCaseCallback(); + EamDb dbManager = EamDb.getInstance(); + + int caseId = dbManager.getCase(currentCase).getID(); + + dbManager.processInstanceTableWhere(correlationType, String.format(interCaseWhereClause, caseId, + TskData.FileKnown.KNOWN.getFileKnownValue()), + instancetableCallback); + + return instancetableCallback.getInstanceCollatedCommonFiles(); + + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex); + } + return new HashMap<>(); + } + /** * Given the current case, fins all intercase common files from the EamDb * and builds maps of obj id to md5 and case. * * @param currentCase The current TSK Case. */ - Map findInterCaseCommonAttributeValues(Case currentCase) { + Map findInterCaseValuesByCount(Case currentCase) { try { - InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback(); - EamDb DbManager = EamDb.getInstance(); + InterCaseByCountCallback instancetableCallback = new InterCaseByCountCallback(); + EamDb dbManager = EamDb.getInstance(); - int caseId = DbManager.getCase(currentCase).getID(); + int caseId = dbManager.getCase(currentCase).getID(); - DbManager.processInstanceTableWhere(correlationType, String.format(interCaseWhereClause, caseId, + dbManager.processInstanceTableWhere(correlationType, String.format(interCaseWhereClause, caseId, TskData.FileKnown.KNOWN.getFileKnownValue()), instancetableCallback); @@ -154,13 +184,13 @@ final class InterCaseSearchResultsProcessor { * @param currentCase The current TSK Case. * @param singleCase The case of interest. Matches must exist in this case. */ - Map findSingleInterCaseCommonAttributeValues(Case currentCase, CorrelationCase singleCase) { + Map findSingleInterCaseValuesByCount(Case currentCase, CorrelationCase singleCase) { try { - InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback(); - EamDb DbManager = EamDb.getInstance(); - int caseId = DbManager.getCase(currentCase).getID(); + InterCaseByCountCallback instancetableCallback = new InterCaseByCountCallback(); + EamDb dbManager = EamDb.getInstance(); + int caseId = dbManager.getCase(currentCase).getID(); int targetCaseId = singleCase.getID(); - DbManager.processInstanceTableWhere(correlationType, String.format(singleInterCaseWhereClause, caseId, + dbManager.processInstanceTableWhere(correlationType, String.format(singleInterCaseWhereClause, caseId, TskData.FileKnown.KNOWN.getFileKnownValue(), caseId, targetCaseId), instancetableCallback); return instancetableCallback.getInstanceCollatedCommonFiles(); } catch (EamDbException ex) { @@ -170,10 +200,38 @@ final class InterCaseSearchResultsProcessor { } /** - * Callback to use with findInterCaseCommonAttributeValues which generates a - * list of md5s for common files search + * Given the current case, and a specific case of interest, finds common + * files which exist between cases from the EamDb. Builds map of case name + * to maps of data source name to CommonAttributeValueList. + * + * @param currentCase The current TSK Case. + * + * @return map of Case name to Maps of Datasources and their + * CommonAttributeValueLists + * + * @param currentCase The current TSK Case. + * @param singleCase The case of interest. Matches must exist in this case. */ - private class InterCaseCommonAttributesCallback implements InstanceTableCallback { + Map> findSingleInterCaseValuesByCase(Case currentCase, CorrelationCase singleCase) { + try { + InterCaseByCaseCallback instancetableCallback = new InterCaseByCaseCallback(); + EamDb dbManager = EamDb.getInstance(); + int caseId = dbManager.getCase(currentCase).getID(); + int targetCaseId = singleCase.getID(); + dbManager.processInstanceTableWhere(correlationType, String.format(singleInterCaseWhereClause, caseId, + TskData.FileKnown.KNOWN.getFileKnownValue(), caseId, targetCaseId), instancetableCallback); + return instancetableCallback.getInstanceCollatedCommonFiles(); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex); + } + return new HashMap<>(); + } + + /** + * Callback to use with findInterCaseValuesByCount which generates a list of + * md5s for common files search + */ + private class InterCaseByCountCallback implements InstanceTableCallback { final Map instanceCollatedCommonFiles = new HashMap<>(); @@ -242,7 +300,7 @@ final class InterCaseSearchResultsProcessor { // we don't *have* all the information for the rows in the CR, // so we need to consult the present case via the SleuthkitCase object // Later, when the FileInstanceNode is built. Therefore, build node generators for now. - CentralRepoCommonAttributeInstance searchResult = new CentralRepoCommonAttributeInstance(resultId, correlationType); + CentralRepoCommonAttributeInstance searchResult = new CentralRepoCommonAttributeInstance(resultId, correlationType, NODE_TYPE.COUNT_NODE); CorrelationAttributeInstance corrAttr = findSingleCorrelationAttribute(resultId); searchResult.setCurrentAttributeInst(corrAttr); commonAttributeValue.addInstance(searchResult); @@ -253,6 +311,55 @@ final class InterCaseSearchResultsProcessor { } } + /** + * Callback to use with findInterCaseValuesByCount which generates a list of + * md5s for common files search + */ + private class InterCaseByCaseCallback implements InstanceTableCallback { + + final Map> caseCollatedDataSourceCollections = new HashMap<>(); + + @Override + public void process(ResultSet resultSet) { + try { + while (resultSet.next()) { + int resultId = InstanceTableCallback.getId(resultSet); + String corValue = InstanceTableCallback.getValue(resultSet); + if (corValue == null || HashUtility.isNoDataMd5(corValue)) { + continue; + } + CorrelationCase correlationCase = EamDb.getInstance().getCaseById(InstanceTableCallback.getCaseId(resultSet)); + String caseName = correlationCase.getDisplayName(); + CorrelationDataSource correlationDatasource = EamDb.getInstance().getDataSourceById(correlationCase, InstanceTableCallback.getDataSourceId(resultSet)); +// String dataSourceName = correlationDatasource.getName(); + String dataSourceNameKey = correlationDatasource.getName() + correlationDatasource.getDataSourceObjectID(); + if (!caseCollatedDataSourceCollections.containsKey(caseName)) { + caseCollatedDataSourceCollections.put(caseName, new HashMap()); + } + Map dataSourceToFile = caseCollatedDataSourceCollections.get(caseName); + if (!dataSourceToFile.containsKey(dataSourceNameKey)) { + dataSourceToFile.put(dataSourceNameKey, new CommonAttributeValueList()); + } + CommonAttributeValueList valueList = dataSourceToFile.get(dataSourceNameKey); + CentralRepoCommonAttributeInstance searchResult = new CentralRepoCommonAttributeInstance(resultId, correlationType, NODE_TYPE.CASE_NODE); + CorrelationAttributeInstance corrAttr = findSingleCorrelationAttribute(resultId); + searchResult.setCurrentAttributeInst(corrAttr); + CommonAttributeValue commonAttributeValue = new CommonAttributeValue(corValue); + commonAttributeValue.addInstance(searchResult); + valueList.addMetadataToList(commonAttributeValue); + dataSourceToFile.put(dataSourceNameKey, valueList); + caseCollatedDataSourceCollections.put(caseName, dataSourceToFile); + } + } catch (EamDbException | SQLException ex) { + LOGGER.log(Level.WARNING, "Error getting artifact instances from database.", ex); // NON-NLS + } + } + + Map> getInstanceCollatedCommonFiles() { + return Collections.unmodifiableMap(caseCollatedDataSourceCollections); + } + } + /** * Callback to use with findSingleCorrelationAttribute which retrieves a * single CorrelationAttribute from the EamDb. @@ -264,13 +371,13 @@ final class InterCaseSearchResultsProcessor { @Override public void process(ResultSet resultSet) { try { - EamDb DbManager = EamDb.getInstance(); + EamDb dbManager = EamDb.getInstance(); while (resultSet.next()) { - CorrelationCase correlationCase = DbManager.getCaseById(InstanceTableCallback.getCaseId(resultSet)); - CorrelationDataSource dataSource = DbManager.getDataSourceById(correlationCase, InstanceTableCallback.getDataSourceId(resultSet)); + CorrelationCase correlationCase = dbManager.getCaseById(InstanceTableCallback.getCaseId(resultSet)); + CorrelationDataSource dataSource = dbManager.getDataSourceById(correlationCase, InstanceTableCallback.getDataSourceId(resultSet)); try { - correlationAttributeInstance = DbManager.getCorrelationAttributeInstance(correlationType, + correlationAttributeInstance = dbManager.getCorrelationAttributeInstance(correlationType, correlationCase, dataSource, InstanceTableCallback.getValue(resultSet), diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java index cc8990b518..8750965412 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java @@ -28,6 +28,7 @@ import java.util.Set; import java.util.stream.Collectors; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; @@ -36,8 +37,8 @@ import org.sleuthkit.datamodel.TskCoreException; /** * * Generates a List when - * findMatches() is called, which organizes files by md5 to prepare - * to display in viewer. + * findMatchesByCount() is called, which organizes files by md5 to + * prepare to display in viewer. * * This entire thing runs on a background thread where exceptions are handled. */ @@ -99,7 +100,7 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt * @throws SQLException */ @Override - public CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException { + public CommonAttributeCountSearchResults findMatchesByCount() throws TskCoreException, NoCurrentCaseException, SQLException { Map commonFiles = new HashMap<>(); final Case currentCase = Case.getCurrentCaseThrows(); @@ -125,10 +126,10 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt if (commonFiles.containsKey(md5)) { final CommonAttributeValue commonAttributeValue = commonFiles.get(md5); - commonAttributeValue.addInstance(new CaseDBCommonAttributeInstance(objectId, dataSource, caseName)); + commonAttributeValue.addInstance(new CaseDBCommonAttributeInstance(objectId, dataSource, caseName, md5)); } else { final CommonAttributeValue commonAttributeValue = new CommonAttributeValue(md5); - commonAttributeValue.addInstance(new CaseDBCommonAttributeInstance(objectId, dataSource, caseName)); + commonAttributeValue.addInstance(new CaseDBCommonAttributeInstance(objectId, dataSource, caseName, md5)); commonFiles.put(md5, commonAttributeValue); } } @@ -136,7 +137,12 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt Map instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); - return new CommonAttributeSearchResults(instanceCollatedCommonFiles, this.frequencyPercentageThreshold); + return new CommonAttributeCountSearchResults(instanceCollatedCommonFiles, this.frequencyPercentageThreshold); + } + + @Override + public CommonAttributeCaseSearchResults findMatchesByCase() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + throw new EamDbException("Not Supported at the moment"); } /** diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java index ed57fb8d10..5b8910ff60 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java @@ -69,20 +69,38 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri * @throws EamDbException */ @Override - public CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + public CommonAttributeCountSearchResults findMatchesByCount() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { - CorrelationCase cCase = this.getCorrelationCaseFromId(this.corrleationCaseId); - this.correlationCaseName = cCase.getDisplayName(); - return this.findFiles(cCase); - } - - CommonAttributeSearchResults findFiles(CorrelationCase correlationCase) throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + CorrelationCase correlationCase = this.getCorrelationCaseFromId(this.corrleationCaseId); + this.correlationCaseName = correlationCase.getDisplayName(); InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.corAttrType); - Map interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase); + Map interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseValuesByCount(Case.getCurrentCase(), correlationCase); Set mimeTypesToFilterOn = getMimeTypesToFilterOn(); - return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType, mimeTypesToFilterOn); + return new CommonAttributeCountSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType, mimeTypesToFilterOn); } + /** + * Collect metadata required to render the tree table where matches must + * occur in the case with the given ID. + * + * @return business object needed to populate tree table with results + * + * @throws TskCoreException + * @throws NoCurrentCaseException + * @throws SQLException + * @throws EamDbException + */ + @Override + public CommonAttributeCaseSearchResults findMatchesByCase() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + + CorrelationCase correlationCase = this.getCorrelationCaseFromId(this.corrleationCaseId); + this.correlationCaseName = correlationCase.getDisplayName(); + InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.corAttrType); + Map> interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseValuesByCase(Case.getCurrentCase(), correlationCase); + Set mimeTypesToFilterOn = getMimeTypesToFilterOn(); + return new CommonAttributeCaseSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType, mimeTypesToFilterOn); + + } @NbBundle.Messages({ "# {0} - case name", diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java index e10a650974..2be9a9b447 100644 --- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java @@ -75,8 +75,6 @@ public final class UserPreferences { public static final String SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags"; public static final String HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES = "HideCentralRepoCommentsAndOccurrences"; public static final String DISPLAY_TRANSLATED_NAMES = "DisplayTranslatedNames"; - public static final String MAXIMUM_NUMBER_OF_RESULTS = "MaximumNumberOfResults"; - private static final int DEFAULT_MAX_RESULTS = 20000; // Prevent instantiation. private UserPreferences() { @@ -473,20 +471,4 @@ public final class UserPreferences { public static void setLogFileCount(int count) { preferences.putInt(MAX_NUM_OF_LOG_FILE, count); } - - /** - * Set the maximum number of result rows to show in data result tables. - * @param max - */ - public static void setMaximumNumberOfResults(int max) { - preferences.putInt(MAXIMUM_NUMBER_OF_RESULTS, max); - } - - /** - * Get the maximum number of result rows to show in data result tables. - * @return - */ - public static int getMaximumNumberOfResults() { - return preferences.getInt(MAXIMUM_NUMBER_OF_RESULTS, DEFAULT_MAX_RESULTS); - } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 942072e39b..cdc54a12b6 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -176,8 +176,6 @@ ViewPreferencesPanel.globalSettingsPanel.border.title=Global Settings ViewPreferencesPanel.translateNamesInTableRadioButton.text=Table ViewPreferencesPanel.commentsOccurencesColumnWrapAroundText.text=to reduce loading times ViewPreferencesPanel.translateTextLabel.text=Translate text in the: -ViewPreferencesPanel.maximumResultsLabel.toolTipText=Maximum numer of rows to display in result tables. 0 = unlimited -ViewPreferencesPanel.maximumResultsLabel.text=Maximum number of results to display: ViewPreferencesPanel.commentsOccurencesColumnsCheckbox.text=C(omments) and O(ccurences) columns ViewPreferencesPanel.centralRepoLabel.text=Do not use Central Repository for: ViewPreferencesPanel.hideOtherUsersTagsLabel.text=Hide other users' tags in the: diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterChildren.java b/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterChildren.java index 1133101e88..5b70ec4ea7 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterChildren.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterChildren.java @@ -18,27 +18,18 @@ */ package org.sleuthkit.autopsy.corecomponents; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; import org.openide.nodes.Children; import org.openide.nodes.FilterNode; import org.openide.nodes.Node; -import org.openide.util.NbBundle; -import org.openide.windows.WindowManager; -import org.sleuthkit.autopsy.core.UserPreferences; /** - * A Children implementation for a - * TableFilterNode. A - * TableFilterNode creates at most one layer of child - * nodes for the node it wraps. It is designed to be used in the results view - * to ensure the individual viewers display only the first layer of child nodes. + * A Children implementation for a TableFilterNode. A + * TableFilterNode creates at most one layer of child nodes for the + * node it wraps. It is designed to be used in the results view to ensure the + * individual viewers display only the first layer of child nodes. */ class TableFilterChildren extends FilterNode.Children { - private int numberOfNodesCreated; - private static volatile boolean maxResultsDialogShown = false; - /** * Creates a Children object for a TableFilterNode. A TableFilterNode * creates at most one layer of child nodes for the node it wraps. It is @@ -71,7 +62,6 @@ class TableFilterChildren extends FilterNode.Children { */ TableFilterChildren(Node wrappedNode) { super(wrappedNode); - numberOfNodesCreated = 0; } /** @@ -96,41 +86,7 @@ class TableFilterChildren extends FilterNode.Children { * @return */ @Override - @NbBundle.Messages({"# {0} - The results limit", - "TableFilterChildren.createNodes.limitReached.msg=" - + "The limit on the number of results to display has been reached." - + " Only the first {0} results will be shown." - + " The limit can be modified under Tools, Options, View."}) protected Node[] createNodes(Node key) { - int maxNodesToCreate = UserPreferences.getMaximumNumberOfResults(); - - if (maxNodesToCreate == 0 || numberOfNodesCreated < maxNodesToCreate) { - // We either haven't hit the limit yet, or we don't have a limit. - - /** - * We only want to apply the limit to "our" nodes (i.e. not the - * wait node). If we don't do this the "Please wait..." - * node causes the number of results in the table to be off by one. - * Using the Bundle to get the value so that we are not tied to a - * particular locale. - */ - if (!key.getDisplayName().equalsIgnoreCase(NbBundle.getMessage(Node.class, "LBL_WAIT"))) { - numberOfNodesCreated++; - - // If we have a limit and the creation of this node reaches it, - // tell the user if they haven't already been told. - if (numberOfNodesCreated == maxNodesToCreate && !maxResultsDialogShown) { - maxResultsDialogShown = true; - - SwingUtilities.invokeLater(() - -> JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), - Bundle.TableFilterChildren_createNodes_limitReached_msg(maxNodesToCreate)) - ); - } - } - return new Node[]{this.copyNode(key)}; - } else { - return new Node[]{}; - } + return new Node[]{this.copyNode(key)}; } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form index 54aad8faab..8b56df7929 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form @@ -26,7 +26,7 @@ - + @@ -153,11 +153,6 @@ - - - - - @@ -190,11 +185,6 @@ - - - - - @@ -371,16 +361,6 @@ - - - - - - - - - - @@ -427,16 +407,6 @@ - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java index d68a9765b2..4900a48a78 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java @@ -78,7 +78,6 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { commentsOccurencesColumnsCheckbox.setEnabled(EamDbUtil.useCentralRepo()); commentsOccurencesColumnWrapAroundText.setEnabled(EamDbUtil.useCentralRepo()); commentsOccurencesColumnsCheckbox.setSelected(UserPreferences.hideCentralRepoCommentsAndOccurrences()); - maximumResultsSpinner.setValue(UserPreferences.getMaximumNumberOfResults()); hideOtherUsersTagsCheckbox.setSelected(UserPreferences.showOnlyCurrentUserTags()); translateNamesInTableRadioButton.setSelected(UserPreferences.displayTranslatedFileNames()); @@ -115,7 +114,6 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { UserPreferences.setShowOnlyCurrentUserTags(hideOtherUsersTagsCheckbox.isSelected()); UserPreferences.setHideCentralRepoCommentsAndOccurrences(commentsOccurencesColumnsCheckbox.isSelected()); UserPreferences.setDisplayTranslatedFileNames(translateNamesInTableRadioButton.isSelected()); - UserPreferences.setMaximumNumberOfResults((Integer)maximumResultsSpinner.getValue()); storeGroupItemsInTreeByDataSource(); @@ -164,13 +162,11 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { hideOtherUsersTagsLabel = new javax.swing.JLabel(); centralRepoLabel = new javax.swing.JLabel(); commentsOccurencesColumnsCheckbox = new javax.swing.JCheckBox(); - maximumResultsLabel = new javax.swing.JLabel(); jScrollPane1 = new javax.swing.JScrollPane(); timeZoneList = new javax.swing.JList<>(); translateTextLabel = new javax.swing.JLabel(); commentsOccurencesColumnWrapAroundText = new javax.swing.JLabel(); translateNamesInTableRadioButton = new javax.swing.JRadioButton(); - maximumResultsSpinner = new javax.swing.JSpinner(); currentCaseSettingsPanel = new javax.swing.JPanel(); groupByDataSourceCheckbox = new javax.swing.JCheckBox(); currentSessionSettingsPanel = new javax.swing.JPanel(); @@ -270,9 +266,6 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { } }); - org.openide.awt.Mnemonics.setLocalizedText(maximumResultsLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.maximumResultsLabel.text")); // NOI18N - maximumResultsLabel.setToolTipText(org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.maximumResultsLabel.toolTipText")); // NOI18N - timeZoneList.addListSelectionListener(new javax.swing.event.ListSelectionListener() { public void valueChanged(javax.swing.event.ListSelectionEvent evt) { timeZoneListValueChanged(evt); @@ -291,13 +284,6 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { } }); - maximumResultsSpinner.setModel(new javax.swing.SpinnerNumberModel(20000, 0, 100000, 10000)); - maximumResultsSpinner.addChangeListener(new javax.swing.event.ChangeListener() { - public void stateChanged(javax.swing.event.ChangeEvent evt) { - maximumResultsSpinnerStateChanged(evt); - } - }); - javax.swing.GroupLayout globalSettingsPanelLayout = new javax.swing.GroupLayout(globalSettingsPanel); globalSettingsPanel.setLayout(globalSettingsPanelLayout); globalSettingsPanelLayout.setHorizontalGroup( @@ -347,11 +333,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { .addComponent(useBestViewerRadioButton) .addComponent(useLocalTimeRadioButton) .addComponent(useAnotherTimeRadioButton) - .addComponent(translateNamesInTableRadioButton))))) - .addGroup(globalSettingsPanelLayout.createSequentialGroup() - .addComponent(maximumResultsLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(maximumResultsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(translateNamesInTableRadioButton)))))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); globalSettingsPanelLayout.setVerticalGroup( @@ -380,11 +362,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { .addGap(3, 3, 3) .addComponent(commentsOccurencesColumnsCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(commentsOccurencesColumnWrapAroundText) - .addGap(6, 6, 6) - .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(maximumResultsLabel) - .addComponent(maximumResultsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(commentsOccurencesColumnWrapAroundText)) .addGroup(globalSettingsPanelLayout.createSequentialGroup() .addComponent(selectFileLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -489,7 +467,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(viewPreferencesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 528, Short.MAX_VALUE) + .addComponent(viewPreferencesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); }// //GEN-END:initComponents @@ -615,14 +593,6 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { } }//GEN-LAST:event_useBestViewerRadioButtonActionPerformed - private void maximumResultsSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_maximumResultsSpinnerStateChanged - if (immediateUpdates) { - UserPreferences.setMaximumNumberOfResults((Integer)maximumResultsSpinner.getValue()); - } else { - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - } - }//GEN-LAST:event_maximumResultsSpinnerStateChanged - // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel centralRepoLabel; @@ -642,8 +612,6 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { private javax.swing.JLabel hideSlackFilesLabel; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JRadioButton keepCurrentViewerRadioButton; - private javax.swing.JLabel maximumResultsLabel; - private javax.swing.JSpinner maximumResultsSpinner; private javax.swing.JLabel selectFileLabel; private javax.swing.JList timeZoneList; private javax.swing.JRadioButton translateNamesInTableRadioButton; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index 162b3b978c..ce00c55433 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -469,7 +469,7 @@ public abstract class AbstractAbstractFileNode extends A "AbstractAbstractFileNode.createSheet.noScore.description=No score"}) Pair getScorePropertyAndDescription(List tags) { DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE; - String description = ""; + String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description(); if (content.getKnown() == TskData.FileKnown.BAD) { score = DataResultViewerTable.Score.NOTABLE_SCORE; description = Bundle.AbstractAbstractFileNode_createSheet_notableFile_description(); @@ -572,7 +572,7 @@ public abstract class AbstractAbstractFileNode extends A CorrelationAttributeInstance getCorrelationAttributeInstance() { CorrelationAttributeInstance attribute = null; - if (EamDbUtil.useCentralRepo()) { + if (EamDbUtil.useCentralRepo() && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) { attribute = EamArtifactUtil.getInstanceFromContent(content); } return attribute; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 5604f328d6..17e36ff5a3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -355,12 +355,12 @@ public class BlackboardArtifactNode extends AbstractContentNode sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,8 +21,10 @@ package org.sleuthkit.autopsy.datamodel; import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstanceNode; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResultRootNode; import org.sleuthkit.autopsy.commonfilesearch.InstanceCountNode; +import org.sleuthkit.autopsy.commonfilesearch.InstanceCaseNode; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueNode; import org.sleuthkit.autopsy.commonfilesearch.CaseDBCommonAttributeInstanceNode; +import org.sleuthkit.autopsy.commonfilesearch.InstanceDataSourceNode; import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsChildren.DeletedContentNode; import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsNode; import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNode; @@ -67,7 +69,7 @@ public interface DisplayableItemNodeVisitor { T visit(ViewsNode vn); T visit(DataSourceGroupingNode dataSourceGroupingNode); - + T visit(org.sleuthkit.autopsy.datamodel.FileTypesByExtension.FileExtensionNode fsfn); T visit(DeletedContentNode dcn); @@ -122,11 +124,15 @@ public interface DisplayableItemNodeVisitor { T visit(CommonAttributeSearchResultRootNode cfn); T visit(CaseDBCommonAttributeInstanceNode fin); - + T visit(CentralRepoCommonAttributeInstanceNode crfin); - + T visit(InstanceCountNode icn); - + + T visit(InstanceCaseNode icn); + + T visit(InstanceDataSourceNode icn); + T visit(CorrelationAttributeInstanceNode cain); /* @@ -211,19 +217,29 @@ public interface DisplayableItemNodeVisitor { public T visit(CommonAttributeSearchResultRootNode cfn) { return defaultVisit(cfn); } - + @Override - public T visit(InstanceCountNode icn){ + public T visit(InstanceCountNode icn) { return defaultVisit(icn); } - + + @Override + public T visit(InstanceCaseNode icn) { + return defaultVisit(icn); + } + + @Override + public T visit(InstanceDataSourceNode icn) { + return defaultVisit(icn); + } + @Override public T visit(CorrelationAttributeInstanceNode cain) { return defaultVisit(cain); } - + @Override - public T visit(CentralRepoCommonAttributeInstanceNode crfin){ + public T visit(CentralRepoCommonAttributeInstanceNode crfin) { return defaultVisit(crfin); } @@ -361,7 +377,7 @@ public interface DisplayableItemNodeVisitor { public T visit(DataSourceGroupingNode dataSourceGroupingNode) { return defaultVisit(dataSourceGroupingNode); } - + @Override public T visit(ResultsNode rn) { return defaultVisit(rn); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 3a27f9fa9b..585111ef49 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -55,6 +55,8 @@ import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.FileTypeExtensions; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode; import org.sleuthkit.autopsy.commonfilesearch.InstanceCountNode; +import org.sleuthkit.autopsy.commonfilesearch.InstanceCaseNode; +import org.sleuthkit.autopsy.commonfilesearch.InstanceDataSourceNode; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueNode; import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstanceNode; import org.sleuthkit.autopsy.datamodel.LayoutFileNode; @@ -132,8 +134,8 @@ public class DataResultFilterNode extends FilterNode { * wrapped node and may filter out some of its children. * * @param node The node to wrap. - * @param em The ExplorerManager for the component that is creating the - * node. + * @param em The ExplorerManager for the component that is creating the + * node. */ public DataResultFilterNode(Node node, ExplorerManager em) { super(node, new DataResultFilterChildren(node, em)); @@ -145,13 +147,13 @@ public class DataResultFilterNode extends FilterNode { * result viewers. The wrapper node defines the actions associated with the * wrapped node and may filter out some of its children. * - * @param node The node to wrap. - * @param em The ExplorerManager for the component that is creating the - * node. + * @param node The node to wrap. + * @param em The ExplorerManager for the component that is creating + * the node. * @param filterKnown Whether or not to filter out children that represent - * known files. + * known files. * @param filterSlack Whether or not to filter out children that represent - * virtual slack space files. + * virtual slack space files. */ private DataResultFilterNode(Node node, ExplorerManager em, boolean filterKnown, boolean filterSlack) { super(node, new DataResultFilterChildren(node, em, filterKnown, filterSlack)); @@ -261,7 +263,7 @@ public class DataResultFilterNode extends FilterNode { * selected. * * @return The child node selection information, or null if no child should - * be selected. + * be selected. */ public NodeSelectionInfo getChildNodeSelectionInfo() { if (getOriginal() instanceof DisplayableItemNode) { @@ -530,20 +532,30 @@ public class DataResultFilterNode extends FilterNode { } @Override - public AbstractAction visit(CommonAttributeValueNode md5n){ + public AbstractAction visit(InstanceCaseNode icn) { return null; } @Override - public AbstractAction visit(CaseDBCommonAttributeInstanceNode fin){ + public AbstractAction visit(InstanceDataSourceNode icn) { return null; } @Override - public AbstractAction visit(CentralRepoCommonAttributeInstanceNode iccan){ + public AbstractAction visit(CommonAttributeValueNode md5n) { return null; } - + + @Override + public AbstractAction visit(CaseDBCommonAttributeInstanceNode fin) { + return null; + } + + @Override + public AbstractAction visit(CentralRepoCommonAttributeInstanceNode iccan) { + return null; + } + @Override public AbstractAction visit(BlackboardArtifactNode ban) { BlackboardArtifact artifact = ban.getArtifact(); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index a1f9c3b537..4868106217 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -173,7 +173,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat case UserPreferences.HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES: case UserPreferences.DISPLAY_TRANSLATED_NAMES: case UserPreferences.KEEP_PREFERRED_VIEWER: - case UserPreferences.MAXIMUM_NUMBER_OF_RESULTS: refreshContentTreeSafe(); break; case UserPreferences.SHOW_ONLY_CURRENT_USER_TAGS: diff --git a/Core/src/org/sleuthkit/autopsy/images/briefcase.png b/Core/src/org/sleuthkit/autopsy/images/briefcase.png new file mode 100644 index 0000000000..5e37c48423 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/briefcase.png differ diff --git a/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/DataSourceIntegrityIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/DataSourceIntegrityIngestModule.java index 1971a83b2b..4389fe5bb7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/DataSourceIntegrityIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/DataSourceIntegrityIngestModule.java @@ -105,7 +105,11 @@ public class DataSourceIntegrityIngestModule implements DataSourceIngestModule { "# {0} - hashAlgorithm", "# {1} - calculatedHashValue", "# {2} - storedHashValue", - "DataSourceIntegrityIngestModule.process.hashFailedForArtifact={0} hash verification failed:\n Calculated hash: {1}\n Stored hash: {2}\n", + "DataSourceIntegrityIngestModule.process.hashFailedForArtifact={0} hash verification failed:\n Calculated hash: {1}\n Stored hash: {2}\n", + "# {0} - imageName", + "DataSourceIntegrityIngestModule.process.verificationSuccess=Integrity of {0} verified", + "# {0} - imageName", + "DataSourceIntegrityIngestModule.process.verificationFailure={0} failed integrity verification", }) @Override public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) { @@ -276,13 +280,16 @@ public class DataSourceIntegrityIngestModule implements DataSourceIngestModule { } String verificationResultStr; + String messageResultStr; MessageType messageType; if (verified) { messageType = MessageType.INFO; verificationResultStr = NbBundle.getMessage(this.getClass(), "DataSourceIntegrityIngestModule.shutDown.verified"); + messageResultStr = Bundle.DataSourceIntegrityIngestModule_process_verificationSuccess(imgName); } else { messageType = MessageType.WARNING; verificationResultStr = NbBundle.getMessage(this.getClass(), "DataSourceIntegrityIngestModule.shutDown.notVerified"); + messageResultStr = Bundle.DataSourceIntegrityIngestModule_process_verificationFailure(imgName); } detailedResults += NbBundle.getMessage(this.getClass(), "DataSourceIntegrityIngestModule.shutDown.resultLi", verificationResultStr); @@ -299,7 +306,7 @@ public class DataSourceIntegrityIngestModule implements DataSourceIngestModule { } services.postMessage(IngestMessage.createMessage(messageType, DataSourceIntegrityModuleFactory.getModuleName(), - imgName + verificationResultStr, detailedResults)); + messageResultStr, detailedResults)); } else { // Store the hashes in the database and update the image diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/ArtifactTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/ArtifactTextExtractor.java new file mode 100644 index 0000000000..ba91a6cc3a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/textextractors/ArtifactTextExtractor.java @@ -0,0 +1,89 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2018 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 java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import org.apache.commons.io.IOUtils; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Extracts text from artifacts by concatenating the values of all of the + * artifact's attributes. + */ +class ArtifactTextExtractor extends TextExtractor { + + private final BlackboardArtifact artifact; + + public ArtifactTextExtractor(Content artifact) { + this.artifact = (BlackboardArtifact) artifact; + } + + @Override + public Reader getReader() throws ExtractionException { + // Concatenate the string values of all attributes into a single + // "content" string to be indexed. + StringBuilder artifactContents = new StringBuilder(); + + Content dataSource = null; + try { + dataSource = artifact.getDataSource(); + } catch (TskCoreException tskCoreException) { + throw new ExtractionException("Unable to get datasource for artifact: " + artifact.toString(), tskCoreException); + } + if (dataSource == null) { + throw new ExtractionException("Datasource was null for artifact: " + artifact.toString()); + } + + try { + for (BlackboardAttribute attribute : artifact.getAttributes()) { + artifactContents.append(attribute.getAttributeType().getDisplayName()); + artifactContents.append(" : "); + // We have also discussed modifying BlackboardAttribute.getDisplayString() + // to magically format datetime attributes but that is complicated by + // the fact that BlackboardAttribute exists in Sleuthkit data model + // while the utility to determine the timezone to use is in ContentUtils + // in the Autopsy datamodel. + switch (attribute.getValueType()) { + case DATETIME: + artifactContents.append(ContentUtils.getStringTime(attribute.getValueLong(), dataSource)); + break; + default: + artifactContents.append(attribute.getDisplayString()); + } + artifactContents.append(System.lineSeparator()); + } + } catch (TskCoreException tskCoreException) { + throw new ExtractionException("Unable to get attributes for artifact: " + artifact.toString(), tskCoreException); + } + + return new InputStreamReader(IOUtils.toInputStream(artifactContents, + StandardCharsets.UTF_8), StandardCharsets.UTF_8); + } + + @Override + public boolean isSupported(Content file, String detectedFormat) { + return true; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/Bundle.properties b/Core/src/org/sleuthkit/autopsy/textextractors/Bundle.properties new file mode 100755 index 0000000000..b2b9a6846d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/textextractors/Bundle.properties @@ -0,0 +1,2 @@ +AbstractFileTikaTextExtract.index.tikaParseTimeout.text=Exception\: Tika parse timeout for content\: {0}, {1} +AbstractFileTikaTextExtract.index.exception.tikaParse.msg=Exception\: Unexpected exception from Tika parse task execution for file\: {0}, {1} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/textextractors/Bundle_ja.properties new file mode 100755 index 0000000000..5d243eba03 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/textextractors/Bundle_ja.properties @@ -0,0 +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 diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java similarity index 81% rename from KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java rename to Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java index 32842dbc03..86dbd15c1b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.keywordsearch; +package org.sleuthkit.autopsy.textextractors; import java.io.IOException; import java.io.Reader; @@ -38,10 +38,11 @@ import org.sleuthkit.datamodel.ReadContentInputStream; /** * Extracts text from HTML content. */ -class HtmlTextExtractor extends ContentTextExtractor { +final class HtmlTextExtractor extends TextExtractor { static final private Logger logger = Logger.getLogger(HtmlTextExtractor.class.getName()); - private static final int MAX_SIZE = 50_000_000; //50MB + private final int MAX_SIZE; + private final Content file; static final List WEB_MIME_TYPES = Arrays.asList( "application/javascript", //NON-NLS @@ -51,27 +52,51 @@ class HtmlTextExtractor extends ContentTextExtractor { "text/html", //NON-NLS NON-NLS "text/javascript" //NON-NLS ); - + static { // Disable Jericho HTML Parser log messages. Config.LoggerProvider = LoggerProvider.DISABLED; } - @Override - boolean isContentTypeSpecific() { - return true; + /** + * Creates a default instance of the HtmlTextExtractor. Supported file size + * is 50MB. + */ + public HtmlTextExtractor(Content file) { + //Set default to be 50 MB. + MAX_SIZE = 50_000_000; + this.file = file; } + /** + * Determines if this content type is supported by this extractor. + * + * @param content Content instance to be analyzed + * @param detectedFormat Mimetype of content instance + * + * @return flag indicating support + */ @Override - boolean isSupported(Content content, String detectedFormat) { + public boolean isSupported(Content content, String detectedFormat) { return detectedFormat != null && WEB_MIME_TYPES.contains(detectedFormat) && content.getSize() <= MAX_SIZE; } + /** + * Returns a reader that will iterate over the text of an HTML document. + * + * @param content Html document source + * + * @return A reader instance containing the document source text + * + * @throws TextExtractorException + */ @Override - public Reader getReader(Content content) throws TextExtractorException { - ReadContentInputStream stream = new ReadContentInputStream(content); + public Reader getReader() throws ExtractionException { + //TODO JIRA-4467, there is only harm in excluding HTML documents greater + //than 50MB due to our troubled approach of extraction. + ReadContentInputStream stream = new ReadContentInputStream(file); //Parse the stream with Jericho and put the results in a Reader try { @@ -164,17 +189,8 @@ class HtmlTextExtractor extends ContentTextExtractor { // All done, now make it a reader return new StringReader(stringBuilder.toString()); } catch (IOException ex) { - throw new TextExtractorException("Error extracting HTML from content.", ex); + logger.log(Level.WARNING, "Error extracting HTML from content.", ex); + throw new ExtractionException("Error extracting HTML from content.", ex); } } - - @Override - public boolean isDisabled() { - return false; - } - - @Override - public void logWarning(final String msg, Exception ex) { - logger.log(Level.WARNING, msg, ex); //NON-NLS } - } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SqliteTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/SqliteTextExtractor.java similarity index 80% rename from KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SqliteTextExtractor.java rename to Core/src/org/sleuthkit/autopsy/textextractors/SqliteTextExtractor.java index f7fff3c134..ea204d5e30 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SqliteTextExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/SqliteTextExtractor.java @@ -1,24 +1,23 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018-2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/* + * Autopsy Forensic Browser + * + * Copyright 2018-2018 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; +package org.sleuthkit.autopsy.textextractors; -import com.google.common.io.CharSource; import java.io.IOException; import java.io.Reader; import java.util.Iterator; @@ -28,37 +27,27 @@ import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.SQLiteTableReaderException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.SQLiteTableReader; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; /** - * Dedicated SqliteTextExtractor to solve the problems associated with Tika's - * Sqlite parser. + * Extracts text from SQLite database files. * - * Tika problems: 1) Tika fails to open virtual tables 2) Tika fails to open - * tables with spaces in table name 3) Tika fails to include the table names in - * output (except for the first table it parses) + * This is a dedicated solution to address the problems associated with + * Tika's sqlite parser (version 1.17), which include the following: + * 1) Virtual tables cause the parser to bail + * 2) Tables that contain spaces in their name are not extracted + * 3) Table names are not included in its output text */ -class SqliteTextExtractor extends ContentTextExtractor { +final class SqliteTextExtractor extends TextExtractor { private static final String SQLITE_MIMETYPE = "application/x-sqlite3"; private static final Logger logger = Logger.getLogger(SqliteTextExtractor.class.getName()); + private final AbstractFile file; - @Override - boolean isContentTypeSpecific() { - return true; + public SqliteTextExtractor(Content file) { + this.file = (AbstractFile) file; } - - @Override - public boolean isDisabled() { - return false; - } - - @Override - public void logWarning(String msg, Exception exception) { - logger.log(Level.WARNING, msg, exception); //NON-NLS - } - /** * Supports only the sqlite mimetypes * @@ -68,44 +57,34 @@ class SqliteTextExtractor extends ContentTextExtractor { * @return true if x-sqlite3 */ @Override - boolean isSupported(Content file, String detectedFormat) { + public boolean isSupported(Content file, String detectedFormat) { return SQLITE_MIMETYPE.equals(detectedFormat); } /** - * Returns a stream that will read from a sqlite database. + * Returns a reader that will iterate over the text of a sqlite database. * * @param source Content file * - * @return An InputStream that reads from a Sqlite database. + * @return An InputStream that reads from a Sqlite database * - * @throws - * org.sleuthkit.autopsy.keywordsearch.TextExtractor.TextExtractorException + * @throws TextExtractorException */ @Override - public Reader getReader(Content source) throws TextExtractorException { - //Firewall for any content that is not an AbstractFile - if (!AbstractFile.class.isInstance(source)) { - try { - return CharSource.wrap("").openStream(); - } catch (IOException ex) { - throw new TextExtractorException("", ex); - } - } - - return new SQLiteStreamReader((AbstractFile) source); + public Reader getReader() throws ExtractionException { + return new SQLiteStreamReader(file); } - + /** * Produces a continuous stream of characters from a database file. To * achieve this, all table names are queues up and a SQLiteTableReader is * used to do the actual queries and table iteration. */ - public class SQLiteStreamReader extends Reader { + private class SQLiteStreamReader extends Reader { private final SQLiteTableReader reader; private final AbstractFile file; - + private Iterator tableNames; private String currentTableName; @@ -217,9 +196,10 @@ class SqliteTextExtractor extends ContentTextExtractor { } /** - * Reads database values into the buffer. This function is responsible for - * getting the next table in the queue, initiating calls to the SQLiteTableReader, - * and filling in any excess bytes that are lingering from the previous call. + * Reads database values into the buffer. This function is responsible + * for getting the next table in the queue, initiating calls to the + * SQLiteTableReader, and filling in any excess bytes that are lingering + * from the previous call. * * @throws IOException */ @@ -255,9 +235,9 @@ class SqliteTextExtractor extends ContentTextExtractor { reader.read(currentTableName, () -> bufIndex == len); } catch (SQLiteTableReaderException ex) { logger.log(Level.WARNING, String.format( - "Error attempting to read file table: [%s]" //NON-NLS - + " for file: [%s] (id=%d).", currentTableName, //NON-NLS - file.getName(), file.getId()), ex.getMessage()); + "Error attempting to read file table: [%s]" //NON-NLS + + " for file: [%s] (id=%d).", currentTableName, //NON-NLS + file.getName(), file.getId()), ex.getMessage()); } } else { if (bufIndex == off) { @@ -290,8 +270,8 @@ class SqliteTextExtractor extends ContentTextExtractor { } /** - * Wrapper that holds the excess bytes that were left over from the previous - * call to read(). + * Wrapper that holds the excess bytes that were left over from the + * previous call to read(). */ private class ExcessBytes { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/StringsTextExtractor.java similarity index 85% rename from KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java rename to Core/src/org/sleuthkit/autopsy/textextractors/StringsTextExtractor.java index 391c7d5a7c..899cec9ef2 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/StringsTextExtractor.java @@ -16,19 +16,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.keywordsearch; +package org.sleuthkit.autopsy.textextractors; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import org.sleuthkit.autopsy.coreutils.Logger; +import java.util.Objects; +import org.openide.util.Lookup; import org.sleuthkit.autopsy.coreutils.StringExtract; import org.sleuthkit.autopsy.coreutils.StringExtract.StringExtractUnicodeTable.SCRIPT; +import org.sleuthkit.autopsy.textextractors.extractionconfigs.DefaultExtractionConfig; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; @@ -36,24 +36,25 @@ import org.sleuthkit.datamodel.TskException; /** * Extracts raw strings from content. */ -class StringsTextExtractor extends ContentTextExtractor { +final class StringsTextExtractor extends TextExtractor { - static final private Logger logger = Logger.getLogger(StringsTextExtractor.class.getName()); - - /** - * Options for this extractor - */ - enum ExtractOptions { - EXTRACT_UTF16, ///< extract UTF16 text, true/false - EXTRACT_UTF8, ///< extract UTF8 text, true/false - }; + private boolean extractUTF8; + private boolean extractUTF16; + private final Content content; + private final static String DEFAULT_INDEXED_TEXT_CHARSET = "UTF-8"; private final List