Merge branch 'develop' into 6267_null_exif_geodata

This commit is contained in:
Ethan Roseman 2020-04-22 10:53:06 -04:00
commit 3ddab0ae57
34 changed files with 741 additions and 229 deletions

View File

@ -72,6 +72,9 @@
<copy file="${thirdparty.dir}/sevenzip/sevenzipjbinding.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/sevenzip/sevenzipjbinding.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/sevenzip/sevenzipjbinding-AllPlatforms.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/sevenzip/sevenzipjbinding-AllPlatforms.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/stix/StixLib.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/stix/StixLib.jar" todir="${ext.dir}" />
<copy todir="${ext.dir}">
<fileset dir="${thirdparty.dir}/IcePDF 6.2.2/"/>
</copy>
<copy file="${thirdparty.dir}/jdom/jdom-2.0.5.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/jdom/jdom-2.0.5.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/jdom/jdom-2.0.5-contrib.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/jdom/jdom-2.0.5-contrib.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/DatCon/3.6.9/DatCon.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/DatCon/3.6.9/DatCon.jar" todir="${ext.dir}" />

View File

@ -2,8 +2,15 @@ file.reference.activemq-all-5.11.1.jar=release/modules/ext/activemq-all-5.11.1.j
file.reference.apache-mime4j-core-0.8.2.jar=release\\modules\\ext\\apache-mime4j-core-0.8.2.jar file.reference.apache-mime4j-core-0.8.2.jar=release\\modules\\ext\\apache-mime4j-core-0.8.2.jar
file.reference.apache-mime4j-dom-0.8.2.jar=release\\modules\\ext\\apache-mime4j-dom-0.8.2.jar file.reference.apache-mime4j-dom-0.8.2.jar=release\\modules\\ext\\apache-mime4j-dom-0.8.2.jar
file.reference.asm-7.0.jar=release\\modules\\ext\\asm-7.0.jar file.reference.asm-7.0.jar=release\\modules\\ext\\asm-7.0.jar
file.reference.batik-awt-util-1.6.jar=release/modules/ext/batik-awt-util-1.6.jar
file.reference.batik-dom-1.6.jar=release/modules/ext/batik-dom-1.6.jar
file.reference.batik-svg-dom-1.6.jar=release/modules/ext/batik-svg-dom-1.6.jar
file.reference.batik-svggen-1.6.jar=release/modules/ext/batik-svggen-1.6.jar
file.reference.batik-util-1.6.jar=release/modules/ext/batik-util-1.6.jar
file.reference.batik-xml-1.6.jar=release/modules/ext/batik-xml-1.6.jar
file.reference.bcmail-jdk15on-1.60.jar=release\\modules\\ext\\bcmail-jdk15on-1.60.jar file.reference.bcmail-jdk15on-1.60.jar=release\\modules\\ext\\bcmail-jdk15on-1.60.jar
file.reference.bcpkix-jdk15on-1.60.jar=release\\modules\\ext\\bcpkix-jdk15on-1.60.jar file.reference.bcpkix-jdk15on-1.60.jar=release\\modules\\ext\\bcpkix-jdk15on-1.60.jar
file.reference.bcprov-ext-jdk15on-1.54.jar=release/modules/ext/bcprov-ext-jdk15on-1.54.jar
file.reference.bcprov-jdk15on-1.60.jar=release\\modules\\ext\\bcprov-jdk15on-1.60.jar file.reference.bcprov-jdk15on-1.60.jar=release\\modules\\ext\\bcprov-jdk15on-1.60.jar
file.reference.boilerpipe-1.1.0.jar=release\\modules\\ext\\boilerpipe-1.1.0.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.c3p0-0.9.5.jar=release/modules/ext/c3p0-0.9.5.jar
@ -17,14 +24,17 @@ file.reference.commons-io-2.6.jar=release\\modules\\ext\\commons-io-2.6.jar
file.reference.commons-lang3-3.8.1.jar=release\\modules\\ext\\commons-lang3-3.8.1.jar file.reference.commons-lang3-3.8.1.jar=release\\modules\\ext\\commons-lang3-3.8.1.jar
file.reference.commons-pool2-2.4.2.jar=release/modules/ext/commons-pool2-2.4.2.jar file.reference.commons-pool2-2.4.2.jar=release/modules/ext/commons-pool2-2.4.2.jar
file.reference.cxf-rt-rs-client-3.3.0.jar=release\\modules\\ext\\cxf-rt-rs-client-3.3.0.jar file.reference.cxf-rt-rs-client-3.3.0.jar=release\\modules\\ext\\cxf-rt-rs-client-3.3.0.jar
file.reference.DatCon.jar=release/modules/ext/DatCon.jar
file.reference.dec-0.1.2.jar=release\\modules\\ext\\dec-0.1.2.jar file.reference.dec-0.1.2.jar=release\\modules\\ext\\dec-0.1.2.jar
file.reference.decodetect-core-0.3.jar=release\\modules\\ext\\decodetect-core-0.3.jar file.reference.decodetect-core-0.3.jar=release/modules/ext/decodetect-core-0.3.jar
file.reference.fontbox-2.0.13.jar=release\\modules\\ext\\fontbox-2.0.13.jar file.reference.fontbox-2.0.13.jar=release\\modules\\ext\\fontbox-2.0.13.jar
file.reference.geoapi-3.0.1.jar=release\\modules\\ext\\geoapi-3.0.1.jar file.reference.geoapi-3.0.1.jar=release\\modules\\ext\\geoapi-3.0.1.jar
file.reference.grib-4.5.5.jar=release\\modules\\ext\\grib-4.5.5.jar file.reference.grib-4.5.5.jar=release\\modules\\ext\\grib-4.5.5.jar
file.reference.httpclient-4.5.6.jar=release\\modules\\ext\\httpclient-4.5.6.jar file.reference.httpclient-4.5.6.jar=release\\modules\\ext\\httpclient-4.5.6.jar
file.reference.httpmime-4.5.6.jar=release\\modules\\ext\\httpmime-4.5.6.jar file.reference.httpmime-4.5.6.jar=release\\modules\\ext\\httpmime-4.5.6.jar
file.reference.httpservices-4.5.5.jar=release\\modules\\ext\\httpservices-4.5.5.jar file.reference.httpservices-4.5.5.jar=release\\modules\\ext\\httpservices-4.5.5.jar
file.reference.icepdf-core-6.2.2.jar=release/modules/ext/icepdf-core-6.2.2.jar
file.reference.icepdf-viewer-6.2.2.jar=release/modules/ext/icepdf-viewer-6.2.2.jar
file.reference.isoparser-1.1.22.jar=release\\modules\\ext\\isoparser-1.1.22.jar file.reference.isoparser-1.1.22.jar=release\\modules\\ext\\isoparser-1.1.22.jar
file.reference.jackcess-2.2.0.jar=release\\modules\\ext\\jackcess-2.2.0.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.jackcess-encrypt-2.1.4.jar=release\\modules\\ext\\jackcess-encrypt-2.1.4.jar
@ -32,6 +42,8 @@ file.reference.jackson-annotations-2.9.7.jar=release\\modules\\ext\\jackson-anno
file.reference.jackson-core-2.9.7.jar=release\\modules\\ext\\jackson-core-2.9.7.jar file.reference.jackson-core-2.9.7.jar=release\\modules\\ext\\jackson-core-2.9.7.jar
file.reference.jackson-databind-2.9.7.jar=release\\modules\\ext\\jackson-databind-2.9.7.jar file.reference.jackson-databind-2.9.7.jar=release\\modules\\ext\\jackson-databind-2.9.7.jar
file.reference.jai-imageio-core-1.4.0.jar=release\\modules\\ext\\jai-imageio-core-1.4.0.jar file.reference.jai-imageio-core-1.4.0.jar=release\\modules\\ext\\jai-imageio-core-1.4.0.jar
file.reference.jai_core-1.1.3.jar=release/modules/ext/jai_core-1.1.3.jar
file.reference.jai_imageio-1.1.jar=release/modules/ext/jai_imageio-1.1.jar
file.reference.java-libpst-0.8.1.jar=release\\modules\\ext\\java-libpst-0.8.1.jar file.reference.java-libpst-0.8.1.jar=release\\modules\\ext\\java-libpst-0.8.1.jar
file.reference.javax.activation-1.2.0.jar=release\\modules\\ext\\javax.activation-1.2.0.jar file.reference.javax.activation-1.2.0.jar=release\\modules\\ext\\javax.activation-1.2.0.jar
file.reference.javax.annotation-api-1.3.2.jar=release\\modules\\ext\\javax.annotation-api-1.3.2.jar file.reference.javax.annotation-api-1.3.2.jar=release\\modules\\ext\\javax.annotation-api-1.3.2.jar
@ -50,7 +62,7 @@ file.reference.jsoup-1.11.3.jar=release\\modules\\ext\\jsoup-1.11.3.jar
file.reference.jul-to-slf4j-1.7.25.jar=release\\modules\\ext\\jul-to-slf4j-1.7.25.jar file.reference.jul-to-slf4j-1.7.25.jar=release\\modules\\ext\\jul-to-slf4j-1.7.25.jar
file.reference.juniversalchardet-1.0.3.jar=release\\modules\\ext\\juniversalchardet-1.0.3.jar file.reference.juniversalchardet-1.0.3.jar=release\\modules\\ext\\juniversalchardet-1.0.3.jar
file.reference.junrar-2.0.0.jar=release\\modules\\ext\\junrar-2.0.0.jar file.reference.junrar-2.0.0.jar=release\\modules\\ext\\junrar-2.0.0.jar
file.reference.jutf7-1.0.0.jar=release\\modules\\ext\\jutf7-1.0.0.jar file.reference.jutf7-1.0.0.jar=release/modules/ext/jutf7-1.0.0.jar
file.reference.jxmapviewer2-2.4.jar=release/modules/ext/jxmapviewer2-2.4.jar file.reference.jxmapviewer2-2.4.jar=release/modules/ext/jxmapviewer2-2.4.jar
file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone-2.7.0.jar file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone-2.7.0.jar
file.reference.libphonenumber-3.5.jar=release/modules/ext/libphonenumber-3.5.jar file.reference.libphonenumber-3.5.jar=release/modules/ext/libphonenumber-3.5.jar

