mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge remote-tracking branch 'upstream/develop' into 6965-Complete-aLeapp-Module
This commit is contained in:
commit
ffd2e1e719
12
.gitignore
vendored
Normal file → Executable file
12
.gitignore
vendored
Normal file → Executable file
@ -89,3 +89,15 @@ hs_err_pid*.log
|
||||
*.img
|
||||
*.vhd
|
||||
*.E01
|
||||
|
||||
/thirdparty/yara/yarabridge/yarabridge/x64/
|
||||
/thirdparty/yara/yarabridge/yarabridge.VC.db
|
||||
/thirdparty/yara/yarabridge/yarabridge.VC.VC.opendb
|
||||
/thirdparty/yara/yarabridge/x64/
|
||||
/thirdparty/yara/YaraWrapperTest/nbproject/private/
|
||||
/thirdparty/yara/YaraWrapperTest/build/
|
||||
/thirdparty/yara/YaraJNIWrapper/dist/
|
||||
/thirdparty/yara/YaraJNIWrapper/build/
|
||||
/thirdparty/yara/YaraJNIWrapper/nbproject/private/
|
||||
/thirdparty/yara/yarabridge/.vs/
|
||||
|
||||
|
@ -6,6 +6,7 @@ jobs:
|
||||
- os: linux
|
||||
dist: bionic
|
||||
- os: osx
|
||||
osx_image: xcode12.2
|
||||
|
||||
env:
|
||||
global:
|
||||
|
116
Core/build.xml
116
Core/build.xml
@ -103,6 +103,11 @@
|
||||
<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}/DatCon/3.6.9/DatCon.jar" todir="${ext.dir}" />
|
||||
<!--Copy YARA to release-->
|
||||
<copy todir="${basedir}/release/yara" >
|
||||
<fileset dir="${thirdparty.dir}/yara/bin"/>
|
||||
</copy>
|
||||
<copy file="${thirdparty.dir}/yara/bin/YaraJNIWrapper.jar" todir="${ext.dir}" />
|
||||
</target>
|
||||
|
||||
|
||||
@ -123,8 +128,8 @@
|
||||
tofile="${ext.dir}/sleuthkit-${TSK_VERSION}.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/sqlite-jdbc-3.25.2.jar"
|
||||
tofile="${ext.dir}/sqlite-jdbc-3.25.2.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/postgresql-9.4.1211.jre7.jar"
|
||||
tofile="${ext.dir}/postgresql-9.4.1211.jre7.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/postgresql-42.2.18.jar"
|
||||
tofile="${ext.dir}/postgresql-42.2.18.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/mchange-commons-java-0.2.9.jar"
|
||||
tofile="${ext.dir}/mchange-commons-java-0.2.9.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/c3p0-0.9.5.jar"
|
||||
@ -167,7 +172,7 @@
|
||||
<get src="https://drive.google.com/uc?id=1ns2olaWsBu_c4EoE4Seh8t_B3U5RnLKd" dest="${test-input}/CommonFilesAttrs_img1_v1.vhd" skipexisting="true"/>
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
<target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist">
|
||||
<mkdir dir="${ext.dir}"/>
|
||||
<copy file="${thirdparty.dir}/LICENSE-2.0.txt" todir="${ext.dir}" />
|
||||
@ -200,5 +205,110 @@
|
||||
<globmapper from="*" to="*-MERGED"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!--sets up integration test system properties, calls underlying test-init and then sets up the pathing jar-->
|
||||
<target name="test-init" depends="projectized-common.test-init,getTestDataFiles,qa-functional-pathing-jar,unit-test-path-simplification" />
|
||||
|
||||
<!--
|
||||
The paths specified in 'module.run.classpath' are incorporated into the manifest of a jar and then the path to the
|
||||
jar is used as part of the classpath for '-do-junit' instead of 'module.run.classpath'. This was done to prevent
|
||||
classpath length issues on windows. More information on this technique can be found here:
|
||||
https://stackoverflow.com/a/201969.
|
||||
-->
|
||||
<target name="qa-functional-pathing-jar" depends="projectized-common.test-init">
|
||||
<sequential>
|
||||
<!--set up pathing jar based on module.run.classpath as classpath-->
|
||||
<path id="test.qa-functional.pathing-jar.module-cp.classpath" path="${module.run.classpath}"/>
|
||||
<pathconvert pathsep=" " refid="test.qa-functional.pathing-jar.module-cp.classpath" property="test.qa-functional.pathing-jar.module-cp.classpathstr"/>
|
||||
<property name="test.qa-functional.pathing-jar.module-cp.loc" value="${cluster}/test.qa-functional.pathing.module-cp.jar"/>
|
||||
<jar destfile="${test.qa-functional.pathing-jar.module-cp.loc}">
|
||||
<manifest>
|
||||
<attribute name="Class-Path" value="${test.qa-functional.pathing-jar.module-cp.classpathstr}"/>
|
||||
</manifest>
|
||||
</jar>
|
||||
|
||||
<!--grab properties from common.xml:test-init so that "test.qa-functional.run.cp" can be properly formed-->
|
||||
<property name="build.test.qa-functional.dir" location="${build.dir}/test/qa-functional"/>
|
||||
<property name="build.test.qa-functional.classes.dir" location="${build.test.qa-functional.dir}/classes"/>
|
||||
<property name="test.qa-functional.cp.extra" value=""/>
|
||||
|
||||
<!--set up "test.qa-functional.run.cp" to be used by common.xml:-do-junit-->
|
||||
<path id="test.qa-functional.run.cp">
|
||||
<pathelement path="${build.test.qa-functional.classes.dir}"/>
|
||||
<!-- Cannot use <path refid="cp"/> since that uses ${module.classpath} and we want ${module.run.classpath}: -->
|
||||
<pathelement path="${test.qa-functional.runtime.cp}"/>
|
||||
<pathelement path="${cp.extra}"/>
|
||||
<pathelement location="${cluster}/${module.jar}"/>
|
||||
<path refid="test.unit.lib.cp"/>
|
||||
<!-- for compatibility with property based classpath-->
|
||||
<pathelement path="${test.qa-functional.pathing-jar.module-cp.loc}"/>
|
||||
<pathelement path="${test.qa-functional.run.cp.extra}"/>
|
||||
<pathelement path="${test.qa-functional.cp.extra}"/>
|
||||
<pathelement path="${test.extra.nb.javac.deps}"/>
|
||||
</path>
|
||||
</sequential>
|
||||
</target>
|
||||
|
||||
|
||||
<!--
|
||||
This specifies the classpath for unit tests using * notation
|
||||
(i.e. https://stackoverflow.com/questions/219585/including-all-the-jars-in-a-directory-within-the-java-classpath).
|
||||
This solution involves taking the initial ‘module.run.classpath’ property and simplifying it to the directories containing jars
|
||||
(i.e. instead of “/dir/lib1.jar:/dir/lib2.jar:/dir/lib3.jar” it becomes “/dir/*” ).
|
||||
More information on ‘module.run.classpath’ can be found in “netbeans-plat\11.3\harness\README” and it appears that
|
||||
“netbeans-plat\11.3\harness\build.xml:build-init target is in charge of setting the ‘module.run.classpath’ variable.
|
||||
More information in Jira: 6970.
|
||||
-->
|
||||
<target name="unit-test-path-simplification" depends="projectized-common.test-init">
|
||||
<sequential>
|
||||
<script language="javascript">
|
||||
<![CDATA[
|
||||
var moduleRunClasspath = project.getProperty("module.run.classpath");
|
||||
|
||||
var directories = [];
|
||||
// searches for jar file parent directories with path separators of \ or /
|
||||
var individualPathRegex = /^\s*(.+?[\\\/])[^\\\/]+?\.jar\s*$/i;
|
||||
// split on ':' but not 'C:\'
|
||||
var classPathRegex = /((C:\\)?.+?)(:|$)/gi;
|
||||
var match;
|
||||
while((match = classPathRegex.exec(moduleRunClasspath)) !== null) {
|
||||
var thisPath = match[1];
|
||||
var pathMatch = thisPath.match(individualPathRegex);
|
||||
// find unique matches
|
||||
if (pathMatch && directories.indexOf(pathMatch[1]) < 0) {
|
||||
directories.push(pathMatch[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// suffix with *
|
||||
for (var i = 0; i < directories.length; i++) {
|
||||
directories[i] = directories[i] + "*";
|
||||
}
|
||||
|
||||
// set project property
|
||||
project.setNewProperty("test.unit.abbreviatedModuleRunClassPath", directories.join(":"));
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<!--grab properties from common.xml:test-init so that "test.unit.run.cp" can be properly formed-->
|
||||
<property name="build.test.unit.dir" location="${build.dir}/test/unit"/>
|
||||
<property name="build.test.unit.classes.dir" location="${build.test.unit.dir}/classes"/>
|
||||
<property name="test.unit.cp.extra" value=""/>
|
||||
|
||||
<!--set up "test.unit.run.cp" to be used by common.xml:-do-junit-->
|
||||
<path id="test.unit.run.cp">
|
||||
<pathelement path="${build.test.unit.classes.dir}"/>
|
||||
<!-- Cannot use <path refid="cp"/> since that uses ${module.classpath} and we want ${module.run.classpath}: -->
|
||||
<pathelement path="${test.unit.runtime.cp}"/>
|
||||
<pathelement path="${cp.extra}"/>
|
||||
<pathelement location="${cluster}/${module.jar}"/>
|
||||
<path refid="test.unit.lib.cp"/>
|
||||
<!-- for compatibility with property based classpath-->
|
||||
<pathelement path="${test.unit.abbreviatedModuleRunClassPath}"/>
|
||||
<pathelement path="${test.unit.run.cp.extra}"/>
|
||||
<pathelement path="${test.unit.cp.extra}"/>
|
||||
<pathelement path="${test.extra.nb.javac.deps}"/>
|
||||
</path>
|
||||
</sequential>
|
||||
</target>
|
||||
</project>
|
||||
|
@ -99,7 +99,7 @@ file.reference.opencensus-api-0.19.2.jar=release\\modules\\ext\\opencensus-api-0
|
||||
file.reference.opencensus-contrib-grpc-metrics-0.19.2.jar=release\\modules\\ext\\opencensus-contrib-grpc-metrics-0.19.2.jar
|
||||
file.reference.opencensus-contrib-http-util-0.19.2.jar=release\\modules\\ext\\opencensus-contrib-http-util-0.19.2.jar
|
||||
file.reference.opennlp-tools-1.9.1.jar=release\\modules\\ext\\opennlp-tools-1.9.1.jar
|
||||
file.reference.postgresql-9.4.1211.jre7.jar=release\\modules\\ext\\postgresql-9.4.1211.jre7.jar
|
||||
file.reference.postgresql-42.2.18.jar=release\\modules\\ext\\postgresql-42.2.18.jar
|
||||
file.reference.proto-google-cloud-translate-v3beta1-0.53.0.jar=release\\modules\\ext\\proto-google-cloud-translate-v3beta1-0.53.0.jar
|
||||
file.reference.proto-google-common-protos-1.15.0.jar=release\\modules\\ext\\proto-google-common-protos-1.15.0.jar
|
||||
file.reference.proto-google-iam-v1-0.12.0.jar=release\\modules\\ext\\proto-google-iam-v1-0.12.0.jar
|
||||
@ -118,6 +118,7 @@ file.reference.StixLib.jar=release\\modules\\ext\\StixLib.jar
|
||||
file.reference.threetenbp-1.3.3.jar=release\\modules\\ext\\threetenbp-1.3.3.jar
|
||||
file.reference.webp-imageio-sejda-0.1.0.jar=release\\modules\\ext\\webp-imageio-sejda-0.1.0.jar
|
||||
file.reference.xmpcore-5.1.3.jar=release\\modules\\ext\\xmpcore-5.1.3.jar
|
||||
file.reference.YaraJNIWrapper.jar=release/modules/ext/YaraJNIWrapper.jar
|
||||
file.reference.zookeeper-3.4.6.jar=release\\modules\\ext\\zookeeper-3.4.6.jar
|
||||
javac.source=1.8
|
||||
javac.compilerargs=-Xlint -Xlint:-serial
|
||||
|
@ -232,7 +232,8 @@
|
||||
<build-prerequisite/>
|
||||
<compile-dependency/>
|
||||
<run-dependency>
|
||||
<specification-version>1.0</specification-version>
|
||||
<release-version>1</release-version>
|
||||
<specification-version>23</specification-version>
|
||||
</run-dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -435,6 +436,10 @@
|
||||
<runtime-relative-path>ext/commons-codec-1.11.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\commons-codec-1.11.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/postgresql-42.2.18.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\postgresql-42.2.18.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/commons-pool2-2.4.2.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\commons-pool2-2.4.2.jar</binary-origin>
|
||||
@ -547,18 +552,10 @@
|
||||
<runtime-relative-path>ext/checker-compat-qual-2.5.3.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\checker-compat-qual-2.5.3.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sleuthkit-4.10.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sleuthkit-4.10.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/animal-sniffer-annotations-1.17.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\animal-sniffer-annotations-1.17.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sleuthkit-caseuco-4.10.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sleuthkit-caseuco-4.10.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/gax-1.44.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\gax-1.44.0.jar</binary-origin>
|
||||
@ -567,6 +564,10 @@
|
||||
<runtime-relative-path>ext/jsoup-1.10.3.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jsoup-1.10.3.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/YaraJNIWrapper.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/YaraJNIWrapper.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/grpc-context-1.19.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\grpc-context-1.19.0.jar</binary-origin>
|
||||
@ -671,6 +672,10 @@
|
||||
<runtime-relative-path>ext/grpc-alts-1.19.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\grpc-alts-1.19.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sleuthkit-caseuco-4.10.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sleuthkit-caseuco-4.10.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jdom-2.0.5.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jdom-2.0.5.jar</binary-origin>
|
||||
@ -723,10 +728,6 @@
|
||||
<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>
|
||||
<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>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/junit-3.8.1.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\junit-3.8.1.jar</binary-origin>
|
||||
@ -795,6 +796,10 @@
|
||||
<runtime-relative-path>ext/sevenzipjbinding-AllPlatforms.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\sevenzipjbinding-AllPlatforms.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sleuthkit-4.10.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sleuthkit-4.10.1.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>
|
||||
|
@ -253,3 +253,7 @@ UnpackagePortableCaseProgressDialog.resultLabel.text=resultLabel
|
||||
UnpackagePortableCaseDialog.extractLabel.text=Folder to extract to:
|
||||
UnpackagePortableCaseDialog.caseLabel.text=Portable Case:
|
||||
NewCaseVisualPanel1.caseDataStoredLabel.text_1=Case data will be stored in the following directory:
|
||||
SolrNotConfiguredDialog.okButton.text=OK
|
||||
SolrNotConfiguredDialog.title=Solr 8 Server Not Configured
|
||||
SolrNotConfiguredDialog.EmptyKeywordSearchHostName=Solr 8 connection parameters are not configured. Please go to Tools->Options->Multi User.
|
||||
SolrNotConfiguredDialog.messageLabel.text=<html>Multi-User cases are enabled but Solr 8 server has not been configured.<br>\nNew cases can only be created with Solr 8. Please go to Tools->Options->Multi User.\n</html>
|
||||
|
@ -471,3 +471,7 @@ UnpackagePortableCaseProgressDialog.resultLabel.text=resultLabel
|
||||
UnpackagePortableCaseDialog.extractLabel.text=Folder to extract to:
|
||||
UnpackagePortableCaseDialog.caseLabel.text=Portable Case:
|
||||
NewCaseVisualPanel1.caseDataStoredLabel.text_1=Case data will be stored in the following directory:
|
||||
SolrNotConfiguredDialog.okButton.text=OK
|
||||
SolrNotConfiguredDialog.title=Solr 8 Server Not Configured
|
||||
SolrNotConfiguredDialog.EmptyKeywordSearchHostName=Solr 8 connection parameters are not configured. Please go to Tools->Options->Multi User.
|
||||
SolrNotConfiguredDialog.messageLabel.text=<html>Multi-User cases are enabled but Solr 8 server has not been configured.<br>\nNew cases can only be created with Solr 8. Please go to Tools->Options->Multi User.\n</html>
|
||||
|
78
Core/src/org/sleuthkit/autopsy/casemodule/SolrNotConfiguredDialog.form
Executable file
78
Core/src/org/sleuthkit/autopsy/casemodule/SolrNotConfiguredDialog.form
Executable file
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<Properties>
|
||||
<Property name="defaultCloseOperation" type="int" value="2"/>
|
||||
<Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/core/Bundle.properties" key="SolrNotConfiguredDialog.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="modal" type="boolean" value="true"/>
|
||||
<Property name="name" type="java.lang.String" value="toolsNotFound" noResource="true"/>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||
</SyntheticProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="0" pref="15" max="32767" attributes="0"/>
|
||||
<Component id="messageLabel" min="-2" pref="420" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="189" max="-2" attributes="0"/>
|
||||
<Component id="okButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="193" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="messageLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="okButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="okButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="SolrNotConfiguredDialog.okButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="messageLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="SolrNotConfiguredDialog.messageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
110
Core/src/org/sleuthkit/autopsy/casemodule/SolrNotConfiguredDialog.java
Executable file
110
Core/src/org/sleuthkit/autopsy/casemodule/SolrNotConfiguredDialog.java
Executable file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.casemodule;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import javax.swing.JFrame;
|
||||
import org.openide.util.ImageUtilities;
|
||||
import org.openide.windows.WindowManager;
|
||||
|
||||
/**
|
||||
* A dialog to notify the user on startup when Solr 8 server is not configured.
|
||||
*/
|
||||
class SolrNotConfiguredDialog extends javax.swing.JDialog {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates new form SolrNotConfiguredDialog
|
||||
*/
|
||||
SolrNotConfiguredDialog() {
|
||||
super((JFrame) WindowManager.getDefault().getMainWindow(), true);
|
||||
// Center the startup window.
|
||||
Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
int width = getSize().width;
|
||||
int height = getSize().height;
|
||||
setLocation((screenDimension.width - width) / 2, (screenDimension.height - height) / 2);
|
||||
initComponents();
|
||||
setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/warning16.png", false));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
okButton = new javax.swing.JButton();
|
||||
messageLabel = new javax.swing.JLabel();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setTitle(org.openide.util.NbBundle.getMessage(SolrNotConfiguredDialog.class, "SolrNotConfiguredDialog.title")); // NOI18N
|
||||
setModal(true);
|
||||
setName("toolsNotFound"); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(SolrNotConfiguredDialog.class, "SolrNotConfiguredDialog.okButton.text")); // NOI18N
|
||||
okButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
okButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(messageLabel, org.openide.util.NbBundle.getMessage(SolrNotConfiguredDialog.class, "SolrNotConfiguredDialog.messageLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(0, 15, Short.MAX_VALUE)
|
||||
.addComponent(messageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 420, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(189, 189, 189)
|
||||
.addComponent(okButton)
|
||||
.addGap(0, 193, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(messageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(okButton)
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
|
||||
this.dispose();
|
||||
}//GEN-LAST:event_okButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel messageLabel;
|
||||
private javax.swing.JButton okButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -23,11 +23,15 @@ import java.util.Iterator;
|
||||
import java.util.logging.Level;
|
||||
import org.netbeans.spi.sendopts.OptionProcessor;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.commandlineingest.CommandLineIngestManager;
|
||||
import org.sleuthkit.autopsy.commandlineingest.CommandLineOpenCaseManager;
|
||||
import org.sleuthkit.autopsy.commandlineingest.CommandLineOptionProcessor;
|
||||
import org.sleuthkit.autopsy.commandlineingest.CommandLineStartupWindow;
|
||||
import org.sleuthkit.autopsy.core.RuntimeProperties;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
|
||||
/**
|
||||
* Provides the start up window to rest of the application. It may return the
|
||||
@ -76,6 +80,10 @@ public class StartupWindowProvider implements StartupWindowInterface {
|
||||
}
|
||||
}
|
||||
|
||||
if (RuntimeProperties.runningWithGUI()) {
|
||||
checkSolr();
|
||||
}
|
||||
|
||||
//discover the registered windows
|
||||
Collection<? extends StartupWindowInterface> startupWindows
|
||||
= Lookup.getDefault().lookupAll(StartupWindowInterface.class);
|
||||
@ -115,6 +123,21 @@ public class StartupWindowProvider implements StartupWindowInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSolr() {
|
||||
|
||||
// if Multi-User settings are enabled and Solr8 server is not configured,
|
||||
// display an error message and a dialog
|
||||
if (UserPreferences.getIsMultiUserModeEnabled() && UserPreferences.getIndexingServerHost().isEmpty()) {
|
||||
// Solr 8 host name is not configured. This could be the first time user
|
||||
// runs Autopsy with Solr 8. Display a message.
|
||||
MessageNotifyUtil.Notify.error(NbBundle.getMessage(CueBannerPanel.class, "SolrNotConfiguredDialog.title"),
|
||||
NbBundle.getMessage(SolrNotConfiguredDialog.class, "SolrNotConfiguredDialog.EmptyKeywordSearchHostName"));
|
||||
|
||||
SolrNotConfiguredDialog dialog = new SolrNotConfiguredDialog();
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether Autopsy is running from command line. There is an
|
||||
* OptionProcessor that is responsible for processing command line inputs.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019-2019 Basis Technology Corp.
|
||||
* Copyright 2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -382,6 +382,31 @@ public class CentralRepoDbManager {
|
||||
CentralRepoDbUtil.setUseCentralRepo(true);
|
||||
saveNewCentralRepo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a PostgresDb using the settings for the given database choice
|
||||
* enum.
|
||||
*
|
||||
* @param choice Type of postgres DB to set up
|
||||
* @throws CentralRepoException
|
||||
*/
|
||||
public void setupPostgresDb(CentralRepoDbChoice choice) throws CentralRepoException {
|
||||
selectedDbChoice = choice;
|
||||
DatabaseTestResult curStatus = testStatus();
|
||||
if (curStatus == DatabaseTestResult.DB_DOES_NOT_EXIST) {
|
||||
createDb();
|
||||
curStatus = testStatus();
|
||||
}
|
||||
|
||||
// the only successful setup status is tested ok
|
||||
if (curStatus != DatabaseTestResult.TESTED_OK) {
|
||||
throw new CentralRepoException("Unable to successfully create postgres database. Test failed with: " + curStatus);
|
||||
}
|
||||
|
||||
// if successfully got here, then save the settings
|
||||
CentralRepoDbUtil.setUseCentralRepo(true);
|
||||
saveNewCentralRepo();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns if changes to the central repository configuration
|
||||
|
@ -198,11 +198,11 @@ final class PostgresCentralRepo extends RdbmsCentralRepo {
|
||||
if (connectionPool == null) {
|
||||
setupConnectionPool();
|
||||
}
|
||||
}
|
||||
try {
|
||||
return connectionPool.getConnection();
|
||||
} catch (SQLException ex) {
|
||||
throw new CentralRepoException("Error getting connection from connection pool.", Bundle.PostgresEamDb_connectionFailed_message(), ex); // NON-NLS
|
||||
try {
|
||||
return connectionPool.getConnection();
|
||||
} catch (SQLException ex) {
|
||||
throw new CentralRepoException("Error getting connection from connection pool.", Bundle.PostgresEamDb_connectionFailed_message(), ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,10 +121,12 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbConnectiv
|
||||
* @return
|
||||
*/
|
||||
String getConnectionURL(boolean usePostgresDb) {
|
||||
StringBuilder url = new StringBuilder();
|
||||
url.append(getJDBCBaseURI());
|
||||
url.append(getHost());
|
||||
url.append("/"); // NON-NLS
|
||||
StringBuilder url = new StringBuilder()
|
||||
.append(getJDBCBaseURI())
|
||||
.append(getHost())
|
||||
.append(":") // NON-NLS
|
||||
.append(getPort())
|
||||
.append("/"); // NON-NLS
|
||||
if (usePostgresDb) {
|
||||
url.append("postgres"); // NON-NLS
|
||||
} else {
|
||||
@ -153,7 +155,7 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbConnectiv
|
||||
} catch (ClassNotFoundException | SQLException ex) {
|
||||
// TODO: Determine why a connection failure (ConnectionException) re-throws
|
||||
// the SQLException and does not print this log message?
|
||||
LOGGER.log(Level.SEVERE, "Failed to acquire ephemeral connection to postgresql."); // NON-NLS
|
||||
LOGGER.log(Level.SEVERE, "Failed to acquire ephemeral connection to postgresql.", ex); // NON-NLS
|
||||
conn = null;
|
||||
}
|
||||
return conn;
|
||||
|
@ -1,4 +1,10 @@
|
||||
caseeventlistener.evidencetag=Evidence
|
||||
CentralRepositoryNotificationDialog.bulletHeader=This data is used to:
|
||||
CentralRepositoryNotificationDialog.bulletOne=Ignore common items (files, domains, and accounts)
|
||||
CentralRepositoryNotificationDialog.bulletThree=Create personas that group accounts
|
||||
CentralRepositoryNotificationDialog.bulletTwo=Identify where an item was previously seen
|
||||
CentralRepositoryNotificationDialog.finalRemarks=To limit what is stored, use the Central Repository options panel.
|
||||
CentralRepositoryNotificationDialog.header=Autopsy stores data about each case in its Central Repository.
|
||||
IngestEventsListener.ingestmodule.name=Central Repository
|
||||
IngestEventsListener.prevCaseComment.text=Previous Case:
|
||||
# {0} - typeName
|
||||
@ -7,6 +13,3 @@ IngestEventsListener.prevCount.text=Number of previous {0}: {1}
|
||||
IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository)
|
||||
IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)
|
||||
Installer.centralRepoUpgradeFailed.title=Central repository disabled
|
||||
Installer.initialCreateSqlite.messageDesc=It will store information about all hashes and identifiers that you process. You can use this to ignore previously seen files and make connections between cases.
|
||||
Installer.initialCreateSqlite.messageHeader=The Central Repository is not enabled. Would you like to enable it?
|
||||
Installer.initialCreateSqlite.title=Enable Central Repository?
|
||||
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* 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.centralrepository.eventlisteners;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.core.RuntimeProperties;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Version;
|
||||
|
||||
/**
|
||||
* Notifies new installations or old upgrades that the central repository will
|
||||
* be enabled by default.
|
||||
*/
|
||||
public class CentralRepositoryNotificationDialog {
|
||||
|
||||
/**
|
||||
* This dialog should display iff the mode is RELEASE and the
|
||||
* application is running with a GUI.
|
||||
*/
|
||||
static boolean shouldDisplay() {
|
||||
return Version.getBuildType() == Version.Type.RELEASE
|
||||
&& RuntimeProperties.runningWithGUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an informational modal dialog to the user, which is dismissed by
|
||||
* pressing 'OK'.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"CentralRepositoryNotificationDialog.header=Autopsy stores data about each case in its Central Repository.",
|
||||
"CentralRepositoryNotificationDialog.bulletHeader=This data is used to:",
|
||||
"CentralRepositoryNotificationDialog.bulletOne=Ignore common items (files, domains, and accounts)",
|
||||
"CentralRepositoryNotificationDialog.bulletTwo=Identify where an item was previously seen",
|
||||
"CentralRepositoryNotificationDialog.bulletThree=Create personas that group accounts",
|
||||
"CentralRepositoryNotificationDialog.finalRemarks=To limit what is stored, use the Central Repository options panel."
|
||||
})
|
||||
static void display() {
|
||||
assert shouldDisplay();
|
||||
|
||||
MessageNotifyUtil.Message.info(
|
||||
"<html>"
|
||||
+ "<body>"
|
||||
+ "<div>"
|
||||
+ "<p>" + Bundle.CentralRepositoryNotificationDialog_header() + "</p>"
|
||||
+ "<p>" + Bundle.CentralRepositoryNotificationDialog_bulletHeader() + "</p>"
|
||||
+ "<ul>"
|
||||
+ "<li>" + Bundle.CentralRepositoryNotificationDialog_bulletOne() + "</li>"
|
||||
+ "<li>" + Bundle.CentralRepositoryNotificationDialog_bulletTwo() + "</li>"
|
||||
+ "<li>" + Bundle.CentralRepositoryNotificationDialog_bulletThree() + "</li>"
|
||||
+ "</ul>"
|
||||
+ "<p>" + Bundle.CentralRepositoryNotificationDialog_finalRemarks() + "</p>"
|
||||
+ "</div>"
|
||||
+ "</body>"
|
||||
+ "</html>"
|
||||
);
|
||||
}
|
||||
}
|
@ -25,14 +25,13 @@ import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.modules.ModuleInstall;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.core.RuntimeProperties;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.Version;
|
||||
|
||||
/**
|
||||
* Adds/removes application event listeners responsible for adding data to the
|
||||
@ -81,19 +80,10 @@ public class Installer extends ModuleInstall {
|
||||
* the org.sleuthkit.autopsy.core package when the already installed
|
||||
* Autopsy-Core module is restored (during application startup).
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"Installer.initialCreateSqlite.title=Enable Central Repository?",
|
||||
"Installer.initialCreateSqlite.messageHeader=The Central Repository is not enabled. Would you like to enable it?",
|
||||
"Installer.initialCreateSqlite.messageDesc=It will store information about all hashes and identifiers that you process. "
|
||||
+ "You can use this to ignore previously seen files and make connections between cases."
|
||||
})
|
||||
@Override
|
||||
public void restored() {
|
||||
addApplicationEventListeners();
|
||||
|
||||
if (Version.getBuildType() == Version.Type.RELEASE) {
|
||||
setupDefaultCentralRepository();
|
||||
}
|
||||
setupDefaultCentralRepository();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,9 +97,9 @@ public class Installer extends ModuleInstall {
|
||||
|
||||
/**
|
||||
* Checks if the central repository has been set up and configured. If not,
|
||||
* either offers to perform set up (running with a GUI) or does the set up
|
||||
* unconditionally (not running with a GUI, e.g., in an automated ingest
|
||||
* node).
|
||||
* does the set up unconditionally. If the application is running with a
|
||||
* GUI, a notification will be displayed to the user if the mode is RELEASE
|
||||
* (in other words, developers are exempt from seeing the notification).
|
||||
*/
|
||||
private void setupDefaultCentralRepository() {
|
||||
Map<String, String> centralRepoSettings = ModuleSettings.getConfigSettings("CentralRepository");
|
||||
@ -127,62 +117,30 @@ public class Installer extends ModuleInstall {
|
||||
ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true");
|
||||
}
|
||||
}
|
||||
|
||||
// if central repository hasn't been previously initialized, initialize it
|
||||
if (!initialized) {
|
||||
// if running with a GUI, prompt the user
|
||||
if (RuntimeProperties.runningWithGUI()) {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
try {
|
||||
String dialogText
|
||||
= "<html><body>"
|
||||
+ "<div style='width: 400px;'>"
|
||||
+ "<p>" + NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.messageHeader") + "</p>"
|
||||
+ "<p style='margin-top: 10px'>" + NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.messageDesc") + "</p>"
|
||||
+ "</div>"
|
||||
+ "</body></html>";
|
||||
|
||||
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
|
||||
dialogText,
|
||||
NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.title"),
|
||||
JOptionPane.YES_NO_OPTION)) {
|
||||
|
||||
setupDefaultSqliteCentralRepo();
|
||||
}
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex);
|
||||
|
||||
doMessageBoxIfRunningInGUI(ex);
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException | InvocationTargetException ex) {
|
||||
logger.log(Level.SEVERE, "There was an error while running the swing utility invoke later while creating the central repository database", ex);
|
||||
}
|
||||
} // if no GUI, just initialize
|
||||
else {
|
||||
try {
|
||||
setupDefaultSqliteCentralRepo();
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex);
|
||||
|
||||
doMessageBoxIfRunningInGUI(ex);
|
||||
}
|
||||
}
|
||||
|
||||
ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true");
|
||||
|
||||
if(initialized) {
|
||||
return; // Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a default single-user SQLite central repository.
|
||||
*
|
||||
* @throws CentralRepoException If there is an error setting up teh central
|
||||
* repository.
|
||||
*/
|
||||
private void setupDefaultSqliteCentralRepo() throws CentralRepoException {
|
||||
CentralRepoDbManager manager = new CentralRepoDbManager();
|
||||
manager.setupDefaultSqliteDb();
|
||||
if (CentralRepositoryNotificationDialog.shouldDisplay()) {
|
||||
CentralRepositoryNotificationDialog.display();
|
||||
}
|
||||
|
||||
try {
|
||||
CentralRepoDbManager manager = new CentralRepoDbManager();
|
||||
if (UserPreferences.getIsMultiUserModeEnabled()) {
|
||||
// Set up using existing multi-user settings.
|
||||
manager.setupPostgresDb(CentralRepoDbChoice.POSTGRESQL_MULTIUSER);
|
||||
} else {
|
||||
manager.setupDefaultSqliteDb();
|
||||
}
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex);
|
||||
|
||||
doMessageBoxIfRunningInGUI(ex);
|
||||
}
|
||||
|
||||
ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,7 +67,7 @@ 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_TAGGED_NOTABLE_ITEMS = false;
|
||||
static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = false;
|
||||
static final boolean DEFAULT_CREATE_CR_PROPERTIES = true;
|
||||
|
||||
|
@ -18,40 +18,14 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commandlineingest;
|
||||
|
||||
import org.openide.util.NbPreferences;
|
||||
|
||||
/**
|
||||
* Provides convenient access to a UserPreferences node for user preferences
|
||||
* with default values.
|
||||
*/
|
||||
public final class UserPreferences {
|
||||
|
||||
private static final java.util.prefs.Preferences preferences = NbPreferences.forModule(UserPreferences.class);
|
||||
private static final String COMMAND_LINE_MODE_RESULTS_FOLDER = "CommandLineModeResultsFolder"; // NON-NLS
|
||||
private static final String COMMAND_LINE_MODE_CONTEXT_STRING = "CommandLineModeContext"; // NON-NLS
|
||||
|
||||
// Prevent instantiation.
|
||||
private UserPreferences() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get results folder for command line mode from persistent storage.
|
||||
*
|
||||
* @return String Selected output folder.
|
||||
*/
|
||||
public static String getCommandLineModeResultsFolder() {
|
||||
return preferences.get(COMMAND_LINE_MODE_RESULTS_FOLDER, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set results folder for command line mode from persistent storage.
|
||||
*
|
||||
* @param folder Selected output folder.
|
||||
*/
|
||||
public static void setCommandLineModeResultsFolder(String folder) {
|
||||
preferences.put(COMMAND_LINE_MODE_RESULTS_FOLDER, folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get context string for command line mode ingest module settings.
|
||||
*
|
||||
|
@ -96,7 +96,7 @@ Metadata.tableRowTitle.mimeType=MIME Type
|
||||
Metadata.tableRowTitle.name=Name
|
||||
Metadata.tableRowTitle.sectorSize=Sector Size
|
||||
Metadata.tableRowTitle.sha1=SHA1
|
||||
Metadata.tableRowTitle.sha256=SHA256
|
||||
Metadata.tableRowTitle.sha256=SHA-256
|
||||
Metadata.tableRowTitle.size=Size
|
||||
Metadata.tableRowTitle.fileNameAlloc=File Name Allocation
|
||||
Metadata.tableRowTitle.metadataAlloc=Metadata Allocation
|
||||
|
@ -137,7 +137,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
|
||||
"Metadata.tableRowTitle.mimeType=MIME Type",
|
||||
"Metadata.nodeText.truncated=(results truncated)",
|
||||
"Metadata.tableRowTitle.sha1=SHA1",
|
||||
"Metadata.tableRowTitle.sha256=SHA256",
|
||||
"Metadata.tableRowTitle.sha256=SHA-256",
|
||||
"Metadata.tableRowTitle.imageType=Type",
|
||||
"Metadata.tableRowTitle.sectorSize=Sector Size",
|
||||
"Metadata.tableRowTitle.timezone=Time Zone",
|
||||
@ -182,6 +182,11 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
|
||||
md5 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
|
||||
}
|
||||
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5);
|
||||
String sha256 = file.getSha256Hash();
|
||||
if (sha256 == null) {
|
||||
sha256 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
|
||||
}
|
||||
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sha256"), sha256);
|
||||
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.hashLookupResults"), file.getKnown().toString());
|
||||
addAcquisitionDetails(sb, dataSource);
|
||||
|
||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentviewers;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
@ -97,6 +98,9 @@ final class PDFViewer implements FileTypeViewer {
|
||||
|
||||
// Add the IcePDF view to the center of our container.
|
||||
this.container.add(icePdfPanel, BorderLayout.CENTER);
|
||||
|
||||
// Disable all components until the document is ready to view.
|
||||
enableComponents(container, false);
|
||||
|
||||
// Document is the 'M' in IcePDFs MVC set up. Read the data needed to
|
||||
// populate the model in the background.
|
||||
@ -122,12 +126,13 @@ final class PDFViewer implements FileTypeViewer {
|
||||
// will cause UI widgets to be updated.
|
||||
try {
|
||||
Document doc = get();
|
||||
controller.openDocument(doc, null);
|
||||
controller.openDocument(doc, file.getName());
|
||||
// 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);
|
||||
enableComponents(container, true);
|
||||
} catch (InterruptedException ex) {
|
||||
// Do nothing.
|
||||
} catch (ExecutionException ex) {
|
||||
@ -140,10 +145,28 @@ final class PDFViewer implements FileTypeViewer {
|
||||
file.getId(), file.getName()), ex);
|
||||
showErrorDialog();
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively enable/disable all components in this content viewer.
|
||||
* This will disable/enable all internal IcePDF Swing components too.
|
||||
*/
|
||||
private void enableComponents(Container container, boolean enabled) {
|
||||
Component[] components = container.getComponents();
|
||||
for(Component component : components) {
|
||||
component.setEnabled(enabled);
|
||||
if (component instanceof Container) {
|
||||
enableComponents((Container)component, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent() {
|
||||
|
@ -1,20 +1,3 @@
|
||||
# Copyright 2020 Basis Technology Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an "AS
|
||||
# IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
# express or implied. See the License for the specific language
|
||||
# governing permissions and limitations under the License.
|
||||
#
|
||||
|
||||
DefaultArtifactContentViewer.copyMenuItem.text=Copy
|
||||
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
|
||||
MessageArtifactViewer.ccLabel.text=CC:
|
||||
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
|
||||
MessageArtifactViewer.toText.text=to list goes here
|
||||
@ -32,3 +15,19 @@ MessageArtifactViewer.subjectLabel.text=Subject:
|
||||
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
|
||||
MessageArtifactViewer.ccText.text=cc list goes here
|
||||
MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text
|
||||
DefaultTableArtifactContentViewer.selectAllMenuItem.text=Select All
|
||||
# Copyright 2020 Basis Technology Corp.
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an "AS
|
||||
# IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
# express or implied. See the License for the specific language
|
||||
# governing permissions and limitations under the License.
|
||||
|
||||
DefaultTableArtifactContentViewer.copyMenuItem.text=Copy
|
||||
|
@ -1,18 +1,3 @@
|
||||
# Copyright 2020 Basis Technology Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an "AS
|
||||
# IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
# express or implied. See the License for the specific language
|
||||
# governing permissions and limitations under the License.
|
||||
#
|
||||
|
||||
CalllogArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas.
|
||||
CallLogArtifactViewer_heading_metadata=Metadata
|
||||
CallLogArtifactViewer_heading_others=Other Attributes
|
||||
@ -56,11 +41,15 @@ ContactArtifactViewer_persona_unknown=Unknown
|
||||
ContactArtifactViewer_phones_header=Phone
|
||||
DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database
|
||||
DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database
|
||||
DefaultArtifactContentViewer.attrsTableHeader.sources=Source(s)
|
||||
DefaultArtifactContentViewer.attrsTableHeader.type=Type
|
||||
DefaultArtifactContentViewer.attrsTableHeader.value=Value
|
||||
DefaultArtifactContentViewer.copyMenuItem.text=Copy
|
||||
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
|
||||
DefaultTableArtifactContentViewer.attrsTableHeader.sources=Source(s)
|
||||
DefaultTableArtifactContentViewer.attrsTableHeader.type=Type
|
||||
DefaultTableArtifactContentViewer.attrsTableHeader.value=Value
|
||||
GeneralPurposeArtifactViewer.details.attrHeader=Attributes
|
||||
GeneralPurposeArtifactViewer.details.dataSource=Data Source
|
||||
GeneralPurposeArtifactViewer.details.file=File
|
||||
GeneralPurposeArtifactViewer.details.sourceHeader=Source
|
||||
GeneralPurposeArtifactViewer.unknown.text=Unknown
|
||||
GeneralPurposeArtifactViewer_menuitem_copy=Copy
|
||||
MessageAccountPanel.account.justification=Account found in Message artifact
|
||||
MessageAccountPanel_button_create_label=Create
|
||||
MessageAccountPanel_button_view_label=View
|
||||
@ -87,6 +76,22 @@ MessageArtifactViewer.subjectLabel.text=Subject:
|
||||
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
|
||||
MessageArtifactViewer.ccText.text=cc list goes here
|
||||
MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text
|
||||
DefaultTableArtifactContentViewer.selectAllMenuItem.text=Select All
|
||||
# Copyright 2020 Basis Technology Corp.
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an "AS
|
||||
# IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
# express or implied. See the License for the specific language
|
||||
# governing permissions and limitations under the License.
|
||||
|
||||
DefaultTableArtifactContentViewer.copyMenuItem.text=Copy
|
||||
PersonaAccountFetcher.account.justification=Account found in Call Log artifact
|
||||
# {0} - Persona count
|
||||
PersonaDisplayTask_persona_count_suffix=(1 of {0})
|
||||
|
@ -41,8 +41,6 @@ DataContentViewerArtifact.failedToGetSourcePath.message=\u30b1\u30fc\u30b9\u30fb
|
||||
DefaultArtifactContentViewer.attrsTableHeader.sources=\u30bd\u30fc\u30b9
|
||||
DefaultArtifactContentViewer.attrsTableHeader.type=\u30bf\u30a4\u30d7
|
||||
DefaultArtifactContentViewer.attrsTableHeader.value=\u5024
|
||||
DefaultArtifactContentViewer.copyMenuItem.text=\u30b3\u30d4\u30fc
|
||||
DefaultArtifactContentViewer.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e
|
||||
MessageAccountPanel_button_create_label=\u4f5c\u6210
|
||||
MessageAccountPanel_button_view_label=\u8868\u793a
|
||||
MessageAccountPanel_no_matches=\u4e00\u81f4\u3059\u308b\u3082\u306e\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002
|
||||
@ -67,3 +65,5 @@ MessageArtifactViewer.toLabel.text=\u5b9b\u5148\uff1a
|
||||
MessageArtifactViewer.toText.text=\u3053\u3053\u306b\u5b9b\u5148\u30ea\u30b9\u30c8\u3092\u8a18\u5165
|
||||
MessageArtifactViewer.viewInNewWindowButton.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a
|
||||
PersonaDisplayTask_persona_count_suffix=\uff081/{0}\uff09
|
||||
DefaultTableArtifactContentViewer.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e
|
||||
DefaultTableArtifactContentViewer.copyMenuItem.text=\u30b3\u30d4\u30fc
|
||||
|
@ -63,7 +63,7 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param constraints Constraints to use.
|
||||
* @param headerString Heading string to display.
|
||||
*
|
||||
* @return JLabel Heading label added.
|
||||
@ -109,6 +109,23 @@ final class CommunicationArtifactViewerHelper {
|
||||
return headingLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a key value row to the specified panel with the specified layout and
|
||||
* constraints.
|
||||
*
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constraints to use.
|
||||
* @param keyString Key name to display.
|
||||
* @param valueString Value string to display.
|
||||
*
|
||||
*/
|
||||
static void addNameValueRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String keyString, String valueString) {
|
||||
addKey(panel, gridbagLayout, constraints, keyString);
|
||||
addValue(panel, gridbagLayout, constraints, valueString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given component to the panel.
|
||||
*
|
||||
@ -116,7 +133,7 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param constraints Constraints to use.
|
||||
* @param component Component to add.
|
||||
*/
|
||||
static void addComponent(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, JComponent component) {
|
||||
@ -132,7 +149,7 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param constraints Constraints to use.
|
||||
*/
|
||||
static void addLineEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
|
||||
// Place the filler just past the last column.
|
||||
@ -159,7 +176,7 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param constraints Constraints to use.
|
||||
*/
|
||||
static void addPageEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
|
||||
|
||||
@ -185,7 +202,7 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param constraints Constraints to use.
|
||||
*/
|
||||
static void addBlankLine(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
|
||||
constraints.gridy++;
|
||||
@ -203,7 +220,7 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param constraints Constraints to use.
|
||||
* @param keyString Key name to display.
|
||||
*
|
||||
* @return Label added.
|
||||
@ -217,7 +234,7 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param constraints Constraints to use.
|
||||
* @param keyString Key name to display.
|
||||
* @param gridx column index, must be less than MAX_COLS - 1.
|
||||
*
|
||||
@ -246,8 +263,8 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param keyString Value string to display.
|
||||
* @param constraints Constraints to use.
|
||||
* @param valueString Value string to display.
|
||||
*
|
||||
* @return Label added.
|
||||
*/
|
||||
@ -260,7 +277,7 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param constraints Constraints to use.
|
||||
* @param keyString Value string to display.
|
||||
* @param gridx Column index, must be less than MAX_COLS;
|
||||
*
|
||||
@ -367,7 +384,7 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param constraints Constraints to use.
|
||||
* @param accountIdentifier Account identifier to search the persona.
|
||||
*
|
||||
* @return List of AccountPersonaSearcherData objects.
|
||||
@ -435,7 +452,7 @@ final class CommunicationArtifactViewerHelper {
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param constraints Constraints to use.
|
||||
* @param contactId Contact name to display.
|
||||
*
|
||||
* @return A JLabel with the contact information.
|
||||
|
@ -11,14 +11,14 @@
|
||||
<MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="DefaultArtifactContentViewer.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="DefaultTableArtifactContentViewer.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="DefaultArtifactContentViewer.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="DefaultTableArtifactContentViewer.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</MenuItem>
|
||||
@ -27,7 +27,7 @@
|
||||
</NonVisualComponents>
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[100, 58]"/>
|
||||
<Dimension value="[0, 0]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
@ -59,8 +59,11 @@
|
||||
<Properties>
|
||||
<Property name="horizontalScrollBarPolicy" type="int" value="32"/>
|
||||
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 0]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[620, 34]"/>
|
||||
<Dimension value="[0, 0]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
@ -54,37 +54,36 @@ import com.google.gson.JsonArray;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel;
|
||||
//import org.sleuthkit.autopsy.contentviewers.Bundle;
|
||||
|
||||
/**
|
||||
* This class displays a Blackboard artifact as a table listing all it's
|
||||
* attributes names and values.
|
||||
* This class displays a Blackboard artifact as a table of its attributes.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class DefaultArtifactContentViewer extends javax.swing.JPanel implements ArtifactContentViewer {
|
||||
public class DefaultTableArtifactContentViewer extends AbstractArtifactDetailsPanel implements ArtifactContentViewer {
|
||||
|
||||
@NbBundle.Messages({
|
||||
"DefaultArtifactContentViewer.attrsTableHeader.type=Type",
|
||||
"DefaultArtifactContentViewer.attrsTableHeader.value=Value",
|
||||
"DefaultArtifactContentViewer.attrsTableHeader.sources=Source(s)",
|
||||
"DefaultTableArtifactContentViewer.attrsTableHeader.type=Type",
|
||||
"DefaultTableArtifactContentViewer.attrsTableHeader.value=Value",
|
||||
"DefaultTableArtifactContentViewer.attrsTableHeader.sources=Source(s)",
|
||||
"DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
|
||||
"DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
|
||||
})
|
||||
|
||||
private final static Logger logger = Logger.getLogger(DefaultArtifactContentViewer.class.getName());
|
||||
|
||||
|
||||
private final static Logger logger = Logger.getLogger(DefaultTableArtifactContentViewer.class.getName());
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
private static final String[] COLUMN_HEADERS = {
|
||||
Bundle.DefaultArtifactContentViewer_attrsTableHeader_type(),
|
||||
Bundle.DefaultArtifactContentViewer_attrsTableHeader_value(),
|
||||
Bundle.DefaultArtifactContentViewer_attrsTableHeader_sources()};
|
||||
Bundle.DefaultTableArtifactContentViewer_attrsTableHeader_type(),
|
||||
Bundle.DefaultTableArtifactContentViewer_attrsTableHeader_value(),
|
||||
Bundle.DefaultTableArtifactContentViewer_attrsTableHeader_sources()};
|
||||
private static final int[] COLUMN_WIDTHS = {100, 800, 100};
|
||||
private static final int CELL_BOTTOM_MARGIN = 5;
|
||||
private static final int CELL_RIGHT_MARGIN = 1;
|
||||
|
||||
public DefaultArtifactContentViewer() {
|
||||
public DefaultTableArtifactContentViewer() {
|
||||
initResultsTable();
|
||||
initComponents();
|
||||
resultsTableScrollPane.setViewportView(resultsTable);
|
||||
@ -124,7 +123,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void columnMarginChanged(ChangeEvent e) {
|
||||
updateRowHeights(); //When the user changes column width we may need to resize row height
|
||||
}
|
||||
@ -153,12 +152,12 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
Component comp = resultsTable.prepareRenderer(
|
||||
resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex);
|
||||
final int rowHeight;
|
||||
if (comp instanceof JTextArea) {
|
||||
if (comp instanceof JTextArea) {
|
||||
final JTextArea tc = (JTextArea) comp;
|
||||
final View rootView = tc.getUI().getRootView(tc);
|
||||
java.awt.Insets i = tc.getInsets();
|
||||
rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex)
|
||||
.getWidth() - (i.left + i.right +CELL_RIGHT_MARGIN), //current width minus borders
|
||||
.getWidth() - (i.left + i.right + CELL_RIGHT_MARGIN), //current width minus borders
|
||||
Integer.MAX_VALUE);
|
||||
rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS);
|
||||
} else {
|
||||
@ -202,17 +201,18 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
selectAllMenuItem = new javax.swing.JMenuItem();
|
||||
resultsTableScrollPane = new javax.swing.JScrollPane();
|
||||
|
||||
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultArtifactContentViewer.class, "DefaultArtifactContentViewer.copyMenuItem.text")); // NOI18N
|
||||
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultTableArtifactContentViewer.class, "DefaultTableArtifactContentViewer.copyMenuItem.text")); // NOI18N
|
||||
rightClickMenu.add(copyMenuItem);
|
||||
|
||||
selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultArtifactContentViewer.class, "DefaultArtifactContentViewer.selectAllMenuItem.text")); // NOI18N
|
||||
selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultTableArtifactContentViewer.class, "DefaultTableArtifactContentViewer.selectAllMenuItem.text")); // NOI18N
|
||||
rightClickMenu.add(selectAllMenuItem);
|
||||
|
||||
setPreferredSize(new java.awt.Dimension(100, 58));
|
||||
setPreferredSize(new java.awt.Dimension(0, 0));
|
||||
|
||||
resultsTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
|
||||
resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(620, 34));
|
||||
resultsTableScrollPane.setMinimumSize(new java.awt.Dimension(0, 0));
|
||||
resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(0, 0));
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
@ -267,7 +267,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
* Resets the components to an empty view state.
|
||||
*/
|
||||
private void resetComponents() {
|
||||
|
||||
|
||||
((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
|
||||
}
|
||||
|
||||
@ -279,7 +279,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
@Override
|
||||
public void setArtifact(BlackboardArtifact artifact) {
|
||||
try {
|
||||
ResultsTableArtifact resultsTableArtifact = new ResultsTableArtifact(artifact, artifact.getParent());
|
||||
ResultsTableArtifact resultsTableArtifact = artifact == null ? null : new ResultsTableArtifact(artifact, artifact.getParent());
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
@ -289,7 +289,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
});
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error getting parent content for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
|
||||
logger.log(Level.SEVERE, String.format("Error getting parent content for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
|
||||
}
|
||||
|
||||
}
|
||||
@ -301,7 +301,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a container to hold the data necessary for the artifact
|
||||
* This class is a container to hold the data necessary for the artifact
|
||||
* being viewed.
|
||||
*/
|
||||
private class ResultsTableArtifact {
|
||||
@ -340,20 +340,20 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
*/
|
||||
String value;
|
||||
switch (attr.getAttributeType().getValueType()) {
|
||||
|
||||
|
||||
// Use Autopsy date formatting settings, not TSK defaults
|
||||
case DATETIME:
|
||||
value = epochTimeToString(attr.getValueLong());
|
||||
break;
|
||||
case JSON:
|
||||
case JSON:
|
||||
// Get the attribute's JSON value and convert to indented multiline display string
|
||||
String jsonVal = attr.getValueString();
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject json = parser.parse(jsonVal).getAsJsonObject();
|
||||
|
||||
|
||||
value = toJsonDisplayString(json, "");
|
||||
break;
|
||||
|
||||
|
||||
case STRING:
|
||||
case INTEGER:
|
||||
case LONG:
|
||||
@ -398,43 +398,43 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
String getArtifactDisplayName() {
|
||||
return artifactDisplayName;
|
||||
}
|
||||
|
||||
|
||||
private static final String INDENT_RIGHT = " ";
|
||||
private static final String NEW_LINE = "\n";
|
||||
|
||||
|
||||
/**
|
||||
* Recursively converts a JSON element into an indented multi-line
|
||||
* display string.
|
||||
*
|
||||
* @param element JSON element to convert
|
||||
* @param element JSON element to convert
|
||||
* @param startIndent Starting indentation for the element.
|
||||
*
|
||||
* @return A multi-line display string.
|
||||
*/
|
||||
private String toJsonDisplayString(JsonElement element, String startIndent) {
|
||||
|
||||
|
||||
StringBuilder sb = new StringBuilder("");
|
||||
JsonObject obj = element.getAsJsonObject();
|
||||
|
||||
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
|
||||
appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb );
|
||||
appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb);
|
||||
}
|
||||
|
||||
String returnString = sb.toString();
|
||||
if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
|
||||
if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
|
||||
returnString = returnString.substring(NEW_LINE.length());
|
||||
}
|
||||
return returnString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Converts the given JSON element into string and appends to the given string builder.
|
||||
*
|
||||
* Converts the given JSON element into string and appends to the given
|
||||
* string builder.
|
||||
*
|
||||
* @param jsonKey
|
||||
* @param jsonElement
|
||||
* @param startIndent Starting indentation for the element.
|
||||
* @param sb String builder to append to.
|
||||
* @param sb String builder to append to.
|
||||
*/
|
||||
private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) {
|
||||
if (jsonElement.isJsonArray()) {
|
||||
@ -463,11 +463,12 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts epoch time to readable string.
|
||||
*
|
||||
*
|
||||
* @param epochTime epoch time value to be converted to string.
|
||||
*
|
||||
* @return String with human readable time.
|
||||
*/
|
||||
private String epochTimeToString(long epochTime) {
|
||||
@ -482,21 +483,20 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the table view with the given artifact data.
|
||||
*
|
||||
* Updates the table view with the given artifact data.
|
||||
*
|
||||
* It should be called on EDT.
|
||||
*
|
||||
* @param resultsTableArtifact Artifact data to display in the view.
|
||||
*/
|
||||
private void updateView(ResultsTableArtifact resultsTableArtifact) {
|
||||
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
|
||||
DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
|
||||
tModel.setDataVector(resultsTableArtifact.getRows(), COLUMN_HEADERS);
|
||||
String[][] rows = resultsTableArtifact == null ? new String[0][0] : resultsTableArtifact.getRows();
|
||||
tModel.setDataVector(rows, COLUMN_HEADERS);
|
||||
updateColumnSizes();
|
||||
updateRowHeights();
|
||||
resultsTable.clearSelection();
|
||||
|
||||
this.setCursor(null);
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 0]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Form>
|
@ -0,0 +1,383 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* 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.artifactviewers;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Panel to display the details for an Artifact.
|
||||
*/
|
||||
@ServiceProvider(service = ArtifactContentViewer.class)
|
||||
public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel implements ArtifactContentViewer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(GeneralPurposeArtifactViewer.class.getName());
|
||||
// Number of columns in the gridbag layout.
|
||||
private final static int MAX_COLS = 4;
|
||||
private final static Insets ROW_INSETS = new java.awt.Insets(0, 12, 0, 0);
|
||||
private final static Insets HEADER_INSETS = new java.awt.Insets(0, 0, 0, 0);
|
||||
private final static double GLUE_WEIGHT_X = 1.0;
|
||||
private final static double TEXT_WEIGHT_X = 0.0;
|
||||
private final static int LABEL_COLUMN = 0;
|
||||
private final static int VALUE_COLUMN = 1;
|
||||
private final static int VALUE_WIDTH = 2;
|
||||
private final static int LABEL_WIDTH = 1;
|
||||
private static final Integer[] DEFAULT_ORDERING = new Integer[]{BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_REFERRER.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()};
|
||||
private final GridBagLayout gridBagLayout = new GridBagLayout();
|
||||
private final GridBagConstraints gridBagConstraints = new GridBagConstraints();
|
||||
private final Map<Integer, Integer[]> orderingMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Creates new form GeneralPurposeArtifactViewer.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
public GeneralPurposeArtifactViewer() {
|
||||
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID(), DEFAULT_ORDERING);
|
||||
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID(), DEFAULT_ORDERING);
|
||||
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), DEFAULT_ORDERING);
|
||||
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), DEFAULT_ORDERING);
|
||||
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(), DEFAULT_ORDERING);
|
||||
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID(), DEFAULT_ORDERING);
|
||||
initComponents();
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"GeneralPurposeArtifactViewer.unknown.text=Unknown"})
|
||||
@Override
|
||||
public void setArtifact(BlackboardArtifact artifact) {
|
||||
resetComponent();
|
||||
if (artifact != null) {
|
||||
String dataSourceName = Bundle.GeneralPurposeArtifactViewer_unknown_text();
|
||||
String sourceFileName = Bundle.GeneralPurposeArtifactViewer_unknown_text();
|
||||
Map<Integer, List<BlackboardAttribute>> attributeMap = new HashMap<>();
|
||||
try {
|
||||
// Get all the attributes and group them by the attributeType
|
||||
for (BlackboardAttribute bba : artifact.getAttributes()) {
|
||||
List<BlackboardAttribute> attrList = attributeMap.get(bba.getAttributeType().getTypeID());
|
||||
if (attrList == null) {
|
||||
attrList = new ArrayList<>();
|
||||
}
|
||||
attrList.add(bba);
|
||||
attributeMap.put(bba.getAttributeType().getTypeID(), attrList);
|
||||
}
|
||||
dataSourceName = artifact.getDataSource().getName();
|
||||
sourceFileName = artifact.getParent().getName();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get attributes for artifact " + artifact.getArtifactID(), ex);
|
||||
}
|
||||
updateView(artifact.getArtifactTypeID(), attributeMap, dataSourceName, sourceFileName);
|
||||
}
|
||||
this.setLayout(this.gridBagLayout);
|
||||
this.revalidate();
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the panel so that it is empty.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void resetComponent() {
|
||||
// clear the panel
|
||||
this.removeAll();
|
||||
gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.gridx = LABEL_COLUMN;
|
||||
gridBagConstraints.weighty = 0.0;
|
||||
gridBagConstraints.weightx = TEXT_WEIGHT_X; // keep components fixed horizontally.
|
||||
gridBagConstraints.fill = GridBagConstraints.NONE;
|
||||
gridBagConstraints.insets = ROW_INSETS;
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public Component getComponent() {
|
||||
// Slap a vertical scrollbar on the panel.
|
||||
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public boolean isSupported(BlackboardArtifact artifact) {
|
||||
return (artifact != null)
|
||||
&& (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()
|
||||
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()
|
||||
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID()
|
||||
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID());
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"GeneralPurposeArtifactViewer.details.attrHeader=Attributes",
|
||||
"GeneralPurposeArtifactViewer.details.sourceHeader=Source",
|
||||
"GeneralPurposeArtifactViewer.details.dataSource=Data Source",
|
||||
"GeneralPurposeArtifactViewer.details.file=File"})
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
setPreferredSize(new java.awt.Dimension(0, 0));
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 400, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 300, Short.MAX_VALUE)
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
/**
|
||||
* Update the view to reflect the current artifact's details.
|
||||
*
|
||||
* @param artifactTypeId The BlackboardArtifact type id for the artifact
|
||||
* being displayed.
|
||||
* @param attributeMap The map of attributes that exist for the artifact.
|
||||
* @param dataSourceName The name of the datasource that caused the creation
|
||||
* of the artifact.
|
||||
* @param sourceFileName The name of the file that caused the creation of
|
||||
* the artifact.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void updateView(Integer artifactTypeId, Map<Integer, List<BlackboardAttribute>> attributeMap, String dataSourceName, String sourceFileName) {
|
||||
if (!(artifactTypeId < 1 || artifactTypeId >= Integer.MAX_VALUE)) {
|
||||
addHeader(Bundle.GeneralPurposeArtifactViewer_details_attrHeader());
|
||||
Integer[] orderingArray = orderingMap.get(artifactTypeId);
|
||||
if (orderingArray == null) {
|
||||
orderingArray = DEFAULT_ORDERING;
|
||||
}
|
||||
for (Integer attrId : orderingArray) {
|
||||
List<BlackboardAttribute> attrList = attributeMap.remove(attrId);
|
||||
if (attrList != null) {
|
||||
for (BlackboardAttribute bba : attrList) {
|
||||
addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int key : attributeMap.keySet()) {
|
||||
for (BlackboardAttribute bba : attributeMap.get(key)) {
|
||||
addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString());
|
||||
}
|
||||
}
|
||||
addHeader(Bundle.GeneralPurposeArtifactViewer_details_sourceHeader());
|
||||
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_dataSource(), dataSourceName);
|
||||
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_file(), sourceFileName);
|
||||
// add veritcal glue at the end
|
||||
addPageEndGlue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new heading to the panel.
|
||||
*
|
||||
* @param headerString Heading string to display.
|
||||
*
|
||||
* @return JLabel Heading label added.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private JLabel addHeader(String headerString) {
|
||||
// create label for heading
|
||||
javax.swing.JLabel headingLabel = new javax.swing.JLabel();
|
||||
// add a blank line before the start of new section, unless it's
|
||||
// the first section
|
||||
if (gridBagConstraints.gridy != 0) {
|
||||
gridBagConstraints.gridy++;
|
||||
add(new javax.swing.JLabel(" "), gridBagConstraints);
|
||||
addLineEndGlue();
|
||||
}
|
||||
gridBagConstraints.gridy++;
|
||||
gridBagConstraints.gridx = LABEL_COLUMN;;
|
||||
// let the header span all of the row
|
||||
gridBagConstraints.gridwidth = MAX_COLS;
|
||||
gridBagConstraints.insets = HEADER_INSETS;
|
||||
// set text
|
||||
headingLabel.setText(headerString);
|
||||
// make it large and bold
|
||||
headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2));
|
||||
// add to panel
|
||||
add(headingLabel, gridBagConstraints);
|
||||
// reset constraints to normal
|
||||
gridBagConstraints.gridwidth = LABEL_WIDTH;
|
||||
// add line end glue
|
||||
addLineEndGlue();
|
||||
gridBagConstraints.insets = ROW_INSETS;
|
||||
return headingLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a key value row to the specified panel with the specified layout and
|
||||
* constraints.
|
||||
*
|
||||
* @param keyString Key name to display.
|
||||
* @param valueString Value string to display.
|
||||
*/
|
||||
private void addNameValueRow(String keyString, String valueString) {
|
||||
addKeyAtCol(keyString);
|
||||
addValueAtCol(valueString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a filler/glue at the end of the line to keep the other columns
|
||||
* aligned, in case the panel is resized.
|
||||
*/
|
||||
private void addLineEndGlue() {
|
||||
// Place the filler just past the last column.
|
||||
gridBagConstraints.gridx = MAX_COLS;
|
||||
gridBagConstraints.weightx = GLUE_WEIGHT_X; // take up all the horizontal space
|
||||
gridBagConstraints.fill = GridBagConstraints.BOTH;
|
||||
javax.swing.Box.Filler horizontalFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0));
|
||||
add(horizontalFiller, gridBagConstraints);
|
||||
// restore fill & weight
|
||||
gridBagConstraints.fill = GridBagConstraints.NONE;
|
||||
gridBagConstraints.weightx = TEXT_WEIGHT_X;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a filler/glue at the bottom of the panel to keep the data rows
|
||||
* aligned, in case the panel is resized.
|
||||
*/
|
||||
private void addPageEndGlue() {
|
||||
gridBagConstraints.weighty = 1.0; // take up all the vertical space
|
||||
gridBagConstraints.fill = GridBagConstraints.VERTICAL;
|
||||
javax.swing.Box.Filler vertFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(0, 32767));
|
||||
add(vertFiller, gridBagConstraints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a label/key to the panel.
|
||||
*
|
||||
* @param keyString Key name to display.
|
||||
*
|
||||
* @return Label added.
|
||||
*/
|
||||
private JLabel addKeyAtCol(String keyString) {
|
||||
// create label
|
||||
javax.swing.JLabel keyLabel = new javax.swing.JLabel();
|
||||
gridBagConstraints.gridy++;
|
||||
gridBagConstraints.gridx = LABEL_COLUMN;
|
||||
gridBagConstraints.gridwidth = LABEL_WIDTH;
|
||||
// set text
|
||||
keyLabel.setText(keyString + ": ");
|
||||
// add to panel
|
||||
add(keyLabel, gridBagConstraints);
|
||||
return keyLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value string to the panel at specified column.
|
||||
*
|
||||
* @param valueString Value string to display.
|
||||
*
|
||||
* @return Label added.
|
||||
*/
|
||||
private JTextPane addValueAtCol(String valueString) {
|
||||
// create label,
|
||||
JTextPane valueField = new JTextPane();
|
||||
valueField.setEditable(false);
|
||||
valueField.setOpaque(false);
|
||||
gridBagConstraints.gridx = VALUE_COLUMN;
|
||||
GridBagConstraints cloneConstraints = (GridBagConstraints) gridBagConstraints.clone();
|
||||
// let the value span 2 cols
|
||||
cloneConstraints.gridwidth = VALUE_WIDTH;
|
||||
cloneConstraints.fill = GridBagConstraints.BOTH;
|
||||
// set text
|
||||
valueField.setText(valueString);
|
||||
// attach a right click menu with Copy option
|
||||
valueField.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||
valueLabelMouseClicked(evt, valueField);
|
||||
}
|
||||
});
|
||||
// add label to panel
|
||||
add(valueField, cloneConstraints);
|
||||
// end the line
|
||||
addLineEndGlue();
|
||||
return valueField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for mouse click event. Attaches a 'Copy' menu item to right
|
||||
* click.
|
||||
*
|
||||
* @param evt Event to check.
|
||||
* @param valueLabel Label to attach the menu item to.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"GeneralPurposeArtifactViewer_menuitem_copy=Copy"
|
||||
})
|
||||
private void valueLabelMouseClicked(java.awt.event.MouseEvent evt, JTextPane valueLabel) {
|
||||
if (SwingUtilities.isRightMouseButton(evt)) {
|
||||
JPopupMenu popup = new JPopupMenu();
|
||||
JMenuItem copyMenu = new JMenuItem(Bundle.CommunicationArtifactViewerHelper_menuitem_copy()); // NON-NLS
|
||||
copyMenu.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(valueLabel.getText()), null);
|
||||
}
|
||||
});
|
||||
popup.add(copyMenu);
|
||||
popup.show(valueLabel, evt.getX(), evt.getY());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentviewers.contextviewer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.sleuthkit.autopsy.contentviewers.contextviewer.ContextViewer.DateTimePanel;
|
||||
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
|
||||
@ -29,27 +30,36 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOC
|
||||
* usage, if known.
|
||||
*
|
||||
*/
|
||||
public final class ContextSourcePanel extends javax.swing.JPanel {
|
||||
public final class ContextSourcePanel extends javax.swing.JPanel implements DateTimePanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// defines a list of artifacts that provide context for a file
|
||||
private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
|
||||
}
|
||||
|
||||
private final BlackboardArtifact sourceContextArtifact;
|
||||
|
||||
private final Long dateTime;
|
||||
|
||||
/**
|
||||
* Creates new form ContextViewer
|
||||
*/
|
||||
public ContextSourcePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact) {
|
||||
public ContextSourcePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact, Long dateTime) {
|
||||
|
||||
initComponents();
|
||||
sourceContextArtifact = associatedArtifact;
|
||||
setSourceName(sourceName);
|
||||
setSourceText(sourceText);
|
||||
this.dateTime = dateTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDateTime() {
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -140,10 +150,10 @@ public final class ContextSourcePanel extends javax.swing.JPanel {
|
||||
private void showSourceText(boolean show) {
|
||||
jSourceTextLabel.setVisible(show);
|
||||
}
|
||||
|
||||
|
||||
private void showSourceButton(boolean show) {
|
||||
jSourceGoToResultButton.setVisible(show);
|
||||
jSourceGoToResultButton.setEnabled(show);
|
||||
jSourceGoToResultButton.setEnabled(show);
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
|
@ -29,27 +29,36 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOC
|
||||
* usage, if known.
|
||||
*
|
||||
*/
|
||||
public final class ContextUsagePanel extends javax.swing.JPanel {
|
||||
public final class ContextUsagePanel extends javax.swing.JPanel implements ContextViewer.DateTimePanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// defines a list of artifacts that provide context for a file
|
||||
private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
|
||||
}
|
||||
|
||||
private final BlackboardArtifact sourceContextArtifact;
|
||||
|
||||
private final Long dateTime;
|
||||
|
||||
/**
|
||||
* Creates new form ContextViewer
|
||||
*/
|
||||
public ContextUsagePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact) {
|
||||
public ContextUsagePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact, Long dateTime) {
|
||||
|
||||
initComponents();
|
||||
sourceContextArtifact = associatedArtifact;
|
||||
setUsageName(sourceName);
|
||||
setUsageText(sourceText);
|
||||
this.dateTime = dateTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDateTime() {
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,10 +147,10 @@ public final class ContextUsagePanel extends javax.swing.JPanel {
|
||||
private void showUsageText(boolean show) {
|
||||
jUsageTextLabel.setVisible(show);
|
||||
}
|
||||
|
||||
|
||||
private void showUsageButton(boolean show) {
|
||||
jUsageGoToResultButton.setVisible(show);
|
||||
jUsageGoToResultButton.setEnabled(show);
|
||||
jUsageGoToResultButton.setEnabled(show);
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
|
@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.contentviewers.contextviewer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -56,8 +58,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
|
||||
// defines a list of artifacts that provide context for a file
|
||||
private static final List<BlackboardArtifact.ARTIFACT_TYPE> CONTEXT_ARTIFACTS = new ArrayList<>();
|
||||
private final List<javax.swing.JPanel> contextSourcePanels = new ArrayList<>();
|
||||
private final List<javax.swing.JPanel> contextUsagePanels = new ArrayList<>();
|
||||
private final List<ContextSourcePanel> contextSourcePanels = new ArrayList<>();
|
||||
private final List<ContextUsagePanel> contextUsagePanels = new ArrayList<>();
|
||||
|
||||
static {
|
||||
CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
|
||||
@ -338,32 +340,36 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
"ContextViewer.programExecution=Program Execution: "
|
||||
})
|
||||
private void addArtifactToPanels(BlackboardArtifact associatedArtifact) throws TskCoreException {
|
||||
Long dateTime = getArtifactDateTime(associatedArtifact);
|
||||
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID()
|
||||
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
||||
String sourceName = Bundle.ContextViewer_attachmentSource();
|
||||
String sourceText = msgArtifactToAbbreviatedString(associatedArtifact);
|
||||
javax.swing.JPanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact);
|
||||
ContextSourcePanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact, dateTime);
|
||||
contextSourcePanels.add(sourcePanel);
|
||||
|
||||
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID()
|
||||
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
||||
String sourceName = Bundle.ContextViewer_downloadSource();
|
||||
String sourceText = webDownloadArtifactToString(associatedArtifact);
|
||||
javax.swing.JPanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact);
|
||||
ContextSourcePanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact, dateTime);
|
||||
contextSourcePanels.add(sourcePanel);
|
||||
|
||||
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
||||
String sourceName = Bundle.ContextViewer_recentDocs();
|
||||
String sourceText = recentDocArtifactToString(associatedArtifact);
|
||||
javax.swing.JPanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact);
|
||||
ContextUsagePanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact, dateTime);
|
||||
contextUsagePanels.add(usagePanel);
|
||||
|
||||
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
||||
String sourceName = Bundle.ContextViewer_programExecution();
|
||||
String sourceText = programExecArtifactToString(associatedArtifact);
|
||||
javax.swing.JPanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact);
|
||||
ContextUsagePanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact, dateTime);
|
||||
contextUsagePanels.add(usagePanel);
|
||||
}
|
||||
|
||||
Collections.sort(contextSourcePanels, new SortByDateTime());
|
||||
Collections.sort(contextUsagePanels, new SortByDateTime());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -532,6 +538,59 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
|
||||
return attributeMap;
|
||||
}
|
||||
|
||||
interface DateTimePanel {
|
||||
/**
|
||||
* Return the date time value for this panel.
|
||||
*
|
||||
* @return Date time value or null of one is not available.
|
||||
*/
|
||||
Long getDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dateTime value for the given message artifact.
|
||||
*
|
||||
* @param artifact
|
||||
*
|
||||
* @return Long dateTime value or null if the attribute was not found.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private Long getArtifactDateTime(BlackboardArtifact artifact) throws TskCoreException {
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME));
|
||||
|
||||
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == artifact.getArtifactTypeID()) {
|
||||
attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT));
|
||||
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == artifact.getArtifactTypeID()
|
||||
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == artifact.getArtifactTypeID()) {
|
||||
attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED));
|
||||
}
|
||||
return (attribute != null ? attribute.getValueLong() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for sorting lists of DateTimePanels.
|
||||
*/
|
||||
class SortByDateTime implements Comparator<DateTimePanel> {
|
||||
|
||||
@Override
|
||||
public int compare(DateTimePanel panel1, DateTimePanel panel2) {
|
||||
Long dateTime1 = panel1.getDateTime();
|
||||
Long dateTime2 = panel2.getDateTime();
|
||||
|
||||
if(dateTime1 == null && dateTime2 == null) {
|
||||
return 0;
|
||||
} else if(dateTime1 == null) {
|
||||
return -1;
|
||||
} else if(dateTime2 == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return dateTime1.compareTo(dateTime2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
|
@ -36,10 +36,9 @@ import org.apache.curator.retry.ExponentialBackoffRetry;
|
||||
import org.apache.zookeeper.CreateMode;
|
||||
import org.apache.zookeeper.KeeperException;
|
||||
import org.apache.zookeeper.KeeperException.NoNodeException;
|
||||
import org.apache.zookeeper.WatchedEvent;
|
||||
import org.apache.zookeeper.ZooDefs;
|
||||
import org.apache.zookeeper.ZooKeeper;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.coordinationservice.utils.CoordinationServiceUtils;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
|
||||
/**
|
||||
@ -52,8 +51,6 @@ public final class CoordinationService {
|
||||
|
||||
private static final int SESSION_TIMEOUT_MILLISECONDS = 300000;
|
||||
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 300000;
|
||||
private static final int ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 3000;
|
||||
private static final int ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS = 15000;
|
||||
private static final int PORT_OFFSET = 1000; // When run in Solr, ZooKeeper defaults to Solr port + 1000
|
||||
private static final String DEFAULT_NAMESPACE_ROOT = "autopsy";
|
||||
@GuardedBy("CoordinationService.class")
|
||||
@ -62,37 +59,6 @@ public final class CoordinationService {
|
||||
@GuardedBy("categoryNodeToPath")
|
||||
private final Map<String, String> categoryNodeToPath;
|
||||
|
||||
/**
|
||||
* Determines if ZooKeeper is accessible with the current settings. Closes
|
||||
* the connection prior to returning.
|
||||
*
|
||||
* @return true if a connection was achieved, false otherwise
|
||||
*
|
||||
* @throws InterruptedException
|
||||
* @throws IOException
|
||||
*/
|
||||
private static boolean isZooKeeperAccessible() throws InterruptedException, IOException {
|
||||
boolean result = false;
|
||||
Object workerThreadWaitNotifyLock = new Object();
|
||||
int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
|
||||
String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
|
||||
ZooKeeper zooKeeper = new ZooKeeper(connectString, ZOOKEEPER_SESSION_TIMEOUT_MILLIS,
|
||||
(WatchedEvent event) -> {
|
||||
synchronized (workerThreadWaitNotifyLock) {
|
||||
workerThreadWaitNotifyLock.notify();
|
||||
}
|
||||
});
|
||||
synchronized (workerThreadWaitNotifyLock) {
|
||||
workerThreadWaitNotifyLock.wait(ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS);
|
||||
}
|
||||
ZooKeeper.States state = zooKeeper.getState();
|
||||
if (state == ZooKeeper.States.CONNECTED || state == ZooKeeper.States.CONNECTEDREADONLY) {
|
||||
result = true;
|
||||
}
|
||||
zooKeeper.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the coordination service for maintaining configuration information
|
||||
* and providing distributed synchronization using a shared hierarchical
|
||||
@ -141,16 +107,26 @@ public final class CoordinationService {
|
||||
*/
|
||||
private CoordinationService(String rootNodeName) throws InterruptedException, IOException, KeeperException, CoordinationServiceException {
|
||||
|
||||
if (false == isZooKeeperAccessible()) {
|
||||
// read ZK connection info
|
||||
String hostName = UserPreferences.getZkServerHost();
|
||||
String port = UserPreferences.getZkServerPort();
|
||||
if (hostName.isEmpty() || port.isEmpty()) {
|
||||
// use defaults for embedded ZK that runs on Solr server
|
||||
hostName = UserPreferences.getIndexingServerHost();
|
||||
int portInt = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
|
||||
port = Integer.toString(portInt);
|
||||
}
|
||||
if (false == CoordinationServiceUtils.isZooKeeperAccessible(hostName, port)) {
|
||||
throw new CoordinationServiceException("Unable to access ZooKeeper");
|
||||
}
|
||||
|
||||
|
||||
// We are using ZK for all coordination/locking, so ZK connection info cannot be changed.
|
||||
// A reboot is required in order to use a different ZK server for coordination services.
|
||||
/*
|
||||
* Connect to ZooKeeper via Curator.
|
||||
*/
|
||||
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
|
||||
int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
|
||||
String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
|
||||
String connectString = hostName + ":" + port;
|
||||
curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy);
|
||||
curator.start();
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 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.coordinationservice.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.apache.zookeeper.WatchedEvent;
|
||||
import org.apache.zookeeper.ZooKeeper;
|
||||
|
||||
/**
|
||||
* A utility class for coordination service and ZooKeeper. This class is in a
|
||||
* separate package to avoid exposing it as public API.
|
||||
*/
|
||||
public final class CoordinationServiceUtils {
|
||||
|
||||
private static final int ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 3000;
|
||||
private static final int ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS = 15000;
|
||||
|
||||
/**
|
||||
* Determines if ZooKeeper is accessible with the current settings. Closes
|
||||
* the connection prior to returning.
|
||||
*
|
||||
* @return true if a connection was achieved, false otherwise
|
||||
*
|
||||
* @throws InterruptedException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static boolean isZooKeeperAccessible(String hostName, String port) throws InterruptedException, IOException {
|
||||
boolean result = false;
|
||||
Object workerThreadWaitNotifyLock = new Object();
|
||||
String connectString = hostName + ":" + port;
|
||||
ZooKeeper zooKeeper = new ZooKeeper(connectString, ZOOKEEPER_SESSION_TIMEOUT_MILLIS,
|
||||
(WatchedEvent event) -> {
|
||||
synchronized (workerThreadWaitNotifyLock) {
|
||||
workerThreadWaitNotifyLock.notify();
|
||||
}
|
||||
});
|
||||
synchronized (workerThreadWaitNotifyLock) {
|
||||
workerThreadWaitNotifyLock.wait(ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS);
|
||||
}
|
||||
ZooKeeper.States state = zooKeeper.getState();
|
||||
if (state == ZooKeeper.States.CONNECTED || state == ZooKeeper.States.CONNECTEDREADONLY) {
|
||||
result = true;
|
||||
}
|
||||
zooKeeper.close();
|
||||
return result;
|
||||
}
|
||||
}
|
@ -21,9 +21,9 @@ ServicesMonitor.statusChange.notify.msg=Status for {0} is {1}
|
||||
ServicesMonitor.nullServiceName.excepton.txt=Requested service name is null
|
||||
ServicesMonitor.unknownServiceName.excepton.txt=Requested service name {0} is unknown
|
||||
ServicesMonitor.KeywordSearchNull=Cannot find Keyword Search service
|
||||
ServicesMonitor.InvalidPortNumber=Invalid port number.
|
||||
ServicesMonitor.InvalidPortNumber=Invalid Solr 8 port number.
|
||||
ServicesMonitor.remoteCaseDatabase.displayName.text=Multi-user case database service
|
||||
ServicesMonitor.remoteKeywordSearch.displayName.text=Multi-user keyword search service
|
||||
ServicesMonitor.messaging.displayName.text=Messaging service
|
||||
ServicesMonitor.databaseConnectionInfo.error.msg=Error accessing case database connection info
|
||||
ServicesMonitor.messagingService.connErr.text=Error accessing messaging service connection info
|
||||
ServicesMonitor.messagingService.connErr.text=Error accessing messaging service connection info
|
||||
|
@ -25,7 +25,7 @@ ServicesMonitor.statusChange.notify.msg=Status for {0} is {1}
|
||||
ServicesMonitor.nullServiceName.excepton.txt=Requested service name is null
|
||||
ServicesMonitor.unknownServiceName.excepton.txt=Requested service name {0} is unknown
|
||||
ServicesMonitor.KeywordSearchNull=Cannot find Keyword Search service
|
||||
ServicesMonitor.InvalidPortNumber=Invalid port number.
|
||||
ServicesMonitor.InvalidPortNumber=Invalid Solr 8 port number.
|
||||
ServicesMonitor.remoteCaseDatabase.displayName.text=Multi-user case database service
|
||||
ServicesMonitor.remoteKeywordSearch.displayName.text=Multi-user keyword search service
|
||||
ServicesMonitor.messaging.displayName.text=Messaging service
|
||||
|
@ -354,20 +354,33 @@ public class ServicesMonitor {
|
||||
KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
|
||||
try {
|
||||
if (kwsService != null) {
|
||||
int port = Integer.parseUnsignedInt(UserPreferences.getIndexingServerPort());
|
||||
kwsService.tryConnect(UserPreferences.getIndexingServerHost(), port);
|
||||
setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.UP.toString(), "");
|
||||
ServiceStatus status = ServiceStatus.DOWN;
|
||||
// check Solr 8
|
||||
String kwsHostName = UserPreferences.getIndexingServerHost();
|
||||
if (!kwsHostName.isEmpty()) {
|
||||
int port = Integer.parseUnsignedInt(UserPreferences.getIndexingServerPort());
|
||||
kwsService.tryConnect(UserPreferences.getIndexingServerHost(), port);
|
||||
status = ServiceStatus.UP;
|
||||
}
|
||||
|
||||
// check Solr 4
|
||||
if (!UserPreferences.getSolr4ServerHost().trim().isEmpty()) {
|
||||
int port = Integer.parseUnsignedInt(UserPreferences.getSolr4ServerPort().trim());
|
||||
kwsService.tryConnect(UserPreferences.getSolr4ServerHost().trim(), port);
|
||||
status = ServiceStatus.UP;
|
||||
}
|
||||
setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), status.toString(), "");
|
||||
} else {
|
||||
setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(),
|
||||
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.KeywordSearchNull"));
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
String rootCause = NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.InvalidPortNumber");
|
||||
logger.log(Level.SEVERE, "Unable to connect to messaging server: " + rootCause, ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Unable to connect to Keyword Search server: " + rootCause, ex); //NON-NLS
|
||||
setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(), rootCause);
|
||||
} catch (KeywordSearchServiceException ex) {
|
||||
String rootCause = ex.getMessage();
|
||||
logger.log(Level.SEVERE, "Unable to connect to messaging server: " + rootCause, ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Unable to connect to Keyword Search server: " + rootCause, ex); //NON-NLS
|
||||
setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(), rootCause);
|
||||
}
|
||||
}
|
||||
|
@ -55,13 +55,18 @@ public final class UserPreferences {
|
||||
public static final String EXTERNAL_DATABASE_USER = "ExternalDatabaseUsername"; //NON-NLS
|
||||
public static final String EXTERNAL_DATABASE_PASSWORD = "ExternalDatabasePassword"; //NON-NLS
|
||||
public static final String EXTERNAL_DATABASE_TYPE = "ExternalDatabaseType"; //NON-NLS
|
||||
public static final String INDEXING_SERVER_HOST = "IndexingServerHost"; //NON-NLS
|
||||
public static final String INDEXING_SERVER_PORT = "IndexingServerPort"; //NON-NLS
|
||||
private static final String SOLR8_SERVER_HOST = "Solr8ServerHost"; //NON-NLS
|
||||
private static final String SOLR8_SERVER_PORT = "Solr8ServerPort"; //NON-NLS
|
||||
private static final String SOLR4_SERVER_HOST = "IndexingServerHost"; //NON-NLS
|
||||
private static final String SOLR4_SERVER_PORT = "IndexingServerPort"; //NON-NLS
|
||||
private static final String ZK_SERVER_HOST = "ZookeeperServerHost"; //NON-NLS
|
||||
private static final String ZK_SERVER_PORT = "ZookeeperServerPort"; //NON-NLS
|
||||
private static final String MESSAGE_SERVICE_PASSWORD = "MessageServicePassword"; //NON-NLS
|
||||
private static final String MESSAGE_SERVICE_USER = "MessageServiceUser"; //NON-NLS
|
||||
private static final String MESSAGE_SERVICE_HOST = "MessageServiceHost"; //NON-NLS
|
||||
private static final String MESSAGE_SERVICE_PORT = "MessageServicePort"; //NON-NLS
|
||||
public static final String TEXT_TRANSLATOR_NAME = "TextTranslatorName";
|
||||
public static final String OCR_TRANSLATION_ENABLED = "OcrTranslationEnabled";
|
||||
public static final String PROCESS_TIME_OUT_ENABLED = "ProcessTimeOutEnabled"; //NON-NLS
|
||||
public static final String PROCESS_TIME_OUT_HOURS = "ProcessTimeOutHours"; //NON-NLS
|
||||
private static final int DEFAULT_PROCESS_TIMEOUT_HR = 60;
|
||||
@ -84,6 +89,7 @@ public final class UserPreferences {
|
||||
private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath";
|
||||
private static final String GEO_OSM_SERVER_ADDRESS = "GeolocationOsmServerAddress";
|
||||
private static final String GEO_MBTILES_FILE_PATH = "GeolcoationMBTilesFilePath";
|
||||
private static final String HEALTH_MONITOR_REPORT_PATH = "HealthMonitorReportPath";
|
||||
|
||||
// Prevent instantiation.
|
||||
private UserPreferences() {
|
||||
@ -325,21 +331,53 @@ public final class UserPreferences {
|
||||
}
|
||||
|
||||
public static String getIndexingServerHost() {
|
||||
return preferences.get(INDEXING_SERVER_HOST, "");
|
||||
return preferences.get(SOLR8_SERVER_HOST, "");
|
||||
}
|
||||
|
||||
public static void setIndexingServerHost(String hostName) {
|
||||
preferences.put(INDEXING_SERVER_HOST, hostName);
|
||||
preferences.put(SOLR8_SERVER_HOST, hostName);
|
||||
}
|
||||
|
||||
public static String getIndexingServerPort() {
|
||||
return preferences.get(INDEXING_SERVER_PORT, "8983");
|
||||
return preferences.get(SOLR8_SERVER_PORT, "8983");
|
||||
}
|
||||
|
||||
public static void setIndexingServerPort(int port) {
|
||||
preferences.putInt(INDEXING_SERVER_PORT, port);
|
||||
preferences.putInt(SOLR8_SERVER_PORT, port);
|
||||
}
|
||||
|
||||
public static String getSolr4ServerHost() {
|
||||
return preferences.get(SOLR4_SERVER_HOST, "");
|
||||
}
|
||||
|
||||
public static void setSolr4ServerHost(String hostName) {
|
||||
preferences.put(SOLR4_SERVER_HOST, hostName);
|
||||
}
|
||||
|
||||
public static String getSolr4ServerPort() {
|
||||
return preferences.get(SOLR4_SERVER_PORT, "");
|
||||
}
|
||||
|
||||
public static void setSolr4ServerPort(String port) {
|
||||
preferences.put(SOLR4_SERVER_PORT, port);
|
||||
}
|
||||
|
||||
public static String getZkServerHost() {
|
||||
return preferences.get(ZK_SERVER_HOST, "");
|
||||
}
|
||||
|
||||
public static void setZkServerHost(String hostName) {
|
||||
preferences.put(ZK_SERVER_HOST, hostName);
|
||||
}
|
||||
|
||||
public static String getZkServerPort() {
|
||||
return preferences.get(ZK_SERVER_PORT, "9983");
|
||||
}
|
||||
|
||||
public static void setZkServerPort(String port) {
|
||||
preferences.put(ZK_SERVER_PORT, port);
|
||||
}
|
||||
|
||||
public static void setTextTranslatorName(String textTranslatorName) {
|
||||
preferences.put(TEXT_TRANSLATOR_NAME, textTranslatorName);
|
||||
}
|
||||
@ -347,6 +385,14 @@ public final class UserPreferences {
|
||||
public static String getTextTranslatorName() {
|
||||
return preferences.get(TEXT_TRANSLATOR_NAME, null);
|
||||
}
|
||||
|
||||
public static void setUseOcrInTranslation(boolean enableOcr) {
|
||||
preferences.putBoolean(OCR_TRANSLATION_ENABLED, enableOcr);
|
||||
}
|
||||
|
||||
public static boolean getUseOcrInTranslation() {
|
||||
return preferences.getBoolean(OCR_TRANSLATION_ENABLED, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists message service connection info.
|
||||
@ -623,4 +669,22 @@ public final class UserPreferences {
|
||||
return Paths.get(UserMachinePreferences.getBaseTempDirectory(), getAppName())
|
||||
.toAbsolutePath().toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last used health monitor report path.
|
||||
*
|
||||
* @param reportPath Last used health monitor report path.
|
||||
*/
|
||||
public static void setHealthMonitorReportPath(String reportPath) {
|
||||
preferences.put(HEALTH_MONITOR_REPORT_PATH, reportPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last used health monitor report path.
|
||||
*
|
||||
* @return Last used health monitor report path. Empty string if no value has been recorded.
|
||||
*/
|
||||
public static String getHealthMonitorReportPath() {
|
||||
return preferences.get(HEALTH_MONITOR_REPORT_PATH, "");
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ import javax.swing.text.ViewFactory;
|
||||
import javax.swing.text.html.HTMLEditorKit;
|
||||
import javax.swing.text.html.InlineView;
|
||||
import javax.swing.text.html.ParagraphView;
|
||||
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
|
||||
|
||||
/**
|
||||
* JTextPane extension that auto wraps input text using an HTMLEditorKit trick.
|
||||
@ -98,6 +99,6 @@ public class AutoWrappingJTextPane extends JTextPane {
|
||||
|
||||
@Override
|
||||
public void setText(String text) {
|
||||
super.setText("<pre>" + text + "</pre>");
|
||||
super.setText("<pre>" + EscapeUtil.escapeHtml(text) + "</pre>");
|
||||
}
|
||||
}
|
||||
|
@ -75,14 +75,17 @@ DataResultViewerThumbnail.switchPage.done.errMsg=Error making thumbnails: {0}
|
||||
AboutWindowPanel.actVerboseLogging.text=Activate verbose logging
|
||||
OptionsCategory_Name_Multi_User_Settings=Multi-User
|
||||
OptionsCategory_Keywords_Multi_User_Options=Multi-User Settings
|
||||
MultiUserSettingsPanel.lbSolrSettings.text=Solr Server Settings
|
||||
MultiUserSettingsPanel.cbEnableMultiUser.text=Enable multi-user cases
|
||||
MultiUserSettingsPanel.lbDatabaseSettings.text=Database Server Settings
|
||||
MultiUserSettingsPanel.validationErrMsg.incomplete=Fill in all values
|
||||
MultiUserSettingsPanel.nonWindowsOs.msg=Multi-user cases are only available on Windows platforms
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidDatabasePort=Invalid database port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidMessageServicePort=Invalid message service port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidIndexingServerPort=Invalid Solr server port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidIndexingServerPort=Invalid Solr 8 server port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidSolr4ServerPort=Invalid Solr 4 server port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidZkServerPort=Invalid ZooKeeper server port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidZkServerHostName=ZooKeeper server host name not set
|
||||
MultiUserSettingsPanel.validationErrMsg.solrNotConfigured=Either Solr 8 or/and Solr 4 server needs to be configured
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidMessgeServiceURI=Message service host and/or port not valid
|
||||
DataContentViewerHex.goToOffsetLabel.text=Jump to Offset
|
||||
DataContentViewerHex.goToOffsetTextField.text=
|
||||
@ -101,8 +104,6 @@ MultiUserSettingsPanel.tbDbPassword.toolTipText=Password
|
||||
MultiUserSettingsPanel.tbDbPassword.text=
|
||||
MultiUserSettingsPanel.lbTestSolr.text=
|
||||
MultiUserSettingsPanel.bnTestSolr.text=Test
|
||||
MultiUserSettingsPanel.tbSolrHostname.toolTipText=Hostname or IP Address
|
||||
MultiUserSettingsPanel.tbSolrPort.toolTipText=Port Number
|
||||
MultiUserSettingsPanel.lbTestMessageService.text=
|
||||
MultiUserSettingsPanel.bnTestMessageService.text=Test
|
||||
MultiUserSettingsPanel.lbMessageServiceSettings.text=ActiveMQ Message Server Settings
|
||||
@ -118,6 +119,7 @@ MultiUserSettingsPanel.lbTestMessageWarning.text=
|
||||
MultiUserSettingsPanel.lbTestSolrWarning.text=
|
||||
MultiUserSettingsPanel.lbTestDbWarning.text=
|
||||
MultiUserSettingsPanel.KeywordSearchNull=Cannot find keyword search service
|
||||
MultiUserSettingsPanel.UnableToConnectToZK=Unable to connect to ZooKeeper service
|
||||
MultiUserSettingsPanel.InvalidPortNumber=Invalid port number
|
||||
AutopsyOptionsPanel.agencyLogoImageLabel.toolTipText=
|
||||
SortChooserDialog.label=remove
|
||||
@ -175,6 +177,19 @@ DataResultViewerTable.pagesLabel.text=Pages:
|
||||
DataResultViewerTable.pageNumLabel.text=
|
||||
DataResultViewerTable.pageLabel.text=Page:
|
||||
DataResultViewerTable.exportCSVButton.text=Save Table as CSV
|
||||
MultiUserSettingsPanel.tbSolr4Hostname.toolTipText=Solr 4 Hostname or IP Address
|
||||
MultiUserSettingsPanel.tbSolr4Port.toolTipText=Solr 4 Port Number
|
||||
MultiUserSettingsPanel.lbZkSettings.text=ZooKeeper Server Settings
|
||||
MultiUserSettingsPanel.lbSolr4Settings.text=Solr 4 Server Settings
|
||||
MultiUserSettingsPanel.lbSolr8Settings.text=Solr 8 Server Settings
|
||||
MultiUserSettingsPanel.tbZkHostname.toolTipText=ZooKeeper Hostname or IP Address
|
||||
MultiUserSettingsPanel.tbZkPort.toolTipText=ZooKeeper Port Number
|
||||
MultiUserSettingsPanel.tbSolr8Hostname.toolTipText=Solr 8 Hostname or IP Address
|
||||
MultiUserSettingsPanel.tbSolr8Port.toolTipText=Solr 8 Port Number
|
||||
MultiUserSettingsPanel.restartRequiredLabel.text=Application restart required to take effect.
|
||||
MultiUserSettingsPanel.MustRestart=Autopsy must be restarted for new configuration to take effect
|
||||
MultiUserSettingsPanel.lbSolrNote1.text=NOTE: Enter Solr 8 and/or Solr 4 server settings.
|
||||
MultiUserSettingsPanel.lbSolrNote2.text=New cases can only be created with Solr 8.
|
||||
AutopsyOptionsPanel.logoPanel.border.title=Logo
|
||||
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text=
|
||||
AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo
|
||||
@ -223,3 +238,4 @@ ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=For example, change fr
|
||||
ViewPreferencesPanel.useBestViewerRadioButton.text=Change to the most specific file viewer
|
||||
ViewPreferencesPanel.selectFileLabel.text=When selecting a file:
|
||||
ViewPreferencesPanel.globalSettingsPanel.border.title=Global Settings
|
||||
|
||||
|
@ -130,14 +130,17 @@ DataResultViewerThumbnail.switchPage.done.errMsg=Error making thumbnails: {0}
|
||||
AboutWindowPanel.actVerboseLogging.text=Activate verbose logging
|
||||
OptionsCategory_Name_Multi_User_Settings=Multi-User
|
||||
OptionsCategory_Keywords_Multi_User_Options=Multi-User Settings
|
||||
MultiUserSettingsPanel.lbSolrSettings.text=Solr Server Settings
|
||||
MultiUserSettingsPanel.cbEnableMultiUser.text=Enable multi-user cases
|
||||
MultiUserSettingsPanel.lbDatabaseSettings.text=Database Server Settings
|
||||
MultiUserSettingsPanel.validationErrMsg.incomplete=Fill in all values
|
||||
MultiUserSettingsPanel.nonWindowsOs.msg=Multi-user cases are only available on Windows platforms
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidDatabasePort=Invalid database port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidMessageServicePort=Invalid message service port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidIndexingServerPort=Invalid Solr server port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidIndexingServerPort=Invalid Solr 8 server port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidSolr4ServerPort=Invalid Solr 4 server port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidZkServerPort=Invalid ZooKeeper server port number
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidZkServerHostName=ZooKeeper server host name not set
|
||||
MultiUserSettingsPanel.validationErrMsg.solrNotConfigured=Either Solr 8 or/and Solr 4 server needs to be configured
|
||||
MultiUserSettingsPanel.validationErrMsg.invalidMessgeServiceURI=Message service host and/or port not valid
|
||||
DataContentViewerHex.goToOffsetLabel.text=Jump to Offset
|
||||
DataContentViewerHex.goToOffsetTextField.text=
|
||||
@ -156,8 +159,6 @@ MultiUserSettingsPanel.tbDbPassword.toolTipText=Password
|
||||
MultiUserSettingsPanel.tbDbPassword.text=
|
||||
MultiUserSettingsPanel.lbTestSolr.text=
|
||||
MultiUserSettingsPanel.bnTestSolr.text=Test
|
||||
MultiUserSettingsPanel.tbSolrHostname.toolTipText=Hostname or IP Address
|
||||
MultiUserSettingsPanel.tbSolrPort.toolTipText=Port Number
|
||||
MultiUserSettingsPanel.lbTestMessageService.text=
|
||||
MultiUserSettingsPanel.bnTestMessageService.text=Test
|
||||
MultiUserSettingsPanel.lbMessageServiceSettings.text=ActiveMQ Message Server Settings
|
||||
@ -173,6 +174,7 @@ MultiUserSettingsPanel.lbTestMessageWarning.text=
|
||||
MultiUserSettingsPanel.lbTestSolrWarning.text=
|
||||
MultiUserSettingsPanel.lbTestDbWarning.text=
|
||||
MultiUserSettingsPanel.KeywordSearchNull=Cannot find keyword search service
|
||||
MultiUserSettingsPanel.UnableToConnectToZK=Unable to connect to ZooKeeper service
|
||||
MultiUserSettingsPanel.InvalidPortNumber=Invalid port number
|
||||
AutopsyOptionsPanel.agencyLogoImageLabel.toolTipText=
|
||||
SortChooserDialog.label=remove
|
||||
@ -232,6 +234,19 @@ DataResultViewerTable.pagesLabel.text=Pages:
|
||||
DataResultViewerTable.pageNumLabel.text=
|
||||
DataResultViewerTable.pageLabel.text=Page:
|
||||
DataResultViewerTable.exportCSVButton.text=Save Table as CSV
|
||||
MultiUserSettingsPanel.tbSolr4Hostname.toolTipText=Solr 4 Hostname or IP Address
|
||||
MultiUserSettingsPanel.tbSolr4Port.toolTipText=Solr 4 Port Number
|
||||
MultiUserSettingsPanel.lbZkSettings.text=ZooKeeper Server Settings
|
||||
MultiUserSettingsPanel.lbSolr4Settings.text=Solr 4 Server Settings
|
||||
MultiUserSettingsPanel.lbSolr8Settings.text=Solr 8 Server Settings
|
||||
MultiUserSettingsPanel.tbZkHostname.toolTipText=ZooKeeper Hostname or IP Address
|
||||
MultiUserSettingsPanel.tbZkPort.toolTipText=ZooKeeper Port Number
|
||||
MultiUserSettingsPanel.tbSolr8Hostname.toolTipText=Solr 8 Hostname or IP Address
|
||||
MultiUserSettingsPanel.tbSolr8Port.toolTipText=Solr 8 Port Number
|
||||
MultiUserSettingsPanel.restartRequiredLabel.text=Application restart required to take effect.
|
||||
MultiUserSettingsPanel.MustRestart=Autopsy must be restarted for new configuration to take effect
|
||||
MultiUserSettingsPanel.lbSolrNote1.text=NOTE: Enter Solr 8 and/or Solr 4 server settings.
|
||||
MultiUserSettingsPanel.lbSolrNote2.text=New cases can only be created with Solr 8.
|
||||
AutopsyOptionsPanel.logoPanel.border.title=Logo
|
||||
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text=
|
||||
AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo
|
||||
@ -280,3 +295,4 @@ ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=For example, change fr
|
||||
ViewPreferencesPanel.useBestViewerRadioButton.text=Change to the most specific file viewer
|
||||
ViewPreferencesPanel.selectFileLabel.text=When selecting a file:
|
||||
ViewPreferencesPanel.globalSettingsPanel.border.title=Global Settings
|
||||
|
||||
|
@ -170,6 +170,59 @@ HINT_DataContentTopComponent=\u3053\u308c\u306fDataContent\u30a6\u30a3\u30f3\u30
|
||||
HINT_NodeTableTopComponent=\u3053\u308c\u306fDataResult\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u3059
|
||||
INDEX_FOR_LOCAL_HELP=/docs/index.html
|
||||
LBL_Close=\u9589\u3058\u308b
|
||||
DataContentViewerHex.copyMenuItem.text=\u30b3\u30d4\u30fc
|
||||
DataContentViewerHex.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
|
||||
DataContentViewerArtifact.totalPageLabel.text=100
|
||||
DataContentViewerArtifact.prevPageButton.text=
|
||||
DataContentViewerArtifact.pageLabel2.text=\u7d50\u679c
|
||||
DataContentViewerArtifact.nextPageButton.text=
|
||||
DataContentViewerArtifact.currentPageLabel.text=1
|
||||
DataContentViewerArtifact.ofLabel.text=/
|
||||
DataContentViewerArtifact.pageLabel.text=\u7d50\u679c:
|
||||
AdvancedConfigurationDialog.applyButton.text=OK
|
||||
DataContentViewerHex.goToPageTextField.text=
|
||||
DataContentViewerHex.goToPageLabel.text=\u30da\u30fc\u30b8\u306b\u79fb\u52d5:
|
||||
DataResultViewerThumbnail.pageLabel.text=\u30da\u30fc\u30b8:
|
||||
DataResultViewerThumbnail.pagesLabel.text=\u30da\u30fc\u30b8:
|
||||
DataResultViewerThumbnail.pagePrevButton.text=
|
||||
DataResultViewerThumbnail.pageNextButton.text=
|
||||
DataResultViewerThumbnail.imagesLabel.text=\u30a4\u30e1\u30fc\u30b8:
|
||||
DataResultViewerThumbnail.imagesRangeLabel.text=-
|
||||
DataResultViewerThumbnail.pageNumLabel.text=-
|
||||
DataResultViewerThumbnail.filePathLabel.text=\ \ \
|
||||
DataResultViewerThumbnail.goToPageLabel.text=\u30da\u30fc\u30b8\u306b\u79fb\u52d5:
|
||||
DataResultViewerThumbnail.goToPageField.text=
|
||||
AdvancedConfigurationDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057
|
||||
DataContentViewerArtifact.waitText=\u30c7\u30fc\u30bf\u3092\u691c\u7d22\u3057\u3066\u6e96\u5099\u4e2d\u3067\u3059\u3002\u304a\u5f85\u3061\u304f\u3060\u3055\u3044...
|
||||
DataContentViewerArtifact.errorText=\u7d50\u679c\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
|
||||
DataContentViewerArtifact.title=\u7d50\u679c
|
||||
DataContentViewerArtifact.toolTip=\u30d5\u30a1\u30a4\u30eb\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u7d50\u679c\u3092\u8868\u793a
|
||||
DataContentViewerHex.goToPageTextField.msgDlg=1 \u304b\u3089 {0} \u307e\u3067\u306e\u6709\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044
|
||||
DataContentViewerHex.goToPageTextField.err=\u7121\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3067\u3059
|
||||
DataContentViewerHex.setDataView.errorText=(\u30aa\u30d5\u30bb\u30c3\u30c8 {0}-{1} \u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f)
|
||||
DataContentViewerHex.title=16\u9032\u6570
|
||||
DataContentViewerHex.toolTip=\u30d5\u30a1\u30a4\u30eb\u306e\u30d0\u30a4\u30ca\u30ea\u30fc\u30b3\u30f3\u30c6\u30f3\u30c4\u309216\u9032\u6570\u3068\u3057\u3066\u8868\u793a\u3057\u307e\u3059\u3002\u53f3\u5074\u306bASCII\u6587\u5b57\u3068\u3057\u3066\u8868\u793a\u53ef\u80fd\u306a\u30d0\u30a4\u30c8\u304c\u793a\u3055\u308c\u307e\u3059\u3002
|
||||
DataResultPanel.pleasewaitNodeDisplayName=\u304a\u5f85\u3061\u304f\u3060\u3055\u3044...
|
||||
DataResultViewerTable.illegalArgExc.noChildFromParent=\u6307\u5b9a\u3057\u305f\u89aa\u304b\u3089\u5b50\u30ce\u30fc\u30c9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
|
||||
DataResultViewerTable.illegalArgExc.childWithoutPropertySet=\u5b50\u30ce\u30fc\u30c9\u306b\u306f\u6b63\u898f\u306ePropertySet\u304c\u3042\u308a\u307e\u305b\u3093\u3002
|
||||
DataResultViewerThumbnail.title=\u30b5\u30e0\u30cd\u30a4\u30eb
|
||||
DataResultViewerThumbnail.goToPageTextField.msgDlg=1 \u304b\u3089 {0} \u307e\u3067\u306e\u6709\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044
|
||||
DataResultViewerThumbnail.goToPageTextField.err=\u7121\u52b9\u306a\u30da\u30fc\u30b8\u756a\u53f7\u3067\u3059
|
||||
DataResultViewerThumbnail.genThumbs=\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u4f5c\u6210\u4e2d\u3067\u3059...
|
||||
DataResultViewerThumbnail.pageNumbers.curOfTotal={0} / {1}
|
||||
GeneralOptionsPanelController.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc
|
||||
GeneralOptionsPanelController.moduleErr.msg=GeneralOptionsPanelController\u66f4\u65b0\u306e\u30ea\u30c3\u30b9\u30f3\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u5224\u65ad\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002
|
||||
ProductInformationPanel.verbLoggingEnabled.text=\u8a73\u7d30\u30ed\u30ae\u30f3\u30b0\u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u3059
|
||||
ProductInformationPanel.propertyUnknown.text=\u4e0d\u660e
|
||||
ProductInformationPanel.getVMValue.text={0} {1}
|
||||
TableFilterNode.displayName.text=\u540d\u524d
|
||||
DataResultViewerThumbnail.comboBox.smallThumbnails=\u5c0f\u30b5\u30a4\u30ba\u306e\u30b5\u30e0\u30cd\u30a4\u30eb
|
||||
DataResultViewerThumbnail.comboBox.mediumThumbnails=\u4e2d\u30b5\u30a4\u30ba\u306e\u30b5\u30e0\u30cd\u30a4\u30eb
|
||||
DataResultViewerThumbnail.comboBox.largeThumbnails=\u5927\u30b5\u30a4\u30ba\u306e\u30b5\u30e0\u30cd\u30a4\u30eb
|
||||
DataResultViewerThumbnail.switchPage.done.errMsg=\u6b21\u306e\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
|
||||
AboutWindowPanel.actVerboseLogging.text=\u8a73\u7d30\u30ed\u30ae\u30f3\u30b0\u3092\u30a2\u30af\u30c6\u30a3\u30d6\u5316
|
||||
OptionsCategory_Name_Multi_User_Settings=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc
|
||||
OptionsCategory_Keywords_Multi_User_Options=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u8a2d\u5b9a
|
||||
LBL_Copyright=<div style \= "font-size\uff1a12pt; font-family\uff1aVerdana\u3001 'Verdana CE'\u3001Arial\u3001 'Arial CE'\u3001 'Lucida Grande CE'\u3001lucida\u3001 'Helvetica CE'\u3001sans-serif;">Autopsy™ \u306f The Sleuth Kit™ \u304a\u3088\u3073\u305d\u306e\u4ed6\u306e\u30c4\u30fc\u30eb\u306b\u57fa\u3065\u304f\u30c7\u30b8\u30bf\u30eb\u79d1\u5b66\u635c\u67fb\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u3002 <br> <ul> <li>\u60c5\u5831\uff1a<a style \= "color\uff1a\uff031E2A60;" href \= "http\://www.sleuthkit.org"> http\://www.sleuthkit.org </a>\u3002</ li> <li>\u30c8\u30ec\u30fc\u30cb\u30f3\u30b0\uff1a<a style \= "color\uff1a\uff031E2A60;" href \= "https\://www.autopsy.com/support/training/"> https\://www.autopsy.com/support/training/ </a> </ li> <li>\u30b5\u30dd\u30fc\u30c8\uff1a<a style\="color\: \#1E2A60;" href\="https\://www.sleuthkit.org/support.php">https\://www.sleuthkit.org/support.php</a></li></ul>Copyright © 2003-2020. </div>
|
||||
LBL_Description=<div style\="font-size\: 12pt; font-family\: Verdana, 'Verdana CE', Arial, 'Arial CE', 'Lucida Grande CE', lucida, 'Helvetica CE', sans-serif;">\n <b>\u88fd\u54c1\u30d0\u30fc\u30b8\u30e7\u30f3\:</b> {0} ({9}) <br><b>Sleuth Kit\u30d0\u30fc\u30b8\u30e7\u30f3\:</b> {7} <br><b>Netbeans RCP\u30d3\u30eb\u30c9\:</b> {8} <br> <b>Java\:</b> {1}; {2}<br> <b>\u30b7\u30b9\u30c6\u30e0\:</b> {3}; {4}; {5}<br><b>Userdir\:</b> {6}</div>
|
||||
MultiUserSettingsPanel.InvalidPortNumber=\u7121\u52b9\u306a\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u3059
|
||||
@ -195,6 +248,14 @@ MultiUserSettingsPanel.tbDbPassword.toolTipText=\u30d1\u30b9\u30ef\u30fc\u30c9
|
||||
MultiUserSettingsPanel.tbDbPort.text=
|
||||
MultiUserSettingsPanel.tbDbPort.toolTipText=\u30dd\u30fc\u30c8\u756a\u53f7
|
||||
MultiUserSettingsPanel.tbDbUsername.text=
|
||||
MultiUserSettingsPanel.tbDbPassword.toolTipText=\u30d1\u30b9\u30ef\u30fc\u30c9
|
||||
MultiUserSettingsPanel.tbDbPassword.text=
|
||||
MultiUserSettingsPanel.lbTestSolr.text=
|
||||
MultiUserSettingsPanel.bnTestSolr.text=\u30c6\u30b9\u30c8
|
||||
MultiUserSettingsPanel.lbTestMessageService.text=
|
||||
MultiUserSettingsPanel.bnTestMessageService.text=\u30c6\u30b9\u30c8
|
||||
MultiUserSettingsPanel.lbMessageServiceSettings.text=ActiveMQ\u30e1\u30c3\u30bb\u30fc\u30b8\u30b5\u30fc\u30d3\u30b9\u8a2d\u5b9a
|
||||
MultiUserSettingsPanel.tbMsgPort.toolTipText=\u30dd\u30fc\u30c8\u756a\u53f7
|
||||
MultiUserSettingsPanel.tbDbUsername.toolTipText=\u30e6\u30fc\u30b6\u30fc\u540d
|
||||
MultiUserSettingsPanel.tbMsgHostname.text=
|
||||
MultiUserSettingsPanel.tbMsgHostname.toolTipText=\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9
|
||||
@ -239,6 +300,18 @@ ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\n\u3053\u306e\u5024\u309
|
||||
ViewPreferencesPanel.maxResultsLabel.text=\u30c6\u30fc\u30d6\u30eb\u3067\u8868\u793a\u3059\u308b\u6700\u5927\u7d50\u679c\u6570:
|
||||
ViewPreferencesPanel.fileNameTranslationColumnCheckbox.text=\u7d50\u679c\u30d3\u30e5\u30fc\u30ef\u30fc\u306b\u30d5\u30a1\u30a4\u30eb\u540d\u7ffb\u8a33\u7528\u5217\u3092\u8ffd\u52a0
|
||||
ViewPreferencesPanel.scoColumnsWrapAroundText.text=\u975e\u8868\u793a\u306b\u3059\u308b\u3068\u8aad\u8fbc\u307f\u304c\u65e9\u304f\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002
|
||||
ViewPreferencesPanel.scoColumnsLabel.text=\u6b21\u306e\u305f\u3081\u306e\u5217\u3092\u8ffd\u52a0\u3057\u306a\u3044:
|
||||
MultiUserSettingsPanel.tbSolr4Port.toolTipText=\u30dd\u30fc\u30c8\u756a\u53f7
|
||||
MultiUserSettingsPanel.lbZkSettings.text=Solr\u8a2d\u5b9a
|
||||
MultiUserSettingsPanel.lbSolr4Settings.text=Solr\u8a2d\u5b9a
|
||||
MultiUserSettingsPanel.lbSolr8Settings.text=Solr\u8a2d\u5b9a
|
||||
MultiUserSettingsPanel.tbZkPort.toolTipText=\u30dd\u30fc\u30c8\u756a\u53f7
|
||||
MultiUserSettingsPanel.tbSolr8Hostname.toolTipText=\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9
|
||||
MultiUserSettingsPanel.tbSolr8Port.toolTipText=\u30dd\u30fc\u30c8\u756a\u53f7
|
||||
MultiUserSettingsPanel.lbSolrNote1.text=Solr\u8a2d\u5b9a
|
||||
MultiUserSettingsPanel.lbSolrNote2.text=Solr\u8a2d\u5b9a
|
||||
ViewPreferencesPanel.selectFileLabel.text=\u30d5\u30a1\u30a4\u30eb\u9078\u629e\u6642\:
|
||||
ViewPreferencesPanel.translateTextLabel.text=\u30c6\u30ad\u30b9\u30c8\u3092\u7ffb\u8a33\:
|
||||
ViewPreferencesPanel.translateTextLabel.text=\u30c6\u30ad\u30b9\u30c8\u3092\u7ffb\u8a33:
|
||||
ViewPreferencesPanel.scoColumnsCheckbox.text=S(\u30b9\u30b3\u30a2)\u3001C(\u30b3\u30e1\u30f3\u30c8)\u3001O(\u767a\u751f)
|
||||
ViewPreferencesPanel.scoColumnsLabel.text=\u6b21\u306e\u305f\u3081\u306e\u5217\u3092\u8ffd\u52a0\u3057\u306a\u3044:
|
||||
|
@ -41,7 +41,7 @@ import org.sleuthkit.datamodel.TskException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import org.sleuthkit.autopsy.contentviewers.artifactviewers.ArtifactContentViewer;
|
||||
import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultArtifactContentViewer;
|
||||
import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultTableArtifactContentViewer;
|
||||
|
||||
/**
|
||||
* Instances of this class display the BlackboardArtifacts associated with the
|
||||
@ -357,7 +357,9 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
||||
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())
|
||||
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID())
|
||||
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID())
|
||||
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID())) {
|
||||
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID())
|
||||
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID())
|
||||
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID())) {
|
||||
return 3;
|
||||
} else {
|
||||
return 6;
|
||||
@ -370,7 +372,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
||||
return viewer;
|
||||
}
|
||||
}
|
||||
return new DefaultArtifactContentViewer();
|
||||
return new DefaultTableArtifactContentViewer();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -434,7 +434,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
}
|
||||
|
||||
setColumnWidths();
|
||||
|
||||
|
||||
/*
|
||||
* Load column sorting information from preferences file and apply it to
|
||||
* columns.
|
||||
@ -460,26 +460,27 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
* If one of the child nodes of the root node is to be selected, select
|
||||
* it.
|
||||
*/
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (rootNode instanceof TableFilterNode) {
|
||||
NodeSelectionInfo selectedChildInfo = ((TableFilterNode) rootNode).getChildNodeSelectionInfo();
|
||||
if (null != selectedChildInfo) {
|
||||
Node[] childNodes = rootNode.getChildren().getNodes(true);
|
||||
for (int i = 0; i < childNodes.length; ++i) {
|
||||
Node childNode = childNodes[i];
|
||||
if (selectedChildInfo.matches(childNode)) {
|
||||
if (rootNode instanceof TableFilterNode) {
|
||||
NodeSelectionInfo selectedChildInfo = ((TableFilterNode) rootNode).getChildNodeSelectionInfo();
|
||||
if (null != selectedChildInfo) {
|
||||
Node[] childNodes = rootNode.getChildren().getNodes(true);
|
||||
for (int i = 0; i < childNodes.length; ++i) {
|
||||
Node childNode = childNodes[i];
|
||||
if (selectedChildInfo.matches(childNode)) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
this.getExplorerManager().setSelectedNodes(new Node[]{childNode});
|
||||
this.getExplorerManager().setExploredContextAndSelection(this.rootNode, new Node[]{childNode});
|
||||
} catch (PropertyVetoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to select node specified by selected child info", ex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
((TableFilterNode) rootNode).setChildNodeSelectionInfo(null);
|
||||
}
|
||||
((TableFilterNode) rootNode).setChildNodeSelectionInfo(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* The table setup is done, so any added/removed events can now be
|
||||
@ -516,7 +517,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
protected void setColumnWidths() {
|
||||
if (rootNode.getChildren().getNodesCount() != 0) {
|
||||
final Graphics graphics = outlineView.getGraphics();
|
||||
|
||||
|
||||
if (graphics != null) {
|
||||
// Current width of the outlineView
|
||||
double outlineViewWidth = outlineView.getSize().getWidth();
|
||||
@ -526,10 +527,10 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
|
||||
int margin = 4;
|
||||
int padding = 8;
|
||||
|
||||
|
||||
int totalColumnWidth = 0;
|
||||
int cntMaxSizeColumns =0;
|
||||
|
||||
int cntMaxSizeColumns = 0;
|
||||
|
||||
// Calulate the width for each column keeping track of the number
|
||||
// of columns that were set to columnwidthLimit.
|
||||
for (int column = 0; column < outline.getModel().getColumnCount(); column++) {
|
||||
@ -552,40 +553,40 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
|
||||
columnWidth = Math.min(columnWidth, columnWidthLimit);
|
||||
columnWidths.add(columnWidth);
|
||||
|
||||
|
||||
totalColumnWidth += columnWidth;
|
||||
|
||||
if( columnWidth == columnWidthLimit) {
|
||||
|
||||
if (columnWidth == columnWidthLimit) {
|
||||
cntMaxSizeColumns++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Figure out how much extra, if any can be given to the columns
|
||||
// so that the table is as wide as outlineViewWidth. If cntMaxSizeColumns
|
||||
// is greater than 0 divide the extra space between the columns
|
||||
// that could use more space. Otherwise divide evenly amoung
|
||||
// all columns.
|
||||
int extraWidth = 0;
|
||||
|
||||
|
||||
if (totalColumnWidth < outlineViewWidth) {
|
||||
if (cntMaxSizeColumns > 0) {
|
||||
extraWidth = (int) ((outlineViewWidth - totalColumnWidth)/cntMaxSizeColumns);
|
||||
if (cntMaxSizeColumns > 0) {
|
||||
extraWidth = (int) ((outlineViewWidth - totalColumnWidth) / cntMaxSizeColumns);
|
||||
} else {
|
||||
extraWidth = (int) ((outlineViewWidth - totalColumnWidth)/columnWidths.size());
|
||||
extraWidth = (int) ((outlineViewWidth - totalColumnWidth) / columnWidths.size());
|
||||
}
|
||||
}
|
||||
|
||||
for(int column = 0; column < columnWidths.size(); column++) {
|
||||
|
||||
for (int column = 0; column < columnWidths.size(); column++) {
|
||||
int columnWidth = columnWidths.get(column);
|
||||
|
||||
if(cntMaxSizeColumns > 0) {
|
||||
if(columnWidth >= ((column == 0) ? 350 : 300)) {
|
||||
|
||||
if (cntMaxSizeColumns > 0) {
|
||||
if (columnWidth >= ((column == 0) ? 350 : 300)) {
|
||||
columnWidth += extraWidth;
|
||||
}
|
||||
} else {
|
||||
columnWidth += extraWidth;
|
||||
}
|
||||
|
||||
|
||||
outline.getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
|
||||
}
|
||||
}
|
||||
|
@ -16,14 +16,14 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane" alignment="0" pref="555" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane" alignment="0" pref="1191" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane" pref="537" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane" pref="695" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
@ -47,11 +47,12 @@
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbOops" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="pnSolrSettings" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="pnDatabaseSettings" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="pnMessagingSettings" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="pnMessagingSettings" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="pnSolrSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -63,12 +64,15 @@
|
||||
<Component id="cbEnableMultiUser" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="pnDatabaseSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="pnSolrSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="pnMessagingSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="39" max="32767" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="pnDatabaseSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="pnMessagingSettings" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="pnSolrSettings" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="201" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -92,7 +96,7 @@
|
||||
<Component id="tbDbHostname" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="lbDatabaseSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<EmptySpace pref="231" max="32767" attributes="0"/>
|
||||
<Component id="bnTestDatabase" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="lbTestDatabase" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
@ -116,7 +120,7 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="bnTestDatabase" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbTestDatabase" alignment="0" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<Component id="lbDatabaseSettings" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbDatabaseSettings" alignment="1" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="tbDbHostname" min="-2" max="-2" attributes="0"/>
|
||||
@ -252,17 +256,30 @@
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="tbSolrHostname" max="32767" attributes="0"/>
|
||||
<Component id="tbSolr8Hostname" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="lbSolrSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="lbSolr8Settings" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbSolrNote1" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbSolrNote2" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="57" max="32767" attributes="0"/>
|
||||
<Component id="bnTestSolr" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="lbTestSolr" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="tbSolrPort" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="lbTestSolrWarning" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="tbSolr8Port" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="tbSolr4Hostname" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="tbSolr4Port" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="tbZkHostname" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="tbZkPort" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="lbTestSolrWarning" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbSolr4Settings" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbZkSettings" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
@ -273,19 +290,35 @@
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
|
||||
<Component id="lbSolrNote1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="lbSolrNote2" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="bnTestSolr" alignment="1" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbSolrSettings" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="lbSolr8Settings" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnTestSolr" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="lbTestSolr" alignment="1" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<Component id="lbTestSolr" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbSolrHostname" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="tbSolrPort" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="tbSolr8Hostname" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbSolr8Port" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="lbSolr4Settings" min="-2" pref="21" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbSolr4Hostname" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbSolr4Port" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="lbZkSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbZkHostname" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbZkPort" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="lbTestSolrWarning" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@ -293,39 +326,39 @@
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="lbSolrSettings">
|
||||
<Component class="javax.swing.JLabel" name="lbSolr8Settings">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="lbSolrSettings" property="font" relativeSize="true" size="1"/>
|
||||
<Font component="lbSolr8Settings" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbSolrSettings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbSolr8Settings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="tbSolrHostname">
|
||||
<Component class="javax.swing.JTextField" name="tbSolr8Hostname">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="tbSolrHostname" property="font" relativeSize="true" size="1"/>
|
||||
<Font component="tbSolr8Hostname" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbSolrHostname.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbSolr8Hostname.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="tbSolrPort">
|
||||
<Component class="javax.swing.JTextField" name="tbSolr8Port">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="tbSolrPort" property="font" relativeSize="true" size="1"/>
|
||||
<Font component="tbSolr8Port" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbSolrPort.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbSolr8Port.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
@ -356,6 +389,107 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="tbSolr4Hostname">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="tbSolr4Hostname" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbSolr4Hostname.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="tbSolr4Port">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="tbSolr4Port" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbSolr4Port.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AccessibilityProperties>
|
||||
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbSolr4Port.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</AccessibilityProperties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lbSolr4Settings">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="lbSolr4Settings" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbSolr4Settings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lbZkSettings">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="lbZkSettings" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbZkSettings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="tbZkHostname">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="tbZkHostname" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbZkHostname.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="tbZkPort">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="tbZkPort" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbZkPort.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lbSolrNote1">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="lbSolrNote1" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbSolrNote1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lbSolrNote2">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="lbSolrNote2" property="font" relativeSize="true" size="1"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbSolrNote2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="pnMessagingSettings">
|
||||
@ -376,7 +510,7 @@
|
||||
<Component id="tbMsgHostname" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="lbMessageServiceSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="229" max="32767" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="bnTestMessageService" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="lbTestMessageService" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
@ -398,9 +532,9 @@
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="bnTestMessageService" alignment="1" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbMessageServiceSettings" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" alignment="1" groupAlignment="3" attributes="0">
|
||||
<Component id="bnTestMessageService" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbMessageServiceSettings" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="lbTestMessageService" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
|
@ -32,10 +32,14 @@ import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import java.awt.Cursor;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.util.ImageUtilities;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.coordinationservice.utils.CoordinationServiceUtils;
|
||||
import org.sleuthkit.autopsy.core.UserPreferencesException;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.events.MessageServiceException;
|
||||
@ -60,6 +64,17 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
private static final String INVALID_DB_PORT_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidDatabasePort");
|
||||
private static final String INVALID_MESSAGE_SERVICE_PORT_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidMessageServicePort");
|
||||
private static final String INVALID_INDEXING_SERVER_PORT_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidIndexingServerPort");
|
||||
private static final String INVALID_SOLR4_SERVER_PORT_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidSolr4ServerPort");
|
||||
private static final String SOLR_SERVER_NOT_CONFIGURED_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.solrNotConfigured");
|
||||
private static final String INVALID_ZK_SERVER_HOST_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidZkServerHostName");
|
||||
private static final String INVALID_ZK_SERVER_PORT_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidZkServerPort");
|
||||
private static final String SOLR8_HOST_NAME_OR_IP_PROMPT = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr8Hostname.toolTipText");
|
||||
private static final String SOLR8_PORT_PROMPT = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr8Port.toolTipText");
|
||||
private static final String SOLR4_HOST_NAME_OR_IP_PROMPT = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr4Hostname.toolTipText");
|
||||
private static final String SOLR4_PORT_PROMPT = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr4Port.toolTipText");
|
||||
private static final String ZK_HOST_NAME_OR_IP_PROMPT = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbZkHostname.toolTipText");
|
||||
private static final String ZK_PORT_PROMPT = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbZkPort.toolTipText");
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final MultiUserSettingsPanelController controller;
|
||||
private final Collection<JTextField> textBoxes = new ArrayList<>();
|
||||
@ -91,8 +106,12 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
textPrompts.add(new TextPrompt(PORT_PROMPT, tbMsgPort));
|
||||
textPrompts.add(new TextPrompt(USER_NAME_PROMPT_OPT, tbMsgUsername));
|
||||
textPrompts.add(new TextPrompt(PASSWORD_PROMPT_OPT, tbMsgPassword));
|
||||
textPrompts.add(new TextPrompt(HOST_NAME_OR_IP_PROMPT, tbSolrHostname));
|
||||
textPrompts.add(new TextPrompt(PORT_PROMPT, tbSolrPort));
|
||||
textPrompts.add(new TextPrompt(SOLR8_HOST_NAME_OR_IP_PROMPT, tbSolr8Hostname));
|
||||
textPrompts.add(new TextPrompt(SOLR8_PORT_PROMPT, tbSolr8Port));
|
||||
textPrompts.add(new TextPrompt(SOLR4_HOST_NAME_OR_IP_PROMPT, tbSolr4Hostname));
|
||||
textPrompts.add(new TextPrompt(SOLR4_PORT_PROMPT, tbSolr4Port));
|
||||
textPrompts.add(new TextPrompt(ZK_HOST_NAME_OR_IP_PROMPT, tbZkHostname));
|
||||
textPrompts.add(new TextPrompt(ZK_PORT_PROMPT, tbZkPort));
|
||||
configureTextPrompts(textPrompts);
|
||||
|
||||
/*
|
||||
@ -104,8 +123,12 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
tbDbUsername.getDocument().putProperty("statusIcon", lbTestDatabase);
|
||||
tbDbPassword.getDocument().putProperty("statusIcon", lbTestDatabase);
|
||||
|
||||
tbSolrHostname.getDocument().putProperty("statusIcon", lbTestSolr);
|
||||
tbSolrPort.getDocument().putProperty("statusIcon", lbTestSolr);
|
||||
tbSolr8Hostname.getDocument().putProperty("statusIcon", lbTestSolr);
|
||||
tbSolr8Port.getDocument().putProperty("statusIcon", lbTestSolr);
|
||||
tbSolr4Hostname.getDocument().putProperty("statusIcon", lbTestSolr);
|
||||
tbSolr4Port.getDocument().putProperty("statusIcon", lbTestSolr);
|
||||
tbZkHostname.getDocument().putProperty("statusIcon", lbTestSolr);
|
||||
tbZkPort.getDocument().putProperty("statusIcon", lbTestSolr);
|
||||
|
||||
tbMsgHostname.getDocument().putProperty("statusIcon", lbTestMessageService);
|
||||
tbMsgPort.getDocument().putProperty("statusIcon", lbTestMessageService);
|
||||
@ -122,8 +145,15 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
textBoxes.add(tbMsgPort);
|
||||
textBoxes.add(tbMsgUsername);
|
||||
textBoxes.add(tbMsgPassword);
|
||||
textBoxes.add(tbSolrHostname);
|
||||
textBoxes.add(tbSolrPort);
|
||||
textBoxes.add(tbSolr8Hostname);
|
||||
textBoxes.add(tbSolr8Port);
|
||||
textBoxes.add(tbSolr4Hostname);
|
||||
textBoxes.add(tbSolr4Port);
|
||||
textBoxes.add(tbZkHostname);
|
||||
textBoxes.add(tbZkPort);
|
||||
|
||||
// as the user enters Solr 8 settings, we fill in the ZK settings with the embedded Solr 8 ZK connection info.
|
||||
tbSolr8Hostname.getDocument().addDocumentListener(new MyDocumentListener());
|
||||
|
||||
addDocumentListeners(textBoxes, textBoxChangedListener);
|
||||
goodIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/good.png", false));
|
||||
@ -178,12 +208,20 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
lbTestDatabase = new javax.swing.JLabel();
|
||||
lbTestDbWarning = new javax.swing.JLabel();
|
||||
pnSolrSettings = new javax.swing.JPanel();
|
||||
lbSolrSettings = new javax.swing.JLabel();
|
||||
tbSolrHostname = new javax.swing.JTextField();
|
||||
tbSolrPort = new javax.swing.JTextField();
|
||||
lbSolr8Settings = new javax.swing.JLabel();
|
||||
tbSolr8Hostname = new javax.swing.JTextField();
|
||||
tbSolr8Port = new javax.swing.JTextField();
|
||||
bnTestSolr = new javax.swing.JButton();
|
||||
lbTestSolr = new javax.swing.JLabel();
|
||||
lbTestSolrWarning = new javax.swing.JLabel();
|
||||
tbSolr4Hostname = new javax.swing.JTextField();
|
||||
tbSolr4Port = new javax.swing.JTextField();
|
||||
lbSolr4Settings = new javax.swing.JLabel();
|
||||
lbZkSettings = new javax.swing.JLabel();
|
||||
tbZkHostname = new javax.swing.JTextField();
|
||||
tbZkPort = new javax.swing.JTextField();
|
||||
lbSolrNote1 = new javax.swing.JLabel();
|
||||
lbSolrNote2 = new javax.swing.JLabel();
|
||||
pnMessagingSettings = new javax.swing.JPanel();
|
||||
lbMessageServiceSettings = new javax.swing.JLabel();
|
||||
tbMsgHostname = new javax.swing.JTextField();
|
||||
@ -241,7 +279,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
.addComponent(tbDbHostname)
|
||||
.addGroup(pnDatabaseSettingsLayout.createSequentialGroup()
|
||||
.addComponent(lbDatabaseSettings)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 231, Short.MAX_VALUE)
|
||||
.addComponent(bnTestDatabase)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
@ -260,7 +298,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
.addGroup(pnDatabaseSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(bnTestDatabase)
|
||||
.addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(lbDatabaseSettings))
|
||||
.addComponent(lbDatabaseSettings, javax.swing.GroupLayout.Alignment.TRAILING))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(tbDbHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
@ -276,14 +314,14 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
|
||||
pnSolrSettings.setBorder(javax.swing.BorderFactory.createEtchedBorder());
|
||||
|
||||
lbSolrSettings.setFont(lbSolrSettings.getFont().deriveFont(lbSolrSettings.getFont().getSize()+1f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbSolrSettings, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbSolrSettings.text")); // NOI18N
|
||||
lbSolr8Settings.setFont(lbSolr8Settings.getFont().deriveFont(lbSolr8Settings.getFont().getSize()+1f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbSolr8Settings, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbSolr8Settings.text")); // NOI18N
|
||||
|
||||
tbSolrHostname.setFont(tbSolrHostname.getFont().deriveFont(tbSolrHostname.getFont().getSize()+1f));
|
||||
tbSolrHostname.setToolTipText(org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolrHostname.toolTipText")); // NOI18N
|
||||
tbSolr8Hostname.setFont(tbSolr8Hostname.getFont().deriveFont(tbSolr8Hostname.getFont().getSize()+1f));
|
||||
tbSolr8Hostname.setToolTipText(org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr8Hostname.toolTipText")); // NOI18N
|
||||
|
||||
tbSolrPort.setFont(tbSolrPort.getFont().deriveFont(tbSolrPort.getFont().getSize()+1f));
|
||||
tbSolrPort.setToolTipText(org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolrPort.toolTipText")); // NOI18N
|
||||
tbSolr8Port.setFont(tbSolr8Port.getFont().deriveFont(tbSolr8Port.getFont().getSize()+1f));
|
||||
tbSolr8Port.setToolTipText(org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr8Port.toolTipText")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnTestSolr, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.bnTestSolr.text")); // NOI18N
|
||||
bnTestSolr.addActionListener(new java.awt.event.ActionListener() {
|
||||
@ -297,6 +335,30 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
lbTestSolrWarning.setForeground(new java.awt.Color(255, 0, 0));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbTestSolrWarning, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestSolrWarning.text")); // NOI18N
|
||||
|
||||
tbSolr4Hostname.setFont(tbSolr4Hostname.getFont().deriveFont(tbSolr4Hostname.getFont().getSize()+1f));
|
||||
tbSolr4Hostname.setToolTipText(org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr4Hostname.toolTipText")); // NOI18N
|
||||
|
||||
tbSolr4Port.setFont(tbSolr4Port.getFont().deriveFont(tbSolr4Port.getFont().getSize()+1f));
|
||||
tbSolr4Port.setToolTipText(org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr4Port.toolTipText")); // NOI18N
|
||||
|
||||
lbSolr4Settings.setFont(lbSolr4Settings.getFont().deriveFont(lbSolr4Settings.getFont().getSize()+1f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbSolr4Settings, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbSolr4Settings.text")); // NOI18N
|
||||
|
||||
lbZkSettings.setFont(lbZkSettings.getFont().deriveFont(lbZkSettings.getFont().getSize()+1f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbZkSettings, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbZkSettings.text")); // NOI18N
|
||||
|
||||
tbZkHostname.setFont(tbZkHostname.getFont().deriveFont(tbZkHostname.getFont().getSize()+1f));
|
||||
tbZkHostname.setToolTipText(org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbZkHostname.toolTipText")); // NOI18N
|
||||
|
||||
tbZkPort.setFont(tbZkPort.getFont().deriveFont(tbZkPort.getFont().getSize()+1f));
|
||||
tbZkPort.setToolTipText(org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbZkPort.toolTipText")); // NOI18N
|
||||
|
||||
lbSolrNote1.setFont(lbSolrNote1.getFont().deriveFont(lbSolrNote1.getFont().getSize()+1f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbSolrNote1, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbSolrNote1.text")); // NOI18N
|
||||
|
||||
lbSolrNote2.setFont(lbSolrNote2.getFont().deriveFont(lbSolrNote2.getFont().getSize()+1f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbSolrNote2, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbSolrNote2.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout pnSolrSettingsLayout = new javax.swing.GroupLayout(pnSolrSettings);
|
||||
pnSolrSettings.setLayout(pnSolrSettingsLayout);
|
||||
pnSolrSettingsLayout.setHorizontalGroup(
|
||||
@ -304,37 +366,66 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(tbSolrHostname)
|
||||
.addComponent(tbSolr8Hostname)
|
||||
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
|
||||
.addComponent(lbSolrSettings)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(lbSolr8Settings)
|
||||
.addComponent(lbSolrNote1)
|
||||
.addComponent(lbSolrNote2))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 57, Short.MAX_VALUE)
|
||||
.addComponent(bnTestSolr)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(lbTestSolr, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(tbSolrPort)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(lbTestSolr, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(3, 3, 3))
|
||||
.addComponent(tbSolr8Port)
|
||||
.addComponent(tbSolr4Hostname)
|
||||
.addComponent(tbSolr4Port)
|
||||
.addComponent(tbZkHostname)
|
||||
.addComponent(tbZkPort)
|
||||
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
|
||||
.addComponent(lbTestSolrWarning)
|
||||
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(lbTestSolrWarning)
|
||||
.addComponent(lbSolr4Settings)
|
||||
.addComponent(lbZkSettings))
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
pnSolrSettingsLayout.setVerticalGroup(
|
||||
pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGap(8, 8, 8)
|
||||
.addComponent(lbSolrNote1)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(lbSolrNote2)
|
||||
.addGap(20, 20, 20)
|
||||
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(bnTestSolr, javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(lbSolrSettings))
|
||||
.addComponent(lbTestSolr, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(lbSolr8Settings)
|
||||
.addComponent(bnTestSolr))
|
||||
.addComponent(lbTestSolr, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbSolrHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(tbSolr8Hostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbSolr8Port, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(lbSolr4Settings, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbSolr4Hostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbSolr4Port, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(lbZkSettings)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbZkHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbZkPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(tbSolrPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(lbTestSolrWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
tbSolr4Port.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr4Port.toolTipText")); // NOI18N
|
||||
|
||||
pnMessagingSettings.setBorder(javax.swing.BorderFactory.createEtchedBorder());
|
||||
|
||||
lbMessageServiceSettings.setFont(lbMessageServiceSettings.getFont().deriveFont((float)12));
|
||||
@ -378,7 +469,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
.addComponent(tbMsgHostname)
|
||||
.addGroup(pnMessagingSettingsLayout.createSequentialGroup()
|
||||
.addComponent(lbMessageServiceSettings)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 229, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(bnTestMessageService)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(lbTestMessageService, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
@ -395,8 +486,8 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
.addGroup(pnMessagingSettingsLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(pnMessagingSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(pnMessagingSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(bnTestMessageService, javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(pnMessagingSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(bnTestMessageService)
|
||||
.addComponent(lbMessageServiceSettings))
|
||||
.addComponent(lbTestMessageService, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
@ -436,10 +527,11 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
.addComponent(cbEnableMultiUser)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbOops))
|
||||
.addComponent(pnSolrSettings, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(pnDatabaseSettings, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(pnMessagingSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addContainerGap())
|
||||
.addComponent(pnMessagingSettings, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(pnSolrSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(39, 39, 39))
|
||||
);
|
||||
pnOverallPanelLayout.setVerticalGroup(
|
||||
pnOverallPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
@ -448,12 +540,13 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
.addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(cbEnableMultiUser))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(pnDatabaseSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(pnSolrSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(pnMessagingSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(39, Short.MAX_VALUE))
|
||||
.addGroup(pnOverallPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
||||
.addGroup(pnOverallPanelLayout.createSequentialGroup()
|
||||
.addComponent(pnDatabaseSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(pnMessagingSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(pnSolrSettings, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap(201, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
jScrollPane.setViewportView(pnOverallPanel);
|
||||
@ -462,13 +555,13 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 555, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1191, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 537, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 695, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
@ -477,7 +570,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
* Enables/disables the multi-user settings, based upon input provided
|
||||
*
|
||||
* @param textFields The text fields to enable/disable.
|
||||
* @param enabled True means enable, false means disable.
|
||||
* @param enabled True means enable, false means disable.
|
||||
*/
|
||||
private static void enableMultiUserComponents(Collection<JTextField> textFields, boolean enabled) {
|
||||
for (JTextField textField : textFields) {
|
||||
@ -570,8 +663,31 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
|
||||
try {
|
||||
if (kwsService != null) {
|
||||
int port = Integer.parseInt(tbSolrPort.getText().trim());
|
||||
kwsService.tryConnect(tbSolrHostname.getText().trim(), port);
|
||||
// test Solr 8 connectivity
|
||||
if (!tbSolr8Port.getText().trim().isEmpty() && !tbSolr8Hostname.getText().trim().isEmpty()) {
|
||||
int port = Integer.parseInt(tbSolr8Port.getText().trim());
|
||||
kwsService.tryConnect(tbSolr8Hostname.getText().trim(), port);
|
||||
}
|
||||
|
||||
// test Solr 4 conenctivity
|
||||
if (!tbSolr4Port.getText().trim().isEmpty() && !tbSolr4Hostname.getText().trim().isEmpty()) {
|
||||
int port = Integer.parseInt(tbSolr4Port.getText().trim());
|
||||
kwsService.tryConnect(tbSolr4Hostname.getText().trim(), port);
|
||||
}
|
||||
|
||||
// test ZooKeeper connectivity (ZK settings are mandatory)
|
||||
if (tbZkPort.getText().trim().isEmpty() || tbZkHostname.getText().trim().isEmpty()) {
|
||||
lbTestSolr.setIcon(badIcon);
|
||||
lbTestSolrWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.UnableToConnectToZK"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (false == CoordinationServiceUtils.isZooKeeperAccessible(tbZkHostname.getText().trim(), tbZkPort.getText().trim())) {
|
||||
lbTestSolr.setIcon(badIcon);
|
||||
lbTestSolrWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.UnableToConnectToZK"));
|
||||
return;
|
||||
}
|
||||
|
||||
lbTestSolr.setIcon(goodIcon);
|
||||
lbTestSolrWarning.setText("");
|
||||
} else {
|
||||
@ -584,6 +700,10 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
} catch (KeywordSearchServiceException ex) {
|
||||
lbTestSolr.setIcon(badIcon);
|
||||
lbTestSolrWarning.setText(ex.getMessage());
|
||||
} catch (InterruptedException | IOException ex) {
|
||||
// ZK exceptions
|
||||
lbTestSolr.setIcon(badIcon);
|
||||
lbTestSolrWarning.setText(ex.getMessage());
|
||||
} finally {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
@ -617,14 +737,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
logger.log(Level.SEVERE, "Error accessing case database connection info", ex); //NON-NLS
|
||||
}
|
||||
|
||||
String indexingServerHost = UserPreferences.getIndexingServerHost().trim();
|
||||
if (!indexingServerHost.isEmpty()) {
|
||||
tbSolrHostname.setText(indexingServerHost);
|
||||
}
|
||||
String indexingServerPort = UserPreferences.getIndexingServerPort().trim();
|
||||
if (portNumberIsValid(indexingServerPort)) {
|
||||
tbSolrPort.setText(indexingServerPort);
|
||||
}
|
||||
populateSolrAndZkSettings();
|
||||
|
||||
lbTestDatabase.setIcon(null);
|
||||
lbTestSolr.setIcon(null);
|
||||
@ -638,6 +751,54 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
this.valid(); // trigger validation to enable buttons based on current settings
|
||||
}
|
||||
|
||||
private void populateSolrAndZkSettings() {
|
||||
|
||||
String indexingServerHost = UserPreferences.getIndexingServerHost().trim();
|
||||
if (!indexingServerHost.isEmpty()) {
|
||||
tbSolr8Hostname.setText(indexingServerHost);
|
||||
}
|
||||
String indexingServerPort = UserPreferences.getIndexingServerPort().trim();
|
||||
if (portNumberIsValid(indexingServerPort)) {
|
||||
tbSolr8Port.setText(indexingServerPort);
|
||||
}
|
||||
|
||||
String solr4ServerHost = UserPreferences.getSolr4ServerHost().trim();
|
||||
if (!solr4ServerHost.isEmpty()) {
|
||||
tbSolr4Hostname.setText(solr4ServerHost);
|
||||
}
|
||||
String solr4ServerPort = UserPreferences.getSolr4ServerPort().trim();
|
||||
if (portNumberIsValid(solr4ServerPort)) {
|
||||
tbSolr4Port.setText(solr4ServerPort);
|
||||
}
|
||||
|
||||
// if there are existing valid ZK settings, use those
|
||||
String zkServerPort = UserPreferences.getZkServerPort().trim();
|
||||
if (portNumberIsValid(zkServerPort)) {
|
||||
tbZkPort.setText(zkServerPort); // gets default ZK port, which is Solr port number + 1000
|
||||
}
|
||||
String zkServerHost = UserPreferences.getZkServerHost().trim();
|
||||
if (!zkServerHost.isEmpty()) {
|
||||
tbZkHostname.setText(zkServerHost);
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are no previous Solr 4 settings, use Solr 8 settings
|
||||
// to fill in the ZK settings with the embedded Solr 8 ZK connection info.
|
||||
if (solr4ServerHost.isEmpty() && !indexingServerHost.isEmpty()) {
|
||||
tbZkHostname.setText(indexingServerHost);
|
||||
tbZkPort.setText(zkServerPort); // gets default ZK port, which is Solr port number + 1000
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are existing Solr 4 settings and no Solr 8 settings,
|
||||
// pre-populate the ZK settings with the Solr 4 embedded ZK settings.
|
||||
if (!solr4ServerHost.isEmpty() && indexingServerHost.isEmpty()) {
|
||||
tbZkHostname.setText(solr4ServerHost);
|
||||
tbZkPort.setText(zkServerPort); // gets default ZK port, which is Solr port number + 1000
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether or not values have been entered in all of the database
|
||||
* settings text fields.
|
||||
@ -652,14 +813,31 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether or not values have been entered in all of the Solr settings
|
||||
* text fields.
|
||||
* Tests whether or not values have been entered in all of the mandatory
|
||||
* Solr settings text fields. Test optional settings for completeness.
|
||||
*
|
||||
* @return True or false.
|
||||
*/
|
||||
private boolean solrFieldsArePopulated() {
|
||||
return !tbSolrHostname.getText().trim().isEmpty()
|
||||
&& !tbSolrPort.getText().trim().isEmpty();
|
||||
|
||||
// either Solr 8 or/and Solr 4 seetings must be specified
|
||||
boolean solrConfigured = false;
|
||||
|
||||
// check if Solr 8 settings are set
|
||||
if (!tbSolr8Hostname.getText().trim().isEmpty()
|
||||
&& !tbSolr8Port.getText().trim().isEmpty()) {
|
||||
solrConfigured = true;
|
||||
}
|
||||
|
||||
// check if Solr 4 settings are set
|
||||
if (!tbSolr4Hostname.getText().trim().isEmpty()
|
||||
&& !tbSolr4Port.getText().trim().isEmpty()) {
|
||||
solrConfigured = true;
|
||||
}
|
||||
|
||||
// ZK settings are mandatory
|
||||
return (solrConfigured && !tbZkHostname.getText().trim().isEmpty()
|
||||
&& !tbZkPort.getText().trim().isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -680,8 +858,6 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
boolean isPwSet = (tbMsgPassword.getPassword().length != 0);
|
||||
return (isUserSet == isPwSet);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void store() {
|
||||
boolean prevSelected = UserPreferences.getIsMultiUserModeEnabled();
|
||||
@ -694,11 +870,18 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
|
||||
boolean multiUserCasesEnabled = cbEnableMultiUser.isSelected();
|
||||
UserPreferences.setIsMultiUserModeEnabled(multiUserCasesEnabled);
|
||||
|
||||
|
||||
CaseDbConnectionInfo info = null;
|
||||
|
||||
|
||||
if (multiUserCasesEnabled == true) {
|
||||
|
||||
// Check if aplication restart is required.
|
||||
boolean needsRestart = false;
|
||||
// don't check if entring multi user data for the first time
|
||||
if (prevSelected == true) {
|
||||
needsRestart = isRestartRequired();
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently only supporting multi-user cases with PostgreSQL case
|
||||
* databases.
|
||||
@ -735,37 +918,63 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
logger.log(Level.SEVERE, "Error saving messaging service connection info", ex); //NON-NLS
|
||||
}
|
||||
|
||||
UserPreferences.setIndexingServerHost(tbSolrHostname.getText().trim());
|
||||
UserPreferences.setIndexingServerPort(Integer.parseInt(tbSolrPort.getText().trim()));
|
||||
UserPreferences.setIndexingServerHost(tbSolr8Hostname.getText().trim());
|
||||
String solr8port = tbSolr8Port.getText().trim();
|
||||
if (!solr8port.isEmpty()) {
|
||||
UserPreferences.setIndexingServerPort(Integer.parseInt(solr8port));
|
||||
}
|
||||
UserPreferences.setSolr4ServerHost(tbSolr4Hostname.getText().trim());
|
||||
UserPreferences.setSolr4ServerPort(tbSolr4Port.getText().trim());
|
||||
UserPreferences.setZkServerHost(tbZkHostname.getText().trim());
|
||||
UserPreferences.setZkServerPort(tbZkPort.getText().trim());
|
||||
|
||||
if (needsRestart) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.MustRestart"),
|
||||
NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.restartRequiredLabel.text"),
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// trigger changes to whether or not user can use multi user settings for central repository
|
||||
if (prevSelected != multiUserCasesEnabled || !areCaseDbConnectionEqual(prevConn, info))
|
||||
if (prevSelected != multiUserCasesEnabled || !areCaseDbConnectionEqual(prevConn, info)) {
|
||||
GlobalSettingsPanel.onMultiUserChange(this, prevSelected, multiUserCasesEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRestartRequired() {
|
||||
// if ZK was previously configured
|
||||
if (!UserPreferences.getZkServerHost().isEmpty()) {
|
||||
// Restart is required any time ZK info has changed
|
||||
if (!(tbZkHostname.getText().trim().equalsIgnoreCase(UserPreferences.getZkServerHost()))
|
||||
|| !(tbZkPort.getText().trim().equals(UserPreferences.getZkServerPort()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean arePropsEqual(Object a, Object b) {
|
||||
if (a == null || b == null) {
|
||||
return (a == null && b == null);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return a.equals(b);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean areCaseDbConnectionEqual(CaseDbConnectionInfo a, CaseDbConnectionInfo b) {
|
||||
|
||||
private static boolean areCaseDbConnectionEqual(CaseDbConnectionInfo a, CaseDbConnectionInfo b) {
|
||||
if (a == null || b == null) {
|
||||
return (a == null && b == null);
|
||||
}
|
||||
|
||||
return
|
||||
arePropsEqual(a.getDbType(), b.getDbType()) &&
|
||||
arePropsEqual(a.getHost(), b.getHost()) &&
|
||||
arePropsEqual(a.getPassword(), b.getPassword()) &&
|
||||
arePropsEqual(a.getPort(), b.getPort()) &&
|
||||
arePropsEqual(a.getUserName(), b.getUserName());
|
||||
}
|
||||
|
||||
return arePropsEqual(a.getDbType(), b.getDbType())
|
||||
&& arePropsEqual(a.getHost(), b.getHost())
|
||||
&& arePropsEqual(a.getPassword(), b.getPassword())
|
||||
&& arePropsEqual(a.getPort(), b.getPort())
|
||||
&& arePropsEqual(a.getUserName(), b.getUserName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the form is filled out correctly for our usage.
|
||||
@ -849,11 +1058,55 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
* @return True or false.
|
||||
*/
|
||||
boolean indexingServerSettingsAreValid() {
|
||||
if (!portNumberIsValid(tbSolrPort.getText().trim())) {
|
||||
|
||||
String solr8Port = tbSolr8Port.getText().trim();
|
||||
if (!solr8Port.isEmpty() && !portNumberIsValid(solr8Port)) {
|
||||
// if the port is specified, it has to be valid
|
||||
tbOops.setText(INVALID_INDEXING_SERVER_PORT_MSG);
|
||||
return false;
|
||||
}
|
||||
|
||||
String solr4Port = tbSolr4Port.getText().trim();
|
||||
if (!solr4Port.isEmpty() && !portNumberIsValid(solr4Port)) {
|
||||
// if the port is specified, it has to be valid
|
||||
tbOops.setText(INVALID_SOLR4_SERVER_PORT_MSG);
|
||||
return false;
|
||||
}
|
||||
|
||||
// either Solr 8 or/and Solr 4 seetings must be specified
|
||||
boolean solrConfigured = false;
|
||||
|
||||
// check if Solr 8 settings are set
|
||||
if (!tbSolr8Hostname.getText().trim().isEmpty()
|
||||
&& !tbSolr8Port.getText().trim().isEmpty()) {
|
||||
solrConfigured = true;
|
||||
}
|
||||
|
||||
// check if Solr 4 settings are set
|
||||
if (!tbSolr4Hostname.getText().trim().isEmpty()
|
||||
&& !tbSolr4Port.getText().trim().isEmpty()) {
|
||||
solrConfigured = true;
|
||||
}
|
||||
|
||||
if (!solrConfigured) {
|
||||
tbOops.setText(SOLR_SERVER_NOT_CONFIGURED_MSG);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ZK settings are mandatory
|
||||
if (tbZkHostname.getText().trim().isEmpty()) {
|
||||
tbOops.setText(INVALID_ZK_SERVER_HOST_MSG);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ZK settings are mandatory
|
||||
String zkPort = tbZkPort.getText().trim();
|
||||
if (zkPort.isEmpty() || !portNumberIsValid(zkPort)) {
|
||||
// if the port is specified, it has to be valid
|
||||
tbOops.setText(INVALID_ZK_SERVER_PORT_MSG);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -885,13 +1138,17 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
private javax.swing.JScrollPane jScrollPane;
|
||||
private javax.swing.JLabel lbDatabaseSettings;
|
||||
private javax.swing.JLabel lbMessageServiceSettings;
|
||||
private javax.swing.JLabel lbSolrSettings;
|
||||
private javax.swing.JLabel lbSolr4Settings;
|
||||
private javax.swing.JLabel lbSolr8Settings;
|
||||
private javax.swing.JLabel lbSolrNote1;
|
||||
private javax.swing.JLabel lbSolrNote2;
|
||||
private javax.swing.JLabel lbTestDatabase;
|
||||
private javax.swing.JLabel lbTestDbWarning;
|
||||
private javax.swing.JLabel lbTestMessageService;
|
||||
private javax.swing.JLabel lbTestMessageWarning;
|
||||
private javax.swing.JLabel lbTestSolr;
|
||||
private javax.swing.JLabel lbTestSolrWarning;
|
||||
private javax.swing.JLabel lbZkSettings;
|
||||
private javax.swing.JPanel pnDatabaseSettings;
|
||||
private javax.swing.JPanel pnMessagingSettings;
|
||||
private javax.swing.JPanel pnOverallPanel;
|
||||
@ -905,8 +1162,12 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
private javax.swing.JTextField tbMsgPort;
|
||||
private javax.swing.JTextField tbMsgUsername;
|
||||
private javax.swing.JTextField tbOops;
|
||||
private javax.swing.JTextField tbSolrHostname;
|
||||
private javax.swing.JTextField tbSolrPort;
|
||||
private javax.swing.JTextField tbSolr4Hostname;
|
||||
private javax.swing.JTextField tbSolr4Port;
|
||||
private javax.swing.JTextField tbSolr8Hostname;
|
||||
private javax.swing.JTextField tbSolr8Port;
|
||||
private javax.swing.JTextField tbZkHostname;
|
||||
private javax.swing.JTextField tbZkPort;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
@ -942,4 +1203,22 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
|
||||
controller.changed();
|
||||
}
|
||||
}
|
||||
|
||||
private class MyDocumentListener implements DocumentListener {
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
tbZkHostname.setText(tbSolr8Hostname.getText().trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
tbZkHostname.setText(tbSolr8Hostname.getText().trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
tbZkHostname.setText(tbSolr8Hostname.getText().trim());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
197
Core/src/org/sleuthkit/autopsy/coreutils/DomainTokenizer.java
Normal file
197
Core/src/org/sleuthkit/autopsy/coreutils/DomainTokenizer.java
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.coreutils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Attempts to get the domain from a url/domain provided removing the
|
||||
* subdomain(s).
|
||||
*/
|
||||
class DomainTokenizer {
|
||||
|
||||
/**
|
||||
* This is a node in the trie. Children in the hashmap are identified by
|
||||
* token. So for example, for a domain google.co.uk, the top level category
|
||||
* would have an entry for "uk" linking to a domain category with "co".
|
||||
*/
|
||||
private static class DomainCategory extends HashMap<String, DomainCategory> {
|
||||
|
||||
private DomainCategory getOrAddChild(String childKey) {
|
||||
DomainCategory cat = this.get(childKey);
|
||||
if (cat == null) {
|
||||
cat = new DomainCategory();
|
||||
this.put(childKey, cat);
|
||||
}
|
||||
|
||||
return cat;
|
||||
}
|
||||
}
|
||||
|
||||
// Character for joining domain segments.
|
||||
private static final String JOINER = ".";
|
||||
// delimiter when used with regex
|
||||
private static final String DELIMITER = "\\" + JOINER;
|
||||
|
||||
private static final String WILDCARD = "*";
|
||||
private static final String EXCEPTION_PREFIX = "!";
|
||||
|
||||
// taken from https://publicsuffix.org/list/public_suffix_list.dat
|
||||
// file containing line seperated suffixes
|
||||
// rules for parsing can be found here: https://publicsuffix.org/list/
|
||||
private static final String DOMAIN_LIST = "public_suffix_list.dat";
|
||||
|
||||
// token for comments
|
||||
private static final String COMMENT_TOKEN = "//";
|
||||
|
||||
// singleton instance of this class.
|
||||
private static DomainTokenizer categorizer = null;
|
||||
|
||||
/**
|
||||
* Returns the singleton instance of this class.
|
||||
*
|
||||
* @return The DomainCategorizer instance.
|
||||
* @throws IOException
|
||||
*/
|
||||
static DomainTokenizer getInstance() throws IOException {
|
||||
if (categorizer == null) {
|
||||
categorizer = load();
|
||||
}
|
||||
|
||||
return categorizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a DomainCategorizer instance using the public suffix list.
|
||||
*
|
||||
* @return The DomainCategorizer instance.
|
||||
* @throws IOException If there is an error reading the file.
|
||||
*/
|
||||
private static DomainTokenizer load() throws IOException {
|
||||
try (InputStream is = DomainTokenizer.class.getResourceAsStream(DOMAIN_LIST);
|
||||
InputStreamReader isReader = new InputStreamReader(is, StandardCharsets.UTF_8);
|
||||
BufferedReader reader = new BufferedReader(isReader)) {
|
||||
|
||||
DomainTokenizer categorizer = new DomainTokenizer();
|
||||
while (reader.ready()) {
|
||||
String line = reader.readLine();
|
||||
String trimmed = line.trim();
|
||||
if (!StringUtils.isBlank(trimmed) && !trimmed.startsWith(COMMENT_TOKEN)) {
|
||||
categorizer.addDomainSuffix(trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
return categorizer;
|
||||
}
|
||||
}
|
||||
|
||||
private DomainTokenizer() {
|
||||
}
|
||||
|
||||
// The top-level trie node.
|
||||
private final DomainCategory trie = new DomainCategory();
|
||||
|
||||
/**
|
||||
* Parses a domain suffix and adds components to the trie in reverse order
|
||||
* (i.e. ".co.uk" becomes "uk" -> "co").
|
||||
*
|
||||
* @param domainSuffix The domain suffix.
|
||||
*/
|
||||
private void addDomainSuffix(String domainSuffix) {
|
||||
if (StringUtils.isBlank(domainSuffix)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] tokens = domainSuffix.toLowerCase().trim().split(DELIMITER);
|
||||
|
||||
DomainCategory cat = trie;
|
||||
for (int i = tokens.length - 1; i >= 0; i--) {
|
||||
String token = tokens[i];
|
||||
if (StringUtils.isBlank(token)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cat = cat.getOrAddChild(tokens[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the domain by attempting to identify the host without the subdomain.
|
||||
* If no domain can be determined, the domain is returned.
|
||||
*
|
||||
* @param domain The domain to query for.
|
||||
* @return If provided argument is blank, null is returned. If no domain
|
||||
* suffixes can be identified, the full host is returned. If a host and
|
||||
* suffixes are identified, the domain (all suffixes with a prefix of the
|
||||
* next token) are returned.
|
||||
*/
|
||||
String getDomain(String domain) {
|
||||
if (StringUtils.isBlank(domain)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
List<String> tokens = Stream.of(domain.toLowerCase().split(DELIMITER))
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
int idx = tokens.size() - 1;
|
||||
DomainCategory cat = trie;
|
||||
|
||||
for (; idx >= 0; idx--) {
|
||||
// an exception rule must be at the beginning of a suffix, and, in
|
||||
// practice, indicates a domain that would otherwise be a further
|
||||
// suffix with a wildcard rule per: https://publicsuffix.org/list/
|
||||
if (cat.get(EXCEPTION_PREFIX + tokens.get(idx)) != null) {
|
||||
break;
|
||||
}
|
||||
|
||||
DomainCategory newCat = cat.get(tokens.get(idx));
|
||||
|
||||
// if no matching token can be found, look for wildcard token
|
||||
if (newCat == null) {
|
||||
// if no wildcard token can be found, the portion found
|
||||
// so far is the suffix.
|
||||
newCat = cat.get(WILDCARD);
|
||||
if (newCat == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cat = newCat;
|
||||
}
|
||||
|
||||
// if first suffix cannot be found, return the whole domain
|
||||
if (idx == tokens.size() - 1) {
|
||||
return domain;
|
||||
} else {
|
||||
int minIndex = Math.max(0, idx);
|
||||
List<String> subList = tokens.subList(minIndex, tokens.size());
|
||||
return String.join(JOINER, subList);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,13 +18,17 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.coreutils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
public class NetworkUtils {
|
||||
|
||||
|
||||
private static final Logger logger = Logger.getLogger(NetworkUtils.class.getName());
|
||||
|
||||
private NetworkUtils() {
|
||||
}
|
||||
|
||||
@ -32,7 +36,7 @@ public class NetworkUtils {
|
||||
* Set the host name variable. Sometimes the network can be finicky, so the
|
||||
* answer returned by getHostName() could throw an exception or be null.
|
||||
* Have it read the environment variable if getHostName() is unsuccessful.
|
||||
*
|
||||
*
|
||||
* @return the local host name
|
||||
*/
|
||||
public static String getLocalHostName() {
|
||||
@ -49,16 +53,16 @@ public class NetworkUtils {
|
||||
}
|
||||
return hostName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempt to manually extract the domain from a URL.
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
* @return empty string if no domain could be found
|
||||
*/
|
||||
private static String getBaseDomain(String url) {
|
||||
String host = null;
|
||||
|
||||
|
||||
//strip protocol
|
||||
String cleanUrl = url.replaceFirst(".*:\\/\\/", "");
|
||||
|
||||
@ -70,42 +74,30 @@ public class NetworkUtils {
|
||||
host = cleanUrl;
|
||||
}
|
||||
|
||||
//get the domain part from host (last 2)
|
||||
StringTokenizer tok = new StringTokenizer(host, ".");
|
||||
StringBuilder hostB = new StringBuilder();
|
||||
int toks = tok.countTokens();
|
||||
|
||||
for (int count = 0; count < toks; ++count) {
|
||||
String part = tok.nextToken();
|
||||
int diff = toks - count;
|
||||
if (diff < 3) {
|
||||
hostB.append(part);
|
||||
}
|
||||
if (diff == 2) {
|
||||
hostB.append(".");
|
||||
}
|
||||
String base = host;
|
||||
try {
|
||||
base = DomainTokenizer.getInstance().getDomain(host);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Unable to load resources for domain categorization.", ex);
|
||||
}
|
||||
|
||||
|
||||
String base = hostB.toString();
|
||||
|
||||
// verify there are no special characters in there
|
||||
if (base.matches(".*[~`!@#$%^&\\*\\(\\)\\+={}\\[\\];:\\?<>,/ ].*")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
//verify that the base domain actually has a '.', details JIRA-4609
|
||||
if(!base.contains(".")) {
|
||||
if (!base.contains(".")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to extract the domain from a URL.
|
||||
* Will start by using the built-in URL class, and if that fails will
|
||||
* try to extract it manually.
|
||||
*
|
||||
* Attempt to extract the domain from a URL. Will start by using the
|
||||
* built-in URL class, and if that fails will try to extract it manually.
|
||||
*
|
||||
* @param urlString The URL to extract the domain from
|
||||
* @return empty string if no domain name was found
|
||||
*/
|
||||
@ -113,20 +105,22 @@ public class NetworkUtils {
|
||||
if (urlString == null) {
|
||||
return "";
|
||||
}
|
||||
String result = "";
|
||||
String urlHost = null;
|
||||
|
||||
try {
|
||||
URL url = new URL(urlString);
|
||||
result = url.getHost();
|
||||
urlHost = url.getHost();
|
||||
} catch (MalformedURLException ex) {
|
||||
//do not log if not a valid URL - we will try to extract it ourselves
|
||||
}
|
||||
|
||||
//was not a valid URL, try a less picky method
|
||||
if (result == null || result.trim().isEmpty()) {
|
||||
return getBaseDomain(urlString);
|
||||
}
|
||||
// if there is a valid url host, get base domain from that host
|
||||
// otherwise use urlString and parse the domain
|
||||
String result = (StringUtils.isNotBlank(urlHost))
|
||||
? getBaseDomain(urlHost)
|
||||
: getBaseDomain(urlString);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -170,6 +170,7 @@ public class PlatformUtil {
|
||||
return javaPath;
|
||||
}
|
||||
|
||||
// by default, use Java that came with Autopsy
|
||||
File jrePath = new File(getInstallPath() + File.separator + "jre");
|
||||
if (jrePath.exists() && jrePath.isDirectory()) {
|
||||
System.out.println(
|
||||
@ -177,10 +178,12 @@ public class PlatformUtil {
|
||||
"PlatformUtil.jrePath.jreDir.msg",
|
||||
jrePath.getAbsolutePath()));
|
||||
javaPath = jrePath.getAbsolutePath() + File.separator + "bin" + File.separator + "java"; //NON-NLS
|
||||
} else if (System.getProperty("java.home") != null && !(System.getProperty("java.home").isEmpty())) {
|
||||
// if OS knows where Java is located
|
||||
return System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; //NON-NLS
|
||||
} else {
|
||||
//else use system installed java in PATH env variable
|
||||
javaPath = "java"; //NON-NLS
|
||||
|
||||
}
|
||||
|
||||
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.jrePath.usingJavaPath.msg", javaPath));
|
||||
|
@ -71,12 +71,19 @@ public class TextUtil {
|
||||
* have chosen to ignore the extended planes above Unicode BMP for the time
|
||||
* being. The net result of this is some non-BMP characters may be
|
||||
* interspersed with '^' characters in Autopsy.
|
||||
*
|
||||
* Strip all non-characters
|
||||
* http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:Noncharacter_Code_Point=True:]
|
||||
* and non-printable control characters except tabulator, new line and carriage return
|
||||
*
|
||||
* @param ch the character to test
|
||||
*
|
||||
* @return Returns true if the character is valid UTF-8, false if not.
|
||||
*/
|
||||
public static boolean isValidSolrUTF8(char ch) {
|
||||
return ((ch <= 0xFDD0 || ch >= 0xFDEF) && (ch > 0x1F || ch == 0x9 || ch == 0xA || ch == 0xD) && (ch != 0xFFFF) && (ch != 0xFFFE));
|
||||
return ((ch <= 0xFDD0 || ch >= 0xFDEF) // 0xfdd0 - 0xfdef
|
||||
&& (ch > 0x1F || ch == 0x9 || ch == 0xA || ch == 0xD)
|
||||
&& (ch % 0x10000 != 0xFFFF) // 0xffff - 0x10ffff range step 0x10000
|
||||
&& (ch % 0x10000 != 0xFFFE)); // 0xfffe - 0x10fffe range
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012-2014 Basis Technology Corp.
|
||||
* Copyright 2012-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -54,6 +54,40 @@ import org.xml.sax.SAXException;
|
||||
* -Loading documents from disk
|
||||
*/
|
||||
public class XMLUtil {
|
||||
|
||||
private static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
|
||||
// See JIRA-6958 for details about class loading and jaxb.
|
||||
ClassLoader original = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(XMLUtil.class.getClassLoader());
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
return builderFactory.newDocumentBuilder();
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(original);
|
||||
}
|
||||
}
|
||||
|
||||
private static SchemaFactory getSchemaFactory(String schemaLanguage) {
|
||||
// See JIRA-6958 for details about class loading and jaxb.
|
||||
ClassLoader original = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(XMLUtil.class.getClassLoader());
|
||||
return SchemaFactory.newInstance(schemaLanguage);
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(original);
|
||||
}
|
||||
}
|
||||
|
||||
private static TransformerFactory getTransformerFactory() {
|
||||
// See JIRA-6958 for details about class loading and jaxb.
|
||||
ClassLoader original = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(XMLUtil.class.getClassLoader());
|
||||
return TransformerFactory.newInstance();
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(original);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a W3C DOM.
|
||||
@ -63,9 +97,7 @@ public class XMLUtil {
|
||||
* @throws ParserConfigurationException
|
||||
*/
|
||||
public static Document createDocument() throws ParserConfigurationException {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
return builder.newDocument();
|
||||
return getDocumentBuilder().newDocument();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,8 +132,7 @@ public class XMLUtil {
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Document loadDocument(String docPath) throws ParserConfigurationException, SAXException, IOException {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
DocumentBuilder builder = getDocumentBuilder();
|
||||
Document doc = builder.parse(new FileInputStream(docPath));
|
||||
return doc;
|
||||
}
|
||||
@ -119,7 +150,7 @@ public class XMLUtil {
|
||||
public static <T> void validateDocument(final Document doc, Class<T> clazz, String schemaResourceName) throws SAXException, IOException {
|
||||
PlatformUtil.extractResourceToUserConfigDir(clazz, schemaResourceName, false);
|
||||
File schemaFile = new File(Paths.get(PlatformUtil.getUserConfigDirectory(), schemaResourceName).toAbsolutePath().toString());
|
||||
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
SchemaFactory schemaFactory = getSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
Schema schema = schemaFactory.newSchema(schemaFile);
|
||||
Validator validator = schema.newValidator();
|
||||
validator.validate(new DOMSource(doc), new DOMResult());
|
||||
@ -140,7 +171,7 @@ public class XMLUtil {
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void saveDocument(final Document doc, String encoding, String docPath) throws TransformerConfigurationException, FileNotFoundException, UnsupportedEncodingException, TransformerException, IOException {
|
||||
TransformerFactory xf = TransformerFactory.newInstance();
|
||||
TransformerFactory xf = getTransformerFactory();
|
||||
xf.setAttribute("indent-number", 1); //NON-NLS
|
||||
Transformer xformer = xf.newTransformer();
|
||||
xformer.setOutputProperty(OutputKeys.METHOD, "xml"); //NON-NLS
|
||||
@ -178,7 +209,7 @@ public class XMLUtil {
|
||||
try {
|
||||
PlatformUtil.extractResourceToUserConfigDir(clazz, schemaFile, false);
|
||||
File schemaLoc = new File(PlatformUtil.getUserConfigDirectory() + File.separator + schemaFile);
|
||||
SchemaFactory schm = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
SchemaFactory schm = getSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
try {
|
||||
Schema schema = schm.newSchema(schemaLoc);
|
||||
Validator validator = schema.newValidator();
|
||||
@ -226,10 +257,9 @@ public class XMLUtil {
|
||||
*/
|
||||
// TODO: Deprecate.
|
||||
public static <T> Document loadDoc(Class<T> clazz, String xmlPath) {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
Document ret = null;
|
||||
try {
|
||||
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
DocumentBuilder builder = getDocumentBuilder();
|
||||
ret = builder.parse(new FileInputStream(xmlPath));
|
||||
} catch (ParserConfigurationException e) {
|
||||
Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file " + xmlPath + " : can't initialize parser.", e); //NON-NLS
|
||||
@ -268,7 +298,7 @@ public class XMLUtil {
|
||||
*/
|
||||
// TODO: Deprecate.
|
||||
public static <T> boolean saveDoc(Class<T> clazz, String xmlPath, String encoding, final Document doc) {
|
||||
TransformerFactory xf = TransformerFactory.newInstance();
|
||||
TransformerFactory xf = getTransformerFactory();
|
||||
xf.setAttribute("indent-number", 1); //NON-NLS
|
||||
boolean success = false;
|
||||
try {
|
||||
|
13494
Core/src/org/sleuthkit/autopsy/coreutils/public_suffix_list.dat
Normal file
13494
Core/src/org/sleuthkit/autopsy/coreutils/public_suffix_list.dat
Normal file
File diff suppressed because it is too large
Load Diff
@ -278,6 +278,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
"AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)",
|
||||
"AbstractAbstractFileNode.knownColLbl=Known",
|
||||
"AbstractAbstractFileNode.md5HashColLbl=MD5 Hash",
|
||||
"AbstractAbstractFileNode.sha256HashColLbl=SHA-256 Hash",
|
||||
"AbstractAbstractFileNode.objectId=Object ID",
|
||||
"AbstractAbstractFileNode.mimeType=MIME Type",
|
||||
"AbstractAbstractFileNode.extensionColLbl=Extension"})
|
||||
@ -305,6 +306,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
TYPE_META(AbstractAbstractFileNode_typeMetaColLbl()),
|
||||
KNOWN(AbstractAbstractFileNode_knownColLbl()),
|
||||
MD5HASH(AbstractAbstractFileNode_md5HashColLbl()),
|
||||
SHA256HASH(AbstractAbstractFileNode_sha256HashColLbl()),
|
||||
ObjectID(AbstractAbstractFileNode_objectId()),
|
||||
MIMETYPE(AbstractAbstractFileNode_mimeType()),
|
||||
EXTENSION(AbstractAbstractFileNode_extensionColLbl());
|
||||
@ -358,6 +360,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
properties.add(new NodeProperty<>(KNOWN.toString(), KNOWN.toString(), NO_DESCR, content.getKnown().getName()));
|
||||
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
|
||||
properties.add(new NodeProperty<>(MD5HASH.toString(), MD5HASH.toString(), NO_DESCR, StringUtils.defaultString(content.getMd5Hash())));
|
||||
properties.add(new NodeProperty<>(SHA256HASH.toString(), SHA256HASH.toString(), NO_DESCR, StringUtils.defaultString(content.getSha256Hash())));
|
||||
properties.add(new NodeProperty<>(MIMETYPE.toString(), MIMETYPE.toString(), NO_DESCR, StringUtils.defaultString(content.getMIMEType())));
|
||||
properties.add(new NodeProperty<>(EXTENSION.toString(), EXTENSION.toString(), NO_DESCR, content.getNameExtension()));
|
||||
|
||||
@ -577,6 +580,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
map.put(FLAGS_META.toString(), content.getMetaFlagsAsString());
|
||||
map.put(KNOWN.toString(), content.getKnown().getName());
|
||||
map.put(MD5HASH.toString(), StringUtils.defaultString(content.getMd5Hash()));
|
||||
map.put(SHA256HASH.toString(), StringUtils.defaultString(content.getSha256Hash()));
|
||||
map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType()));
|
||||
map.put(EXTENSION.toString(), content.getNameExtension());
|
||||
}
|
||||
|
@ -128,8 +128,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
};
|
||||
|
||||
private final BlackboardArtifact artifact;
|
||||
private Content srcContent;
|
||||
private volatile String translatedSourceName;
|
||||
private Content srcContent;
|
||||
private volatile String translatedSourceName;
|
||||
|
||||
/*
|
||||
* A method has been provided to allow the injection of properties into this
|
||||
@ -284,17 +284,49 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
*/
|
||||
private static Lookup createLookup(BlackboardArtifact artifact) {
|
||||
final long objectID = artifact.getObjectID();
|
||||
Content content = null;
|
||||
try {
|
||||
Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
|
||||
if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() || artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID()) {
|
||||
content = getPathIdFile(artifact);
|
||||
}
|
||||
if (content == null) {
|
||||
return Lookups.fixed(artifact);
|
||||
} else {
|
||||
return Lookups.fixed(artifact, content);
|
||||
content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
|
||||
}
|
||||
} catch (ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
|
||||
return Lookups.fixed(artifact);
|
||||
content = null;
|
||||
}
|
||||
if (content == null) {
|
||||
return Lookups.fixed(artifact);
|
||||
} else {
|
||||
return Lookups.fixed(artifact, content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Private helper method to allow content specified in a path id attribute
|
||||
* to be retrieved.
|
||||
*
|
||||
* @param artifact The artifact for which content may be specified as a tsk
|
||||
* path attribute.
|
||||
*
|
||||
* @return The Content specified by the artifact's path id attribute or null
|
||||
* if there was no content available.
|
||||
*
|
||||
* @throws ExecutionException Error retrieving the file specified by the
|
||||
* path id from the cache.
|
||||
*/
|
||||
private static Content getPathIdFile(BlackboardArtifact artifact) throws ExecutionException {
|
||||
try {
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
|
||||
if (attribute != null) {
|
||||
return contentCache.get(attribute.getValueLong(), () -> artifact.getSleuthkitCase().getContentById(attribute.getValueLong()));
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, MessageFormat.format("Error getting content for path id attrbiute for artifact: ", artifact.getId()), ex); //NON-NLS
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -950,7 +982,21 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
}
|
||||
map.put(attribute.getAttributeType().getDisplayName(), value);
|
||||
} else {
|
||||
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
|
||||
switch(attribute.getAttributeType().getValueType()) {
|
||||
case INTEGER:
|
||||
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt());
|
||||
break;
|
||||
case DOUBLE:
|
||||
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble());
|
||||
break;
|
||||
case LONG:
|
||||
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong());
|
||||
break;
|
||||
default:
|
||||
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
|
@ -30,6 +30,7 @@ AbstractAbstractFileNode.modifiedTimeColLbl=Modified Time
|
||||
AbstractAbstractFileNode.nameColLbl=Name
|
||||
AbstractAbstractFileNode.objectId=Object ID
|
||||
AbstractAbstractFileNode.originalName=Original Name
|
||||
AbstractAbstractFileNode.sha256HashColLbl=SHA-256 Hash
|
||||
AbstractAbstractFileNode.sizeColLbl=Size
|
||||
AbstractAbstractFileNode.tagsProperty.displayName=Tags
|
||||
AbstractAbstractFileNode.typeDirColLbl=Type(Dir)
|
||||
|
@ -529,9 +529,11 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ getRejectedArtifactFilterClause(); //NON-NLS
|
||||
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
||||
ResultSet rs = results.getResultSet();) {
|
||||
List<Long> tempList = new ArrayList<>();
|
||||
while (rs.next()) {
|
||||
list.add(rs.getLong("artifact_id")); //NON-NLS
|
||||
tempList.add(rs.getLong("artifact_id")); // NON-NLS
|
||||
}
|
||||
list.addAll(tempList);
|
||||
} catch (TskCoreException | SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
|
||||
}
|
||||
|
@ -121,6 +121,8 @@ public final class IconsUtil {
|
||||
imageFile = "web-account-type.png"; //NON-NLS
|
||||
} else if (typeID == ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()) {
|
||||
imageFile = "web-form-address.png"; //NON-NLS
|
||||
} else if (typeID == ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID()) {
|
||||
imageFile = "gps-area.png"; //NON-NLS
|
||||
} else {
|
||||
imageFile = "artifact-icon.png"; //NON-NLS
|
||||
}
|
||||
|
@ -398,6 +398,20 @@ final class DataSourceInfoUtilities {
|
||||
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
|
||||
return (attr == null) ? null : attr.getValueLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the int value of a certain attribute type from an artifact.
|
||||
*
|
||||
* @param artifact The artifact.
|
||||
* @param attributeType The attribute type.
|
||||
*
|
||||
* @return The 'getValueInt()' value or null if the attribute could not be
|
||||
* retrieved.
|
||||
*/
|
||||
static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) {
|
||||
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
|
||||
return (attr == null) ? null : attr.getValueInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the long value of a certain attribute type from an artifact and
|
||||
|
@ -28,10 +28,11 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
@ -93,16 +94,56 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
return ARTIFACT_UPDATE_TYPE_IDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes fileDetails entries with redundant paths, sorts by date
|
||||
* descending and limits to the limit provided.
|
||||
*
|
||||
* @param fileDetails The file details list.
|
||||
* @param limit The maximum number of entries to return.
|
||||
* @return The sorted limited list with unique paths.
|
||||
*/
|
||||
private <T extends RecentFileDetails> List<T> getSortedLimited(List<T> fileDetails, int limit) {
|
||||
Map<String, T> fileDetailsMap = fileDetails.stream()
|
||||
.filter(details -> details != null)
|
||||
.collect(Collectors.toMap(
|
||||
d -> d.getPath().toUpperCase(),
|
||||
d -> d,
|
||||
(d1, d2) -> Long.compare(d1.getDateAsLong(), d2.getDateAsLong()) > 0 ? d1 : d2));
|
||||
|
||||
return fileDetailsMap.values().stream()
|
||||
.sorted((a, b) -> -Long.compare(a.getDateAsLong(), b.getDateAsLong()))
|
||||
.limit(limit)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a RecentFileDetails object as derived from the recent document
|
||||
* artifact or null if no appropriate object can be made.
|
||||
*
|
||||
* @param artifact The artifact.
|
||||
* @return The derived object or null if artifact is invalid.
|
||||
*/
|
||||
private RecentFileDetails getRecentlyOpenedDocument(BlackboardArtifact artifact) {
|
||||
String path = DataSourceInfoUtilities.getStringOrNull(artifact, PATH_ATT);
|
||||
Long lastOpened = DataSourceInfoUtilities.getLongOrNull(artifact, DATETIME_ATT);
|
||||
|
||||
if (StringUtils.isBlank(path) || lastOpened == null || lastOpened == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return new RecentFileDetails(path, lastOpened);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the most recently opened documents based on the
|
||||
* TSK_RECENT_OBJECT artifact.
|
||||
*
|
||||
* @param dataSource The data source to query.
|
||||
* @param maxCount The maximum number of results to return, pass 0 to get
|
||||
* a list of all results.
|
||||
* @param maxCount The maximum number of results to return, pass 0 to get a
|
||||
* list of all results.
|
||||
*
|
||||
* @return A list RecentFileDetails representing the most recently opened
|
||||
* documents or an empty list if none were found.
|
||||
* documents or an empty list if none were found.
|
||||
*
|
||||
* @throws SleuthkitCaseProviderException
|
||||
* @throws TskCoreException
|
||||
@ -112,37 +153,45 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<BlackboardArtifact> artifactList
|
||||
= DataSourceInfoUtilities.getArtifacts(provider.get(),
|
||||
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_RECENT_OBJECT),
|
||||
dataSource,
|
||||
DATETIME_ATT,
|
||||
DataSourceInfoUtilities.SortOrder.DESCENDING,
|
||||
10);
|
||||
throwOnNonPositiveCount(maxCount);
|
||||
|
||||
List<RecentFileDetails> fileDetails = new ArrayList<>();
|
||||
for (BlackboardArtifact artifact : artifactList) {
|
||||
Long accessedTime = null;
|
||||
String path = "";
|
||||
List<RecentFileDetails> details = provider.get().getBlackboard()
|
||||
.getArtifacts(ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID(), dataSource.getId()).stream()
|
||||
.map(art -> getRecentlyOpenedDocument(art))
|
||||
.filter(d -> d != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Get all the attributes in one call.
|
||||
List<BlackboardAttribute> attributeList = artifact.getAttributes();
|
||||
for (BlackboardAttribute attribute : attributeList) {
|
||||
return getSortedLimited(details, maxCount);
|
||||
}
|
||||
|
||||
if (attribute.getAttributeType().equals(DATETIME_ATT)) {
|
||||
accessedTime = attribute.getValueLong();
|
||||
} else if (attribute.getAttributeType().equals(PATH_ATT)) {
|
||||
path = attribute.getValueString();
|
||||
}
|
||||
|
||||
if (accessedTime != null) {
|
||||
fileDetails.add(new RecentFileDetails(path, accessedTime));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns a RecentDownloadDetails object as derived from the recent
|
||||
* download artifact or null if no appropriate object can be made.
|
||||
*
|
||||
* @param artifact The artifact.
|
||||
* @return The derived object or null if artifact is invalid.
|
||||
*/
|
||||
private RecentDownloadDetails getRecentDownload(BlackboardArtifact artifact) {
|
||||
Long accessedTime = DataSourceInfoUtilities.getLongOrNull(artifact, DATETIME_ACCESSED_ATT);
|
||||
String domain = DataSourceInfoUtilities.getStringOrNull(artifact, DOMAIN_ATT);
|
||||
String path = DataSourceInfoUtilities.getStringOrNull(artifact, PATH_ATT);
|
||||
|
||||
if (StringUtils.isBlank(path) || accessedTime == null || accessedTime == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return new RecentDownloadDetails(path, accessedTime, domain);
|
||||
}
|
||||
}
|
||||
|
||||
return fileDetails;
|
||||
/**
|
||||
* Throws an IllegalArgumentException if count is less than 1.
|
||||
*
|
||||
* @param count The count.
|
||||
*/
|
||||
private void throwOnNonPositiveCount(int count) {
|
||||
if (count < 1) {
|
||||
throw new IllegalArgumentException("Invalid count: value must be greater than 0.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -150,11 +199,11 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
* artifact TSK_DATETIME_ACCESSED attribute.
|
||||
*
|
||||
* @param dataSource Data source to query.
|
||||
* @param maxCount Maximum number of results to return, passing 0 will
|
||||
* return all results.
|
||||
* @param maxCount Maximum number of results to return, passing 0 will
|
||||
* return all results.
|
||||
*
|
||||
* @return A list of RecentFileDetails objects or empty list if none were
|
||||
* found.
|
||||
* found.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
* @throws SleuthkitCaseProviderException
|
||||
@ -164,46 +213,23 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<BlackboardArtifact> artifactList
|
||||
= DataSourceInfoUtilities.getArtifacts(provider.get(),
|
||||
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD),
|
||||
dataSource,
|
||||
DATETIME_ACCESSED_ATT,
|
||||
DataSourceInfoUtilities.SortOrder.DESCENDING,
|
||||
maxCount);
|
||||
throwOnNonPositiveCount(maxCount);
|
||||
|
||||
List<RecentDownloadDetails> fileDetails = new ArrayList<>();
|
||||
for (BlackboardArtifact artifact : artifactList) {
|
||||
// Get all the attributes in one call.
|
||||
Long accessedTime = null;
|
||||
String domain = "";
|
||||
String path = "";
|
||||
List<RecentDownloadDetails> details = provider.get().getBlackboard()
|
||||
.getArtifacts(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), dataSource.getId()).stream()
|
||||
.map(art -> getRecentDownload(art))
|
||||
.filter(d -> d != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<BlackboardAttribute> attributeList = artifact.getAttributes();
|
||||
for (BlackboardAttribute attribute : attributeList) {
|
||||
|
||||
if (attribute.getAttributeType().equals(DATETIME_ACCESSED_ATT)) {
|
||||
accessedTime = attribute.getValueLong();
|
||||
} else if (attribute.getAttributeType().equals(DOMAIN_ATT)) {
|
||||
domain = attribute.getValueString();
|
||||
} else if (attribute.getAttributeType().equals(PATH_ATT)) {
|
||||
path = attribute.getValueString();
|
||||
}
|
||||
}
|
||||
if (accessedTime != null) {
|
||||
fileDetails.add(new RecentDownloadDetails(path, accessedTime, domain));
|
||||
}
|
||||
}
|
||||
|
||||
return fileDetails;
|
||||
return getSortedLimited(details, maxCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the most recent message attachments.
|
||||
*
|
||||
* @param dataSource Data source to query.
|
||||
* @param maxCount Maximum number of results to return, passing 0 will
|
||||
* return all results.
|
||||
* @param maxCount Maximum number of results to return, passing 0 will
|
||||
* return all results.
|
||||
*
|
||||
* @return A list of RecentFileDetails of the most recent attachments.
|
||||
*
|
||||
@ -215,111 +241,72 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return createListFromMap(buildAttachmentMap(dataSource), maxCount);
|
||||
throwOnNonPositiveCount(maxCount);
|
||||
|
||||
SleuthkitCase skCase = provider.get();
|
||||
|
||||
List<BlackboardArtifact> associatedArtifacts = skCase.getBlackboard()
|
||||
.getArtifacts(ASSOCATED_OBJ_ART.getTypeID(), dataSource.getId());
|
||||
|
||||
List<RecentAttachmentDetails> details = new ArrayList<>();
|
||||
for (BlackboardArtifact artifact : associatedArtifacts) {
|
||||
RecentAttachmentDetails thisDetails = getRecentAttachment(artifact, skCase);
|
||||
|
||||
if (thisDetails != null) {
|
||||
details.add(thisDetails);
|
||||
}
|
||||
}
|
||||
|
||||
return getSortedLimited(details, maxCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a map of all of the message attachment sorted in date order.
|
||||
* Creates a RecentAttachmentDetails object from the associated object
|
||||
* artifact or null if no RecentAttachmentDetails object can be derived.
|
||||
*
|
||||
* @param dataSource Data source to query.
|
||||
*
|
||||
* @return Returns a SortedMap of details objects returned in descending
|
||||
* order.
|
||||
*
|
||||
* @throws SleuthkitCaseProviderException
|
||||
* @param artifact The associated object artifact.
|
||||
* @param skCase The current case.
|
||||
* @return The derived object or null.
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private SortedMap<Long, List<RecentAttachmentDetails>> buildAttachmentMap(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException {
|
||||
SleuthkitCase skCase = provider.get();
|
||||
TreeMap<Long, List<RecentAttachmentDetails>> sortedMap = new TreeMap<>();
|
||||
|
||||
List<BlackboardArtifact> associatedArtifacts = skCase.getBlackboard().getArtifacts(ASSOCATED_OBJ_ART.getTypeID(), dataSource.getId());
|
||||
for (BlackboardArtifact artifact : associatedArtifacts) {
|
||||
BlackboardAttribute attribute = artifact.getAttribute(ASSOCATED_ATT);
|
||||
if (attribute == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong());
|
||||
if (isMessageArtifact(messageArtifact)) {
|
||||
Content content = artifact.getParent();
|
||||
if (content instanceof AbstractFile) {
|
||||
String sender;
|
||||
Long date = null;
|
||||
String path;
|
||||
|
||||
BlackboardAttribute senderAttribute = messageArtifact.getAttribute(EMAIL_FROM_ATT);
|
||||
if (senderAttribute != null) {
|
||||
sender = senderAttribute.getValueString();
|
||||
} else {
|
||||
sender = "";
|
||||
}
|
||||
senderAttribute = messageArtifact.getAttribute(MSG_DATEIME_SENT_ATT);
|
||||
if (senderAttribute != null) {
|
||||
date = senderAttribute.getValueLong();
|
||||
}
|
||||
|
||||
AbstractFile abstractFile = (AbstractFile) content;
|
||||
|
||||
path = Paths.get(abstractFile.getParentPath(), abstractFile.getName()).toString();
|
||||
|
||||
if (date != null && date != 0) {
|
||||
List<RecentAttachmentDetails> list = sortedMap.get(date);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
sortedMap.put(date, list);
|
||||
}
|
||||
RecentAttachmentDetails details = new RecentAttachmentDetails(path, date, sender);
|
||||
if (!list.contains(details)) {
|
||||
list.add(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sortedMap.descendingMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list of detail objects from the given sorted map of the max
|
||||
* size.
|
||||
*
|
||||
* @param sortedMap A Map of attachment details sorted by date.
|
||||
* @param maxCount Maximum number of values to return.
|
||||
*
|
||||
* @return A list of the details of the most recent attachments or empty
|
||||
* list if none where found.
|
||||
*/
|
||||
private List<RecentAttachmentDetails> createListFromMap(SortedMap<Long, List<RecentAttachmentDetails>> sortedMap, int maxCount) {
|
||||
List<RecentAttachmentDetails> fileList = new ArrayList<>();
|
||||
|
||||
for (List<RecentAttachmentDetails> mapList : sortedMap.values()) {
|
||||
if (maxCount == 0 || fileList.size() + mapList.size() <= maxCount) {
|
||||
fileList.addAll(mapList);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (maxCount == fileList.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (RecentAttachmentDetails details : mapList) {
|
||||
if (fileList.size() < maxCount) {
|
||||
fileList.add(details);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
private RecentAttachmentDetails getRecentAttachment(BlackboardArtifact artifact, SleuthkitCase skCase) throws TskCoreException {
|
||||
// get associated artifact or return no result
|
||||
BlackboardAttribute attribute = artifact.getAttribute(ASSOCATED_ATT);
|
||||
if (attribute == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fileList;
|
||||
// get associated message artifact if exists or return no result
|
||||
BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong());
|
||||
if (messageArtifact == null || !isMessageArtifact(messageArtifact)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// get abstract file if exists or return no result
|
||||
Content content = artifact.getParent();
|
||||
if (!(content instanceof AbstractFile)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AbstractFile abstractFile = (AbstractFile) content;
|
||||
|
||||
// get the path, sender, and date
|
||||
String path = Paths.get(abstractFile.getParentPath(), abstractFile.getName()).toString();
|
||||
String sender = DataSourceInfoUtilities.getStringOrNull(messageArtifact, EMAIL_FROM_ATT);
|
||||
Long date = DataSourceInfoUtilities.getLongOrNull(messageArtifact, MSG_DATEIME_SENT_ATT);
|
||||
|
||||
if (date == null || date == 0 || StringUtils.isBlank(path)) {
|
||||
return null;
|
||||
} else {
|
||||
return new RecentAttachmentDetails(path, date, sender);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given artifact a message.
|
||||
*
|
||||
* @param nodeArtifact An artifact that might be a message. Must not be
|
||||
* null.
|
||||
* null.
|
||||
*
|
||||
* @return True if the given artifact is a message artifact
|
||||
*/
|
||||
@ -327,6 +314,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
final int artifactTypeID = nodeArtifact.getArtifactTypeID();
|
||||
return artifactTypeID == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
|
||||
|| artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -388,8 +376,8 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
/**
|
||||
* Constructor for files with just a path and date.
|
||||
*
|
||||
* @param path File path.
|
||||
* @param date File access date\time in seconds with java epoch.
|
||||
* @param path File path.
|
||||
* @param date File access date\time in seconds with java epoch.
|
||||
* @param webDomain The webdomain from which the file was downloaded.
|
||||
*/
|
||||
RecentDownloadDetails(String path, long date, String webDomain) {
|
||||
@ -401,7 +389,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
* Returns the web domain.
|
||||
*
|
||||
* @return The web domain or empty string if not available or
|
||||
* applicable.
|
||||
* applicable.
|
||||
*/
|
||||
public String getWebDomain() {
|
||||
return webDomain;
|
||||
@ -419,10 +407,10 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
* Constructor for recent download files which have a path, date and
|
||||
* domain value.
|
||||
*
|
||||
* @param path File path.
|
||||
* @param date File crtime.
|
||||
* @param path File path.
|
||||
* @param date File crtime.
|
||||
* @param sender The sender of the message from which the file was
|
||||
* attached.
|
||||
* attached.
|
||||
*/
|
||||
RecentAttachmentDetails(String path, long date, String sender) {
|
||||
super(path, date);
|
||||
@ -433,7 +421,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
* Return the sender of the attached file.
|
||||
*
|
||||
* @return The sender of the attached file or empty string if not
|
||||
* available.
|
||||
* available.
|
||||
*/
|
||||
public String getSender() {
|
||||
return sender;
|
||||
|
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* 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.datasourcesummary.datamodel;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import org.joda.time.Interval;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
import org.sleuthkit.datamodel.TimelineFilter;
|
||||
import org.sleuthkit.datamodel.TimelineFilter.DataSourcesFilter;
|
||||
import org.sleuthkit.datamodel.TimelineFilter.RootFilter;
|
||||
import org.sleuthkit.datamodel.TimelineManager;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import java.util.function.Supplier;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
|
||||
/**
|
||||
* Provides data source summary information pertaining to Timeline data.
|
||||
*/
|
||||
public class TimelineSummary implements DefaultUpdateGovernor {
|
||||
|
||||
private static final long DAY_SECS = 24 * 60 * 60;
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
|
||||
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
|
||||
|
||||
private static final Set<TimelineEventType> FILE_SYSTEM_EVENTS
|
||||
= new HashSet<>(Arrays.asList(
|
||||
TimelineEventType.FILE_MODIFIED,
|
||||
TimelineEventType.FILE_ACCESSED,
|
||||
TimelineEventType.FILE_CREATED,
|
||||
TimelineEventType.FILE_CHANGED));
|
||||
|
||||
private final SleuthkitCaseProvider caseProvider;
|
||||
private final Supplier<TimeZone> timeZoneProvider;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public TimelineSummary() {
|
||||
this(SleuthkitCaseProvider.DEFAULT, () -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct object with given SleuthkitCaseProvider
|
||||
*
|
||||
* @param caseProvider SleuthkitCaseProvider provider, cannot be null.
|
||||
* @param timeZoneProvider The timezone provider, cannot be null.
|
||||
*/
|
||||
public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier<TimeZone> timeZoneProvider) {
|
||||
this.caseProvider = caseProvider;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(ModuleContentEvent evt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(AbstractFile file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
|
||||
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
|
||||
return INGEST_JOB_EVENTS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves timeline summary data.
|
||||
*
|
||||
* @param dataSource The data source for which timeline data will be
|
||||
* retrieved.
|
||||
* @param recentDaysNum The maximum number of most recent days' activity to
|
||||
* include.
|
||||
* @return The retrieved data.
|
||||
* @throws SleuthkitCaseProviderException
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException {
|
||||
TimeZone timeZone = this.timeZoneProvider.get();
|
||||
TimelineManager timelineManager = this.caseProvider.get().getTimelineManager();
|
||||
|
||||
// get a mapping of days from epoch to the activity for that day
|
||||
Map<Long, DailyActivityAmount> dateCounts = getTimelineEventsByDay(dataSource, timelineManager, timeZone);
|
||||
|
||||
// get minimum and maximum usage date by iterating through
|
||||
Long minDay = null;
|
||||
Long maxDay = null;
|
||||
for (long daysFromEpoch : dateCounts.keySet()) {
|
||||
minDay = (minDay == null) ? daysFromEpoch : Math.min(minDay, daysFromEpoch);
|
||||
maxDay = (maxDay == null) ? daysFromEpoch : Math.max(maxDay, daysFromEpoch);
|
||||
}
|
||||
|
||||
// if no min date or max date, no usage; return null.
|
||||
if (minDay == null || maxDay == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Date minDate = new Date(minDay * 1000 * DAY_SECS);
|
||||
Date maxDate = new Date(maxDay * 1000 * DAY_SECS);
|
||||
|
||||
// The minimum recent day will be within recentDaysNum from the maximum day
|
||||
// (+1 since maxDay included) or the minimum day of activity
|
||||
long minRecentDay = Math.max(maxDay - recentDaysNum + 1, minDay);
|
||||
|
||||
// get most recent days activity
|
||||
List<DailyActivityAmount> mostRecentActivityAmt = getMostRecentActivityAmounts(dateCounts, minRecentDay, maxDay);
|
||||
|
||||
return new TimelineSummaryData(minDate, maxDate, mostRecentActivityAmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given activity by day, converts to most recent days' activity handling
|
||||
* empty values.
|
||||
*
|
||||
* @param dateCounts The day from epoch mapped to activity amounts for that
|
||||
* day.
|
||||
* @param minRecentDay The minimum recent day in days from epoch.
|
||||
* @param maxDay The maximum recent day in days from epoch;
|
||||
* @return The most recent daily activity amounts.
|
||||
*/
|
||||
private List<DailyActivityAmount> getMostRecentActivityAmounts(Map<Long, DailyActivityAmount> dateCounts, long minRecentDay, long maxDay) {
|
||||
List<DailyActivityAmount> mostRecentActivityAmt = new ArrayList<>();
|
||||
|
||||
for (long curRecentDay = minRecentDay; curRecentDay <= maxDay; curRecentDay++) {
|
||||
DailyActivityAmount prevCounts = dateCounts.get(curRecentDay);
|
||||
DailyActivityAmount countsHandleNotFound = prevCounts != null
|
||||
? prevCounts
|
||||
: new DailyActivityAmount(new Date(curRecentDay * DAY_SECS * 1000), 0, 0);
|
||||
|
||||
mostRecentActivityAmt.add(countsHandleNotFound);
|
||||
}
|
||||
return mostRecentActivityAmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches timeline events per day for a particular data source.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param timelineManager The timeline manager to use while fetching the
|
||||
* data.
|
||||
* @param timeZone The time zone to use to determine which day activity
|
||||
* belongs.
|
||||
* @return A Map mapping days from epoch to the activity for that day.
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private Map<Long, DailyActivityAmount> getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone) throws TskCoreException {
|
||||
|
||||
DataSourcesFilter dataSourceFilter = new DataSourcesFilter();
|
||||
dataSourceFilter.addSubFilter(new TimelineFilter.DataSourceFilter(dataSource.getName(), dataSource.getId()));
|
||||
|
||||
RootFilter dataSourceRootFilter = new RootFilter(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
dataSourceFilter,
|
||||
null,
|
||||
Collections.emptySet());
|
||||
|
||||
// get events for data source
|
||||
long curRunTime = System.currentTimeMillis();
|
||||
List<TimelineEvent> events = timelineManager.getEvents(new Interval(1, curRunTime), dataSourceRootFilter);
|
||||
|
||||
// get counts of events per day (left is file system events, right is everything else)
|
||||
Map<Long, DailyActivityAmount> dateCounts = new HashMap<>();
|
||||
for (TimelineEvent evt : events) {
|
||||
long curSecondsFromEpoch = evt.getTime();
|
||||
long curDaysFromEpoch = Instant.ofEpochMilli(curSecondsFromEpoch * 1000)
|
||||
.atZone(timeZone.toZoneId())
|
||||
.toLocalDate()
|
||||
.toEpochDay();
|
||||
|
||||
DailyActivityAmount prevAmt = dateCounts.get(curDaysFromEpoch);
|
||||
long prevFileEvtCount = prevAmt == null ? 0 : prevAmt.getFileActivityCount();
|
||||
long prevArtifactEvtCount = prevAmt == null ? 0 : prevAmt.getArtifactActivityCount();
|
||||
Date thisDay = prevAmt == null ? new Date(curDaysFromEpoch * 1000 * DAY_SECS) : prevAmt.getDay();
|
||||
|
||||
boolean isFileEvt = FILE_SYSTEM_EVENTS.contains(evt.getEventType());
|
||||
long curFileEvtCount = prevFileEvtCount + (isFileEvt ? 1 : 0);
|
||||
long curArtifactEvtCount = prevArtifactEvtCount + (isFileEvt ? 0 : 1);
|
||||
|
||||
dateCounts.put(curDaysFromEpoch, new DailyActivityAmount(thisDay, curFileEvtCount, curArtifactEvtCount));
|
||||
}
|
||||
|
||||
return dateCounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* All the data to be represented in the timeline summary tab.
|
||||
*/
|
||||
public static class TimelineSummaryData {
|
||||
|
||||
private final Date minDate;
|
||||
private final Date maxDate;
|
||||
private final List<DailyActivityAmount> histogramActivity;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param minDate Earliest usage date recorded for the data source.
|
||||
* @param maxDate Latest usage date recorded for the data source.
|
||||
* @param recentDaysActivity A list of activity prior to and including
|
||||
* the latest usage date by day.
|
||||
*/
|
||||
TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity) {
|
||||
this.minDate = minDate;
|
||||
this.maxDate = maxDate;
|
||||
this.histogramActivity = (recentDaysActivity == null) ? Collections.emptyList() : Collections.unmodifiableList(recentDaysActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Earliest usage date recorded for the data source.
|
||||
*/
|
||||
public Date getMinDate() {
|
||||
return minDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Latest usage date recorded for the data source.
|
||||
*/
|
||||
public Date getMaxDate() {
|
||||
return maxDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A list of activity prior to and including the latest usage
|
||||
* date by day.
|
||||
*/
|
||||
public List<DailyActivityAmount> getMostRecentDaysActivity() {
|
||||
return histogramActivity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the amount of usage based on timeline events for a day.
|
||||
*/
|
||||
public static class DailyActivityAmount {
|
||||
|
||||
private final Date day;
|
||||
private final long fileActivityCount;
|
||||
private final long artifactActivityCount;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param day The day for which activity is being measured.
|
||||
* @param fileActivityCount The amount of file activity timeline events.
|
||||
* @param artifactActivityCount The amount of artifact timeline events.
|
||||
*/
|
||||
DailyActivityAmount(Date day, long fileActivityCount, long artifactActivityCount) {
|
||||
this.day = day;
|
||||
this.fileActivityCount = fileActivityCount;
|
||||
this.artifactActivityCount = artifactActivityCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The day for which activity is being measured.
|
||||
*/
|
||||
public Date getDay() {
|
||||
return day;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The amount of file activity timeline events.
|
||||
*/
|
||||
public long getFileActivityCount() {
|
||||
return fileActivityCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The amount of artifact timeline events.
|
||||
*/
|
||||
public long getArtifactActivityCount() {
|
||||
return artifactActivityCount;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,370 +0,0 @@
|
||||
/*
|
||||
* 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.datasourcesummary.datamodel;
|
||||
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
|
||||
import java.io.File;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Provides information to populate Top Programs Summary queries.
|
||||
*/
|
||||
public class TopProgramsSummary implements DefaultArtifactUpdateGovernor {
|
||||
|
||||
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
|
||||
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
|
||||
));
|
||||
|
||||
/**
|
||||
* A SQL join type.
|
||||
*/
|
||||
private enum JoinType {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
INNER,
|
||||
OUTER
|
||||
}
|
||||
|
||||
/**
|
||||
* A blackboard attribute value column.
|
||||
*/
|
||||
private enum AttributeColumn {
|
||||
value_text,
|
||||
value_int32,
|
||||
value_int64
|
||||
}
|
||||
|
||||
/**
|
||||
* The suffix joined to a key name for use as an identifier of a query.
|
||||
*/
|
||||
private static final String QUERY_SUFFIX = "_query";
|
||||
|
||||
/**
|
||||
* Functions that determine the folder name of a list of path elements. If
|
||||
* not matched, function returns null.
|
||||
*/
|
||||
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
|
||||
// handle Program Files and Program Files (x86) - if true, return the next folder
|
||||
(pathList) -> {
|
||||
if (pathList.size() < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String rootParent = pathList.get(0).toUpperCase();
|
||||
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
|
||||
return pathList.get(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
// if there is a folder named "APPLICATION DATA" or "APPDATA"
|
||||
(pathList) -> {
|
||||
for (String pathEl : pathList) {
|
||||
String uppered = pathEl.toUpperCase();
|
||||
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
|
||||
return "AppData";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates a sql statement querying the blackboard attributes table for a
|
||||
* particular attribute type and returning a specified value. That query
|
||||
* also joins with the blackboard artifact table.
|
||||
*
|
||||
* @param joinType The type of join statement to create.
|
||||
* @param attributeColumn The blackboard attribute column that should be
|
||||
* returned.
|
||||
* @param attrType The attribute type to query for.
|
||||
* @param keyName The aliased name of the attribute to return. This
|
||||
* is also used to calculate the alias of the query
|
||||
* same as getFullKey.
|
||||
* @param bbaName The blackboard artifact table alias.
|
||||
*
|
||||
* @return The generated sql statement.
|
||||
*/
|
||||
private static String getAttributeJoin(JoinType joinType, AttributeColumn attributeColumn, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String keyName, String bbaName) {
|
||||
String queryName = keyName + QUERY_SUFFIX;
|
||||
String innerQueryName = "inner_attribute_" + queryName;
|
||||
|
||||
return "\n" + joinType + " JOIN (\n"
|
||||
+ " SELECT \n"
|
||||
+ " " + innerQueryName + ".artifact_id,\n"
|
||||
+ " " + innerQueryName + "." + attributeColumn + " AS " + keyName + "\n"
|
||||
+ " FROM blackboard_attributes " + innerQueryName + "\n"
|
||||
+ " WHERE " + innerQueryName + ".attribute_type_id = " + attrType.getTypeID() + " -- " + attrType.name() + "\n"
|
||||
+ ") " + queryName + " ON " + queryName + ".artifact_id = " + bbaName + ".artifact_id\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a column key, creates the full name for the column key.
|
||||
*
|
||||
* @param key The column key.
|
||||
*
|
||||
* @return The full identifier for the column key.
|
||||
*/
|
||||
private static String getFullKey(String key) {
|
||||
return key + QUERY_SUFFIX + "." + key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a SQL 'where' statement from a list of clauses and puts
|
||||
* parenthesis around each clause.
|
||||
*
|
||||
* @param clauses The clauses
|
||||
*
|
||||
* @return The generated 'where' statement.
|
||||
*/
|
||||
private static String getWhereString(List<String> clauses) {
|
||||
if (clauses.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
List<String> parenthesized = clauses.stream()
|
||||
.map(c -> "(" + c + ")")
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return "\nWHERE " + String.join("\n AND ", parenthesized) + "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a [column] LIKE sql clause.
|
||||
*
|
||||
* @param column The column identifier.
|
||||
* @param likeString The string that will be used as column comparison.
|
||||
* @param isLike if false, the statement becomes NOT LIKE.
|
||||
*
|
||||
* @return The generated statement.
|
||||
*/
|
||||
private static String getLikeClause(String column, String likeString, boolean isLike) {
|
||||
return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'";
|
||||
}
|
||||
|
||||
private final SleuthkitCaseProvider provider;
|
||||
|
||||
public TopProgramsSummary() {
|
||||
this(SleuthkitCaseProvider.DEFAULT);
|
||||
}
|
||||
|
||||
public TopProgramsSummary(SleuthkitCaseProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Integer> getArtifactTypeIdsForRefresh() {
|
||||
return ARTIFACT_UPDATE_TYPE_IDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of the top programs used on the data source. Currently
|
||||
* determines this based off of which prefetch results return the highest
|
||||
* count.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param count The number of programs to return.
|
||||
*
|
||||
* @return The top results objects found.
|
||||
*
|
||||
* @throws SleuthkitCaseProviderException
|
||||
* @throws TskCoreException
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count)
|
||||
throws SleuthkitCaseProviderException, TskCoreException, SQLException {
|
||||
if (dataSource == null || count <= 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// ntosboot should be ignored
|
||||
final String ntosBootIdentifier = "NTOSBOOT";
|
||||
// programs in windows directory to be ignored
|
||||
final String windowsDir = "/WINDOWS%";
|
||||
|
||||
final String nameParam = "name";
|
||||
final String pathParam = "path";
|
||||
final String runCountParam = "run_count";
|
||||
final String lastRunParam = "last_run";
|
||||
|
||||
String bbaQuery = "bba";
|
||||
|
||||
final String query = "SELECT\n"
|
||||
+ " " + getFullKey(nameParam) + " AS " + nameParam + ",\n"
|
||||
+ " " + getFullKey(pathParam) + " AS " + pathParam + ",\n"
|
||||
+ " MAX(" + getFullKey(runCountParam) + ") AS " + runCountParam + ",\n"
|
||||
+ " MAX(" + getFullKey(lastRunParam) + ") AS " + lastRunParam + "\n"
|
||||
+ "FROM blackboard_artifacts " + bbaQuery + "\n"
|
||||
+ getAttributeJoin(JoinType.INNER, AttributeColumn.value_text, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, nameParam, bbaQuery)
|
||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_text, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, pathParam, bbaQuery)
|
||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int32, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, runCountParam, bbaQuery)
|
||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int64, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, lastRunParam, bbaQuery)
|
||||
+ getWhereString(Arrays.asList(
|
||||
bbaQuery + ".artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(),
|
||||
bbaQuery + ".data_source_obj_id = " + dataSource.getId(),
|
||||
// exclude ntosBootIdentifier from results
|
||||
getLikeClause(getFullKey(nameParam), ntosBootIdentifier, false),
|
||||
// exclude windows directory items from results
|
||||
getFullKey(pathParam) + " IS NULL OR " + getLikeClause(getFullKey(pathParam), windowsDir, false)
|
||||
))
|
||||
+ "GROUP BY " + getFullKey(nameParam) + ", " + getFullKey(pathParam) + "\n"
|
||||
+ "ORDER BY \n"
|
||||
+ " MAX(" + getFullKey(runCountParam) + ") DESC,\n"
|
||||
+ " MAX(" + getFullKey(lastRunParam) + ") DESC,\n"
|
||||
+ " " + getFullKey(nameParam) + " ASC";
|
||||
|
||||
DataSourceInfoUtilities.ResultSetHandler<List<TopProgramsResult>> handler = (resultSet) -> {
|
||||
List<TopProgramsResult> progResults = new ArrayList<>();
|
||||
|
||||
boolean quitAtCount = false;
|
||||
|
||||
while (resultSet.next() && (!quitAtCount || progResults.size() < count)) {
|
||||
long lastRunEpoch = resultSet.getLong(lastRunParam);
|
||||
Date lastRun = (resultSet.wasNull()) ? null : new Date(lastRunEpoch * 1000);
|
||||
|
||||
Long runCount = resultSet.getLong(runCountParam);
|
||||
if (resultSet.wasNull()) {
|
||||
runCount = null;
|
||||
}
|
||||
|
||||
if (lastRun != null || runCount != null) {
|
||||
quitAtCount = true;
|
||||
}
|
||||
|
||||
progResults.add(new TopProgramsResult(
|
||||
resultSet.getString(nameParam),
|
||||
resultSet.getString(pathParam),
|
||||
runCount,
|
||||
lastRun));
|
||||
}
|
||||
|
||||
return progResults;
|
||||
};
|
||||
|
||||
try (SleuthkitCase.CaseDbQuery dbQuery = provider.get().executeQuery(query);
|
||||
ResultSet resultSet = dbQuery.getResultSet()) {
|
||||
|
||||
return handler.process(resultSet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines a short folder name if any. Otherwise, returns empty string.
|
||||
*
|
||||
* @param strPath The string path.
|
||||
* @param applicationName The application name.
|
||||
*
|
||||
* @return The short folder name or empty string if not found.
|
||||
*/
|
||||
public String getShortFolderName(String strPath, String applicationName) {
|
||||
if (strPath == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
|
||||
|
||||
File file = new File(strPath);
|
||||
while (file != null && StringUtils.isNotBlank(file.getName())) {
|
||||
pathEls.add(file.getName());
|
||||
file = file.getParentFile();
|
||||
}
|
||||
|
||||
Collections.reverse(pathEls);
|
||||
|
||||
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
|
||||
String result = matchEntry.apply(pathEls);
|
||||
if (StringUtils.isNotBlank(result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a result of a program run on a datasource.
|
||||
*/
|
||||
public static class TopProgramsResult {
|
||||
|
||||
private final String programName;
|
||||
private final String programPath;
|
||||
private final Long runTimes;
|
||||
private final Date lastRun;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param programName The name of the program.
|
||||
* @param programPath The path of the program.
|
||||
* @param runTimes The number of runs.
|
||||
*/
|
||||
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
|
||||
this.programName = programName;
|
||||
this.programPath = programPath;
|
||||
this.runTimes = runTimes;
|
||||
this.lastRun = lastRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The name of the program
|
||||
*/
|
||||
public String getProgramName() {
|
||||
return programName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The path of the program.
|
||||
*/
|
||||
public String getProgramPath() {
|
||||
return programPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of run times or null if not present.
|
||||
*/
|
||||
public Long getRunTimes() {
|
||||
return runTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The last time the program was run or null if not present.
|
||||
*/
|
||||
public Date getLastRun() {
|
||||
return lastRun;
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
|
||||
|
||||
import java.io.File;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -30,6 +31,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@ -54,6 +56,36 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
*/
|
||||
public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
|
||||
/**
|
||||
* Functions that determine the folder name of a list of path elements. If
|
||||
* not matched, function returns null.
|
||||
*/
|
||||
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
|
||||
// handle Program Files and Program Files (x86) - if true, return the next folder
|
||||
(pathList) -> {
|
||||
if (pathList.size() < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String rootParent = pathList.get(0).toUpperCase();
|
||||
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
|
||||
return pathList.get(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
// if there is a folder named "APPLICATION DATA" or "APPDATA"
|
||||
(pathList) -> {
|
||||
for (String pathEl : pathList) {
|
||||
String uppered = pathEl.toUpperCase();
|
||||
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
|
||||
return "AppData";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
private static final BlackboardArtifact.Type TYPE_DEVICE_ATTACHED = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED);
|
||||
private static final BlackboardArtifact.Type TYPE_WEB_HISTORY = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_HISTORY);
|
||||
|
||||
@ -69,17 +101,51 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
private static final BlackboardAttribute.Type TYPE_DATETIME_START = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_START);
|
||||
private static final BlackboardAttribute.Type TYPE_DATETIME_END = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_END);
|
||||
private static final BlackboardAttribute.Type TYPE_DOMAIN = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN);
|
||||
private static final BlackboardAttribute.Type TYPE_PROG_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PROG_NAME);
|
||||
private static final BlackboardAttribute.Type TYPE_PATH = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH);
|
||||
private static final BlackboardAttribute.Type TYPE_COUNT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COUNT);
|
||||
|
||||
private static final String NTOS_BOOT_IDENTIFIER = "NTOSBOOT";
|
||||
private static final String WINDOWS_PREFIX = "/WINDOWS";
|
||||
|
||||
private static final Comparator<TopAccountResult> TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccess().compareTo(b.getLastAccess());
|
||||
private static final Comparator<TopWebSearchResult> TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getDateAccessed().compareTo(b.getDateAccessed());
|
||||
|
||||
/**
|
||||
* Sorts TopProgramsResults pushing highest run time count then most recent
|
||||
* run and then the program name that comes earliest in the alphabet.
|
||||
*/
|
||||
private static final Comparator<TopProgramsResult> TOP_PROGRAMS_RESULT_COMPARE = (a, b) -> {
|
||||
// first priority for sorting is the run times
|
||||
// if non-0, this is the return value for the comparator
|
||||
int runTimesCompare = nullableCompare(a.getRunTimes(), b.getRunTimes());
|
||||
if (runTimesCompare != 0) {
|
||||
return -runTimesCompare;
|
||||
}
|
||||
|
||||
// second priority for sorting is the last run date
|
||||
// if non-0, this is the return value for the comparator
|
||||
int lastRunCompare = nullableCompare(
|
||||
a.getLastRun() == null ? null : a.getLastRun().getTime(),
|
||||
b.getLastRun() == null ? null : b.getLastRun().getTime());
|
||||
|
||||
if (lastRunCompare != 0) {
|
||||
return -lastRunCompare;
|
||||
}
|
||||
|
||||
// otherwise sort alphabetically
|
||||
return (a.getProgramName() == null ? "" : a.getProgramName())
|
||||
.compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName()));
|
||||
};
|
||||
|
||||
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
|
||||
ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()
|
||||
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
|
||||
));
|
||||
|
||||
private static final Set<String> DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20"));
|
||||
@ -106,9 +172,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* is designed with unit testing in mind since mocked dependencies can be
|
||||
* utilized.
|
||||
*
|
||||
* @param provider The object providing the current SleuthkitCase.
|
||||
* @param provider The object providing the current SleuthkitCase.
|
||||
* @param translationService The translation service.
|
||||
* @param logger The logger to use.
|
||||
* @param logger The logger to use.
|
||||
*/
|
||||
public UserActivitySummary(
|
||||
SleuthkitCaseProvider provider,
|
||||
@ -140,7 +206,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* Gets a list of recent domains based on the datasource.
|
||||
*
|
||||
* @param dataSource The datasource to query for recent domains.
|
||||
* @param count The max count of items to return.
|
||||
* @param count The max count of items to return.
|
||||
*
|
||||
* @return The list of items retrieved from the database.
|
||||
*
|
||||
@ -176,12 +242,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* Creates a TopDomainsResult from data or null if no visit date exists
|
||||
* within DOMAIN_WINDOW_MS of mostRecentMs.
|
||||
*
|
||||
* @param domain The domain.
|
||||
* @param visits The number of visits.
|
||||
* @param domain The domain.
|
||||
* @param visits The number of visits.
|
||||
* @param mostRecentMs The most recent visit of any domain.
|
||||
*
|
||||
* @return The TopDomainsResult or null if no visits to this domain within
|
||||
* 30 days of mostRecentMs.
|
||||
* 30 days of mostRecentMs.
|
||||
*/
|
||||
private TopDomainsResult getDomainsResult(String domain, List<Long> visits, long mostRecentMs) {
|
||||
long visitCount = 0;
|
||||
@ -214,9 +280,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* @param dataSource The datasource.
|
||||
*
|
||||
* @return A tuple where the first value is the latest web history accessed
|
||||
* date in milliseconds and the second value maps normalized
|
||||
* (lowercase; trimmed) domain names to when those domains were
|
||||
* visited.
|
||||
* date in milliseconds and the second value maps normalized (lowercase;
|
||||
* trimmed) domain names to when those domains were visited.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
* @throws SleuthkitCaseProviderException
|
||||
@ -283,7 +348,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* @param artifact The artifact.
|
||||
*
|
||||
* @return The TopWebSearchResult or null if the search string or date
|
||||
* accessed cannot be determined.
|
||||
* accessed cannot be determined.
|
||||
*/
|
||||
private static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact) {
|
||||
String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
|
||||
@ -298,11 +363,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* term.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param count The maximum number of records to be shown (must be >
|
||||
* 0).
|
||||
* @param count The maximum number of records to be shown (must be > 0).
|
||||
*
|
||||
* @return The list of most recent web searches where most recent search
|
||||
* appears first.
|
||||
* appears first.
|
||||
*
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
|
||||
@ -320,21 +384,22 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
.getArtifacts(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), dataSource.getId());
|
||||
|
||||
// group by search string (case insensitive)
|
||||
Collection<List<TopWebSearchResult>> resultGroups = webSearchArtifacts
|
||||
Collection<TopWebSearchResult> resultGroups = webSearchArtifacts
|
||||
.stream()
|
||||
// get items where search string and date is not null
|
||||
.map(UserActivitySummary::getWebSearchResult)
|
||||
// remove null records
|
||||
.filter(result -> result != null)
|
||||
// get these messages grouped by search to string
|
||||
.collect(Collectors.groupingBy((result) -> result.getSearchString().toUpperCase()))
|
||||
// get the latest message for each search string
|
||||
.collect(Collectors.toMap(
|
||||
(result) -> result.getSearchString().toUpperCase(),
|
||||
result -> result,
|
||||
(result1, result2) -> TOP_WEBSEARCH_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
|
||||
.values();
|
||||
|
||||
// get the most recent date for each search term
|
||||
List<TopWebSearchResult> results = resultGroups
|
||||
.stream()
|
||||
// get the most recent access per search type
|
||||
.map((list) -> list.stream().max(TOP_WEBSEARCH_RESULT_DATE_COMPARE).get())
|
||||
// get most recent searches first
|
||||
.sorted(TOP_WEBSEARCH_RESULT_DATE_COMPARE.reversed())
|
||||
.limit(count)
|
||||
@ -358,7 +423,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* @param original The original text.
|
||||
*
|
||||
* @return The translated text or null if no translation can be determined
|
||||
* or exists.
|
||||
* or exists.
|
||||
*/
|
||||
private String getTranslationOrNull(String original) {
|
||||
if (!translationService.hasProvider() || StringUtils.isBlank(original)) {
|
||||
@ -382,15 +447,34 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
return translated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the most recent TopDeviceAttachedResult. If one is null, the other
|
||||
* is returned.
|
||||
*
|
||||
* @param r1 A result.
|
||||
* @param r2 Another result.
|
||||
* @return The most recent one with a non-null date.
|
||||
*/
|
||||
private TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2) {
|
||||
if (r2.getDateAccessed() == null) {
|
||||
return r1;
|
||||
}
|
||||
|
||||
if (r1.getDateAccessed() == null) {
|
||||
return r2;
|
||||
}
|
||||
|
||||
return r1.getDateAccessed().compareTo(r2.getDateAccessed()) >= 0 ? r1 : r2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves most recent devices used by most recent date attached.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param count The maximum number of records to be shown (must be >
|
||||
* 0).
|
||||
* @param count The maximum number of records to be shown (must be > 0).
|
||||
*
|
||||
* @return The list of most recent devices attached where most recent device
|
||||
* attached appears first.
|
||||
* attached appears first.
|
||||
*
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
|
||||
@ -403,7 +487,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_DEVICE_ATTACHED,
|
||||
Collection<TopDeviceAttachedResult> results = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_DEVICE_ATTACHED,
|
||||
dataSource, TYPE_DATETIME, DataSourceInfoUtilities.SortOrder.DESCENDING, 0)
|
||||
.stream()
|
||||
.map(artifact -> {
|
||||
@ -416,9 +500,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
})
|
||||
// remove Root Hub identifier
|
||||
.filter(result -> {
|
||||
return result.getDeviceModel() == null
|
||||
return result.getDeviceId() == null
|
||||
|| result.getDeviceModel() == null
|
||||
|| !DEVICE_EXCLUDE_LIST.contains(result.getDeviceModel().trim().toUpperCase());
|
||||
})
|
||||
.collect(Collectors.toMap(result -> result.getDeviceId(), result -> result, (r1, r2) -> getMostRecentDevice(r1, r2)))
|
||||
.values();
|
||||
|
||||
return results.stream()
|
||||
.limit(count)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@ -429,7 +518,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* @param artifact The artifact.
|
||||
*
|
||||
* @return The TopAccountResult or null if the account type or message date
|
||||
* cannot be determined.
|
||||
* cannot be determined.
|
||||
*/
|
||||
private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) {
|
||||
String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE);
|
||||
@ -443,12 +532,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* Obtains a TopAccountResult from a blackboard artifact. The date is
|
||||
* maximum of any found dates for attribute types provided.
|
||||
*
|
||||
* @param artifact The artifact.
|
||||
* @param artifact The artifact.
|
||||
* @param messageType The type of message this is.
|
||||
* @param dateAttrs The date attribute types.
|
||||
* @param dateAttrs The date attribute types.
|
||||
*
|
||||
* @return The TopAccountResult or null if the account type or max date are
|
||||
* not provided.
|
||||
* not provided.
|
||||
*/
|
||||
private static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs) {
|
||||
String type = messageType;
|
||||
@ -472,11 +561,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* sent.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param count The maximum number of records to be shown (must be >
|
||||
* 0).
|
||||
* @param count The maximum number of records to be shown (must be > 0).
|
||||
*
|
||||
* @return The list of most recent accounts used where the most recent
|
||||
* account by last message sent occurs first.
|
||||
* account by last message sent occurs first.
|
||||
*
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
|
||||
@ -519,18 +607,19 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
Stream<TopAccountResult> allResults = Stream.concat(messageResults, Stream.concat(emailResults, calllogResults));
|
||||
|
||||
// get them grouped by account type
|
||||
Collection<List<TopAccountResult>> groupedResults = allResults
|
||||
Collection<TopAccountResult> groupedResults = allResults
|
||||
// remove null records
|
||||
.filter(result -> result != null)
|
||||
// get these messages grouped by account type
|
||||
.collect(Collectors.groupingBy(TopAccountResult::getAccountType))
|
||||
// get these messages grouped by account type and get the most recent of each type
|
||||
.collect(Collectors.toMap(
|
||||
result -> result.getAccountType(),
|
||||
result -> result,
|
||||
(result1, result2) -> TOP_ACCOUNT_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
|
||||
.values();
|
||||
|
||||
// get account type sorted by most recent date
|
||||
return groupedResults
|
||||
.stream()
|
||||
// get the most recent access per account type
|
||||
.map((accountGroup) -> accountGroup.stream().max(TOP_ACCOUNT_RESULT_DATE_COMPARE).get())
|
||||
// get most recent accounts accessed
|
||||
.sorted(TOP_ACCOUNT_RESULT_DATE_COMPARE.reversed())
|
||||
// limit to count
|
||||
@ -539,6 +628,190 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines a short folder name if any. Otherwise, returns empty string.
|
||||
*
|
||||
* @param strPath The string path.
|
||||
* @param applicationName The application name.
|
||||
*
|
||||
* @return The short folder name or empty string if not found.
|
||||
*/
|
||||
public String getShortFolderName(String strPath, String applicationName) {
|
||||
if (strPath == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
|
||||
|
||||
File file = new File(strPath);
|
||||
while (file != null && org.apache.commons.lang.StringUtils.isNotBlank(file.getName())) {
|
||||
pathEls.add(file.getName());
|
||||
file = file.getParentFile();
|
||||
}
|
||||
|
||||
Collections.reverse(pathEls);
|
||||
|
||||
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
|
||||
String result = matchEntry.apply(pathEls);
|
||||
if (org.apache.commons.lang.StringUtils.isNotBlank(result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TopProgramsResult from a TSK_PROG_RUN blackboard artifact.
|
||||
*
|
||||
* @param artifact The TSK_PROG_RUN blackboard artifact.
|
||||
*
|
||||
* @return The generated TopProgramsResult.
|
||||
*/
|
||||
private TopProgramsResult getTopProgramsResult(BlackboardArtifact artifact) {
|
||||
String programName = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PROG_NAME);
|
||||
|
||||
// ignore items with no name or a ntos boot identifier
|
||||
if (StringUtils.isBlank(programName) || NTOS_BOOT_IDENTIFIER.equalsIgnoreCase(programName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String path = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PATH);
|
||||
|
||||
// ignore windows directory
|
||||
if (StringUtils.startsWithIgnoreCase(path, WINDOWS_PREFIX)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer count = DataSourceInfoUtilities.getIntOrNull(artifact, TYPE_COUNT);
|
||||
Long longCount = (count == null) ? null : (long) count;
|
||||
|
||||
return new TopProgramsResult(
|
||||
programName,
|
||||
path,
|
||||
longCount,
|
||||
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the maximum date given two (possibly null) dates.
|
||||
*
|
||||
* @param date1 First date.
|
||||
* @param date2 Second date.
|
||||
*
|
||||
* @return The maximum non-null date or null if both items are null.
|
||||
*/
|
||||
private static Date getMax(Date date1, Date date2) {
|
||||
if (date1 == null) {
|
||||
return date2;
|
||||
} else if (date2 == null) {
|
||||
return date1;
|
||||
} else {
|
||||
return date1.compareTo(date2) > 0 ? date1 : date2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the compare value favoring the higher non-null number.
|
||||
*
|
||||
* @param long1 First possibly null long.
|
||||
* @param long2 Second possibly null long.
|
||||
*
|
||||
* @return Returns the compare value: 1,0,-1 favoring the higher non-null
|
||||
* value.
|
||||
*/
|
||||
private static int nullableCompare(Long long1, Long long2) {
|
||||
if (long1 == null && long2 == null) {
|
||||
return 0;
|
||||
} else if (long1 != null && long2 == null) {
|
||||
return 1;
|
||||
} else if (long1 == null && long2 != null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return Long.compare(long1, long2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if number is non-null and higher than 0.
|
||||
*
|
||||
* @param longNum The number.
|
||||
*
|
||||
* @return True if non-null and higher than 0.
|
||||
*/
|
||||
private static boolean isPositiveNum(Long longNum) {
|
||||
return longNum != null && longNum > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the top programs results for the given data source limited to
|
||||
* the count provided as a parameter. The highest run times are at the top
|
||||
* of the list. If that information isn't available the last run date is
|
||||
* used. If both, the last run date and the number of run times are
|
||||
* unavailable, the programs will be sorted alphabetically, the count will
|
||||
* be ignored and all items will be returned.
|
||||
*
|
||||
* @param dataSource The datasource. If the datasource is null, an empty
|
||||
* list will be returned.
|
||||
* @param count The number of results to return. This value must be > 0 or
|
||||
* an IllegalArgumentException will be thrown.
|
||||
*
|
||||
* @return The sorted list and limited to the count if last run or run count
|
||||
* information is available on any item.
|
||||
*
|
||||
* @throws SleuthkitCaseProviderException
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
|
||||
assertValidCount(count);
|
||||
|
||||
if (dataSource == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Get TopProgramsResults for each TSK_PROG_RUN artifact
|
||||
Collection<TopProgramsResult> results = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(), dataSource.getId())
|
||||
.stream()
|
||||
// convert to a TopProgramsResult object or null if missing critical information
|
||||
.map((art) -> getTopProgramsResult(art))
|
||||
// remove any null items
|
||||
.filter((res) -> res != null)
|
||||
// group by the program name and program path
|
||||
// The value will be a TopProgramsResult with the max run times
|
||||
// and most recent last run date for each program name / program path pair.
|
||||
.collect(Collectors.toMap(
|
||||
res -> Pair.of(
|
||||
res.getProgramName() == null ? null : res.getProgramName().toUpperCase(),
|
||||
res.getProgramPath() == null ? null : res.getProgramPath().toUpperCase()),
|
||||
res -> res,
|
||||
(res1, res2) -> {
|
||||
return new TopProgramsResult(
|
||||
res1.getProgramName(),
|
||||
res1.getProgramPath(),
|
||||
getMax(res1.getRunTimes(), res2.getRunTimes()),
|
||||
getMax(res1.getLastRun(), res2.getLastRun()));
|
||||
})).values();
|
||||
|
||||
List<TopProgramsResult> orderedResults = results.stream()
|
||||
.sorted(TOP_PROGRAMS_RESULT_COMPARE)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// only limit the list to count if there is no last run date and no run times.
|
||||
if (!orderedResults.isEmpty()) {
|
||||
TopProgramsResult topResult = orderedResults.get(0);
|
||||
// if run times / last run information is available, the first item should have some value,
|
||||
// and then the items should be limited accordingly.
|
||||
if (isPositiveNum(topResult.getRunTimes())
|
||||
|| (topResult.getLastRun() != null && isPositiveNum(topResult.getLastRun().getTime()))) {
|
||||
return orderedResults.stream().limit(count).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise return the alphabetized list with no limit applied.
|
||||
return orderedResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object containing information about a web search artifact.
|
||||
*/
|
||||
@ -603,10 +876,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param deviceId The device id.
|
||||
* @param deviceId The device id.
|
||||
* @param dateAccessed The date last attached.
|
||||
* @param deviceMake The device make.
|
||||
* @param deviceModel The device model.
|
||||
* @param deviceMake The device make.
|
||||
* @param deviceModel The device model.
|
||||
*/
|
||||
public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel) {
|
||||
this.deviceId = deviceId;
|
||||
@ -657,7 +930,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* Main constructor.
|
||||
*
|
||||
* @param accountType The account type.
|
||||
* @param lastAccess The date the account was last accessed.
|
||||
* @param lastAccess The date the account was last accessed.
|
||||
*/
|
||||
public TopAccountResult(String accountType, Date lastAccess) {
|
||||
this.accountType = accountType;
|
||||
@ -691,9 +964,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
/**
|
||||
* Describes a top domain result.
|
||||
*
|
||||
* @param domain The domain.
|
||||
* @param domain The domain.
|
||||
* @param visitTimes The number of times it was visited.
|
||||
* @param lastVisit The date of the last visit.
|
||||
* @param lastVisit The date of the last visit.
|
||||
*/
|
||||
public TopDomainsResult(String domain, Long visitTimes, Date lastVisit) {
|
||||
this.domain = domain;
|
||||
@ -722,4 +995,57 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
return lastVisit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a result of a program run on a datasource.
|
||||
*/
|
||||
public static class TopProgramsResult {
|
||||
|
||||
private final String programName;
|
||||
private final String programPath;
|
||||
private final Long runTimes;
|
||||
private final Date lastRun;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param programName The name of the program.
|
||||
* @param programPath The path of the program.
|
||||
* @param runTimes The number of runs.
|
||||
*/
|
||||
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
|
||||
this.programName = programName;
|
||||
this.programPath = programPath;
|
||||
this.runTimes = runTimes;
|
||||
this.lastRun = lastRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The name of the program
|
||||
*/
|
||||
public String getProgramName() {
|
||||
return programName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The path of the program.
|
||||
*/
|
||||
public String getProgramPath() {
|
||||
return programPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of run times or null if not present.
|
||||
*/
|
||||
public Long getRunTimes() {
|
||||
return runTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The last time the program was run or null if not present.
|
||||
*/
|
||||
public Date getLastRun() {
|
||||
return lastRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,3 +42,4 @@ RecentFilesPanel.attachmentLabel.text=Recent Attachments
|
||||
PastCasesPanel.notableFileLabel.text=Cases with Common Items That Were Tagged as Notable
|
||||
PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs
|
||||
DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected.
|
||||
TimelinePanel.activityRangeLabel.text=Activity Range
|
||||
|
@ -3,6 +3,7 @@ AnalysisPanel_keyColumn_title=Name
|
||||
AnalysisPanel_keywordSearchModuleName=Keyword Search
|
||||
# {0} - module name
|
||||
BaseDataSourceSummaryPanel_defaultNotIngestMessage=The {0} ingest module has not been run on this data source.
|
||||
ContainerPanel_setFieldsForNonImageDataSource_na=N/A
|
||||
CTL_DataSourceSummaryAction=Data Source Summary
|
||||
DataSourceSummaryDialog.closeButton.text=Close
|
||||
ContainerPanel.displayNameLabel.text=Display Name:
|
||||
@ -47,6 +48,7 @@ DataSourceSummaryTabbedPane_detailsTab_title=Container
|
||||
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
|
||||
DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases
|
||||
DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files
|
||||
DataSourceSummaryTabbedPane_timelineTab_title=Timeline
|
||||
DataSourceSummaryTabbedPane_typesTab_title=Types
|
||||
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
|
||||
PastCasesPanel_caseColumn_title=Case
|
||||
@ -64,6 +66,11 @@ SizeRepresentationUtil_units_kilobytes=\ kB
|
||||
SizeRepresentationUtil_units_megabytes=\ MB
|
||||
SizeRepresentationUtil_units_petabytes=\ PB
|
||||
SizeRepresentationUtil_units_terabytes=\ TB
|
||||
TimelinePanel_earliestLabel_title=Earliest
|
||||
TimelinePanel_latestLabel_title=Latest
|
||||
TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events
|
||||
TimlinePanel_last30DaysChart_fileEvts_title=File Events
|
||||
TimlinePanel_last30DaysChart_title=Last 30 Days
|
||||
TypesPanel_artifactsTypesPieChart_title=Artifact Types
|
||||
TypesPanel_fileMimeTypesChart_audio_title=Audio
|
||||
TypesPanel_fileMimeTypesChart_documents_title=Documents
|
||||
@ -95,6 +102,7 @@ RecentFilesPanel.attachmentLabel.text=Recent Attachments
|
||||
PastCasesPanel.notableFileLabel.text=Cases with Common Items That Were Tagged as Notable
|
||||
PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs
|
||||
DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected.
|
||||
TimelinePanel.activityRangeLabel.text=Activity Range
|
||||
UserActivityPanel_noDataExists=No communication data exists
|
||||
UserActivityPanel_tab_title=User Activity
|
||||
UserActivityPanel_TopAccountTableModel_accountType_header=Account Type
|
||||
|
@ -26,6 +26,7 @@ import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
|
||||
@ -52,7 +53,7 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param dataSource The original datasource.
|
||||
* @param dataSource The original datasource.
|
||||
* @param unallocatedFilesSize The unallocated file size.
|
||||
*/
|
||||
ContainerPanelData(DataSource dataSource, Long unallocatedFilesSize) {
|
||||
@ -165,8 +166,6 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize) {
|
||||
clearTableValues();
|
||||
if (selectedDataSource != null) {
|
||||
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(unallocatedFilesSize));
|
||||
timeZoneValue.setText(selectedDataSource.getTimeZone());
|
||||
displayNameValue.setText(selectedDataSource.getName());
|
||||
originalNameValue.setText(selectedDataSource.getName());
|
||||
deviceIdValue.setText(selectedDataSource.getDeviceId());
|
||||
@ -178,24 +177,48 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
}
|
||||
|
||||
if (selectedDataSource instanceof Image) {
|
||||
setFieldsForImage((Image) selectedDataSource);
|
||||
setFieldsForImage((Image) selectedDataSource, unallocatedFilesSize);
|
||||
} else {
|
||||
setFieldsForNonImageDataSource();
|
||||
}
|
||||
}
|
||||
updateFieldVisibility();
|
||||
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"ContainerPanel_setFieldsForNonImageDataSource_na=N/A"
|
||||
})
|
||||
private void setFieldsForNonImageDataSource() {
|
||||
String NA = Bundle.ContainerPanel_setFieldsForNonImageDataSource_na();
|
||||
|
||||
unallocatedSizeValue.setText(NA);
|
||||
imageTypeValue.setText(NA);
|
||||
sizeValue.setText(NA);
|
||||
sectorSizeValue.setText(NA);
|
||||
timeZoneValue.setText(NA);
|
||||
|
||||
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{NA});
|
||||
|
||||
md5HashValue.setText(NA);
|
||||
sha1HashValue.setText(NA);
|
||||
sha256HashValue.setText(NA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets text fields for an image. This should be called after
|
||||
* clearTableValues and before updateFieldVisibility to ensure the proper
|
||||
* rendering.
|
||||
*
|
||||
* @param selectedImage The selected image.
|
||||
* @param unallocatedFilesSize Unallocated file size in bytes.
|
||||
*/
|
||||
private void setFieldsForImage(Image selectedImage) {
|
||||
private void setFieldsForImage(Image selectedImage, Long unallocatedFilesSize) {
|
||||
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(unallocatedFilesSize));
|
||||
imageTypeValue.setText(selectedImage.getType().getName());
|
||||
sizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSize()));
|
||||
sectorSizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSsize()));
|
||||
timeZoneValue.setText(selectedImage.getTimeZone());
|
||||
|
||||
for (String path : selectedImage.getPaths()) {
|
||||
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path});
|
||||
@ -233,41 +256,6 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the visibility of all fields and their labels based on whether
|
||||
* they have contents. Empty fields have them and their contents hidden.
|
||||
*/
|
||||
private void updateFieldVisibility() {
|
||||
displayNameValue.setVisible(!displayNameValue.getText().isEmpty());
|
||||
displayNameLabel.setVisible(!displayNameValue.getText().isEmpty());
|
||||
originalNameValue.setVisible(!originalNameValue.getText().isEmpty());
|
||||
originalNameLabel.setVisible(!originalNameValue.getText().isEmpty());
|
||||
deviceIdValue.setVisible(!deviceIdValue.getText().isEmpty());
|
||||
deviceIdLabel.setVisible(!deviceIdValue.getText().isEmpty());
|
||||
timeZoneValue.setVisible(!timeZoneValue.getText().isEmpty());
|
||||
timeZoneLabel.setVisible(!timeZoneValue.getText().isEmpty());
|
||||
acquisitionDetailsTextArea.setVisible(!acquisitionDetailsTextArea.getText().isEmpty());
|
||||
acquisitionDetailsLabel.setVisible(!acquisitionDetailsTextArea.getText().isEmpty());
|
||||
acquisitionDetailsScrollPane.setVisible(!acquisitionDetailsTextArea.getText().isEmpty());
|
||||
imageTypeValue.setVisible(!imageTypeValue.getText().isEmpty());
|
||||
imageTypeLabel.setVisible(!imageTypeValue.getText().isEmpty());
|
||||
sizeValue.setVisible(!sizeValue.getText().isEmpty());
|
||||
sizeLabel.setVisible(!sizeValue.getText().isEmpty());
|
||||
sectorSizeValue.setVisible(!sectorSizeValue.getText().isEmpty());
|
||||
sectorSizeLabel.setVisible(!sectorSizeValue.getText().isEmpty());
|
||||
md5HashValue.setVisible(!md5HashValue.getText().isEmpty());
|
||||
md5HashLabel.setVisible(!md5HashValue.getText().isEmpty());
|
||||
sha1HashValue.setVisible(!sha1HashValue.getText().isEmpty());
|
||||
sha1HashLabel.setVisible(!sha1HashValue.getText().isEmpty());
|
||||
sha256HashValue.setVisible(!sha256HashValue.getText().isEmpty());
|
||||
sha256HashLabel.setVisible(!sha256HashValue.getText().isEmpty());
|
||||
unallocatedSizeValue.setVisible(!unallocatedSizeValue.getText().isEmpty());
|
||||
unallocatedSizeLabel.setVisible(!unallocatedSizeValue.getText().isEmpty());
|
||||
filePathsTable.setVisible(filePathsTable.getRowCount() > 0);
|
||||
filePathsLabel.setVisible(filePathsTable.getRowCount() > 0);
|
||||
filePathsScrollPane.setVisible(filePathsTable.getRowCount() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contents of all fields to be empty.
|
||||
*/
|
||||
|
@ -38,7 +38,8 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
"DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History",
|
||||
"DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files",
|
||||
"DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases",
|
||||
"DataSourceSummaryTabbedPane_analysisTab_title=Analysis"
|
||||
"DataSourceSummaryTabbedPane_analysisTab_title=Analysis",
|
||||
"DataSourceSummaryTabbedPane_timelineTab_title=Timeline"
|
||||
})
|
||||
public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
|
||||
@ -56,10 +57,10 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param tabTitle The title of the tab.
|
||||
* @param component The component to be displayed.
|
||||
* @param tabTitle The title of the tab.
|
||||
* @param component The component to be displayed.
|
||||
* @param onDataSource The function to be called on a new data source.
|
||||
* @param onClose Called to cleanup resources when closing tabs.
|
||||
* @param onClose Called to cleanup resources when closing tabs.
|
||||
*/
|
||||
DataSourceTab(String tabTitle, Component component, Consumer<DataSource> onDataSource, Runnable onClose) {
|
||||
this.tabTitle = tabTitle;
|
||||
@ -72,7 +73,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
* Main constructor.
|
||||
*
|
||||
* @param tabTitle The title of the tab.
|
||||
* @param panel The component to be displayed in the tab.
|
||||
* @param panel The component to be displayed in the tab.
|
||||
*/
|
||||
DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) {
|
||||
this.tabTitle = tabTitle;
|
||||
@ -123,6 +124,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel()),
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()),
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_pastCasesTab_title(), new PastCasesPanel()),
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_timelineTab_title(), new TimelinePanel()),
|
||||
// do nothing on closing
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> {
|
||||
}),
|
||||
|
@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory;
|
||||
@ -28,7 +27,6 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
@ -103,31 +101,8 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
|
||||
* @param result The result.
|
||||
*/
|
||||
private void handleResult(DataFetchResult<PastCasesResult> result) {
|
||||
showResultWithModuleCheck(notableFileTable, getSubResult(result, (res) -> res.getTaggedNotable()), CR_FACTORY, CR_NAME);
|
||||
showResultWithModuleCheck(sameIdTable, getSubResult(result, (res) -> res.getSameIdsResults()), CR_FACTORY, CR_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an input data fetch result, creates an error result if the original
|
||||
* is an error. Otherwise, uses the getSubResult function on the underlying
|
||||
* data to create a new DataFetchResult.
|
||||
*
|
||||
* @param inputResult The input result.
|
||||
* @param getSubComponent The means of getting the data given the original
|
||||
* data.
|
||||
*
|
||||
* @return The new result with the error of the original or the processed
|
||||
* data.
|
||||
*/
|
||||
private <O> DataFetchResult<O> getSubResult(DataFetchResult<PastCasesResult> inputResult, Function<PastCasesResult, O> getSubResult) {
|
||||
if (inputResult == null) {
|
||||
return null;
|
||||
} else if (inputResult.getResultType() == ResultType.SUCCESS) {
|
||||
O innerData = (inputResult.getData() == null) ? null : getSubResult.apply(inputResult.getData());
|
||||
return DataFetchResult.getSuccessResult(innerData);
|
||||
} else {
|
||||
return DataFetchResult.getErrorResult(inputResult.getException());
|
||||
}
|
||||
showResultWithModuleCheck(notableFileTable, DataFetchResult.getSubResult(result, (res) -> res.getTaggedNotable()), CR_FACTORY, CR_NAME);
|
||||
showResultWithModuleCheck(sameIdTable, DataFetchResult.getSubResult(result, (res) -> res.getSameIdsResults()), CR_FACTORY, CR_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,214 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="mainScrollPane" alignment="0" pref="400" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="mainScrollPane" alignment="0" pref="300" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="mainContentPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
|
||||
<EmptyBorder bottom="10" left="10" right="10" top="10"/>
|
||||
</Border>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
|
||||
<Property name="axis" type="int" value="3"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="ingestRunningPanel">
|
||||
<Properties>
|
||||
<Property name="alignmentX" type="float" value="0.0"/>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[32767, 25]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[10, 25]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[10, 25]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="ingestRunningLabel"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="activityRangeLabel">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Segoe UI" size="12" style="1"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="TimelinePanel.activityRangeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AccessibilityProperties>
|
||||
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="PastCasesPanel.notableFileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</AccessibilityProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.Box$Filler" name="filler1">
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 2]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 2]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 2]"/>
|
||||
</Property>
|
||||
<Property name="alignmentX" type="float" value="0.0"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="earliestLabelPanel">
|
||||
<Properties>
|
||||
<Property name="alignmentX" type="float" value="0.0"/>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[32767, 20]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[100, 20]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[100, 20]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="earliestLabel"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="latestLabelPanel">
|
||||
<Properties>
|
||||
<Property name="alignmentX" type="float" value="0.0"/>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[32767, 20]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[100, 20]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[100, 20]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="latestLabel"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||
</Container>
|
||||
<Component class="javax.swing.Box$Filler" name="filler2">
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 20]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 20]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 20]"/>
|
||||
</Property>
|
||||
<Property name="alignmentX" type="float" value="0.0"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="sameIdPanel">
|
||||
<Properties>
|
||||
<Property name="alignmentX" type="float" value="0.0"/>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[600, 300]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[600, 300]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[600, 300]"/>
|
||||
</Property>
|
||||
<Property name="verifyInputWhenFocusTarget" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="last30DaysChart"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||
</Container>
|
||||
<Component class="javax.swing.Box$Filler" name="filler5">
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 32767]"/>
|
||||
</Property>
|
||||
<Property name="alignmentX" type="float" value="0.0"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalGlue"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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.datasourcesummary.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.BarChartItem;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.BarChartSeries;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.OrderedKey;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
* A tab shown in data source summary displaying information about a data
|
||||
* source's timeline events.
|
||||
*/
|
||||
@Messages({
|
||||
"TimelinePanel_earliestLabel_title=Earliest",
|
||||
"TimelinePanel_latestLabel_title=Latest",
|
||||
"TimlinePanel_last30DaysChart_title=Last 30 Days",
|
||||
"TimlinePanel_last30DaysChart_fileEvts_title=File Events",
|
||||
"TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events",})
|
||||
public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy");
|
||||
private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d");
|
||||
private static final int MOST_RECENT_DAYS_COUNT = 30;
|
||||
|
||||
/**
|
||||
* Creates a DateFormat formatter that uses UTC for time zone.
|
||||
*
|
||||
* @param formatString The date format string.
|
||||
* @return The data format.
|
||||
*/
|
||||
private static DateFormat getUtcFormat(String formatString) {
|
||||
return new SimpleDateFormat(formatString, Locale.getDefault());
|
||||
}
|
||||
|
||||
// components displayed in the tab
|
||||
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
|
||||
private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title());
|
||||
private final LoadableLabel latestLabel = new LoadableLabel(Bundle.TimelinePanel_latestLabel_title());
|
||||
private final BarChartPanel last30DaysChart = new BarChartPanel(Bundle.TimlinePanel_last30DaysChart_title(), "", "");
|
||||
|
||||
// all loadable components on this tab
|
||||
private final List<LoadableComponent<?>> loadableComponents = Arrays.asList(earliestLabel, latestLabel, last30DaysChart);
|
||||
|
||||
// actions to load data for this tab
|
||||
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
|
||||
|
||||
public TimelinePanel() {
|
||||
this(new TimelineSummary());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new form PastCasesPanel
|
||||
*/
|
||||
public TimelinePanel(TimelineSummary timelineData) {
|
||||
// set up data acquisition methods
|
||||
dataFetchComponents = Arrays.asList(
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT),
|
||||
(result) -> handleResult(result))
|
||||
);
|
||||
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a date using a DateFormat. In the event that the date is null,
|
||||
* returns a null string.
|
||||
*
|
||||
* @param date The date to format.
|
||||
* @param formatter The DateFormat to use to format the date.
|
||||
* @return The formatted string generated from the formatter or null if the
|
||||
* date is null.
|
||||
*/
|
||||
private static String formatDate(Date date, DateFormat formatter) {
|
||||
return date == null ? null : formatter.format(date);
|
||||
}
|
||||
|
||||
private static final Color FILE_EVT_COLOR = new Color(228, 22, 28);
|
||||
private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100);
|
||||
|
||||
/**
|
||||
* Converts DailyActivityAmount data retrieved from TimelineSummary into
|
||||
* data to be displayed as a bar chart.
|
||||
*
|
||||
* @param recentDaysActivity The data retrieved from TimelineSummary.
|
||||
* @return The data to be displayed in the BarChart.
|
||||
*/
|
||||
private List<BarChartSeries> parseChartData(List<DailyActivityAmount> recentDaysActivity) {
|
||||
// if no data, return null indicating no result.
|
||||
if (CollectionUtils.isEmpty(recentDaysActivity)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a bar chart item for each recent days activity item
|
||||
List<BarChartItem> fileEvtCounts = new ArrayList<>();
|
||||
List<BarChartItem> artifactEvtCounts = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < recentDaysActivity.size(); i++) {
|
||||
DailyActivityAmount curItem = recentDaysActivity.get(i);
|
||||
|
||||
long fileAmt = curItem.getFileActivityCount();
|
||||
long artifactAmt = curItem.getArtifactActivityCount() * 100;
|
||||
String formattedDate = (i == 0 || i == recentDaysActivity.size() - 1)
|
||||
? formatDate(curItem.getDay(), CHART_FORMAT) : "";
|
||||
|
||||
OrderedKey thisKey = new OrderedKey(formattedDate, i);
|
||||
fileEvtCounts.add(new BarChartItem(thisKey, fileAmt));
|
||||
artifactEvtCounts.add(new BarChartItem(thisKey, artifactAmt));
|
||||
}
|
||||
|
||||
return Arrays.asList(
|
||||
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_fileEvts_title(), FILE_EVT_COLOR, fileEvtCounts),
|
||||
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles displaying the result for each displayable item in the
|
||||
* TimelinePanel by breaking the TimelineSummaryData result into its
|
||||
* constituent parts and then sending each data item to the pertinent
|
||||
* component.
|
||||
*
|
||||
* @param result The result to be displayed on this tab.
|
||||
*/
|
||||
private void handleResult(DataFetchResult<TimelineSummaryData> result) {
|
||||
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
|
||||
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
|
||||
last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity())));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fetchInformation(DataSource dataSource) {
|
||||
fetchInformation(dataFetchComponents, dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewDataSource(DataSource dataSource) {
|
||||
onNewDataSource(dataFetchComponents, loadableComponents, dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
ingestRunningLabel.unregister();
|
||||
super.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
javax.swing.JScrollPane mainScrollPane = new javax.swing.JScrollPane();
|
||||
javax.swing.JPanel mainContentPanel = new javax.swing.JPanel();
|
||||
javax.swing.JPanel ingestRunningPanel = ingestRunningLabel;
|
||||
javax.swing.JLabel activityRangeLabel = new javax.swing.JLabel();
|
||||
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2));
|
||||
javax.swing.JPanel earliestLabelPanel = earliestLabel;
|
||||
javax.swing.JPanel latestLabelPanel = latestLabel;
|
||||
javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20));
|
||||
javax.swing.JPanel sameIdPanel = last30DaysChart;
|
||||
javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
|
||||
|
||||
mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
mainContentPanel.setLayout(new javax.swing.BoxLayout(mainContentPanel, javax.swing.BoxLayout.PAGE_AXIS));
|
||||
|
||||
ingestRunningPanel.setAlignmentX(0.0F);
|
||||
ingestRunningPanel.setMaximumSize(new java.awt.Dimension(32767, 25));
|
||||
ingestRunningPanel.setMinimumSize(new java.awt.Dimension(10, 25));
|
||||
ingestRunningPanel.setPreferredSize(new java.awt.Dimension(10, 25));
|
||||
mainContentPanel.add(ingestRunningPanel);
|
||||
|
||||
activityRangeLabel.setFont(new java.awt.Font("Segoe UI", 1, 12)); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(activityRangeLabel, org.openide.util.NbBundle.getMessage(TimelinePanel.class, "TimelinePanel.activityRangeLabel.text")); // NOI18N
|
||||
mainContentPanel.add(activityRangeLabel);
|
||||
activityRangeLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(TimelinePanel.class, "PastCasesPanel.notableFileLabel.text")); // NOI18N
|
||||
|
||||
filler1.setAlignmentX(0.0F);
|
||||
mainContentPanel.add(filler1);
|
||||
|
||||
earliestLabelPanel.setAlignmentX(0.0F);
|
||||
earliestLabelPanel.setMaximumSize(new java.awt.Dimension(32767, 20));
|
||||
earliestLabelPanel.setMinimumSize(new java.awt.Dimension(100, 20));
|
||||
earliestLabelPanel.setPreferredSize(new java.awt.Dimension(100, 20));
|
||||
mainContentPanel.add(earliestLabelPanel);
|
||||
|
||||
latestLabelPanel.setAlignmentX(0.0F);
|
||||
latestLabelPanel.setMaximumSize(new java.awt.Dimension(32767, 20));
|
||||
latestLabelPanel.setMinimumSize(new java.awt.Dimension(100, 20));
|
||||
latestLabelPanel.setPreferredSize(new java.awt.Dimension(100, 20));
|
||||
mainContentPanel.add(latestLabelPanel);
|
||||
|
||||
filler2.setAlignmentX(0.0F);
|
||||
mainContentPanel.add(filler2);
|
||||
|
||||
sameIdPanel.setAlignmentX(0.0F);
|
||||
sameIdPanel.setMaximumSize(new java.awt.Dimension(600, 300));
|
||||
sameIdPanel.setMinimumSize(new java.awt.Dimension(600, 300));
|
||||
sameIdPanel.setPreferredSize(new java.awt.Dimension(600, 300));
|
||||
sameIdPanel.setVerifyInputWhenFocusTarget(false);
|
||||
mainContentPanel.add(sameIdPanel);
|
||||
|
||||
filler5.setAlignmentX(0.0F);
|
||||
mainContentPanel.add(filler5);
|
||||
|
||||
mainScrollPane.setViewportView(mainContentPanel);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.sql.SQLException;
|
||||
import java.text.DecimalFormat;
|
||||
@ -30,7 +29,6 @@ import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.swing.JLabel;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
|
||||
@ -40,13 +38,13 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.AbstractLoadableComponent;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel.PieChartItem;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory;
|
||||
@ -78,46 +76,6 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
"TypesPanel_sizeLabel_title=Size"})
|
||||
class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
/**
|
||||
* A label that allows for displaying loading messages and can be used with
|
||||
* a DataFetchResult. Text displays as "<key>:<value | message>".
|
||||
*/
|
||||
private static class LoadableLabel extends AbstractLoadableComponent<String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final JLabel label = new JLabel();
|
||||
private final String key;
|
||||
|
||||
/**
|
||||
* Main constructor for the label.
|
||||
*
|
||||
* @param key The key to be displayed.
|
||||
*/
|
||||
LoadableLabel(String key) {
|
||||
this.key = key;
|
||||
setLayout(new BorderLayout());
|
||||
add(label, BorderLayout.CENTER);
|
||||
this.showResults(null);
|
||||
}
|
||||
|
||||
private void setValue(String value) {
|
||||
String formattedKey = StringUtils.isBlank(key) ? "" : key;
|
||||
String formattedValue = StringUtils.isBlank(value) ? "" : value;
|
||||
label.setText(String.format("%s: %s", formattedKey, formattedValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setMessage(boolean visible, String message) {
|
||||
setValue(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setResults(String data) {
|
||||
setValue(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data for types pie chart.
|
||||
*/
|
||||
@ -129,9 +87,9 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param pieSlices The pie slices.
|
||||
* @param pieSlices The pie slices.
|
||||
* @param usefulContent True if this is useful content; false if there
|
||||
* is 0 mime type information.
|
||||
* is 0 mime type information.
|
||||
*/
|
||||
public TypesPieChartData(List<PieChartItem> pieSlices, boolean usefulContent) {
|
||||
this.pieSlices = pieSlices;
|
||||
@ -165,9 +123,9 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param label The label for this slice.
|
||||
* @param label The label for this slice.
|
||||
* @param mimeTypes The mime types associated with this slice.
|
||||
* @param color The color associated with this slice.
|
||||
* @param color The color associated with this slice.
|
||||
*/
|
||||
TypesPieCategory(String label, Set<String> mimeTypes, Color color) {
|
||||
this.label = label;
|
||||
@ -178,9 +136,9 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
/**
|
||||
* Constructor that accepts FileTypeCategory.
|
||||
*
|
||||
* @param label The label for this slice.
|
||||
* @param label The label for this slice.
|
||||
* @param mimeTypes The mime types associated with this slice.
|
||||
* @param color The color associated with this slice.
|
||||
* @param color The color associated with this slice.
|
||||
*/
|
||||
TypesPieCategory(String label, FileTypeCategory fileCategory, Color color) {
|
||||
this(label, fileCategory.getMediaTypes(), color);
|
||||
@ -278,8 +236,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
/**
|
||||
* Creates a new TypesPanel.
|
||||
*
|
||||
* @param mimeTypeData The service for mime types.
|
||||
* @param typeData The service for file types data.
|
||||
* @param mimeTypeData The service for mime types.
|
||||
* @param typeData The service for file types data.
|
||||
* @param containerData The service for container information.
|
||||
*/
|
||||
public TypesPanel(
|
||||
@ -358,7 +316,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
* Gets all the data for the file type pie chart.
|
||||
*
|
||||
* @param mimeTypeData The means of acquiring data.
|
||||
* @param dataSource The datasource.
|
||||
* @param dataSource The datasource.
|
||||
*
|
||||
* @return The pie chart items.
|
||||
*/
|
||||
|
@ -29,12 +29,11 @@ import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsSummary.TopProgramsResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
@ -227,35 +226,30 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
|
||||
|
||||
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
|
||||
private final TopProgramsSummary topProgramsData;
|
||||
|
||||
private final UserActivitySummary userActivityData;
|
||||
|
||||
/**
|
||||
* Creates a new UserActivityPanel.
|
||||
*/
|
||||
public UserActivityPanel() {
|
||||
this(new TopProgramsSummary(), new UserActivitySummary());
|
||||
this(new UserActivitySummary());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new UserActivityPanel.
|
||||
*
|
||||
* @param topProgramsData Class from which to obtain top programs data.
|
||||
* @param userActivityData Class from which to obtain remaining user
|
||||
* activity data.
|
||||
*/
|
||||
public UserActivityPanel(
|
||||
TopProgramsSummary topProgramsData,
|
||||
UserActivitySummary userActivityData) {
|
||||
|
||||
super(topProgramsData, userActivityData);
|
||||
|
||||
this.topProgramsData = topProgramsData;
|
||||
public UserActivityPanel(UserActivitySummary userActivityData) {
|
||||
super(userActivityData);
|
||||
this.userActivityData = userActivityData;
|
||||
|
||||
// set up data acquisition methods
|
||||
this.dataFetchComponents = Arrays.asList(
|
||||
// top programs query
|
||||
new DataFetchComponents<DataSource, List<TopProgramsResult>>(
|
||||
(dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
|
||||
(dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
|
||||
(result) -> {
|
||||
showResultWithModuleCheck(topProgramsTable, result,
|
||||
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
|
||||
@ -307,7 +301,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
* @return The underlying short folder name if one exists.
|
||||
*/
|
||||
private String getShortFolderName(String path, String appName) {
|
||||
return this.topProgramsData.getShortFolderName(path, appName);
|
||||
return this.userActivityData.getShortFolderName(path, appName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* 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.datasourcesummary.uiutils;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.swing.JLabel;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.jfree.chart.ChartFactory;
|
||||
import org.jfree.chart.ChartPanel;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
import org.jfree.chart.axis.ValueAxis;
|
||||
import org.jfree.chart.plot.CategoryPlot;
|
||||
import org.jfree.chart.plot.PlotOrientation;
|
||||
import org.jfree.chart.renderer.category.BarRenderer;
|
||||
import org.jfree.chart.renderer.category.StandardBarPainter;
|
||||
import org.jfree.data.category.DefaultCategoryDataset;
|
||||
|
||||
/**
|
||||
* A bar chart panel.
|
||||
*/
|
||||
public class BarChartPanel extends AbstractLoadableComponent<List<BarChartPanel.BarChartSeries>> {
|
||||
|
||||
/**
|
||||
* Represents a series in a bar chart where all items pertain to one
|
||||
* category.
|
||||
*/
|
||||
public static class BarChartSeries {
|
||||
|
||||
private final Comparable<?> key;
|
||||
private final Color color;
|
||||
private final List<BarChartItem> items;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param color The color for this series.
|
||||
* @param items The bars to be displayed for this series.
|
||||
*/
|
||||
public BarChartSeries(Comparable<?> key, Color color, List<BarChartItem> items) {
|
||||
this.key = key;
|
||||
this.color = color;
|
||||
this.items = (items == null) ? Collections.emptyList() : Collections.unmodifiableList(items);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The color for this series.
|
||||
*/
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The bars to be displayed for this series.
|
||||
*/
|
||||
public List<BarChartItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The key for this item.
|
||||
*/
|
||||
public Comparable<?> getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An individual bar to be displayed in the bar chart.
|
||||
*/
|
||||
public static class BarChartItem {
|
||||
|
||||
private final Comparable<?> key;
|
||||
private final double value;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param label The key for this bar. Also serves as the label using
|
||||
* toString().
|
||||
* @param value The value for this item.
|
||||
*/
|
||||
public BarChartItem(Comparable<?> key, double value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The key for this item.
|
||||
*/
|
||||
public Comparable<?> getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The value for this item.
|
||||
*/
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JFreeChart bar charts don't preserve the order of bars provided to the
|
||||
* chart, but instead uses the comparable nature to order items. This
|
||||
* provides order using a provided index as well as the value for the axis.
|
||||
*/
|
||||
public static class OrderedKey implements Comparable<OrderedKey> {
|
||||
|
||||
private final Object keyValue;
|
||||
private final int keyIndex;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param keyValue The value for the key to be displayed in the domain
|
||||
* axis.
|
||||
* @param keyIndex The index at which it will be displayed.
|
||||
*/
|
||||
public OrderedKey(Object keyValue, int keyIndex) {
|
||||
this.keyValue = keyValue;
|
||||
this.keyIndex = keyIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The value for the key to be displayed in the domain axis.
|
||||
*/
|
||||
Object getKeyValue() {
|
||||
return keyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The index at which it will be displayed.
|
||||
*/
|
||||
int getKeyIndex() {
|
||||
return keyIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(OrderedKey o) {
|
||||
// this will have a higher value than null.
|
||||
if (o == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// compare by index
|
||||
return Integer.compare(this.getKeyIndex(), o.getKeyIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final OrderedKey other = (OrderedKey) obj;
|
||||
if (this.keyIndex != other.keyIndex) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// use toString on the key.
|
||||
return this.getKeyValue() == null ? null : this.getKeyValue().toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Font DEFAULT_FONT = new JLabel().getFont();
|
||||
private static final Font DEFAULT_HEADER_FONT = new Font(DEFAULT_FONT.getName(), DEFAULT_FONT.getStyle(), (int) (DEFAULT_FONT.getSize() * 1.5));
|
||||
|
||||
private final ChartMessageOverlay overlay = new ChartMessageOverlay();
|
||||
private final DefaultCategoryDataset dataset = new DefaultCategoryDataset();
|
||||
private final JFreeChart chart;
|
||||
private final CategoryPlot plot;
|
||||
|
||||
/**
|
||||
* Main constructor assuming null values for all items.
|
||||
*/
|
||||
public BarChartPanel() {
|
||||
this(null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor for the pie chart.
|
||||
*
|
||||
* @param title The title for this pie chart.
|
||||
* @param categoryLabel The x-axis label.
|
||||
* @param valueLabel The y-axis label.
|
||||
*/
|
||||
public BarChartPanel(String title, String categoryLabel, String valueLabel) {
|
||||
this.chart = ChartFactory.createStackedBarChart(
|
||||
title,
|
||||
categoryLabel,
|
||||
valueLabel,
|
||||
dataset,
|
||||
PlotOrientation.VERTICAL,
|
||||
true, false, false);
|
||||
|
||||
// set style to match autopsy components
|
||||
chart.setBackgroundPaint(null);
|
||||
chart.getTitle().setFont(DEFAULT_HEADER_FONT);
|
||||
|
||||
this.plot = ((CategoryPlot) chart.getPlot());
|
||||
this.plot.getRenderer().setBaseItemLabelFont(DEFAULT_FONT);
|
||||
plot.setBackgroundPaint(null);
|
||||
plot.setOutlinePaint(null);
|
||||
|
||||
// hide y axis labels
|
||||
ValueAxis range = plot.getRangeAxis();
|
||||
range.setVisible(false);
|
||||
|
||||
// make sure x axis labels don't get cut off
|
||||
plot.getDomainAxis().setMaximumCategoryLabelWidthRatio(10);
|
||||
|
||||
((BarRenderer) plot.getRenderer()).setBarPainter(new StandardBarPainter());
|
||||
|
||||
// Create Panel
|
||||
ChartPanel panel = new ChartPanel(chart);
|
||||
panel.addOverlay(overlay);
|
||||
panel.setPopupMenu(null);
|
||||
|
||||
this.setLayout(new BorderLayout());
|
||||
this.add(panel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The title for this chart if one exists.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return (this.chart == null || this.chart.getTitle() == null)
|
||||
? null
|
||||
: this.chart.getTitle().getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title for this pie chart.
|
||||
*
|
||||
* @param title The title.
|
||||
*
|
||||
* @return As a utility, returns this.
|
||||
*/
|
||||
public BarChartPanel setTitle(String title) {
|
||||
this.chart.getTitle().setText(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setMessage(boolean visible, String message) {
|
||||
this.overlay.setVisible(visible);
|
||||
this.overlay.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setResults(List<BarChartPanel.BarChartSeries> data) {
|
||||
this.dataset.clear();
|
||||
|
||||
if (CollectionUtils.isNotEmpty(data)) {
|
||||
for (int s = 0; s < data.size(); s++) {
|
||||
BarChartPanel.BarChartSeries series = data.get(s);
|
||||
if (series != null && CollectionUtils.isNotEmpty(series.getItems())) {
|
||||
if (series.getColor() != null) {
|
||||
this.plot.getRenderer().setSeriesPaint(s, series.getColor());
|
||||
}
|
||||
|
||||
for (int i = 0; i < series.getItems().size(); i++) {
|
||||
BarChartItem bar = series.getItems().get(i);
|
||||
this.dataset.setValue(bar.getValue(), series.getKey(), bar.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.datasourcesummary.uiutils;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import org.jfree.chart.ChartPanel;
|
||||
import org.jfree.chart.panel.AbstractOverlay;
|
||||
import org.jfree.chart.panel.Overlay;
|
||||
|
||||
/**
|
||||
* A JFreeChart message overlay that can show a message for the purposes of the
|
||||
* LoadableComponent.
|
||||
*/
|
||||
class ChartMessageOverlay extends AbstractOverlay implements Overlay {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final BaseMessageOverlay overlay = new BaseMessageOverlay();
|
||||
|
||||
// multiply this value by the smaller dimension (height or width) of the component
|
||||
// to determine width of text to be displayed.
|
||||
private static final double MESSAGE_WIDTH_FACTOR = .6;
|
||||
|
||||
/**
|
||||
* Sets this layer visible when painted. In order to be shown in UI, this
|
||||
* component needs to be repainted.
|
||||
*
|
||||
* @param visible Whether or not it is visible.
|
||||
*/
|
||||
void setVisible(boolean visible) {
|
||||
overlay.setVisible(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message to be displayed in the child jlabel.
|
||||
*
|
||||
* @param message The message to be displayed.
|
||||
*/
|
||||
void setMessage(String message) {
|
||||
overlay.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintOverlay(Graphics2D gd, ChartPanel cp) {
|
||||
int labelWidth = (int) (Math.min(cp.getWidth(), cp.getHeight()) * MESSAGE_WIDTH_FACTOR);
|
||||
overlay.paintOverlay(gd, cp.getWidth(), cp.getHeight(), labelWidth);
|
||||
}
|
||||
}
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* The result of a loading process.
|
||||
*/
|
||||
@ -30,6 +32,29 @@ public final class DataFetchResult<R> {
|
||||
SUCCESS, ERROR
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method that, given an input data fetch result, creates an error
|
||||
* result if the original is an error. Otherwise, uses the getSubResult
|
||||
* function on the underlying data to create a new DataFetchResult.
|
||||
*
|
||||
* @param inputResult The input result.
|
||||
* @param getSubComponent The means of getting the data given the original
|
||||
* data.
|
||||
*
|
||||
* @return The new result with the error of the original or the processed
|
||||
* data.
|
||||
*/
|
||||
public static <I, O> DataFetchResult<O> getSubResult(DataFetchResult<I> inputResult, Function<I, O> getSubResult) {
|
||||
if (inputResult == null) {
|
||||
return null;
|
||||
} else if (inputResult.getResultType() == ResultType.SUCCESS) {
|
||||
O innerData = (inputResult.getData() == null) ? null : getSubResult.apply(inputResult.getData());
|
||||
return DataFetchResult.getSuccessResult(innerData);
|
||||
} else {
|
||||
return DataFetchResult.getErrorResult(inputResult.getException());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DataFetchResult of loaded data including the data.
|
||||
*
|
||||
@ -59,9 +84,8 @@ public final class DataFetchResult<R> {
|
||||
/**
|
||||
* Main constructor for the DataLoadingResult.
|
||||
*
|
||||
* @param state The state of the result.
|
||||
* @param data If the result is SUCCESS, the data related to this
|
||||
* result.
|
||||
* @param state The state of the result.
|
||||
* @param data If the result is SUCCESS, the data related to this result.
|
||||
* @param exception If the result is ERROR, the related exception.
|
||||
*/
|
||||
private DataFetchResult(ResultType state, R data, Throwable exception) {
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.datasourcesummary.uiutils;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import javax.swing.JLabel;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* A label that allows for displaying loading messages and can be used with a
|
||||
* DataFetchResult. Text displays as "<key>:<value | message>".
|
||||
*/
|
||||
public class LoadableLabel extends AbstractLoadableComponent<String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final JLabel label = new JLabel();
|
||||
private final String key;
|
||||
|
||||
/**
|
||||
* Main constructor for the label.
|
||||
*
|
||||
* @param key The key to be displayed.
|
||||
*/
|
||||
public LoadableLabel(String key) {
|
||||
this.key = key;
|
||||
setLayout(new BorderLayout());
|
||||
add(label, BorderLayout.CENTER);
|
||||
this.showResults(null);
|
||||
}
|
||||
|
||||
private void setValue(String value) {
|
||||
String formattedKey = StringUtils.isBlank(key) ? "" : key;
|
||||
String formattedValue = StringUtils.isBlank(value) ? "" : value;
|
||||
label.setText(String.format("%s: %s", formattedKey, formattedValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setMessage(boolean visible, String message) {
|
||||
setValue(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setResults(String data) {
|
||||
setValue(data);
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
import javax.swing.JLabel;
|
||||
@ -30,8 +29,6 @@ import org.jfree.chart.ChartPanel;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
import org.jfree.chart.labels.PieSectionLabelGenerator;
|
||||
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
|
||||
import org.jfree.chart.panel.AbstractOverlay;
|
||||
import org.jfree.chart.panel.Overlay;
|
||||
import org.jfree.chart.plot.PiePlot;
|
||||
import org.jfree.data.general.DefaultPieDataset;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
@ -59,7 +56,7 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
|
||||
* @param label The label for this pie slice.
|
||||
* @param value The value for this item.
|
||||
* @param color The color for the pie slice. Can be null for
|
||||
* auto-determined.
|
||||
* auto-determined.
|
||||
*/
|
||||
public PieChartItem(String label, double value, Color color) {
|
||||
this.label = label;
|
||||
@ -89,46 +86,6 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A JFreeChart message overlay that can show a message for the purposes of
|
||||
* the LoadableComponent.
|
||||
*/
|
||||
private static class MessageOverlay extends AbstractOverlay implements Overlay {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final BaseMessageOverlay overlay = new BaseMessageOverlay();
|
||||
|
||||
// multiply this value by the smaller dimension (height or width) of the component
|
||||
// to determine width of text to be displayed.
|
||||
private static final double MESSAGE_WIDTH_FACTOR = .6;
|
||||
|
||||
/**
|
||||
* Sets this layer visible when painted. In order to be shown in UI,
|
||||
* this component needs to be repainted.
|
||||
*
|
||||
* @param visible Whether or not it is visible.
|
||||
*/
|
||||
void setVisible(boolean visible) {
|
||||
overlay.setVisible(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message to be displayed in the child jlabel.
|
||||
*
|
||||
* @param message The message to be displayed.
|
||||
*/
|
||||
void setMessage(String message) {
|
||||
overlay.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintOverlay(Graphics2D gd, ChartPanel cp) {
|
||||
int labelWidth = (int) (Math.min(cp.getWidth(), cp.getHeight()) * MESSAGE_WIDTH_FACTOR);
|
||||
overlay.paintOverlay(gd, cp.getWidth(), cp.getHeight(), labelWidth);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Font DEFAULT_FONT = new JLabel().getFont();
|
||||
@ -146,7 +103,7 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
|
||||
= new StandardPieSectionLabelGenerator(
|
||||
"{0}: {1} ({2})", new DecimalFormat("#,###"), new DecimalFormat("0.0%"));
|
||||
|
||||
private final MessageOverlay overlay = new MessageOverlay();
|
||||
private final ChartMessageOverlay overlay = new ChartMessageOverlay();
|
||||
private final DefaultPieDataset dataset = new DefaultPieDataset();
|
||||
private final JFreeChart chart;
|
||||
private final PiePlot plot;
|
||||
@ -242,7 +199,7 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
|
||||
/**
|
||||
* Shows a message on top of data.
|
||||
*
|
||||
* @param data The data.
|
||||
* @param data The data.
|
||||
* @param message The message.
|
||||
*/
|
||||
public synchronized void showDataWithMessage(List<PieChartPanel.PieChartItem> data, String message) {
|
||||
|
@ -19,12 +19,14 @@
|
||||
package org.sleuthkit.autopsy.discovery.search;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
|
||||
import org.sleuthkit.autopsy.discovery.search.SearchData.Type;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
|
||||
/**
|
||||
* Class to handle event bus and events for discovery tool.
|
||||
@ -88,13 +90,13 @@ public final class DiscoveryEventUtils {
|
||||
//no arg constructor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Event to signal that any background tasks currently running should
|
||||
* be cancelled.
|
||||
* Event to signal that any background tasks currently running should be
|
||||
* cancelled.
|
||||
*/
|
||||
public static final class CancelBackgroundTasksEvent {
|
||||
|
||||
|
||||
public CancelBackgroundTasksEvent() {
|
||||
//no-arg constructor
|
||||
}
|
||||
@ -124,6 +126,30 @@ public final class DiscoveryEventUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event to signal that the list should be populated.
|
||||
*/
|
||||
public static final class PopulateDomainTabsEvent {
|
||||
|
||||
private final String domain;
|
||||
|
||||
/**
|
||||
* Construct a new PopulateDomainTabsEvent.
|
||||
*/
|
||||
public PopulateDomainTabsEvent(String domain) {
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the domain for the details area.
|
||||
*
|
||||
* @return The the domain for the details area.
|
||||
*/
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event to signal the completion of a search being performed.
|
||||
*/
|
||||
@ -203,6 +229,47 @@ public final class DiscoveryEventUtils {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Event to signal the completion of a search being performed.
|
||||
*/
|
||||
public static final class ArtifactSearchResultEvent {
|
||||
|
||||
private final List<BlackboardArtifact> listOfArtifacts = new ArrayList<>();
|
||||
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
|
||||
|
||||
/**
|
||||
* Construct a new ArtifactSearchResultEvent with a list of specified
|
||||
* artifacts and an artifact type.
|
||||
*
|
||||
* @param artifactType The type of artifacts in the list.
|
||||
* @param listOfArtifacts The list of artifacts retrieved.
|
||||
*/
|
||||
public ArtifactSearchResultEvent(BlackboardArtifact.ARTIFACT_TYPE artifactType, List<BlackboardArtifact> listOfArtifacts) {
|
||||
if (listOfArtifacts != null) {
|
||||
this.listOfArtifacts.addAll(listOfArtifacts);
|
||||
}
|
||||
this.artifactType = artifactType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of artifacts included in the event.
|
||||
*
|
||||
* @return The list of artifacts retrieved.
|
||||
*/
|
||||
public List<BlackboardArtifact> getListOfArtifacts() {
|
||||
return Collections.unmodifiableList(listOfArtifacts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of BlackboardArtifact type of which exist in the list.
|
||||
*
|
||||
* @return The BlackboardArtifact type of which exist in the list.
|
||||
*/
|
||||
public BlackboardArtifact.ARTIFACT_TYPE getArtifactType() {
|
||||
return artifactType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event to signal the completion of page retrieval and include the page
|
||||
* contents.
|
||||
|
@ -33,6 +33,7 @@ import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -217,6 +218,15 @@ public class SearchFiltering {
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of artifact types specified by the filter.
|
||||
*
|
||||
* @return The list of artifact types specified by the filter.
|
||||
*/
|
||||
public List<ARTIFACT_TYPE> getTypes() {
|
||||
return Collections.unmodifiableList(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWhereClause() {
|
||||
StringJoiner joiner = new StringJoiner(",");
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* 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.discovery.ui;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
|
||||
/**
|
||||
* Class for ensuring all ArtifactDetailsPanels have a setArtifact method.
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractArtifactDetailsPanel extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Called to display the contents of the given artifact.
|
||||
*
|
||||
* @param artifact the artifact to display.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
abstract public void setArtifact(BlackboardArtifact artifact);
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ import javax.swing.JCheckBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
|
||||
/**
|
||||
* Abstract class extending JPanel for filter controls.
|
||||
@ -41,6 +42,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
|
||||
* selected, null to indicate leaving selected items
|
||||
* unchanged or that there are no items to select.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
abstract void configurePanel(boolean selected, int[] indicesSelected);
|
||||
|
||||
/**
|
||||
@ -48,6 +50,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
|
||||
*
|
||||
* @return The JCheckBox which enables and disables this filter.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
abstract JCheckBox getCheckbox();
|
||||
|
||||
/**
|
||||
@ -57,6 +60,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
|
||||
* @return The JList which contains the values available for selection for
|
||||
* this filter.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
abstract JList<?> getList();
|
||||
|
||||
/**
|
||||
@ -65,6 +69,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
|
||||
*
|
||||
* @return The JLabel to display under the JCheckBox.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
abstract JLabel getAdditionalLabel();
|
||||
|
||||
/**
|
||||
@ -73,6 +78,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
|
||||
* @return If the settings are invalid returns the error that has occurred,
|
||||
* otherwise returns empty string.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
abstract String checkForError();
|
||||
|
||||
/**
|
||||
@ -82,6 +88,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
|
||||
* @param actionlistener The listener for the checkbox selection events.
|
||||
* @param listListener The listener for the list selection events.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void addListeners(ActionListener actionListener, ListSelectionListener listListener) {
|
||||
if (getCheckbox() != null) {
|
||||
getCheckbox().addActionListener(actionListener);
|
||||
@ -97,11 +104,13 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
|
||||
* @return The AbstractFilter for the selected settings, null if the
|
||||
* settings are not in use.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
abstract AbstractFilter getFilter();
|
||||
|
||||
/**
|
||||
* Remove listeners from the checkbox and the list if they exist.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void removeListeners() {
|
||||
if (getCheckbox() != null) {
|
||||
for (ActionListener listener : getCheckbox().getActionListeners()) {
|
||||
|
@ -32,6 +32,7 @@ import javax.swing.JSplitPane;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes.GroupingAttributeType;
|
||||
import org.sleuthkit.autopsy.discovery.search.Group;
|
||||
import org.sleuthkit.autopsy.discovery.search.ResultsSorter.SortingMethod;
|
||||
@ -65,6 +66,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
/**
|
||||
* Setup necessary for implementations of this abstract class.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
AbstractFiltersPanel() {
|
||||
firstColumnPanel.setLayout(new GridBagLayout());
|
||||
secondColumnPanel.setLayout(new GridBagLayout());
|
||||
@ -75,6 +77,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
*
|
||||
* @return The type of results this panel filters.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
abstract SearchData.Type getType();
|
||||
|
||||
/**
|
||||
@ -88,7 +91,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
* list, null if none are selected.
|
||||
* @param column The column to add the DiscoveryFilterPanel to.
|
||||
*/
|
||||
final synchronized void addFilter(AbstractDiscoveryFilterPanel filterPanel, boolean isSelected, int[] indicesSelected, int column) {
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
final void addFilter(AbstractDiscoveryFilterPanel filterPanel, boolean isSelected, int[] indicesSelected, int column) {
|
||||
if (!isInitialized) {
|
||||
constraints.gridy = 0;
|
||||
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
|
||||
@ -132,6 +136,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
*
|
||||
* @param splitPane The JSplitPane which the columns are added to.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
final void addPanelsToScrollPane(JSplitPane splitPane) {
|
||||
splitPane.setLeftComponent(firstColumnPanel);
|
||||
splitPane.setRightComponent(secondColumnPanel);
|
||||
@ -142,7 +147,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
/**
|
||||
* Clear the filters from the panel
|
||||
*/
|
||||
final synchronized void clearFilters() {
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
final void clearFilters() {
|
||||
for (AbstractDiscoveryFilterPanel filterPanel : filters) {
|
||||
filterPanel.removeListeners();
|
||||
}
|
||||
@ -159,6 +165,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
* column.
|
||||
* @param columnIndex The column to add the Component to.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void addToGridBagLayout(Component componentToAdd, Component additionalComponentToAdd, int columnIndex) {
|
||||
addToColumn(componentToAdd, columnIndex);
|
||||
if (additionalComponentToAdd != null) {
|
||||
@ -174,6 +181,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
* @param component The Component to add.
|
||||
* @param columnNumber The column to add the Component to.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void addToColumn(Component component, int columnNumber) {
|
||||
if (columnNumber == 0) {
|
||||
firstColumnPanel.add(component, constraints);
|
||||
@ -186,7 +194,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
* Check if the fields are valid, and fire a property change event to
|
||||
* indicate any errors.
|
||||
*/
|
||||
synchronized void validateFields() {
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void validateFields() {
|
||||
String errorString = null;
|
||||
for (AbstractDiscoveryFilterPanel filterPanel : filters) {
|
||||
errorString = filterPanel.checkForError();
|
||||
@ -197,6 +206,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
firePropertyChange("FilterError", null, errorString);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
validateFields();
|
||||
@ -209,6 +219,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
*
|
||||
* @return True if the ObjectsDetectedFilter is supported, false otherwise.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
boolean isObjectsFilterSupported() {
|
||||
for (AbstractDiscoveryFilterPanel filter : filters) {
|
||||
if (filter instanceof ObjectDetectedFilterPanel) {
|
||||
@ -223,6 +234,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
*
|
||||
* @return True if the HashSetFilter is supported, false otherwise.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
boolean isHashSetFilterSupported() {
|
||||
for (AbstractDiscoveryFilterPanel filter : filters) {
|
||||
if (filter instanceof HashSetFilterPanel) {
|
||||
@ -237,6 +249,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
*
|
||||
* @return True if the InterestingItemsFilter is supported, false otherwise.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
boolean isInterestingItemsFilterSupported() {
|
||||
for (AbstractDiscoveryFilterPanel filter : filters) {
|
||||
if (filter instanceof InterestingItemsFilterPanel) {
|
||||
@ -251,8 +264,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
*
|
||||
* @return The list of filters selected by the user.
|
||||
*/
|
||||
synchronized List<AbstractFilter> getFilters() {
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
List<AbstractFilter> getFilters() {
|
||||
List<AbstractFilter> filtersToUse = new ArrayList<>();
|
||||
if (getType() != SearchData.Type.DOMAIN) { //Domain type does not have a file type
|
||||
filtersToUse.add(new SearchFiltering.FileTypeFilter(getType()));
|
||||
@ -268,6 +281,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
return filtersToUse;
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent evt) {
|
||||
if (!evt.getValueIsAdjusting()) {
|
||||
@ -282,6 +296,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
*
|
||||
* @return The most recently used sorting method.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
SortingMethod getLastSortingMethod() {
|
||||
return lastSortingMethod;
|
||||
}
|
||||
@ -291,6 +306,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
*
|
||||
* @param lastSortingMethod The most recently used sorting method.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
final void setLastSortingMethod(SortingMethod lastSortingMethod) {
|
||||
this.lastSortingMethod = lastSortingMethod;
|
||||
}
|
||||
@ -300,6 +316,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
*
|
||||
* @return The most recently used grouping attribute.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
GroupingAttributeType getLastGroupingAttributeType() {
|
||||
return lastGroupingAttributeType;
|
||||
}
|
||||
@ -310,6 +327,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
* @param lastGroupingAttributeType The most recently used grouping
|
||||
* attribute.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
final void setLastGroupingAttributeType(GroupingAttributeType lastGroupingAttributeType) {
|
||||
this.lastGroupingAttributeType = lastGroupingAttributeType;
|
||||
}
|
||||
@ -319,6 +337,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
*
|
||||
* @return The most recently used group sorting algorithm.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
Group.GroupSortingAlgorithm getLastGroupSortingAlg() {
|
||||
return lastGroupSortingAlg;
|
||||
}
|
||||
@ -329,6 +348,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
|
||||
* @param lastGroupSortingAlg The most recently used group sorting
|
||||
* algorithm.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
final void setLastGroupSortingAlg(Group.GroupSortingAlgorithm lastGroupSortingAlg) {
|
||||
this.lastGroupSortingAlg = lastGroupSortingAlg;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import javax.swing.JCheckBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.discovery.search.SearchData;
|
||||
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -40,6 +41,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
/**
|
||||
* Creates new form ArtifactTypeFilterPanel
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
ArtifactTypeFilterPanel() {
|
||||
initComponents();
|
||||
setUpArtifactTypeFilter();
|
||||
@ -49,6 +51,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
/**
|
||||
* Initialize the data source filter.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void setUpArtifactTypeFilter() {
|
||||
int count = 0;
|
||||
DefaultListModel<ArtifactTypeItem> artifactTypeModel = (DefaultListModel<ArtifactTypeItem>) artifactList.getModel();
|
||||
@ -104,6 +107,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
artifactList.setEnabled(artifactTypeCheckbox.isSelected());
|
||||
}//GEN-LAST:event_artifactTypeCheckboxActionPerformed
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
void configurePanel(boolean selected, int[] indicesSelected) {
|
||||
artifactTypeCheckbox.setSelected(selected);
|
||||
@ -119,11 +123,13 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
}
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
JCheckBox getCheckbox() {
|
||||
return artifactTypeCheckbox;
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
JList<?> getList() {
|
||||
return artifactList;
|
||||
@ -134,6 +140,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"ArtifactTypeFilterPanel.selectionNeeded.text=At least one Result type must be selected."})
|
||||
@Override
|
||||
String checkForError() {
|
||||
@ -143,6 +150,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
return "";
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
AbstractFilter getFilter() {
|
||||
if (artifactTypeCheckbox.isSelected() && !artifactList.getSelectedValuesList().isEmpty()) {
|
||||
|
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[300, 0]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane1" pref="400" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane1" alignment="0" pref="607" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="null"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 0]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 0]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="jTable1">
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="tableModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
|
||||
<JTableSelectionModel selectionMode="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* 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.discovery.ui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Panel to display list of artifacts for selected domain.
|
||||
*
|
||||
*/
|
||||
class ArtifactsListPanel extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final DomainArtifactTableModel tableModel;
|
||||
private static final Logger logger = Logger.getLogger(ArtifactsListPanel.class.getName());
|
||||
|
||||
/**
|
||||
* Creates new form ArtifactsListPanel.
|
||||
*
|
||||
* @param artifactType The type of artifact displayed in this table.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
ArtifactsListPanel(BlackboardArtifact.ARTIFACT_TYPE artifactType) {
|
||||
tableModel = new DomainArtifactTableModel(artifactType);
|
||||
initComponents();
|
||||
jTable1.getRowSorter().toggleSortOrder(0);
|
||||
jTable1.getRowSorter().toggleSortOrder(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener to the table of artifacts to perform actions when an
|
||||
* artifact is selected.
|
||||
*
|
||||
* @param listener The listener to add to the table of artifacts.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void addSelectionListener(ListSelectionListener listener) {
|
||||
jTable1.getSelectionModel().addListSelectionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener from the table of artifacts.
|
||||
*
|
||||
* @param listener The listener to remove from the table of artifacts.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void removeListSelectionListener(ListSelectionListener listener) {
|
||||
jTable1.getSelectionModel().removeListSelectionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* The artifact which is currently selected, null if no artifact is
|
||||
* selected.
|
||||
*
|
||||
* @return The currently selected BlackboardArtifact or null if none is
|
||||
* selected.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
BlackboardArtifact getSelectedArtifact() {
|
||||
int selectedIndex = jTable1.getSelectionModel().getLeadSelectionIndex();
|
||||
if (selectedIndex < jTable1.getSelectionModel().getMinSelectionIndex() || jTable1.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > jTable1.getSelectionModel().getMaxSelectionIndex()) {
|
||||
return null;
|
||||
}
|
||||
return tableModel.getArtifactByRow(jTable1.convertRowIndexToModel(selectedIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the list of artifacts is empty.
|
||||
*
|
||||
* @return true if the list of artifacts is empty, false if there are
|
||||
* artifacts.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
boolean isEmpty() {
|
||||
return tableModel.getRowCount() <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the first available artifact in the list if it is not empty to
|
||||
* populate the panel to the right.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void selectFirst() {
|
||||
if (!isEmpty()) {
|
||||
jTable1.setRowSelectionInterval(0, 0);
|
||||
} else {
|
||||
jTable1.clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the specified list of artifacts to the list of artifacts which should
|
||||
* be displayed.
|
||||
*
|
||||
* @param artifactList The list of artifacts to display.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void addArtifacts(List<BlackboardArtifact> artifactList) {
|
||||
tableModel.setContents(artifactList);
|
||||
jTable1.validate();
|
||||
jTable1.repaint();
|
||||
tableModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all artifacts from the list of artifacts displayed.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void clearArtifacts() {
|
||||
tableModel.setContents(new ArrayList<>());
|
||||
tableModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
|
||||
jTable1 = new javax.swing.JTable();
|
||||
|
||||
setOpaque(false);
|
||||
setPreferredSize(new java.awt.Dimension(300, 0));
|
||||
|
||||
jScrollPane1.setBorder(null);
|
||||
jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0));
|
||||
jScrollPane1.setPreferredSize(new java.awt.Dimension(0, 0));
|
||||
|
||||
jTable1.setAutoCreateRowSorter(true);
|
||||
jTable1.setModel(tableModel);
|
||||
jTable1.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
jScrollPane1.setViewportView(jTable1);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 607, Short.MAX_VALUE)
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
/**
|
||||
* Table model which allows the artifact table in this panel to mimic a list
|
||||
* of artifacts.
|
||||
*/
|
||||
private class DomainArtifactTableModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final List<BlackboardArtifact> artifactList = new ArrayList<>();
|
||||
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
|
||||
|
||||
/**
|
||||
* Construct a new DomainArtifactTableModel.
|
||||
*
|
||||
* @param artifactType The type of artifact displayed in this table.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
DomainArtifactTableModel(BlackboardArtifact.ARTIFACT_TYPE artifactType) {
|
||||
this.artifactType = artifactType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of artifacts which should be represented by this table
|
||||
* model.
|
||||
*
|
||||
* @param artifacts The list of BlackboardArtifacts to represent.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void setContents(List<BlackboardArtifact> artifacts) {
|
||||
jTable1.clearSelection();
|
||||
artifactList.clear();
|
||||
artifactList.addAll(artifacts);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return artifactList.size();
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE) {
|
||||
return 3;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the BlackboardArtifact at the specified row.
|
||||
*
|
||||
* @param rowIndex The row the artifact to return is at.
|
||||
*
|
||||
* @return The BlackboardArtifact at the specified row.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
BlackboardArtifact getArtifactByRow(int rowIndex) {
|
||||
return artifactList.get(rowIndex);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"ArtifactsListPanel.value.noValue=No value available."})
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (columnIndex < 2 || artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE) {
|
||||
try {
|
||||
for (BlackboardAttribute bba : getArtifactByRow(rowIndex).getAttributes()) {
|
||||
if (!StringUtils.isBlank(bba.getDisplayString())) {
|
||||
String stringFromAttribute = getStringForColumn(bba, columnIndex);
|
||||
if (!StringUtils.isBlank(stringFromAttribute)) {
|
||||
return stringFromAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
return getFallbackValue(rowIndex, columnIndex);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error getting attributes for artifact " + getArtifactByRow(rowIndex).getArtifactID(), ex);
|
||||
}
|
||||
}
|
||||
return Bundle.ArtifactsListPanel_value_noValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate String for the specified column from the
|
||||
* BlackboardAttribute.
|
||||
*
|
||||
* @param bba The BlackboardAttribute which may contain a value.
|
||||
* @param columnIndex The column the value will be displayed in.
|
||||
*
|
||||
* @return The value from the specified attribute which should be
|
||||
* displayed in the specified column, null if the specified
|
||||
* attribute does not contain a value for that column.
|
||||
*
|
||||
* @throws TskCoreException When unable to get abstract files based on
|
||||
* the TSK_PATH_ID.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private String getStringForColumn(BlackboardAttribute bba, int columnIndex) throws TskCoreException {
|
||||
if (columnIndex == 0 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID()) {
|
||||
return bba.getDisplayString();
|
||||
} else if (columnIndex == 1) {
|
||||
if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD || artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE) {
|
||||
if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) {
|
||||
return Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(bba.getValueLong()).getName();
|
||||
} else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
|
||||
return FilenameUtils.getName(bba.getDisplayString());
|
||||
}
|
||||
} else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE.getTypeID()) {
|
||||
return bba.getDisplayString();
|
||||
}
|
||||
} else if (columnIndex == 2 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) {
|
||||
return Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(bba.getValueLong()).getMIMEType();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private helper method to use when the value we want for either date
|
||||
* or title is not available.
|
||||
*
|
||||
*
|
||||
* @param rowIndex The row the artifact to return is at.
|
||||
* @param columnIndex The column index the attribute will be displayed
|
||||
* at.
|
||||
*
|
||||
* @return A string that can be used in place of the accessed date time
|
||||
* attribute title when they are not available.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private String getFallbackValue(int rowIndex, int columnIndex) throws TskCoreException {
|
||||
for (BlackboardAttribute bba : getArtifactByRow(rowIndex).getAttributes()) {
|
||||
if (columnIndex == 0 && bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME") && !StringUtils.isBlank(bba.getDisplayString())) {
|
||||
return bba.getDisplayString();
|
||||
} else if (columnIndex == 1 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.getTypeID() && !StringUtils.isBlank(bba.getDisplayString())) {
|
||||
return bba.getDisplayString();
|
||||
} else if (columnIndex == 1 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID() && !StringUtils.isBlank(bba.getDisplayString())) {
|
||||
return bba.getDisplayString();
|
||||
} else if (columnIndex == 1 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID() && !StringUtils.isBlank(bba.getDisplayString())) {
|
||||
return bba.getDisplayString();
|
||||
}
|
||||
}
|
||||
return Bundle.ArtifactsListPanel_value_noValue();
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"ArtifactsListPanel.titleColumn.name=Title",
|
||||
"ArtifactsListPanel.fileNameColumn.name=Name",
|
||||
"ArtifactsListPanel.dateColumn.name=Date/Time",
|
||||
"ArtifactsListPanel.mimeTypeColumn.name=MIME Type"})
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
switch (column) {
|
||||
case 0:
|
||||
return Bundle.ArtifactsListPanel_dateColumn_name();
|
||||
case 1:
|
||||
if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE || artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD) {
|
||||
return Bundle.ArtifactsListPanel_fileNameColumn_name();
|
||||
} else {
|
||||
return Bundle.ArtifactsListPanel_titleColumn_name();
|
||||
}
|
||||
case 2:
|
||||
return Bundle.ArtifactsListPanel_mimeTypeColumn_name();
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JTable jTable1;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* 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.discovery.ui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
|
||||
import org.sleuthkit.autopsy.discovery.search.DomainSearch;
|
||||
import org.sleuthkit.autopsy.discovery.search.DomainSearchArtifactsRequest;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
|
||||
/**
|
||||
* SwingWorker to retrieve a list of artifacts for a specified type and domain.
|
||||
*/
|
||||
class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
|
||||
|
||||
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
|
||||
private final static Logger logger = Logger.getLogger(ArtifactsWorker.class.getName());
|
||||
private final String domain;
|
||||
|
||||
/**
|
||||
* Construct a new ArtifactsWorker.
|
||||
*
|
||||
* @param artifactType The type of artifact being retrieved.
|
||||
* @param domain The domain the artifacts should have as an attribute.
|
||||
*/
|
||||
ArtifactsWorker(BlackboardArtifact.ARTIFACT_TYPE artifactType, String domain) {
|
||||
this.artifactType = artifactType;
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlackboardArtifact> doInBackground() throws Exception {
|
||||
if (artifactType != null && !StringUtils.isBlank(domain)) {
|
||||
DomainSearch domainSearch = new DomainSearch();
|
||||
return domainSearch.getArtifacts(new DomainSearchArtifactsRequest(Case.getCurrentCase().getSleuthkitCase(), domain, artifactType));
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
List<BlackboardArtifact> listOfArtifacts = new ArrayList<>();
|
||||
if (!isCancelled()) {
|
||||
try {
|
||||
listOfArtifacts.addAll(get());
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while trying to get list of artifacts for Domain details for artifact type: "
|
||||
+ artifactType.getDisplayName() + " and domain: " + domain, ex);
|
||||
} catch (CancellationException ignored) {
|
||||
//Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging
|
||||
}
|
||||
}
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.ArtifactSearchResultEvent(artifactType, listOfArtifacts));
|
||||
}
|
||||
}
|
@ -51,10 +51,12 @@ HashSetFilterPanel.hashSetCheckbox.text=Hash Set:
|
||||
PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences:
|
||||
DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show
|
||||
ObjectDetectedFilterPanel.text=Object Detected:
|
||||
DetailsPanel.instancesList.border.title=Instances
|
||||
DateFilterPanel.mostRecentRadioButton.text=Only last:
|
||||
DateFilterPanel.dateFilterCheckBox.text=Date Filter:
|
||||
DomainSummaryPanel.activityLabel.text=
|
||||
DomainSummaryPanel.pagesLabel.text=
|
||||
DomainSummaryPanel.filesDownloadedLabel.text=
|
||||
DomainSummaryPanel.totalVisitsLabel.text=
|
||||
FileDetailsPanel.instancesList.border.title=Instances
|
||||
CookieDetailsPanel.jLabel1.text=Artifact:
|
||||
CookieDetailsPanel.jLabel2.text=
|
||||
|
@ -1,3 +1,8 @@
|
||||
ArtifactsListPanel.dateColumn.name=Date/Time
|
||||
ArtifactsListPanel.fileNameColumn.name=Name
|
||||
ArtifactsListPanel.mimeTypeColumn.name=MIME Type
|
||||
ArtifactsListPanel.titleColumn.name=Title
|
||||
ArtifactsListPanel.value.noValue=No value available.
|
||||
ArtifactTypeFilterPanel.selectionNeeded.text=At least one Result type must be selected.
|
||||
CTL_OpenDiscoveryAction=Discovery
|
||||
DataSourceFilterPanel.error.text=At least one data source must be selected.
|
||||
@ -126,13 +131,15 @@ HashSetFilterPanel.hashSetCheckbox.text=Hash Set:
|
||||
PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences:
|
||||
DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show
|
||||
ObjectDetectedFilterPanel.text=Object Detected:
|
||||
DetailsPanel.instancesList.border.title=Instances
|
||||
DateFilterPanel.mostRecentRadioButton.text=Only last:
|
||||
DateFilterPanel.dateFilterCheckBox.text=Date Filter:
|
||||
DomainSummaryPanel.activityLabel.text=
|
||||
DomainSummaryPanel.pagesLabel.text=
|
||||
DomainSummaryPanel.filesDownloadedLabel.text=
|
||||
DomainSummaryPanel.totalVisitsLabel.text=
|
||||
FileDetailsPanel.instancesList.border.title=Instances
|
||||
CookieDetailsPanel.jLabel1.text=Artifact:
|
||||
CookieDetailsPanel.jLabel2.text=
|
||||
VideoThumbnailPanel.bytes.text=bytes
|
||||
VideoThumbnailPanel.deleted.text=All instances of file are deleted.
|
||||
VideoThumbnailPanel.gigaBytes.text=GB
|
||||
|
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
</Form>
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* 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.discovery.ui;
|
||||
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
|
||||
/**
|
||||
* Details panel for displaying the collection of content viewers.
|
||||
*/
|
||||
final class ContentViewerDetailsPanel extends AbstractArtifactDetailsPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final DataContentPanel contentViewer = DataContentPanel.createInstance();
|
||||
|
||||
/**
|
||||
* Creates new form ContentViewerDetailsPanel
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
ContentViewerDetailsPanel() {
|
||||
initComponents();
|
||||
add(contentViewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public void setArtifact(BlackboardArtifact artifact) {
|
||||
Node node = Node.EMPTY;
|
||||
if (artifact != null) {
|
||||
node = new BlackboardArtifactNode(artifact);
|
||||
}
|
||||
contentViewer.setNode(node);
|
||||
}
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -30,6 +30,7 @@ import javax.swing.JList;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -45,6 +46,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
/**
|
||||
* Creates new form DataSourceFilterPanel.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
DataSourceFilterPanel() {
|
||||
initComponents();
|
||||
setUpDataSourceFilter();
|
||||
@ -109,6 +111,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
private javax.swing.JScrollPane dataSourceScrollPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
void configurePanel(boolean selected, int[] indicesSelected) {
|
||||
dataSourceCheckbox.setSelected(selected);
|
||||
@ -124,6 +127,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
}
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
JCheckBox getCheckbox() {
|
||||
return dataSourceCheckbox;
|
||||
@ -137,6 +141,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
/**
|
||||
* Initialize the data source filter.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void setUpDataSourceFilter() {
|
||||
int count = 0;
|
||||
try {
|
||||
@ -156,6 +161,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
}
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
JList<?> getList() {
|
||||
return dataSourceList;
|
||||
@ -193,6 +199,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
}
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"DataSourceFilterPanel.error.text=At least one data source must be selected."})
|
||||
@Override
|
||||
String checkForError() {
|
||||
@ -202,6 +209,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
return "";
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
AbstractFilter getFilter() {
|
||||
if (dataSourceCheckbox.isSelected()) {
|
||||
|
@ -33,6 +33,7 @@ import javax.swing.JSpinner;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.communications.Utils;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
|
||||
|
||||
/**
|
||||
@ -48,6 +49,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
*/
|
||||
@NbBundle.Messages({"# {0} - timeZone",
|
||||
"DateFilterPanel.dateRange.text=Date Range ({0}):"})
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
DateFilterPanel() {
|
||||
initComponents();
|
||||
rangeRadioButton.setText(Bundle.DateFilterPanel_dateRange_text(Utils.getUserPreferredZoneId().toString()));
|
||||
@ -225,6 +227,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
endCheckBox.firePropertyChange("EndButtonChange", true, false);
|
||||
}//GEN-LAST:event_rangeRadioButtonStateChanged
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
void configurePanel(boolean selected, int[] indicesSelected) {
|
||||
dateFilterCheckBox.setSelected(selected);
|
||||
@ -238,6 +241,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
}
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
JCheckBox getCheckbox() {
|
||||
return dateFilterCheckBox;
|
||||
@ -253,6 +257,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
void addListeners(ActionListener actionListener, ListSelectionListener listListener) {
|
||||
dateFilterCheckBox.addActionListener(actionListener);
|
||||
@ -274,6 +279,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
});
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
void removeListeners() {
|
||||
for (ActionListener listener : dateFilterCheckBox.getActionListeners()) {
|
||||
@ -302,6 +308,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
}
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"DateFilterPanel.invalidRange.text=Range or Only Last must be selected.",
|
||||
"DateFilterPanel.startOrEndNeeded.text=A start or end date must be specified to use the range filter.",
|
||||
"DateFilterPanel.startAfterEnd.text=Start date should be before the end date when both are enabled."})
|
||||
@ -320,6 +327,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
|
||||
return "";
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
AbstractFilter getFilter() {
|
||||
if (dateFilterCheckBox.isSelected()) {
|
||||
|
@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
|
||||
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
|
||||
import org.sleuthkit.autopsy.discovery.search.Group;
|
||||
@ -99,6 +100,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
/**
|
||||
* Private constructor to construct a new DiscoveryDialog
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Messages("DiscoveryDialog.name.text=Discovery")
|
||||
private DiscoveryDialog() {
|
||||
super(WindowManager.getDefault().getMainWindow(), Bundle.DiscoveryDialog_name_text(), true);
|
||||
@ -116,10 +118,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
if (event.getStateChange() == ItemEvent.SELECTED) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
getSelectedFilterPanel().setLastGroupingAttributeType(groupByCombobox.getItemAt(groupByCombobox.getSelectedIndex()));
|
||||
});
|
||||
|
||||
getSelectedFilterPanel().setLastGroupingAttributeType(groupByCombobox.getItemAt(groupByCombobox.getSelectedIndex()));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -127,9 +126,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
if (event.getStateChange() == ItemEvent.SELECTED) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
getSelectedFilterPanel().setLastSortingMethod(orderByCombobox.getItemAt(orderByCombobox.getSelectedIndex()));
|
||||
});
|
||||
getSelectedFilterPanel().setLastSortingMethod(orderByCombobox.getItemAt(orderByCombobox.getSelectedIndex()));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -137,9 +134,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
if (event.getStateChange() == ItemEvent.SELECTED) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
getSelectedFilterPanel().setLastGroupSortingAlg(groupSortingComboBox.getItemAt(groupSortingComboBox.getSelectedIndex()));
|
||||
});
|
||||
getSelectedFilterPanel().setLastGroupSortingAlg(groupSortingComboBox.getItemAt(groupSortingComboBox.getSelectedIndex()));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -151,6 +146,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
/**
|
||||
* Update the search settings to a default state.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void updateSearchSettings() {
|
||||
removeAllPanels();
|
||||
imageFilterPanel = null;
|
||||
@ -176,6 +172,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
/**
|
||||
* Set the type buttons to a default state where none are selected.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void unselectAllButtons() {
|
||||
imagesButton.setSelected(false);
|
||||
imagesButton.setEnabled(true);
|
||||
@ -194,6 +191,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
/**
|
||||
* Private helper method to perform update of comboboxes update.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void updateComboBoxes() {
|
||||
// Set up the grouping attributes
|
||||
List<GroupingAttributeType> groupingAttrs = new ArrayList<>();
|
||||
@ -230,6 +228,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
*
|
||||
* @return The panel that corresponds to the currently selected type.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private AbstractFiltersPanel getSelectedFilterPanel() {
|
||||
switch (type) {
|
||||
case IMAGE:
|
||||
@ -251,6 +250,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
*
|
||||
* @param type The Type of GroupingAttribute to add.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void addTypeToGroupByComboBox(GroupingAttributeType type) {
|
||||
switch (type) {
|
||||
case FREQUENCY:
|
||||
@ -282,7 +282,8 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
/**
|
||||
* Validate the filter settings for File type filters.
|
||||
*/
|
||||
synchronized void validateDialog() {
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void validateDialog() {
|
||||
AbstractFiltersPanel panel = getSelectedFilterPanel();
|
||||
if (panel != null) {
|
||||
panel.validateFields();
|
||||
@ -551,6 +552,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
/**
|
||||
* Helper method to remove all filter panels and their listeners
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void removeAllPanels() {
|
||||
if (imageFilterPanel != null) {
|
||||
remove(imageFilterPanel);
|
||||
@ -635,6 +637,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
repaint();
|
||||
}//GEN-LAST:event_domainsButtonActionPerformed
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public void dispose() {
|
||||
setVisible(false);
|
||||
@ -643,6 +646,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
/**
|
||||
* Cancel the searchWorker if it exists.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void cancelSearch() {
|
||||
if (searchWorker != null) {
|
||||
searchWorker.cancel(true);
|
||||
@ -656,6 +660,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|
||||
* @param error The error message to display, empty string if there is no
|
||||
* error.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void setValid(String error) {
|
||||
if (StringUtils.isBlank(error)) {
|
||||
errorLabel.setText("");
|
||||
|
@ -27,6 +27,7 @@ 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.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
|
||||
import org.sleuthkit.autopsy.datamodel.FileNode;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
@ -42,28 +43,27 @@ class DiscoveryThumbnailChildren extends Children.Keys<AbstractFile> {
|
||||
/*
|
||||
* Creates the list of thumbnails from the given list of AbstractFiles.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
DiscoveryThumbnailChildren(List<AbstractFile> files) {
|
||||
super(false);
|
||||
|
||||
this.files = files;
|
||||
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
protected Node[] createNodes(AbstractFile t) {
|
||||
return new Node[]{new ThumbnailNode(t)};
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
super.addNotify();
|
||||
|
||||
Set<AbstractFile> thumbnails = new TreeSet<>((AbstractFile file1, AbstractFile file2) -> {
|
||||
int result = Long.compare(file1.getSize(), file2.getSize());
|
||||
if (result == 0) {
|
||||
result = file1.getName().compareTo(file2.getName());
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
thumbnails.addAll(files);
|
||||
@ -75,10 +75,12 @@ class DiscoveryThumbnailChildren extends Children.Keys<AbstractFile> {
|
||||
*/
|
||||
static class ThumbnailNode extends FileNode {
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
ThumbnailNode(AbstractFile file) {
|
||||
super(file, false);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[199, 200]"/>
|
||||
|
@ -28,6 +28,7 @@ import java.beans.PropertyChangeListener;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.plaf.basic.BasicSplitPaneDivider;
|
||||
import javax.swing.plaf.basic.BasicSplitPaneUI;
|
||||
import org.openide.util.NbBundle;
|
||||
@ -40,6 +41,8 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
|
||||
import org.sleuthkit.autopsy.discovery.search.SearchData.Type;
|
||||
import static org.sleuthkit.autopsy.discovery.search.SearchData.Type.DOMAIN;
|
||||
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
|
||||
/**
|
||||
* Create a dialog for displaying the Discovery results.
|
||||
@ -53,12 +56,12 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String PREFERRED_ID = "DiscoveryTc"; // NON-NLS
|
||||
private static final int ANIMATION_INCREMENT = 30;
|
||||
private volatile static int resultsAreaSize = 250;
|
||||
private volatile static int previousDividerLocation = 250;
|
||||
private final GroupListPanel groupListPanel;
|
||||
private final DetailsPanel detailsPanel;
|
||||
private final ResultsPanel resultsPanel;
|
||||
private String selectedDomainTabName;
|
||||
private Type searchType;
|
||||
private int dividerLocation = -1;
|
||||
private int dividerLocation = JSplitPane.UNDEFINED_CONDITION;
|
||||
private SwingAnimator animator = null;
|
||||
|
||||
/**
|
||||
@ -70,10 +73,7 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
setName(Bundle.DiscoveryTopComponent_name());
|
||||
groupListPanel = new GroupListPanel();
|
||||
resultsPanel = new ResultsPanel();
|
||||
detailsPanel = new DetailsPanel();
|
||||
mainSplitPane.setLeftComponent(groupListPanel);
|
||||
rightSplitPane.setTopComponent(resultsPanel);
|
||||
rightSplitPane.setBottomComponent(detailsPanel);
|
||||
//set color of divider
|
||||
rightSplitPane.setUI(new BasicSplitPaneUI() {
|
||||
@Override
|
||||
@ -87,14 +87,19 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)) {
|
||||
//Only change the saved location when it was a manual change by the user and not the animation or the window opening initially
|
||||
if ((animator == null || !animator.isRunning()) && evt.getNewValue() instanceof Integer
|
||||
&& ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize())) {
|
||||
resultsAreaSize = (int) evt.getNewValue();
|
||||
if ((animator == null || !animator.isRunning())
|
||||
&& evt.getNewValue() instanceof Integer
|
||||
&& evt.getOldValue() instanceof Integer
|
||||
&& ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize())
|
||||
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())
|
||||
&& ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION)) {
|
||||
previousDividerLocation = (int) evt.getNewValue();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
rightSplitPane.setTopComponent(resultsPanel);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,6 +113,7 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
* @param ui The component which contains the split pane this divider is
|
||||
* in.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
BasicSplitPaneDividerImpl(BasicSplitPaneUI ui) {
|
||||
super(ui);
|
||||
this.setLayout(new BorderLayout());
|
||||
@ -129,11 +135,13 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
/**
|
||||
* Reset the top component so it isn't displaying any results.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
public void resetTopComponent() {
|
||||
resultsPanel.resetResultViewer();
|
||||
groupListPanel.resetGroupList();
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public void componentOpened() {
|
||||
super.componentOpened();
|
||||
@ -141,9 +149,9 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().register(this);
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().register(resultsPanel);
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().register(groupListPanel);
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().register(detailsPanel);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
protected void componentClosed() {
|
||||
DiscoveryDialog.getDiscoveryDialogInstance().cancelSearch();
|
||||
@ -152,7 +160,11 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().unregister(this);
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel);
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel);
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().unregister(detailsPanel);
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().unregister(rightSplitPane.getBottomComponent());
|
||||
if (rightSplitPane.getBottomComponent() instanceof DomainDetailsPanel) {
|
||||
selectedDomainTabName = ((DomainDetailsPanel) rightSplitPane.getBottomComponent()).getSelectedTabName();
|
||||
}
|
||||
rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION);
|
||||
super.componentClosed();
|
||||
}
|
||||
|
||||
@ -262,7 +274,7 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
*/
|
||||
@Subscribe
|
||||
void handleDetailsVisibleEvent(DiscoveryEventUtils.DetailsVisibleEvent detailsVisibleEvent) {
|
||||
if (resultsPanel.getActiveType() != DOMAIN) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (animator != null && animator.isRunning()) {
|
||||
animator.stop();
|
||||
animator = null;
|
||||
@ -274,7 +286,7 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
animator = new SwingAnimator(new HideDetailsAreaCallback());
|
||||
}
|
||||
animator.start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -289,12 +301,12 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
"DiscoveryTopComponent.searchError.text=Error no type specified for search."})
|
||||
@Subscribe
|
||||
void handleSearchStartedEvent(DiscoveryEventUtils.SearchStartedEvent searchStartedEvent) {
|
||||
newSearchButton.setText(Bundle.DiscoveryTopComponent_cancelButton_text());
|
||||
progressMessageTextArea.setForeground(Color.red);
|
||||
searchType = searchStartedEvent.getType();
|
||||
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchInProgress_text(searchType.name()));
|
||||
rightSplitPane.getComponent(1).setVisible(searchStartedEvent.getType() != DOMAIN);
|
||||
rightSplitPane.getComponent(2).setVisible(searchStartedEvent.getType() != DOMAIN);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
newSearchButton.setText(Bundle.DiscoveryTopComponent_cancelButton_text());
|
||||
progressMessageTextArea.setForeground(Color.red);
|
||||
searchType = searchStartedEvent.getType();
|
||||
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchInProgress_text(searchType.name()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -310,19 +322,52 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
"DiscoveryTopComponent.domainSearch.text=Type: Domain",
|
||||
"DiscoveryTopComponent.additionalFilters.text=; "})
|
||||
void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
|
||||
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
|
||||
progressMessageTextArea.setForeground(Color.black);
|
||||
String descriptionText = "";
|
||||
if (searchType == DOMAIN) {
|
||||
//domain does not have a file type filter to add the type information so it is manually added
|
||||
descriptionText = Bundle.DiscoveryTopComponent_domainSearch_text();
|
||||
if (!searchCompleteEvent.getFilters().isEmpty()) {
|
||||
descriptionText += Bundle.DiscoveryTopComponent_additionalFilters_text();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
|
||||
progressMessageTextArea.setForeground(Color.black);
|
||||
String descriptionText = "";
|
||||
if (searchType == DOMAIN) {
|
||||
//domain does not have a file type filter to add the type information so it is manually added
|
||||
descriptionText = Bundle.DiscoveryTopComponent_domainSearch_text();
|
||||
if (!searchCompleteEvent.getFilters().isEmpty()) {
|
||||
descriptionText += Bundle.DiscoveryTopComponent_additionalFilters_text();
|
||||
}
|
||||
selectedDomainTabName = validateLastSelectedType(searchCompleteEvent);
|
||||
rightSplitPane.setBottomComponent(new DomainDetailsPanel(selectedDomainTabName));
|
||||
} else {
|
||||
rightSplitPane.setBottomComponent(new FileDetailsPanel());
|
||||
}
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().register(rightSplitPane.getBottomComponent());
|
||||
descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; "));
|
||||
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText));
|
||||
progressMessageTextArea.setCaretPosition(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the tab which was last selected unless the tab last
|
||||
* selected would not be included in the types currently being displayed or
|
||||
* was not previously set.
|
||||
*
|
||||
* @return The name of the tab which should be selected in the new
|
||||
* DomainDetailsPanel.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private String validateLastSelectedType(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
|
||||
String typeFilteredOn = selectedDomainTabName;
|
||||
|
||||
for (AbstractFilter filter : searchCompleteEvent.getFilters()) {
|
||||
if (filter instanceof ArtifactTypeFilter) {
|
||||
for (ARTIFACT_TYPE type : ((ArtifactTypeFilter) filter).getTypes()) {
|
||||
typeFilteredOn = type.getDisplayName();
|
||||
if (selectedDomainTabName == null || typeFilteredOn.equalsIgnoreCase(selectedDomainTabName)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; "));
|
||||
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText));
|
||||
progressMessageTextArea.setCaretPosition(0);
|
||||
return typeFilteredOn;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -334,9 +379,11 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
@Messages({"DiscoveryTopComponent.searchCancelled.text=Search has been cancelled."})
|
||||
@Subscribe
|
||||
void handleSearchCancelledEvent(DiscoveryEventUtils.SearchCancelledEvent searchCancelledEvent) {
|
||||
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
|
||||
progressMessageTextArea.setForeground(Color.red);
|
||||
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text());
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
|
||||
progressMessageTextArea.setForeground(Color.red);
|
||||
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@ -345,16 +392,18 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
*/
|
||||
private final class ShowDetailsAreaCallback implements SwingAnimatorCallback {
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public void callback(Object caller) {
|
||||
dividerLocation -= ANIMATION_INCREMENT;
|
||||
repaint();
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public boolean hasTerminated() {
|
||||
if (dividerLocation != JSplitPane.UNDEFINED_CONDITION && dividerLocation < resultsAreaSize) {
|
||||
dividerLocation = resultsAreaSize;
|
||||
if (dividerLocation != JSplitPane.UNDEFINED_CONDITION && dividerLocation < previousDividerLocation) {
|
||||
dividerLocation = previousDividerLocation;
|
||||
animator = null;
|
||||
return true;
|
||||
}
|
||||
@ -368,12 +417,14 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
*/
|
||||
private final class HideDetailsAreaCallback implements SwingAnimatorCallback {
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public void callback(Object caller) {
|
||||
dividerLocation += ANIMATION_INCREMENT;
|
||||
repaint();
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public boolean hasTerminated() {
|
||||
if (dividerLocation > rightSplitPane.getHeight() || dividerLocation == JSplitPane.UNDEFINED_CONDITION) {
|
||||
@ -399,10 +450,11 @@ public final class DiscoveryTopComponent extends TopComponent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
if (animator != null && animator.isRunning() && (dividerLocation == JSplitPane.UNDEFINED_CONDITION
|
||||
|| (dividerLocation <= getHeight() && dividerLocation >= resultsAreaSize))) {
|
||||
|| (dividerLocation <= getHeight() && dividerLocation >= previousDividerLocation))) {
|
||||
setDividerLocation(dividerLocation);
|
||||
}
|
||||
super.paintComponent(g);
|
||||
|
@ -51,6 +51,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import static org.sleuthkit.autopsy.coreutils.VideoUtils.getVideoFileInTempDir;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.autopsy.discovery.search.ResultFile;
|
||||
@ -173,6 +174,7 @@ final class DiscoveryUiUtils {
|
||||
*
|
||||
* @return True if the point is over the icon, false otherwise.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
static boolean isPointOnIcon(Component comp, Point point) {
|
||||
return comp instanceof JComponent && point.x >= comp.getX() && point.x <= comp.getX() + ICON_SIZE && point.y >= comp.getY() && point.y <= comp.getY() + ICON_SIZE;
|
||||
}
|
||||
@ -186,6 +188,7 @@ final class DiscoveryUiUtils {
|
||||
* @param isDeletedLabel The label to set the icon and tooltip for.
|
||||
*/
|
||||
@NbBundle.Messages({"DiscoveryUiUtils.isDeleted.text=All instances of file are deleted."})
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
static void setDeletedIcon(boolean isDeleted, javax.swing.JLabel isDeletedLabel) {
|
||||
if (isDeleted) {
|
||||
isDeletedLabel.setIcon(DELETED_ICON);
|
||||
@ -203,6 +206,7 @@ final class DiscoveryUiUtils {
|
||||
* score of.
|
||||
* @param scoreLabel The label to set the icon and tooltip for.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
static void setScoreIcon(ResultFile resultFile, javax.swing.JLabel scoreLabel) {
|
||||
switch (resultFile.getScore()) {
|
||||
case NOTABLE_SCORE:
|
||||
@ -232,6 +236,7 @@ final class DiscoveryUiUtils {
|
||||
* Helper method to display an error message when the results of the
|
||||
* Discovery Top component may be incomplete.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"DiscoveryUiUtils.resultsIncomplete.text=Discovery results may be incomplete"})
|
||||
static void displayErrorMessage(DiscoveryDialog dialog) {
|
||||
//check if modules run and assemble message
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.discovery.ui;
|
||||
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.discovery.search.SearchData;
|
||||
|
||||
/**
|
||||
@ -32,6 +33,7 @@ final class DocumentFilterPanel extends AbstractFiltersPanel {
|
||||
/**
|
||||
* Constructs a new DocumentFilterPanel.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
DocumentFilterPanel() {
|
||||
super();
|
||||
initComponents();
|
||||
|
@ -79,9 +79,6 @@
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/file-icon-deleted.png"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DocumentPanel.isDeletedLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
|
||||
</Property>
|
||||
@ -91,6 +88,9 @@
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DocumentPanel.isDeletedLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="scoreLabel">
|
||||
@ -98,7 +98,6 @@
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/red-circle-exclamation.png"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" value=""/>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
|
||||
</Property>
|
||||
@ -108,6 +107,7 @@
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" value=""/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="fileSizeLabel">
|
||||
|
@ -29,6 +29,7 @@ import javax.swing.JList;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.discovery.search.SearchData;
|
||||
|
||||
/**
|
||||
@ -43,6 +44,7 @@ class DocumentPanel extends javax.swing.JPanel implements ListCellRenderer<Docum
|
||||
/**
|
||||
* Creates new form DocumentPanel.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
DocumentPanel() {
|
||||
initComponents();
|
||||
}
|
||||
@ -150,7 +152,7 @@ class DocumentPanel extends javax.swing.JPanel implements ListCellRenderer<Docum
|
||||
"DocumentPanel.numberOfImages.text=1 of {0} images",
|
||||
"DocumentPanel.numberOfImages.noImages=No images",
|
||||
"DocumentPanel.noImageExtraction.text=0 of ? images"})
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<? extends DocumentWrapper> list, DocumentWrapper value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
fileSizeLabel.setText(DiscoveryUiUtils.getFileSizeString(value.getResultFile().getFirstInstance().getSize()));
|
||||
@ -180,6 +182,7 @@ class DocumentPanel extends javax.swing.JPanel implements ListCellRenderer<Docum
|
||||
return this;
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Override
|
||||
public String getToolTipText(MouseEvent event) {
|
||||
if (event != null) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user