View File

@ -355,6 +355,14 @@
<runtime-relative-path>ext/commons-lang3-3.8.1.jar</runtime-relative-path> <runtime-relative-path>ext/commons-lang3-3.8.1.jar</runtime-relative-path>
<binary-origin>release\modules\ext\commons-lang3-3.8.1.jar</binary-origin> <binary-origin>release\modules\ext\commons-lang3-3.8.1.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/batik-xml-1.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/batik-xml-1.6.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jai_core-1.1.3.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jai_core-1.1.3.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/gax-grpc-1.44.0.jar</runtime-relative-path> <runtime-relative-path>ext/gax-grpc-1.44.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/gax-grpc-1.44.0.jar</binary-origin> <binary-origin>release/modules/ext/gax-grpc-1.44.0.jar</binary-origin>
@ -371,6 +379,10 @@
<runtime-relative-path>ext/opencensus-api-0.19.2.jar</runtime-relative-path> <runtime-relative-path>ext/opencensus-api-0.19.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/opencensus-api-0.19.2.jar</binary-origin> <binary-origin>release/modules/ext/opencensus-api-0.19.2.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/batik-svg-dom-1.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/batik-svg-dom-1.6.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/gax-httpjson-0.61.0.jar</runtime-relative-path> <runtime-relative-path>ext/gax-httpjson-0.61.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/gax-httpjson-0.61.0.jar</binary-origin> <binary-origin>release/modules/ext/gax-httpjson-0.61.0.jar</binary-origin>
@ -479,6 +491,10 @@
<runtime-relative-path>ext/xmpcore-5.1.3.jar</runtime-relative-path> <runtime-relative-path>ext/xmpcore-5.1.3.jar</runtime-relative-path>
<binary-origin>release/modules/ext/xmpcore-5.1.3.jar</binary-origin> <binary-origin>release/modules/ext/xmpcore-5.1.3.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/batik-util-1.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/batik-util-1.6.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/javax.activation-1.2.0.jar</runtime-relative-path> <runtime-relative-path>ext/javax.activation-1.2.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\javax.activation-1.2.0.jar</binary-origin> <binary-origin>release\modules\ext\javax.activation-1.2.0.jar</binary-origin>
@ -499,6 +515,10 @@
<runtime-relative-path>ext/jgraphx-4.1.0.jar</runtime-relative-path> <runtime-relative-path>ext/jgraphx-4.1.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jgraphx-4.1.0.jar</binary-origin> <binary-origin>release/modules/ext/jgraphx-4.1.0.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/DatCon.jar</runtime-relative-path>
<binary-origin>release/modules/ext/DatCon.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/java-libpst-0.8.1.jar</runtime-relative-path> <runtime-relative-path>ext/java-libpst-0.8.1.jar</runtime-relative-path>
<binary-origin>release\modules\ext\java-libpst-0.8.1.jar</binary-origin> <binary-origin>release\modules\ext\java-libpst-0.8.1.jar</binary-origin>
@ -535,10 +555,6 @@
<runtime-relative-path>ext/google-http-client-1.29.0.jar</runtime-relative-path> <runtime-relative-path>ext/google-http-client-1.29.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/google-http-client-1.29.0.jar</binary-origin> <binary-origin>release/modules/ext/google-http-client-1.29.0.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/sleuthkit-postgresql-4.9.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.9.0.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/bcpkix-jdk15on-1.60.jar</runtime-relative-path> <runtime-relative-path>ext/bcpkix-jdk15on-1.60.jar</runtime-relative-path>
<binary-origin>release\modules\ext\bcpkix-jdk15on-1.60.jar</binary-origin> <binary-origin>release\modules\ext\bcpkix-jdk15on-1.60.jar</binary-origin>
@ -551,6 +567,10 @@
<runtime-relative-path>ext/slf4j-api-1.7.25.jar</runtime-relative-path> <runtime-relative-path>ext/slf4j-api-1.7.25.jar</runtime-relative-path>
<binary-origin>release\modules\ext\slf4j-api-1.7.25.jar</binary-origin> <binary-origin>release\modules\ext\slf4j-api-1.7.25.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/bcprov-ext-jdk15on-1.54.jar</runtime-relative-path>
<binary-origin>release/modules/ext/bcprov-ext-jdk15on-1.54.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/google-cloud-core-1.70.0.jar</runtime-relative-path> <runtime-relative-path>ext/google-cloud-core-1.70.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/google-cloud-core-1.70.0.jar</binary-origin> <binary-origin>release/modules/ext/google-cloud-core-1.70.0.jar</binary-origin>
@ -619,6 +639,14 @@
<runtime-relative-path>ext/commons-validator-1.6.jar</runtime-relative-path> <runtime-relative-path>ext/commons-validator-1.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-validator-1.6.jar</binary-origin> <binary-origin>release/modules/ext/commons-validator-1.6.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/sleuthkit-postgresql-4.9.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.9.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/decodetect-core-0.3.jar</runtime-relative-path>
<binary-origin>release/modules/ext/decodetect-core-0.3.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/jbig2-imageio-3.0.2.jar</runtime-relative-path> <runtime-relative-path>ext/jbig2-imageio-3.0.2.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jbig2-imageio-3.0.2.jar</binary-origin> <binary-origin>release\modules\ext\jbig2-imageio-3.0.2.jar</binary-origin>
@ -667,6 +695,10 @@
<runtime-relative-path>ext/SparseBitSet-1.1.jar</runtime-relative-path> <runtime-relative-path>ext/SparseBitSet-1.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/SparseBitSet-1.1.jar</binary-origin> <binary-origin>release/modules/ext/SparseBitSet-1.1.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/batik-svggen-1.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/batik-svggen-1.6.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/c3p0-0.9.5.jar</runtime-relative-path> <runtime-relative-path>ext/c3p0-0.9.5.jar</runtime-relative-path>
<binary-origin>release/modules/ext/c3p0-0.9.5.jar</binary-origin> <binary-origin>release/modules/ext/c3p0-0.9.5.jar</binary-origin>
@ -735,6 +767,10 @@
<runtime-relative-path>ext/postgresql-9.4.1211.jre7.jar</runtime-relative-path> <runtime-relative-path>ext/postgresql-9.4.1211.jre7.jar</runtime-relative-path>
<binary-origin>release/modules/ext/postgresql-9.4.1211.jre7.jar</binary-origin> <binary-origin>release/modules/ext/postgresql-9.4.1211.jre7.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jai_imageio-1.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jai_imageio-1.1.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/httpclient-4.5.6.jar</runtime-relative-path> <runtime-relative-path>ext/httpclient-4.5.6.jar</runtime-relative-path>
<binary-origin>release\modules\ext\httpclient-4.5.6.jar</binary-origin> <binary-origin>release\modules\ext\httpclient-4.5.6.jar</binary-origin>
@ -747,6 +783,10 @@
<runtime-relative-path>ext/fontbox-2.0.13.jar</runtime-relative-path> <runtime-relative-path>ext/fontbox-2.0.13.jar</runtime-relative-path>
<binary-origin>release\modules\ext\fontbox-2.0.13.jar</binary-origin> <binary-origin>release\modules\ext\fontbox-2.0.13.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/icepdf-core-6.2.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/icepdf-core-6.2.2.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/activemq-all-5.11.1.jar</runtime-relative-path> <runtime-relative-path>ext/activemq-all-5.11.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/activemq-all-5.11.1.jar</binary-origin> <binary-origin>release/modules/ext/activemq-all-5.11.1.jar</binary-origin>
@ -763,6 +803,10 @@
<runtime-relative-path>ext/dec-0.1.2.jar</runtime-relative-path> <runtime-relative-path>ext/dec-0.1.2.jar</runtime-relative-path>
<binary-origin>release\modules\ext\dec-0.1.2.jar</binary-origin> <binary-origin>release\modules\ext\dec-0.1.2.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/batik-dom-1.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/batik-dom-1.6.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/google-http-client-jackson2-1.29.0.jar</runtime-relative-path> <runtime-relative-path>ext/google-http-client-jackson2-1.29.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/google-http-client-jackson2-1.29.0.jar</binary-origin> <binary-origin>release/modules/ext/google-http-client-jackson2-1.29.0.jar</binary-origin>
@ -779,6 +823,14 @@
<runtime-relative-path>ext/sevenzipjbinding-AllPlatforms.jar</runtime-relative-path> <runtime-relative-path>ext/sevenzipjbinding-AllPlatforms.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sevenzipjbinding-AllPlatforms.jar</binary-origin> <binary-origin>release/modules/ext/sevenzipjbinding-AllPlatforms.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jutf7-1.0.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jutf7-1.0.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/batik-awt-util-1.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/batik-awt-util-1.6.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/google-api-services-translate-v2-rev20170525-1.27.0.jar</runtime-relative-path> <runtime-relative-path>ext/google-api-services-translate-v2-rev20170525-1.27.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/google-api-services-translate-v2-rev20170525-1.27.0.jar</binary-origin> <binary-origin>release/modules/ext/google-api-services-translate-v2-rev20170525-1.27.0.jar</binary-origin>
@ -787,6 +839,10 @@
<runtime-relative-path>ext/webp-imageio-sejda-0.1.0.jar</runtime-relative-path> <runtime-relative-path>ext/webp-imageio-sejda-0.1.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/webp-imageio-sejda-0.1.0.jar</binary-origin> <binary-origin>release/modules/ext/webp-imageio-sejda-0.1.0.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/icepdf-viewer-6.2.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/icepdf-viewer-6.2.2.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/bcmail-jdk15on-1.60.jar</runtime-relative-path> <runtime-relative-path>ext/bcmail-jdk15on-1.60.jar</runtime-relative-path>
<binary-origin>release\modules\ext\bcmail-jdk15on-1.60.jar</binary-origin> <binary-origin>release\modules\ext\bcmail-jdk15on-1.60.jar</binary-origin>
@ -795,18 +851,6 @@
<runtime-relative-path>ext/vorbis-java-tika-0.8.jar</runtime-relative-path> <runtime-relative-path>ext/vorbis-java-tika-0.8.jar</runtime-relative-path>
<binary-origin>release\modules\ext\vorbis-java-tika-0.8.jar</binary-origin> <binary-origin>release\modules\ext\vorbis-java-tika-0.8.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/decodetect-core-0.3.jar</runtime-relative-path>
<binary-origin>release/modules/ext/decodetect-core-0.3.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jutf7-1.0.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jutf7-1.0.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/DatCon.jar</runtime-relative-path>
<binary-origin>release/modules/ext/DatCon.jar</binary-origin>
</class-path-extension>
</data> </data>
</configuration> </configuration>
</project> </project>

View File

@ -48,7 +48,8 @@ final class AccountDeviceInstanceNode extends AbstractNode {
this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount(); this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount();
setName(account.getTypeSpecificID()); setName(account.getTypeSpecificID());
setDisplayName(getName()); setDisplayName(getName());
setIconBaseWithExtension(Utils.getIconFilePath(account.getAccountType())); String iconPath = Utils.getIconFilePath(account.getAccountType());
this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
} }
AccountDeviceInstance getAccountDeviceInstance() { AccountDeviceInstance getAccountDeviceInstance() {

View File

@ -32,7 +32,7 @@ import org.openide.util.NbBundle;
final class PinAccountsAction extends AbstractCVTAction { final class PinAccountsAction extends AbstractCVTAction {
static private final ImageIcon ICON = ImageUtilities.loadImageIcon( static private final ImageIcon ICON = ImageUtilities.loadImageIcon(
"/org/sleuthkit/autopsy/communications/images/marker--plus.png", false); "org/sleuthkit/autopsy/communications/images/marker--plus.png", false);
private static final String SINGULAR_TEXT = Bundle.PinAccountsAction_singularText(); private static final String SINGULAR_TEXT = Bundle.PinAccountsAction_singularText();
private static final String PLURAL_TEXT = Bundle.PinAccountsAction_pluralText(); private static final String PLURAL_TEXT = Bundle.PinAccountsAction_pluralText();

View File

@ -32,7 +32,7 @@ import org.openide.util.NbBundle;
final class ResetAndPinAccountsAction extends AbstractCVTAction { final class ResetAndPinAccountsAction extends AbstractCVTAction {
private static final ImageIcon ICON = ImageUtilities.loadImageIcon( private static final ImageIcon ICON = ImageUtilities.loadImageIcon(
"/org/sleuthkit/autopsy/communications/images/marker--pin.png", false); "org/sleuthkit/autopsy/communications/images/marker--pin.png", false);
private static final String SINGULAR_TEXT = Bundle.ResetAndPinAccountsAction_singularText(); private static final String SINGULAR_TEXT = Bundle.ResetAndPinAccountsAction_singularText();
private static final String PLURAL_TEXT = Bundle.ResetAndPinAccountsAction_pluralText(); private static final String PLURAL_TEXT = Bundle.ResetAndPinAccountsAction_pluralText();

View File

@ -32,7 +32,7 @@ import org.openide.util.NbBundle;
final class UnpinAccountsAction extends AbstractCVTAction { final class UnpinAccountsAction extends AbstractCVTAction {
static final private ImageIcon ICON = ImageUtilities.loadImageIcon( static final private ImageIcon ICON = ImageUtilities.loadImageIcon(
"/org/sleuthkit/autopsy/communications/images/marker--minus.png", false); "org/sleuthkit/autopsy/communications/images/marker--minus.png", false);
private static final String SINGULAR_TEXT = Bundle.UnpinAccountsAction_singularText(); private static final String SINGULAR_TEXT = Bundle.UnpinAccountsAction_singularText();
private static final String PLURAL_TEXT = Bundle.UnpinAccountsAction_pluralText(); private static final String PLURAL_TEXT = Bundle.UnpinAccountsAction_pluralText();

View File

@ -112,6 +112,8 @@ MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageContentViewer.viewInNewWindowButton.text=View in New Window MessageContentViewer.viewInNewWindowButton.text=View in New Window
JPEGViewerDummy.jLabel1.text=You are looking at a JPEG file: JPEGViewerDummy.jLabel1.text=You are looking at a JPEG file:
JPEGViewerDummy.jTextField1.text=jTextField1 JPEGViewerDummy.jTextField1.text=jTextField1
PDFViewer.encryptedDialog=This document is password protected.
PDFViewer.errorDialog=An error occurred while opening this PDF document. Check the logs for more information. You may continue to use this feature on other PDF documents.
PListNode.KeyCol=Key PListNode.KeyCol=Key
PListNode.TypeCol=Type PListNode.TypeCol=Type
PListNode.ValueCol=Value PListNode.ValueCol=Value

View File

@ -50,7 +50,8 @@ public class FileViewer extends javax.swing.JPanel implements DataContentViewer
new PListViewer(), new PListViewer(),
new MediaFileViewer(), new MediaFileViewer(),
new HtmlViewer(), new HtmlViewer(),
new WindowsRegistryViewer() new WindowsRegistryViewer(),
new PDFViewer()
}; };
private FileTypeViewer lastViewer; private FileTypeViewer lastViewer;

View File

@ -0,0 +1,192 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
import java.awt.BorderLayout;
import java.awt.Component;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.icepdf.core.exceptions.PDFException;
import org.icepdf.core.exceptions.PDFSecurityException;
import org.icepdf.core.pobjects.Document;
import org.icepdf.ri.common.ComponentKeyBinding;
import org.icepdf.ri.common.SwingController;
import org.icepdf.ri.common.SwingViewBuilder;
import org.icepdf.ri.common.views.DocumentViewControllerImpl;
import org.icepdf.ri.common.views.DocumentViewModelImpl;
import org.icepdf.ri.util.PropertiesManager;
/**
* Application content viewer for PDF files.
*/
public class PDFViewer implements FileTypeViewer {
private static final Logger logger = Logger.getLogger(PDFViewer.class.getName());
private JPanel container;
private final PropertiesManager propsManager;
public PDFViewer() {
container = createNewContainer();
propsManager = getCustomProperties();
}
@Override
public List<String> getSupportedMIMETypes() {
return Arrays.asList("application/pdf");
}
@Override
public void setFile(AbstractFile file) {
// The 'C' in IcePDFs MVC set up.
SwingController controller = new SwingController();
// Builder for the 'V' in IcePDFs MVC set up
SwingViewBuilder viewBuilder = new SwingViewBuilder(controller, propsManager);
// The 'V' in IcePDFs MVC set up.
JPanel icePdfPanel = viewBuilder.buildViewerPanel();
// This connects keyboard commands performed on the view to the controller.
// The only keyboard commands that the controller supports is Ctrl-C for
// copying selected text.
ComponentKeyBinding.install(controller, icePdfPanel);
// Ensure the preferredSize is in sync with the parent container.
icePdfPanel.setPreferredSize(this.container.getPreferredSize());
// Add the IcePDF view to the center of our container.
this.container.add(icePdfPanel, BorderLayout.CENTER);
// Document is the 'M' in IcePDFs MVC set up. Read the data needed to
// populate the model in the background.
new SwingWorker<Document, Void>() {
@Override
protected Document doInBackground() throws PDFException, PDFSecurityException, IOException {
ReadContentInputStream stream = new ReadContentInputStream(file);
Document doc = new Document();
// This will read the stream into memory.
doc.setInputStream(stream, null);
return doc;
}
@Override
protected void done() {
// Customize the view selection modes on the EDT. Each of these
// will cause UI widgets to be updated.
try {
Document doc = get();
controller.openDocument(doc, null);
// This makes the PDF viewer appear as one continuous
// document, which is the default for most popular PDF viewers.
controller.setPageViewMode(DocumentViewControllerImpl.ONE_COLUMN_VIEW, true);
// This makes it possible to select text by left clicking and dragging.
controller.setDisplayTool(DocumentViewModelImpl.DISPLAY_TOOL_TEXT_SELECTION);
} catch (InterruptedException ex) {
// Do nothing.
} catch (ExecutionException ex) {
Throwable exCause = ex.getCause();
if (exCause instanceof PDFSecurityException) {
showEncryptionDialog();
} else {
logger.log(Level.WARNING, String.format("PDF content viewer "
+ "was unable to open document with id %d and name %s",
file.getId(), file.getName()), ex);
showErrorDialog();
}
}
}
}.execute();
}
@Override
public Component getComponent() {
return container;
}
@Override
public void resetComponent() {
container = createNewContainer();
}
// The container should have a BorderLayout otherwise the IcePDF panel may
// not be visible.
private JPanel createNewContainer() {
return new JPanel(new BorderLayout());
}
@Override
public boolean isSupported(AbstractFile file) {
return getSupportedMIMETypes().contains(file.getMIMEType());
}
/**
* Sets property values that will control how the view will be constructed
* in IcePDFs MVC set up.
*/
private PropertiesManager getCustomProperties() {
Properties props = new Properties();
// See link for available properties. https://www.icesoft.org/wiki/display/PDF/Customizing+the+Viewer
props.setProperty(PropertiesManager.PROPERTY_SHOW_UTILITY_SAVE, "false");
props.setProperty(PropertiesManager.PROPERTY_SHOW_UTILITY_OPEN, "false");
props.setProperty(PropertiesManager.PROPERTY_SHOW_UTILITY_PRINT, "false");
props.setProperty(PropertiesManager.PROPERTY_SHOW_TOOLBAR_ANNOTATION, "false");
props.setProperty(PropertiesManager.PROPERTY_SHOW_UTILITYPANE_ANNOTATION, "false");
// This suppresses a pop-up, from IcePDF, that asks if you'd like to
// save configuration changes to disk.
props.setProperty("application.showLocalStorageDialogs", "false");
ResourceBundle defaultMessageBundle = ResourceBundle.getBundle(PropertiesManager.DEFAULT_MESSAGE_BUNDLE);
return new PropertiesManager(System.getProperties(), props, defaultMessageBundle);
}
@NbBundle.Messages({
"PDFViewer.errorDialog=An error occurred while opening this PDF document. "
+ "Check the logs for more information. You may continue to use "
+ "this feature on other PDF documents."
})
private void showErrorDialog() {
MessageNotifyUtil.Message.error(Bundle.PDFViewer_errorDialog());
}
@NbBundle.Messages({
"PDFViewer.encryptedDialog=This document is password protected."
})
private void showEncryptionDialog() {
MessageNotifyUtil.Message.error(Bundle.PDFViewer_encryptedDialog());
}
}

View File

@ -260,7 +260,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
String displayName = srcContent.getName(); String displayName = srcContent.getName();
setDisplayName(displayName); setDisplayName(displayName);
setShortDescription(displayName); setShortDescription(displayName);
setIconBaseWithExtension(iconPath); setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener); Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
} }

View File

@ -290,7 +290,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
super(Children.create(new ArtifactFactory(type), true), Lookups.singleton(type.getDisplayName())); super(Children.create(new ArtifactFactory(type), true), Lookups.singleton(type.getDisplayName()));
super.setName(type.getTypeName()); super.setName(type.getTypeName());
this.type = type; this.type = type;
this.setIconBaseWithExtension(IconsUtil.getIconFilePath(type.getTypeID())); //NON-NLS String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
updateDisplayName(); updateDisplayName();
} }

View File

@ -33,8 +33,10 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -108,6 +110,9 @@ final public class Accounts implements AutopsyVisitableItem {
private final RejectAccounts rejectActionInstance; private final RejectAccounts rejectActionInstance;
private final ApproveAccounts approveActionInstance; private final ApproveAccounts approveActionInstance;
// tracks the number of each account type found
private final AccountTypeResults accountTypeResults;
/** /**
* Constructor * Constructor
* *
@ -129,6 +134,7 @@ final public class Accounts implements AutopsyVisitableItem {
this.rejectActionInstance = new RejectAccounts(); this.rejectActionInstance = new RejectAccounts();
this.approveActionInstance = new ApproveAccounts(); this.approveActionInstance = new ApproveAccounts();
this.accountTypeResults = new AccountTypeResults();
} }
@Override @Override
@ -250,6 +256,63 @@ final public class Accounts implements AutopsyVisitableItem {
} }
} }
/**
* Tracks the account types and the number of account types found.
*/
private class AccountTypeResults {
private final Map<String, Long> counts = new HashMap<>();
AccountTypeResults() {
update();
}
/**
* Given the type name of the Account.Type, provides the count of those type.
* @param accountType The type name of the Account.Type.
* @return The number of results found for the given account type.
*/
Long getCount(String accountType) {
return counts.get(accountType);
}
/**
* Retrieves an alphabetically organized list of all the account types.
* @return An alphabetically organized list of all the account types.
*/
List<String> getTypes() {
List<String> types = new ArrayList<>(counts.keySet());
Collections.sort(types);
return types;
}
/**
* Queries the database and updates the counts for each account type.
*/
private void update() {
String accountTypesInUseQuery
= "SELECT blackboard_attributes.value_text as account_type, COUNT(*) as count "
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ " GROUP BY blackboard_attributes.value_text ";
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery);
ResultSet resultSet = executeQuery.getResultSet()) {
counts.clear();
while (resultSet.next()) {
String accountType = resultSet.getString("account_type");
Long count = resultSet.getLong("count");
counts.put(accountType, count);
}
} catch (TskCoreException | SQLException ex) {
LOGGER.log(Level.SEVERE, "Error querying for account_types", ex);
}
}
}
/** /**
* Creates child nodes for each account type in the db. * Creates child nodes for each account type in the db.
*/ */
@ -281,6 +344,7 @@ final public class Accounts implements AutopsyVisitableItem {
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
accountTypeResults.update();
reviewStatusBus.post(eventData); reviewStatusBus.post(eventData);
} }
} catch (NoCurrentCaseException notUsed) { } catch (NoCurrentCaseException notUsed) {
@ -324,37 +388,31 @@ final public class Accounts implements AutopsyVisitableItem {
@Override @Override
protected boolean createKeys(List<String> list) { protected boolean createKeys(List<String> list) {
String accountTypesInUseQuery list.addAll(accountTypeResults.getTypes());
= "SELECT DISTINCT blackboard_attributes.value_text as account_type "
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ getFilterByDataSourceClause();
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery);
ResultSet resultSet = executeQuery.getResultSet()) {
while (resultSet.next()) {
String accountType = resultSet.getString("account_type");
list.add(accountType);
}
} catch (TskCoreException | SQLException ex) {
LOGGER.log(Level.SEVERE, "Error querying for account_types", ex);
}
return true; return true;
} }
/**
* Registers the given node with the reviewStatusBus and returns
* the node wrapped in an array.
* @param node The node to be wrapped.
* @return The array containing this node.
*/
private Node[] getNodeArr(Node node) {
reviewStatusBus.register(node);
return new Node[]{node};
}
@Override @Override
protected Node[] createNodesForKey(String acountTypeName) { protected Node[] createNodesForKey(String acountTypeName) {
if (Account.Type.CREDIT_CARD.getTypeName().equals(acountTypeName)) { if (Account.Type.CREDIT_CARD.getTypeName().equals(acountTypeName)) {
return new Node[]{new CreditCardNumberAccountTypeNode()}; return getNodeArr(new CreditCardNumberAccountTypeNode());
} else { } else {
try { try {
Account.Type accountType = skCase.getCommunicationsManager().getAccountType(acountTypeName); Account.Type accountType = skCase.getCommunicationsManager().getAccountType(acountTypeName);
return new Node[]{new DefaultAccountTypeNode(accountType)}; return getNodeArr(new DefaultAccountTypeNode(accountType));
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error getting display name for account type. ", ex); LOGGER.log(Level.SEVERE, "Error getting display name for account type. ", ex);
} }
@ -509,11 +567,14 @@ final public class Accounts implements AutopsyVisitableItem {
* no special behavior. * no special behavior.
*/ */
final public class DefaultAccountTypeNode extends DisplayableItemNode { final public class DefaultAccountTypeNode extends DisplayableItemNode {
private final Account.Type accountType;
private DefaultAccountTypeNode(Account.Type accountType) { private DefaultAccountTypeNode(Account.Type accountType) {
super(Children.create(new DefaultAccountFactory(accountType), true), Lookups.singleton(accountType)); super(Children.create(new DefaultAccountFactory(accountType), true), Lookups.singleton(accountType));
setName(accountType.getDisplayName()); this.accountType = accountType;
this.setIconBaseWithExtension(getIconFilePath(accountType)); //NON-NLS String iconPath = getIconFilePath(accountType);
this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); //NON-NLS
updateName();
} }
@Override @Override
@ -530,6 +591,24 @@ final public class Accounts implements AutopsyVisitableItem {
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();
} }
@Subscribe
void handleReviewStatusChange(ReviewStatusChangeEvent event) {
updateName();
}
@Subscribe
void handleDataAdded(ModuleDataEvent event) {
updateName();
}
/**
* Gets the latest counts for the account type and then updates the name.
*/
public void updateName() {
setName(String.format("%s (%d)", accountType.getDisplayName(), accountTypeResults.getCount(accountType.getTypeName())));
}
} }
/** /**
@ -660,6 +739,23 @@ final public class Accounts implements AutopsyVisitableItem {
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
} }
/**
* Gets the latest counts for the account type and then updates the name.
*/
public void updateName() {
setName(String.format("%s (%d)", Account.Type.CREDIT_CARD.getDisplayName(), accountTypeResults.getCount(Account.Type.CREDIT_CARD.getTypeName())));
}
@Subscribe
void handleReviewStatusChange(ReviewStatusChangeEvent event) {
updateName();
}
@Subscribe
void handleDataAdded(ModuleDataEvent event) {
updateName();
}
@Override @Override
public boolean isLeafTypeNode() { public boolean isLeafTypeNode() {
return false; return false;

View File

@ -34,8 +34,8 @@ final class DiscoveryUiUtils {
private static final int ICON_SIZE = 16; private static final int ICON_SIZE = 16;
private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png"; private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png";
private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png"; private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png";
private static final String DELETE_ICON_PATH = "/org/sleuthkit/autopsy/images/file-icon-deleted.png"; private static final String DELETE_ICON_PATH = "org/sleuthkit/autopsy/images/file-icon-deleted.png";
private static final String UNSUPPORTED_DOC_PATH = "/org/sleuthkit/autopsy/images/image-extraction-not-supported.png"; private static final String UNSUPPORTED_DOC_PATH = "org/sleuthkit/autopsy/images/image-extraction-not-supported.png";
private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false)); private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false));
private static final ImageIcon NOTABLE_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false)); private static final ImageIcon NOTABLE_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false));
private static final ImageIcon DELETED_ICON = new ImageIcon(ImageUtilities.loadImage(DELETE_ICON_PATH, false)); private static final ImageIcon DELETED_ICON = new ImageIcon(ImageUtilities.loadImage(DELETE_ICON_PATH, false));

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.util.Pair;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
@ -75,11 +76,11 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter
* *
* @param mapWaypoints List of filtered MapWaypoints. * @param mapWaypoints List of filtered MapWaypoints.
*/ */
abstract void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints); abstract void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks);
@Override @Override
public void process(List<Waypoint> waypoints) { public void process(List<Waypoint> waypoints) {
List<Track> tracks = null; List<Track> tracks = new ArrayList<>();
if (filters.getArtifactTypes().contains(ARTIFACT_TYPE.TSK_GPS_TRACK)) { if (filters.getArtifactTypes().contains(ARTIFACT_TYPE.TSK_GPS_TRACK)) {
try { try {
tracks = Track.getTracks(Case.getCurrentCase().getSleuthkitCase(), filters.getDataSources()); tracks = Track.getTracks(Case.getCurrentCase().getSleuthkitCase(), filters.getDataSources());
@ -87,11 +88,15 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter
logger.log(Level.WARNING, "Exception thrown while retrieving list of Tracks", ex); logger.log(Level.WARNING, "Exception thrown while retrieving list of Tracks", ex);
} }
} }
Pair<List<Waypoint>, List<List<Waypoint>>> waypointsAndTracks = createWaypointList(waypoints, tracks);
List<Waypoint> completeList = createWaypointList(waypoints, tracks); final Set<MapWaypoint> pointSet = MapWaypoint.getWaypoints(waypointsAndTracks.getKey());
final Set<MapWaypoint> pointSet = MapWaypoint.getWaypoints(completeList); final List<Set<MapWaypoint>> trackSets = new ArrayList<>();
for (List<Waypoint> t : waypointsAndTracks.getValue()) {
trackSets.add(MapWaypoint.getWaypoints(t));
}
handleFilteredWaypointSet(pointSet); handleFilteredWaypointSet(pointSet, trackSets);
} }
/** /**
@ -104,8 +109,9 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter
* @return A list of waypoints including the tracks based on the current * @return A list of waypoints including the tracks based on the current
* filters. * filters.
*/ */
private List<Waypoint> createWaypointList(List<Waypoint> waypoints, List<Track> tracks) { private Pair<List<Waypoint>, List<List<Waypoint>>> createWaypointList(List<Waypoint> waypoints, List<Track> tracks) {
final List<Waypoint> completeList = new ArrayList<>(); final List<Waypoint> completeList = new ArrayList<>();
List<List<Waypoint>> filteredTracks = new ArrayList<>();
if (tracks != null) { if (tracks != null) {
Long timeRangeEnd; Long timeRangeEnd;
@ -117,19 +123,22 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter
timeRangeStart = timeRangeEnd - (86400 * filters.getMostRecentNumDays()); timeRangeStart = timeRangeEnd - (86400 * filters.getMostRecentNumDays());
completeList.addAll(getWaypointsInRange(timeRangeStart, timeRangeEnd, waypoints)); completeList.addAll(getWaypointsInRange(timeRangeStart, timeRangeEnd, waypoints));
completeList.addAll(getTracksInRange(timeRangeStart, timeRangeEnd, tracks));
filteredTracks = getTracksInRange(timeRangeStart, timeRangeEnd, tracks);
for (List<Waypoint> filteredTrack : filteredTracks) {
completeList.addAll(filteredTrack);
}
} else { } else {
completeList.addAll(waypoints); completeList.addAll(waypoints);
for (Track track : tracks) { for (Track track : tracks) {
completeList.addAll(track.getPath()); completeList.addAll(track.getPath());
filteredTracks.add(track.getPath());
} }
} }
} else { } else {
completeList.addAll(waypoints); completeList.addAll(waypoints);
} }
return new Pair<>(completeList, filteredTracks);
return completeList;
} }
/** /**
@ -158,31 +167,30 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter
} }
/** /**
* Return a list of waypoints from the given tracks that fall into for * Return a list of lists of waypoints from the given tracks that fall into
* tracks that fall into the given time range. The track start time will * the given time range. The track start time will used for determining if
* used for determining if the whole track falls into the range. * the whole track falls into the range.
* *
* @param timeRangeStart start timestamp of range (seconds from java epoch) * @param timeRangeStart start timestamp of range (seconds from java epoch)
* @param timeRangeEnd start timestamp of range (seconds from java epoch) * @param timeRangeEnd start timestamp of range (seconds from java epoch)
* @param tracks Track list. * @param tracks Track list.
* *
* @return A list of waypoints that that belong to tracks that fall into the * @return A list of lists of waypoints corresponding to belong to tracks
* time range. * that exist within the time range.
*/ */
private List<Waypoint> getTracksInRange(Long timeRangeStart, Long timeRangeEnd, List<Track> tracks) { private List<List<Waypoint>> getTracksInRange(Long timeRangeStart, Long timeRangeEnd, List<Track> tracks) {
List<Waypoint> completeList = new ArrayList<>(); List<List<Waypoint>> ret = new ArrayList<>();
if (tracks != null) { if (tracks != null) {
for (Track track : tracks) { for (Track track : tracks) {
Long trackTime = track.getStartTime(); Long trackTime = track.getStartTime();
if ((trackTime == null && filters.showWaypointsWithoutTimeStamp()) if ((trackTime == null && filters.showWaypointsWithoutTimeStamp())
|| (trackTime != null && (trackTime >= timeRangeStart && trackTime <= timeRangeEnd))) { || (trackTime != null && (trackTime >= timeRangeStart && trackTime <= timeRangeEnd))) {
ret.add(track.getPath());
completeList.addAll(track.getPath());
} }
} }
} }
return completeList; return ret;
} }
/** /**

View File

@ -330,7 +330,7 @@ public final class GeolocationTopComponent extends TopComponent {
* *
* @param waypointList * @param waypointList
*/ */
void addWaypointsToMap(Set<MapWaypoint> waypointList) { void addWaypointsToMap(Set<MapWaypoint> waypointList, List<Set<MapWaypoint>> tracks) {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -347,6 +347,8 @@ public final class GeolocationTopComponent extends TopComponent {
} }
mapPanel.clearWaypoints(); mapPanel.clearWaypoints();
mapPanel.setWaypoints(waypointList); mapPanel.setWaypoints(waypointList);
mapPanel.setTracks(tracks);
mapPanel.initializePainter();
setWaypointLoading(false); setWaypointLoading(false);
geoFilterPanel.setEnabled(true); geoFilterPanel.setEnabled(true);
} }
@ -499,8 +501,8 @@ public final class GeolocationTopComponent extends TopComponent {
} }
@Override @Override
void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints) { void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks) {
addWaypointsToMap(mapWaypoints); addWaypointsToMap(mapWaypoints, tracks);
} }
} }
} }

View File

@ -19,11 +19,13 @@
package org.sleuthkit.autopsy.geolocation; package org.sleuthkit.autopsy.geolocation;
import java.awt.AlphaComposite; import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Point; import java.awt.Point;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter; import java.awt.event.ComponentAdapter;
@ -71,6 +73,9 @@ import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.jxmapviewer.painter.CompoundPainter;
import org.jxmapviewer.painter.Painter;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
/** /**
* The map panel. This panel contains the jxmapviewer MapViewer * The map panel. This panel contains the jxmapviewer MapViewer
@ -84,6 +89,7 @@ final public class MapPanel extends javax.swing.JPanel {
private boolean zoomChanging; private boolean zoomChanging;
private KdTree<MapWaypoint> waypointTree; private KdTree<MapWaypoint> waypointTree;
private Set<MapWaypoint> waypointSet; private Set<MapWaypoint> waypointSet;
private List<Set<MapWaypoint>> tracks = new ArrayList<>();
private Popup currentPopup; private Popup currentPopup;
private final PopupFactory popupFactory; private final PopupFactory popupFactory;
@ -96,6 +102,7 @@ final public class MapPanel extends javax.swing.JPanel {
private BufferedImage transparentWaypointImage; private BufferedImage transparentWaypointImage;
private MapWaypoint currentlySelectedWaypoint; private MapWaypoint currentlySelectedWaypoint;
private Set<MapWaypoint> currentlySelectedTrack;
/** /**
* Creates new form MapPanel * Creates new form MapPanel
@ -204,6 +211,10 @@ final public class MapPanel extends javax.swing.JPanel {
mapViewer.setCenterPosition(new GeoPosition(0, 0)); mapViewer.setCenterPosition(new GeoPosition(0, 0));
initializePainter();
}
void initializePainter() {
// Basic painters for the way points. // Basic painters for the way points.
WaypointPainter<MapWaypoint> waypointPainter = new WaypointPainter<MapWaypoint>() { WaypointPainter<MapWaypoint> waypointPainter = new WaypointPainter<MapWaypoint>() {
@Override @Override
@ -217,7 +228,12 @@ final public class MapPanel extends javax.swing.JPanel {
}; };
waypointPainter.setRenderer(new MapWaypointRenderer()); waypointPainter.setRenderer(new MapWaypointRenderer());
mapViewer.setOverlayPainter(waypointPainter); ArrayList<Painter<JXMapViewer>> painters = new ArrayList<>();
painters.add(new MapTrackRenderer(tracks));
painters.add(waypointPainter);
CompoundPainter<JXMapViewer> compoundPainter = new CompoundPainter<>(painters);
mapViewer.setOverlayPainter(compoundPainter);
} }
/** /**
@ -306,6 +322,15 @@ final public class MapPanel extends javax.swing.JPanel {
mapViewer.repaint(); mapViewer.repaint();
} }
/**
* Stores the given List of tracks from which to draw paths later
*
* @param tracks
*/
void setTracks(List<Set<MapWaypoint>> tracks) {
this.tracks = tracks;
}
/** /**
* Set the current zoom level. * Set the current zoom level.
* *
@ -324,6 +349,7 @@ final public class MapPanel extends javax.swing.JPanel {
void clearWaypoints() { void clearWaypoints() {
waypointTree = null; waypointTree = null;
currentlySelectedWaypoint = null; currentlySelectedWaypoint = null;
currentlySelectedTrack = null;
if (currentPopup != null) { if (currentPopup != null) {
currentPopup.hide(); currentPopup.hide();
} }
@ -661,9 +687,17 @@ final public class MapPanel extends javax.swing.JPanel {
if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) { if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) {
List<MapWaypoint> waypoints = findClosestWaypoint(evt.getPoint()); List<MapWaypoint> waypoints = findClosestWaypoint(evt.getPoint());
if (waypoints.size() > 0) { if (waypoints.size() > 0) {
currentlySelectedWaypoint = waypoints.get(0); MapWaypoint selection = waypoints.get(0);
currentlySelectedWaypoint = selection;
for (Set<MapWaypoint> track : tracks) {
if (track.contains(selection)) {
currentlySelectedTrack = track;
break;
}
}
} else { } else {
currentlySelectedWaypoint = null; currentlySelectedWaypoint = null;
currentlySelectedTrack = null;
} }
showDetailsPopup(); showDetailsPopup();
} }
@ -691,18 +725,18 @@ final public class MapPanel extends javax.swing.JPanel {
*/ */
private class MapWaypointRenderer implements WaypointRenderer<MapWaypoint> { private class MapWaypointRenderer implements WaypointRenderer<MapWaypoint> {
private final Map<Color, BufferedImage> imageCache = new HashMap<>(); private final Map<Color, BufferedImage> dotImageCache = new HashMap<>();
private final Map<Color, BufferedImage> waypointImageCache = new HashMap<>();
/** /**
* *
* @param waypoint the waypoint for which to get the color * @param waypoint the waypoint for which to get the color selected
* @param currentlySelectedWaypoint the waypoint that is currently
* selected
* @return the color that this waypoint should be rendered * @return the color that this waypoint should be rendered
*/ */
private Color getColor(MapWaypoint waypoint, MapWaypoint currentlySelectedWaypoint) { private Color getColor(MapWaypoint waypoint) {
Color baseColor = waypoint.getColor(); Color baseColor = waypoint.getColor();
if (waypoint.equals(currentlySelectedWaypoint)) { if (waypoint.equals(currentlySelectedWaypoint)
|| (currentlySelectedTrack != null && currentlySelectedTrack.contains(waypoint))) {
// Highlight this waypoint since it is selected // Highlight this waypoint since it is selected
return Color.YELLOW; return Color.YELLOW;
} else { } else {
@ -710,10 +744,32 @@ final public class MapPanel extends javax.swing.JPanel {
} }
} }
/**
* Creates a dot image with the specified color
*
* @param color the color of the new image
* @return the new dot image
*/
private BufferedImage createTrackDotImage(Color color) {
int w = 10;
int h = 10;
BufferedImage ret = new BufferedImage(w + 2, h + 2, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = ret.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.fillOval(1, 1, w, h);
g.setColor(Color.BLACK);
g.setStroke(new BasicStroke(1));
g.drawOval(1, 1, w, h);
g.dispose();
return ret;
}
/** /**
* Creates a waypoint image with the specified color * Creates a waypoint image with the specified color
* *
* @param color the color of the new waypoint image * @param color the color of the new image
* @return the new waypoint image * @return the new waypoint image
*/ */
private BufferedImage createWaypointImage(Color color) { private BufferedImage createWaypointImage(Color color) {
@ -723,6 +779,7 @@ final public class MapPanel extends javax.swing.JPanel {
BufferedImage ret = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); BufferedImage ret = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = ret.createGraphics(); Graphics2D g = ret.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(whiteWaypointImage, 0, 0, null); g.drawImage(whiteWaypointImage, 0, 0, null);
g.setComposite(AlphaComposite.SrcIn); g.setComposite(AlphaComposite.SrcIn);
g.setColor(color); g.setColor(color);
@ -734,22 +791,88 @@ final public class MapPanel extends javax.swing.JPanel {
} }
@Override @Override
public void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, MapWaypoint waypoint) { public void paintWaypoint(Graphics2D g, JXMapViewer map, MapWaypoint waypoint) {
Color color = getColor(waypoint, currentlySelectedWaypoint); Color color = getColor(waypoint);
BufferedImage image;
// Store computed images in cache for later use int artifactType = waypoint.getArtifactTypeID();
BufferedImage image = imageCache.computeIfAbsent(color, k -> { Point2D point = map.getTileFactory().geoToPixel(waypoint.getPosition(), map.getZoom());
return createWaypointImage(color);
});
Point2D point = jxmv.getTileFactory().geoToPixel(waypoint.getPosition(), jxmv.getZoom());
int x = (int) point.getX(); int x = (int) point.getX();
int y = (int) point.getY(); int y = (int) point.getY();
gd = (Graphics2D) gd.create(); if (artifactType == ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID()
gd.drawImage(image, x - image.getWidth() / 2, y - image.getHeight(), null); || artifactType == ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID()
gd.dispose(); || artifactType == ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID()) {
image = dotImageCache.computeIfAbsent(color, k -> {
return createTrackDotImage(color);
});
// Center the dot on the GPS coordinate
y -= image.getHeight() / 2;
} else {
image = waypointImageCache.computeIfAbsent(color, k -> {
return createWaypointImage(color);
});
// Align the bottom of the pin with the GPS coordinate
y -= image.getHeight();
}
// Center image horizontally on image
x -= image.getWidth() / 2;
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(image, x, y, null);
g2d.dispose();
}
}
/**
* Renderer for map track routes
*/
private class MapTrackRenderer implements Painter<JXMapViewer> {
private final List<Set<MapWaypoint>> tracks;
MapTrackRenderer(List<Set<MapWaypoint>> tracks) {
this.tracks = tracks;
}
private void drawRoute(Set<MapWaypoint> track, Graphics2D g, JXMapViewer map) {
int lastX = 0;
int lastY = 0;
boolean first = true;
for (MapWaypoint wp : track) {
Point2D p = map.getTileFactory().geoToPixel(wp.getPosition(), map.getZoom());
int thisX = (int) p.getX();
int thisY = (int) p.getY();
if (first) {
first = false;
} else {
g.drawLine(lastX, lastY, thisX, thisY);
}
lastX = thisX;
lastY = thisY;
}
}
@Override
public void paint(Graphics2D g, JXMapViewer map, int w, int h) {
Graphics2D g2d = (Graphics2D) g.create();
Rectangle bounds = map.getViewportBounds();
g2d.translate(-bounds.x, -bounds.y);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(2));
for (Set<MapWaypoint> track : tracks) {
drawRoute(track, g2d, map);
}
g2d.dispose();
} }
} }
} }

View File

@ -84,7 +84,7 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe
private final GeoPosition position; private final GeoPosition position;
/** /**
* Returns a list of of MapWaypoint objects for the given list of * Returns a list of MapWaypoint objects for the given list of
* datamodel.Waypoint objects. * datamodel.Waypoint objects.
* *
* @param dmWaypoints * @param dmWaypoints
@ -199,6 +199,13 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe
return getFormattedDetails(dataModelWaypoint); return getFormattedDetails(dataModelWaypoint);
} }
/**
* Returns the artifact type for this waypoint's data source
*/
int getArtifactTypeID() {
return dataModelWaypoint.getArtifact().getArtifactTypeID();
}
/** /**
* Returns a list of JMenuItems for the waypoint. The list list may contain * Returns a list of JMenuItems for the waypoint. The list list may contain
* nulls which should be removed or replaced with JSeparators. * nulls which should be removed or replaced with JSeparators.

View File

@ -98,7 +98,7 @@ public final class WaypointBuilder {
* *
* @param wwaypoints This of waypoints. * @param wwaypoints This of waypoints.
*/ */
void process(List<Waypoint> wwaypoints); void process(List<Waypoint> waypoints);
} }
/** /**

View File

@ -44,7 +44,6 @@ from org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints import Tr
from org.sleuthkit.autopsy.datamodel import ContentUtils from org.sleuthkit.autopsy.datamodel import ContentUtils
from org.sleuthkit.autopsy.ingest import IngestModule from org.sleuthkit.autopsy.ingest import IngestModule
from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException
from org.sleuthkit.autopsy.ingest import DataSourceIngestModule
from org.sleuthkit.autopsy.ingest import FileIngestModule from org.sleuthkit.autopsy.ingest import FileIngestModule
from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter
from org.sleuthkit.autopsy.ingest import IngestMessage from org.sleuthkit.autopsy.ingest import IngestMessage
@ -60,9 +59,14 @@ import gpxpy
import gpxpy.gpx import gpxpy.gpx
import gpxpy.parser import gpxpy.parser
# to get a random filename to prevent race conditions
import uuid
# Factory that defines the name and details of the module and allows Autopsy # Factory that defines the name and details of the module and allows Autopsy
# to create instances of the modules that will do the analysis. # to create instances of the modules that will do the analysis.
class GPXParserDataSourceIngestModuleFactory(IngestModuleFactoryAdapter):
class GPXParserFileIngestModuleFactory(IngestModuleFactoryAdapter):
moduleName = "GPX Parser" moduleName = "GPX Parser"
@ -75,77 +79,66 @@ class GPXParserDataSourceIngestModuleFactory(IngestModuleFactoryAdapter):
def getModuleVersionNumber(self): def getModuleVersionNumber(self):
return "1.2" return "1.2"
def isDataSourceIngestModuleFactory(self): def isFileIngestModuleFactory(self):
return True return True
def createDataSourceIngestModule(self, ingestOptions): def createFileIngestModule(self, ingestOptions):
return GPXParserDataSourceIngestModule() return GPXParserFileIngestModule()
# Data Source-level ingest module. One gets created per data source. # File level ingest module.
class GPXParserDataSourceIngestModule(DataSourceIngestModule): class GPXParserFileIngestModule(FileIngestModule):
logger = Logger.getLogger(GPXParserDataSourceIngestModuleFactory.moduleName) logger = Logger.getLogger(
GPXParserFileIngestModuleFactory.moduleName)
writeDebugMsgs = False writeDebugMsgs = False
def log(self, level, msg): def log(self, level, msg):
self.logger.logp(level, self.__class__.__name__, inspect.stack()[1][3], msg) self.logger.logp(level, self.__class__.__name__,
inspect.stack()[1][3], msg)
def __init__(self): def __init__(self):
self.context = None self.context = None
self.fileCount = 0
# Where any setup and configuration is done.
def startUp(self, context):
self.context = context
# Where the analysis is done.
def process(self, dataSource, progressBar):
# We don't know how much work there is yet.
progressBar.switchToIndeterminate()
# Get the case database and its blackboard.
skCase = Case.getCurrentCase().getSleuthkitCase()
blackboard = skCase.getBlackboard()
# Get any files with a .gpx extension.
# It would perhaps be better to get these files by MIME type instead.
# RC: It would also be better if this were a file level ingest module so it could process files extracted from archives.
fileManager = Case.getCurrentCase().getServices().getFileManager()
files = fileManager.findFiles(dataSource, "%.gpx")
# Update the progress bar now that we know how much work there is to do.
numFiles = len(files)
if self.writeDebugMsgs: self.log(Level.INFO, "Found " + str(numFiles) + " GPX files")
progressBar.switchToDeterminate(numFiles)
# Get the module name, it will be needed for adding attributes # Get the module name, it will be needed for adding attributes
moduleName = GPXParserDataSourceIngestModuleFactory.moduleName self.moduleName = GPXParserFileIngestModuleFactory.moduleName
# Get the case database and its blackboard.
self.skCase = Case.getCurrentCase().getSleuthkitCase()
self.blackboard = self.skCase.getBlackboard()
# Check if a folder for this module is present in the case Temp directory. # Check if a folder for this module is present in the case Temp directory.
# If not, create it. # If not, create it.
dirName = os.path.join(Case.getCurrentCase().getTempDirectory(), "GPX_Parser_Module") self.dirName = os.path.join(
Case.getCurrentCase().getTempDirectory(), "GPX_Parser_Module")
try: try:
os.stat(dirName) os.stat(self.dirName)
except: except:
os.mkdir(dirName) os.mkdir(self.dirName)
# Where any setup and configuration is done.
def startUp(self, context):
self.context = context
self.fileCount = 0
# Where the file analysis is done.
def process(self, file):
if not file.getName().lower().endswith(".gpx"):
return IngestModule.ProcessResult.OK
# Create a temp file name. It appears that we cannot close and delete # Create a temp file name. It appears that we cannot close and delete
# this file, but we can overwrite it for each file we need to process. # this file, but we can overwrite it for each file we need to process.
fileName = os.path.join(dirName, "tmp.gpx") fileName = os.path.join(self.dirName, uuid.uuid4().hex + ".gpx")
fileCount = 0;
for file in files:
# Create a GeoArtifactsHelper for this file. # Create a GeoArtifactsHelper for this file.
geoArtifactHelper = GeoArtifactsHelper(skCase, moduleName, None, file) geoArtifactHelper = GeoArtifactsHelper(
self.skCase, self.moduleName, None, file)
# Check if the user pressed cancel while we were busy. if self.writeDebugMsgs:
if self.context.isJobCancelled(): self.log(Level.INFO, "Processing " + file.getUniquePath() +
return IngestModule.ProcessResult.OK " (objID = " + str(file.getId()) + ")")
if self.writeDebugMsgs: self.log(Level.INFO, "Processing " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
fileCount += 1
# Write the file so that it can be parsed by gpxpy. # Write the file so that it can be parsed by gpxpy.
localFile = File(fileName) localFile = File(fileName)
@ -155,13 +148,19 @@ class GPXParserDataSourceIngestModule(DataSourceIngestModule):
gpxfile = open(fileName) gpxfile = open(fileName)
try: try:
gpx = gpxpy.parse(gpxfile) gpx = gpxpy.parse(gpxfile)
if self.writeDebugMsgs: self.log(Level.INFO, "Parsed " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") if self.writeDebugMsgs:
self.log(Level.INFO, "Parsed " + file.getUniquePath() +
" (objID = " + str(file.getId()) + ")")
except Exception as e: except Exception as e:
self.log(Level.WARNING, "Error parsing file " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e)) self.log(Level.WARNING, "Error parsing file " + file.getUniquePath() +
continue " (objID = " + str(file.getId()) + "):" + str(e))
return IngestModule.ProcessResult.ERROR
if gpx: if gpx:
if self.writeDebugMsgs: self.log(Level.INFO, "Processing tracks from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") if self.writeDebugMsgs:
self.log(Level.INFO, "Processing tracks from " +
file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
for track in gpx.tracks: for track in gpx.tracks:
for segment in track.segments: for segment in track.segments:
geoPointList = GeoTrackPoints() geoPointList = GeoTrackPoints()
@ -174,59 +173,82 @@ class GPXParserDataSourceIngestModule(DataSourceIngestModule):
timeStamp = 0 timeStamp = 0
try: try:
if (point.time != None): if (point.time != None):
timeStamp = long(time.mktime(point.time.timetuple())) timeStamp = long(time.mktime(
point.time.timetuple()))
except Exception as e: except Exception as e:
self.log(Level.WARNING, "Error getting track timestamp from " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e)) self.log(Level.WARNING, "Error getting track timestamp from " +
file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e))
geoPointList.addPoint(TrackPoint(point.latitude, point.longitude, elevation, None, 0, 0, 0, timeStamp)) geoPointList.addPoint(TrackPoint(
point.latitude, point.longitude, elevation, None, 0, 0, 0, timeStamp))
try: try:
geoArtifactHelper.addTrack("Track", geoPointList, None) geoArtifactHelper.addTrack("Track", geoPointList, None)
except Blackboard.BlackboardException as e: except Blackboard.BlackboardException as e:
self.log(Level.SEVERE, "Error posting GPS track artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) self.log(Level.SEVERE, "Error posting GPS track artifact for " +
file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
except TskCoreException as e: except TskCoreException as e:
self.log(Level.SEVERE, "Error creating GPS track artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) self.log(Level.SEVERE, "Error creating GPS track artifact for " +
file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
if self.writeDebugMsgs:
self.log(Level.INFO, "Processing waypoints from " +
file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
if self.writeDebugMsgs: self.log(Level.INFO, "Processing waypoints from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
for waypoint in gpx.waypoints: for waypoint in gpx.waypoints:
try: try:
art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK) art = file.newArtifact(
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK)
attributes = ArrayList() attributes = ArrayList()
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, waypoint.latitude)) attributes.add(BlackboardAttribute(
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, waypoint.longitude)) BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), self.moduleName, waypoint.latitude))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG.getTypeID(), moduleName, "Waypoint")) attributes.add(BlackboardAttribute(
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, waypoint.name)) BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), self.moduleName, waypoint.longitude))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "GPXParser")) attributes.add(BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG.getTypeID(), self.moduleName, "Waypoint"))
attributes.add(BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), self.moduleName, waypoint.name))
attributes.add(BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), self.moduleName, "GPXParser"))
art.addAttributes(attributes) art.addAttributes(attributes)
blackboard.postArtifact(art, moduleName) self.blackboard.postArtifact(art, self.moduleName)
except Blackboard.BlackboardException as e: except Blackboard.BlackboardException as e:
self.log(Level.SEVERE, "Error posting GPS bookmark artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) self.log(Level.SEVERE, "Error posting GPS bookmark artifact for " +
file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
except TskCoreException as e: except TskCoreException as e:
self.log(Level.SEVERE, "Error creating GPS bookmark artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) self.log(Level.SEVERE, "Error creating GPS bookmark artifact for " +
file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
if self.writeDebugMsgs:
self.log(Level.INFO, "Processing routes from " +
file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
if self.writeDebugMsgs: self.log(Level.INFO, "Processing routes from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
for route in gpx.routes: for route in gpx.routes:
geoWaypoints = GeoWaypoints() geoWaypoints = GeoWaypoints()
for point in route.points: for point in route.points:
geoWaypoints.addPoint(Waypoint(point.latitude, point.longitude, point.elevation, point.name)) geoWaypoints.addPoint(
Waypoint(point.latitude, point.longitude, point.elevation, point.name))
try: try:
geoArtifactHelper.addRoute(None, None, geoWaypoints, None) geoArtifactHelper.addRoute(None, None, geoWaypoints, None)
except Blackboard.BlackboardException as e: except Blackboard.BlackboardException as e:
self.log("Error posting GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) self.log("Error posting GPS route artifact for " + file.getUniquePath() +
" (objID = " + str(file.getId()) + "):" + e.getMessage())
except TskCoreException as e: except TskCoreException as e:
self.log(Level.SEVERE, "Error creating GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) self.log(Level.SEVERE, "Error creating GPS route artifact for " +
file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
# Update the progress bar. self.fileCount += 1
progressBar.progress(fileCount) return IngestModule.ProcessResult.OK
# Post a message to the ingest messages inbox. def shutDown(self):
message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, moduleName, "Processed %d files" % fileCount) message = IngestMessage.createMessage(
IngestServices.getInstance().postMessage(message) IngestMessage.MessageType.DATA, GPXParserFileIngestModuleFactory.moduleName,
return IngestModule.ProcessResult.OK; str(self.fileCount) + " files found")
ingestServices = IngestServices.getInstance().postMessage(message)

View File

@ -23,8 +23,6 @@ There is no need for manual installation of additional dependencies if the Windo
If you want the Japanese localized version, you must have the Japanese language pack (http://support.microsoft.com/kb/972813) installed and the default locale set to JA. (http://windows.microsoft.com/en-us/windows/change-system-locale#1TC=windows-7). If you want the Japanese localized version, you must have the Japanese language pack (http://support.microsoft.com/kb/972813) installed and the default locale set to JA. (http://windows.microsoft.com/en-us/windows/change-system-locale#1TC=windows-7).
Refer to the KNOWN_ISSUES.txt file for known bugs that could cause investigation problems.
SUPPORT SUPPORT

BIN
thirdparty/IcePDF 6.2.2/batik-awt-util-1.6.jar vendored Executable file

Binary file not shown.

BIN
thirdparty/IcePDF 6.2.2/batik-dom-1.6.jar vendored Executable file

Binary file not shown.

BIN
thirdparty/IcePDF 6.2.2/batik-svg-dom-1.6.jar vendored Executable file

Binary file not shown.

BIN
thirdparty/IcePDF 6.2.2/batik-svggen-1.6.jar vendored Executable file

Binary file not shown.

BIN
thirdparty/IcePDF 6.2.2/batik-util-1.6.jar vendored Executable file

Binary file not shown.

BIN
thirdparty/IcePDF 6.2.2/batik-xml-1.6.jar vendored Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
thirdparty/IcePDF 6.2.2/icepdf-core-6.2.2.jar vendored Executable file

Binary file not shown.

Binary file not shown.

BIN
thirdparty/IcePDF 6.2.2/jai_core-1.1.3.jar vendored Executable file

Binary file not shown.

BIN
thirdparty/IcePDF 6.2.2/jai_imageio-1.1.jar vendored Executable file

Binary file not shown.