Merge pull request #6587 from raman-bt/analysis_results

Merged develop -> analysis_results.
This commit is contained in:
Richard Cordovano 2020-12-31 12:02:41 -05:00 committed by GitHub
commit 354b51d310
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
874 changed files with 209923 additions and 4003 deletions

12
.gitignore vendored
View 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/

View File

@ -6,6 +6,7 @@ jobs:
- os: linux
dist: bionic
- os: osx
osx_image: xcode12.2
env:
global:

View File

@ -54,6 +54,11 @@
<fileset dir="${thirdparty.dir}/iLeapp"/>
</copy>
<!--Copy aLeapp to release-->
<copy todir="${basedir}/release/aLeapp" >
<fileset dir="${thirdparty.dir}/aLeapp"/>
</copy>
<!--Copy 7-Zip to release-->
<copy todir="${basedir}/release/7-Zip" >
<fileset dir="${thirdparty.dir}/7-Zip"/>
@ -98,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>
@ -118,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"
@ -197,7 +207,7 @@
</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" />
<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
@ -238,4 +248,67 @@
</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>

View File

@ -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

View File

@ -336,6 +336,7 @@
<package>org.sleuthkit.autopsy.textextractors.configs</package>
<package>org.sleuthkit.autopsy.textsummarizer</package>
<package>org.sleuthkit.autopsy.texttranslation</package>
<package>org.sleuthkit.autopsy.url.analytics</package>
<package>org.sleuthkit.datamodel</package>
<package>org.sleuthkit.datamodel.blackboardutils</package>
<package>org.sleuthkit.datamodel.blackboardutils.attributes</package>
@ -436,6 +437,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>
@ -548,18 +553,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>
@ -568,6 +565,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>
@ -672,6 +673,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>
@ -724,10 +729,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>
@ -796,6 +797,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>

View File

@ -146,13 +146,14 @@ AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel
NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive
NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system
NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive.
NewCaseVisualPanel1.uncPath.error=Error: UNC paths are not allowed for Single-User cases
CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source
CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1}
MissingImageDialog.lbWarning.text=
MissingImageDialog.lbWarning.toolTipText=
NewCaseVisualPanel1.caseParentDirWarningLabel.text=
NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user
NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-user
NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-User
NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-User
NewCaseVisualPanel1.caseTypeLabel.text=Case Type:
SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist!
SingleUserCaseConverter.AlreadyMultiUser=Case is already multi-user!
@ -253,3 +254,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>

View File

@ -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>

View File

@ -469,7 +469,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
return false;
}
if (!PathValidator.isValidForMultiUserCase(path, Case.getCurrentCase().getCaseType())) {
if (!PathValidator.isValidForCaseType(path, Case.getCurrentCase().getCaseType())) {
errorLabel.setVisible(true);
errorLabel.setText(Bundle.ImageFilePanel_validatePanel_dataSourceOnCDriveError());
}

View File

@ -89,8 +89,15 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
return;
}
if (CURRENT_CASE == Case.Events.valueOf(evt.getPropertyName())) {
// Check whether we have a case open or case close event.
if ((CURRENT_CASE == Case.Events.valueOf(evt.getPropertyName()))) {
if (evt.getNewValue() != null) {
// Case open
refresh();
} else {
// Case close
reset();
}
}
});
}
@ -124,7 +131,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
*/
private void refresh() {
try {
if (Case.isCaseOpen()) {
if (Case.isCaseOpen()) { // Note - this will generally return true when handling a case close event
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
this.ingestJobs = skCase.getIngestJobs();
setDataSource(selectedDataSource);
@ -139,6 +146,14 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
}
}
/**
* Reset the panel.
*/
private void reset() {
this.ingestJobs = new ArrayList<>();
setDataSource(null);
}
@Messages({"IngestJobInfoPanel.IngestJobTableModel.StartTime.header=Start Time",
"IngestJobInfoPanel.IngestJobTableModel.EndTime.header=End Time",
"IngestJobInfoPanel.IngestJobTableModel.IngestStatus.header=Ingest Status"})

View File

@ -290,7 +290,7 @@ final class LocalFilesPanel extends javax.swing.JPanel {
final Case.CaseType currentCaseType = Case.getCurrentCaseThrows().getCaseType();
for (String currentPath : pathsList) {
if (!PathValidator.isValidForMultiUserCase(currentPath, currentCaseType)) {
if (!PathValidator.isValidForCaseType(currentPath, currentCaseType)) {
errorLabel.setVisible(true);
errorLabel.setText(Bundle.LocalFilesPanel_pathValidation_dataSourceOnCDriveError());
return;

View File

@ -191,7 +191,7 @@ final class LogicalEvidenceFilePanel extends javax.swing.JPanel implements Docum
}
// display warning if there is one (but don't disable "next" button)
try {
if (!PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) {
if (!PathValidator.isValidForCaseType(path, Case.getCurrentCaseThrows().getCaseType())) {
errorLabel.setVisible(true);
errorLabel.setText(Bundle.LogicalEvidenceFilePanel_pathValidation_dataSourceOnCDriveError());
return false;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -28,7 +28,6 @@ import javax.swing.JPanel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.PathValidator;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
@ -149,13 +148,22 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener {
private void validateSettings() {
/**
* Check the base case directory for the selected case type and show a
* warning if it is a dubious choice.
* warning if it is a dubious choice. For single user cases, disable
* the "Next" button if path is invalid.
*/
caseParentDirWarningLabel.setVisible(false);
String parentDir = getCaseParentDir();
if (!PathValidator.isValidForMultiUserCase(parentDir, getCaseType())) {
if (!PathValidator.isValidForCaseType(parentDir, getCaseType())) {
if (getCaseType() == CaseType.MULTI_USER_CASE) {
caseParentDirWarningLabel.setVisible(true);
caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnCDriveError.text"));
} else {
// disable the "Next" button
caseParentDirWarningLabel.setVisible(true);
caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.uncPath.error"));
wizPanel.setIsFinish(false);
return;
}
}
/**

View 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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View 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
}

View File

@ -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.

View File

@ -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");

View File

@ -40,6 +40,7 @@ public final class CentralRepoAccount {
private final CentralRepoAccountType accountType;
// type specific unique account identifier
// Stores what is in the DB which should have been normalized before insertion.
private final String typeSpecificIdentifier;
/**
@ -106,7 +107,7 @@ public final class CentralRepoAccount {
}
public CentralRepoAccount(long accountId, CentralRepoAccountType accountType, String typeSpecificIdentifier) {
CentralRepoAccount(long accountId, CentralRepoAccountType accountType, String typeSpecificIdentifier) {
this.accountId = accountId;
this.accountType = accountType;
this.typeSpecificIdentifier = typeSpecificIdentifier;
@ -116,6 +117,8 @@ public final class CentralRepoAccount {
* Gets unique identifier (assigned by a provider) for the account. Example
* includes an email address, a phone number, or a website username.
*
* This is the normalized for of the ID.
*
* @return type specific account id.
*/
public String getIdentifier() {
@ -336,7 +339,7 @@ public final class CentralRepoAccount {
normalizedAccountIdentifier = accountIdentifier.toLowerCase();
}
} catch (CorrelationAttributeNormalizationException ex) {
throw new InvalidAccountIDException("Invalid account identifier", ex);
throw new InvalidAccountIDException(String.format("Account id normaization failed, invalid account identifier %s", accountIdentifier), ex);
}
return normalizedAccountIdentifier;

View File

@ -383,6 +383,31 @@ public class CentralRepoDbManager {
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
* were successfully applied.

View File

@ -49,6 +49,15 @@ public class CorrelationAttributeUtil {
private static final Logger logger = Logger.getLogger(CorrelationAttributeUtil.class.getName());
private static final List<String> domainsToSkip = Arrays.asList("localhost", "127.0.0.1");
// artifact ids that specifically have a TSK_DOMAIN attribute that should be handled by CR
private static Set<Integer> DOMAIN_ARTIFACT_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID(),
ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID(),
ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(),
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(),
ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID()
));
/**
* Gets a string that is expected to be the same string that is stored in
* the correlation_types table in the central repository as the display name
@ -68,14 +77,12 @@ public class CorrelationAttributeUtil {
// Most notably, does not include KEYWORD HIT, CALLLOGS, MESSAGES, CONTACTS
// TSK_INTERESTING_ARTIFACT_HIT (See JIRA-6129 for more details on the
// interesting artifact hit).
// IMPORTANT: This set should be updated for new artifacts types that need to
// be inserted into the CR.
private static final Set<Integer> SOURCE_TYPES_FOR_CR_INSERT = new HashSet<Integer>() {{
add(ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID());
add(ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID());
add(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID());
add(ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID());
private static final Set<Integer> SOURCE_TYPES_FOR_CR_INSERT = new HashSet<Integer>() {
{
addAll(DOMAIN_ARTIFACT_TYPE_IDS);
add(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID());
add(ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID());
add(ARTIFACT_TYPE.TSK_WIFI_NETWORK_ADAPTER.getTypeID());
@ -85,15 +92,16 @@ public class CorrelationAttributeUtil {
add(ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID());
add(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID());
add(ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID());
}};
}
};
/**
* Makes zero to many correlation attribute instances from the attributes of
* artifacts that have correlatable data. The intention of this method is to
* use the results to save to the CR, not to correlate with them. If you
* want to correlate, please use makeCorrAttrsForCorrelation. An artifact that can
* have correlatable data != An artifact that should be the source of data
* in the CR, so results may be un-necessarily incomplete.
* want to correlate, please use makeCorrAttrsForCorrelation. An artifact
* that can have correlatable data != An artifact that should be the source
* of data in the CR, so results may be un-necessarily incomplete.
*
* @param artifact An artifact.
*
@ -114,10 +122,10 @@ public class CorrelationAttributeUtil {
/**
* Makes zero to many correlation attribute instances from the attributes of
* artifacts that have correlatable data. The intention of this method is to
* use the results to correlate with, not to save. If you
* want to save, please use makeCorrAttrsToSave. An artifact that can
* have correlatable data != An artifact that should be the source of data
* in the CR, so results may be too lenient.
* use the results to correlate with, not to save. If you want to save,
* please use makeCorrAttrsToSave. An artifact that can have correlatable
* data != An artifact that should be the source of data in the CR, so
* results may be too lenient.
*
* IMPORTANT: The correlation attribute instances are NOT added to the
* central repository by this method.
@ -146,10 +154,7 @@ public class CorrelationAttributeUtil {
if (setNameAttr != null && CorrelationAttributeUtil.getEmailAddressAttrDisplayName().equals(setNameAttr.getValueString())) {
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, CorrelationAttributeInstance.EMAIL_TYPE_ID);
}
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) {
} else if (DOMAIN_ARTIFACT_TYPE_IDS.contains(artifactTypeID)) {
BlackboardAttribute domainAttr = sourceArtifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN));
if ((domainAttr != null)
&& !domainsToSkip.contains(domainAttr.getValueString())) {
@ -192,12 +197,10 @@ public class CorrelationAttributeUtil {
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, String.format("Error normalizing correlation attribute (%s)", artifact), ex); // NON-NLS
return correlationAttrs;
}
catch (InvalidAccountIDException ex) {
} catch (InvalidAccountIDException ex) {
logger.log(Level.WARNING, String.format("Invalid account identifier (artifactID: %d)", artifact.getId())); // NON-NLS
return correlationAttrs;
}
catch (CentralRepoException ex) {
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", artifact), ex); // NON-NLS
return correlationAttrs;
} catch (TskCoreException ex) {
@ -332,16 +335,13 @@ public class CorrelationAttributeUtil {
*
* @param corrAttrInstances A list of correlation attribute instances.
* @param artifact An artifact.
* @param artAttrType The type of the atrribute of the artifact that
* is to be made into a correlatin attribute
* instance.
* @param typeId The type ID for the desired correlation
* attribute instance.
* @param artAttrType The type of the atrribute of the artifact that is to
* be made into a correlatin attribute instance.
* @param typeId The type ID for the desired correlation attribute instance.
*
* @throws CentralRepoException If there is an error querying the central
* repository.
* @throws TskCoreException If there is an error querying the case
* database.
* @throws TskCoreException If there is an error querying the case database.
*/
private static void makeCorrAttrFromArtifactAttr(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact artifact, ATTRIBUTE_TYPE artAttrType, int typeId) throws CentralRepoException, TskCoreException {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(artAttrType));

View File

@ -46,7 +46,7 @@ public class PersonaAccount {
private final long dateAdded;
private final CentralRepoExaminer examiner;
public PersonaAccount(long id, Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
private PersonaAccount(long id, Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
this.id = id;
this.persona = persona;
this.account = account;
@ -326,12 +326,12 @@ public class PersonaAccount {
*/
public static Collection<PersonaAccount> getPersonaAccountsForAccount(Account account) throws CentralRepoException {
String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER(?)"
+ " WHERE LOWER(accounts.account_unique_identifier) = LOWER(?)"
+ " AND type_name = ?"
+ " AND personas.status_id != ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add("%" + account.getTypeSpecificID() + "%"); // substring match
queryParams.add(account.getTypeSpecificID()); // substring match
queryParams.add(account.getAccountType().getTypeName());
queryParams.add(Persona.PersonaStatus.DELETED.getStatusId());

View File

@ -198,13 +198,13 @@ 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
}
}
}
@Override
protected String getConflictClause() {

View File

@ -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;

View File

@ -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?

View File

@ -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>"
);
}
}

View File

@ -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,20 +80,11 @@ 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();
}
}
/**
* Adds the application event listeners responsible for adding data to the
@ -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");
@ -128,62 +118,30 @@ public class Installer extends ModuleInstall {
}
}
// 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(initialized) {
return; // Nothing to do
}
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
dialogText,
NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.title"),
JOptionPane.YES_NO_OPTION)) {
if (CentralRepositoryNotificationDialog.shouldDisplay()) {
CentralRepositoryNotificationDialog.display();
}
setupDefaultSqliteCentralRepo();
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);
}
});
} 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");
}
}
/**
* 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();
}
/**
* Display a central repository exception in a message box if running with a

View File

@ -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;

View File

@ -79,5 +79,5 @@ CreatePersonaAccountDialog.identifierTextField.text=
CreatePersonaAccountDialog.identiferLbl.text=Identifier:
CreatePersonaAccountDialog.okBtn.text=OK
PersonasTopComponent.introText.text=Personas represent an online identity. They span cases and are stored in the Central Repository based on accounts that were found in artifacts. You can create, edit, and delete personas here.
PersonasTopComponent.cbFilterByKeyword.text=Filter personas by keyword
PersonasTopComponent.cbFilterByKeyword.text=Filter personas by name or account
PersonaDetailsPanel.nameField.text=

View File

@ -125,7 +125,7 @@ CreatePersonaAccountDialog.identifierTextField.text=
CreatePersonaAccountDialog.identiferLbl.text=Identifier:
CreatePersonaAccountDialog.okBtn.text=OK
PersonasTopComponent.introText.text=Personas represent an online identity. They span cases and are stored in the Central Repository based on accounts that were found in artifacts. You can create, edit, and delete personas here.
PersonasTopComponent.cbFilterByKeyword.text=Filter personas by keyword
PersonasTopComponent.cbFilterByKeyword.text=Filter personas by name or account
PersonaDetailsPanel.nameField.text=
PersonasTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?
PersonasTopComponent_delete_confirmation_Title=Are you sure?

View File

@ -8,6 +8,9 @@
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<Events>
<EventHandler event="windowOpened" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowOpened"/>
</Events>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>

View File

@ -22,6 +22,7 @@ import java.awt.Component;
import java.util.logging.Level;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
@ -39,6 +40,8 @@ public class PersonaDetailsDialog extends JDialog {
private final PersonaDetailsDialogCallback callback;
private String popupMessageOnStartup = "";
@NbBundle.Messages({
"PersonaDetailsDialogCreateTitle=Create Persona",
"PersonaDetailsDialogEditTitle=Edit Persona",
@ -91,6 +94,11 @@ public class PersonaDetailsDialog extends JDialog {
pdp = new org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowOpened(java.awt.event.WindowEvent evt) {
formWindowOpened(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(cancelBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsDialog.class, "PersonaDetailsDialog.cancelBtn.text")); // NOI18N
cancelBtn.setMaximumSize(new java.awt.Dimension(79, 23));
@ -159,10 +167,20 @@ public class PersonaDetailsDialog extends JDialog {
dispose();
}//GEN-LAST:event_cancelBtnActionPerformed
private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
if(!popupMessageOnStartup.isEmpty()) {
JOptionPane.showMessageDialog(this, popupMessageOnStartup, "Persona Details", JOptionPane.INFORMATION_MESSAGE);
}
}//GEN-LAST:event_formWindowOpened
public PersonaDetailsPanel getDetailsPanel() {
return this.pdp;
}
public void setStartupPopupMessage(String message) {
popupMessageOnStartup = message;
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton cancelBtn;
private javax.swing.JScrollPane jScrollPane1;

View File

@ -22,6 +22,9 @@
-->
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Events>
<EventHandler event="componentShown" listener="java.awt.event.ComponentListener" parameters="java.awt.event.ComponentEvent" handler="formComponentShown"/>
</Events>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>

View File

@ -226,6 +226,10 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
aliasesToEdit.put(alias, new PAlias(alias.getAlias(), justification, confidence));
}
PersonaDetailsMode getMode() {
return mode;
}
/**
* A data bucket class for yet-to-be-created PersonaAccount
*/
@ -384,6 +388,12 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
casesTablePane = new javax.swing.JScrollPane();
casesTable = new javax.swing.JTable();
addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentShown(java.awt.event.ComponentEvent evt) {
formComponentShown(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(examinerLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.examinerLbl.text")); // NOI18N
examinerField.setEditable(false);
@ -622,6 +632,10 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
);
}// </editor-fold>//GEN-END:initComponents
private void formComponentShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentShown
}//GEN-LAST:event_formComponentShown
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel accountsLbl;
private javax.swing.JTable accountsTable;

View File

@ -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.
*

View File

@ -28,3 +28,9 @@ SummaryViewer.contactsLabel.text=Book Entries:
SummaryViewer.accountCountry.text=<account country>
SummaryViewer.fileRefPane.border.title=File References in Current Case
SummaryViewer.selectAccountFileRefLabel.text=<Select a single account to see File References>
SummaryViewer.personaPanel.border.title=Personas
PersonaPanel.personaIDLabel.text=jLabel1
SummaryPersonaPane.noPersonaLabel.text=No personas found
SummaryPersonaPane.messageLabel.text=<Enable Central Repository to create and view personas>
SummaryPersonaPane.createButton.text=Create
PersonaPanel.viewButton.text=View

View File

@ -35,6 +35,8 @@ MessageViewer_viewMessage_all=All
MessageViewer_viewMessage_calllogs=Call Logs
MessageViewer_viewMessage_selected=Selected
MessageViewer_viewMessage_unthreaded=Unthreaded
# {0} - accountIdentifer
SummaryPersonaPane_not_account_in_cr=Unable to find an account with identifier {0} in the Central Repository.
SummaryViewer.countsPanel.border.title=Communications
OutlineViewPanel.messageLabel.text=<Control Disabled>
SummaryViewer.messagesDataLabel.text=messages
@ -52,6 +54,8 @@ SummaryViewer_Device_Account_Description=This account was referenced by a device
SummaryViewer_Fetching_References=<Fetching File References>
SummaryViewer_FileRef_Message=<Select a single account to see File References>
SummaryViewer_FileRefNameColumn_Title=Path
SummaryViewer_Persona_Message=<Enable Central Repository to view Personas>
SummaryViewer_Select_account_for_persona=<Select a single account to see Persona(s)>
SummaryViewer_TabTitle=Summary
ThreadRootMessagePanel.showAllCheckBox.text=Show All Messages
ThreadPane.backButton.text=<---
@ -75,3 +79,9 @@ SummaryViewer.contactsLabel.text=Book Entries:
SummaryViewer.accountCountry.text=<account country>
SummaryViewer.fileRefPane.border.title=File References in Current Case
SummaryViewer.selectAccountFileRefLabel.text=<Select a single account to see File References>
SummaryViewer.personaPanel.border.title=Personas
PersonaPanel.personaIDLabel.text=jLabel1
SummaryPersonaPane.noPersonaLabel.text=No personas found
SummaryPersonaPane.messageLabel.text=<Enable Central Repository to create and view personas>
SummaryPersonaPane.createButton.text=Create
PersonaPanel.viewButton.text=View

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" 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,0,59,0,0,0,-8"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="personaIDLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="PersonaPanel.personaIDLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="0" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="viewButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="PersonaPanel.viewButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[0, 5, 0, 5]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="0" insetsRight="0" anchor="17" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,115 @@
/*
* 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 obt ain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.communications.relationships;
import java.awt.Dimension;
import javax.swing.JButton;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
/**
*
* Panel to show a single persona with a button for viewing persona details.
*/
public class PersonaPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private final Persona persona;
/**
* Creates new form PersonaPanel
*/
PersonaPanel(Persona persona) {
initComponents();
this.persona = persona;
personaIDLabel.setText(persona.getName());
}
/**
* Returns the persona displayed by this panel.
*
* @return
*/
Persona getPersona() {
return persona;
}
/**
* Returns the preferred width for the given persona label.
*
* @return
*/
int getPersonaLabelPreferedWidth() {
return personaIDLabel.getPreferredSize().width;
}
/**
* Sets the preferred width for the persona name label.
*
* @param width
*/
void setPersonalLabelPreferredWidth(int width) {
Dimension currentDim = personaIDLabel.getPreferredSize();
personaIDLabel.setPreferredSize(new Dimension(Math.max(currentDim.width, width), currentDim.height));
}
/**
* Returns the View button for this panel.
*
* @return
*/
JButton getViewButton() {
return viewButton;
}
/**
* 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() {
java.awt.GridBagConstraints gridBagConstraints;
personaIDLabel = new javax.swing.JLabel();
viewButton = new javax.swing.JButton();
setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(personaIDLabel, org.openide.util.NbBundle.getMessage(PersonaPanel.class, "PersonaPanel.personaIDLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0);
add(personaIDLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(viewButton, org.openide.util.NbBundle.getMessage(PersonaPanel.class, "PersonaPanel.viewButton.text")); // NOI18N
viewButton.setMargin(new java.awt.Insets(0, 5, 0, 5));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0);
add(viewButton, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel personaIDLabel;
private javax.swing.JButton viewButton;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,146 @@
/*
* 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 obt ain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.communications.relationships;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.AccountFileInstance;
import org.sleuthkit.datamodel.InvalidAccountIDException;
/**
* Runnable SwingWorker for gather the data that the Summary panel needs.
*/
class SummaryPanelWorker extends SwingWorker<SummaryPanelWorker.SummaryWorkerResults, Void> {
private final static Logger logger = Logger.getLogger(SummaryPanelWorker.class.getName());
private final Account account;
// Construct a instance
SummaryPanelWorker(Account account) {
this.account = account;
}
/**
* Returns the account the worker is gathering data for.
*
* @return
*/
Account getAccount() {
return account;
}
@Override
protected SummaryWorkerResults doInBackground() throws Exception {
CentralRepoAccount crAccount = null;
List<String> stringList = new ArrayList<>();
List<AccountFileInstance> accountFileInstanceList = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().getAccountFileInstances(account);
for (AccountFileInstance instance : accountFileInstanceList) {
stringList.add(instance.getFile().getUniquePath());
}
List<Persona> personaList = new ArrayList<>();
if (CentralRepository.isEnabled()) {
Collection<PersonaAccount> personaAccountList = PersonaAccount.getPersonaAccountsForAccount(account);
PersonaAccount.getPersonaAccountsForAccount(account);
for (PersonaAccount pAccount : personaAccountList) {
personaList.add(pAccount.getPersona());
}
try {
crAccount = CentralRepository.getInstance().getAccount(CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName()), account.getTypeSpecificID());
} catch (InvalidAccountIDException unused) {
// This was probably caused to a phone number not making
// threw the normalization.
logger.log(Level.WARNING, String.format("Exception thrown from CR getAccount for account %s (%d)", account.getTypeSpecificID(), account.getAccountID()));
}
}
return new SummaryWorkerResults(stringList, personaList, crAccount);
}
/**
* Wraps the results of the worker for easy of returning and usage by the
* SummaryViewer.
*/
final static class SummaryWorkerResults {
private final List<String> accountFileInstancePaths;
private final List<Persona> personaList;
private final CentralRepoAccount centralRepoAccount;
/**
* Constructor.
*
* @param accountFileInstancePaths List of instance paths.
* @param personaList List of personas for the account
* @param centralRepoAccount CentralRepoAccount for the given
* account, maybe null if CR is not
* enabled.
*/
SummaryWorkerResults(List<String> accountFileInstancePaths, List<Persona> personaList, CentralRepoAccount centralRepoAccount) {
this.accountFileInstancePaths = accountFileInstancePaths;
this.personaList = personaList;
this.centralRepoAccount = centralRepoAccount;
}
/**
* Returns the list of instance paths for the account given to the
* worker.
*
* @return
*/
List<String> getPaths() {
return accountFileInstancePaths;
}
/**
* Returns the list of personas found for the account given to the
* worker. This list maybe empty if none were found or cr is not
* enabled.
*
* @return
*/
List<Persona> getPersonaList() {
return personaList;
}
/**
* Return the cr account for the account given to the worker. This maybe
* null if the cr was not enabled.
*
* @return
*/
CentralRepoAccount getCRAccount() {
return centralRepoAccount;
}
}
}

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" 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,16,0,0,2,121"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="personaScrollPane">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="persona"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="messagePane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="message"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<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/communications/relationships/Bundle.properties" key="SummaryPersonaPane.messageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="createPersonaPanel">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 100]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="create"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="noPersonaLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryPersonaPane.noPersonaLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="7" insetsLeft="5" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="createButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryPersonaPane.createButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[0, 5, 0, 5]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="createButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="2" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,295 @@
/*
* 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 obt ain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.communications.relationships;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JPanel;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Account;
/**
* Panel to show the Personas for a given account. That is apart SummaryViewer.
*/
public final class SummaryPersonaPane extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private final static Logger logger = Logger.getLogger(SummaryPersonaPane.class.getName());
private final Map<Component, Persona> personaMap;
private final ViewButtonHandler viewButtonHandler = new ViewButtonHandler();
private CentralRepoAccount currentCRAccount = null;
private Account currentAccount = null;
/**
* Creates new form SummaryPersonaPane
*/
SummaryPersonaPane() {
initComponents();
personaScrollPane.setViewportView(new JPanel());
personaMap = new HashMap<>();
}
/**
* Clear the persona list.
*/
void clearList() {
personaScrollPane.setViewportView(new JPanel());
personaMap.clear();
}
/**
* Show the message panel.
*/
void showMessagePanel() {
CardLayout layout = (CardLayout) getLayout();
layout.show(this, "message");
}
/**
* Set the message that appears when the message panel is visible.
*
* @param message Message to show.
*/
void setMessage(String message) {
messageLabel.setText(message);
}
/**
* Update the list of personas to the new given list.
*
* @param personaList New list of personas to show
*/
void updatePersonaList(Account account, CentralRepoAccount crAccount, List<Persona> personaList) {
JPanel panel = new JPanel();
currentCRAccount = crAccount;
currentAccount = account;
CardLayout layout = (CardLayout) getLayout();
if (personaList.isEmpty()) {
layout.show(this, "create");
} else {
panel.setLayout(new GridLayout(personaList.size() + 1, 1));
int maxWidth = 0;
List<PersonaPanel> panelList = new ArrayList<>();
for (Persona persona : personaList) {
PersonaPanel personaPanel = new PersonaPanel(persona);
JButton viewButton = personaPanel.getViewButton();
panel.add(personaPanel);
personaMap.put(viewButton, persona);
viewButton.addActionListener(viewButtonHandler);
panelList.add(personaPanel);
maxWidth = Math.max(personaPanel.getPersonaLabelPreferedWidth(), maxWidth);
}
//Set the preferred width of the labeles to the buttons line up.
if (panelList.size() > 1) {
for (PersonaPanel ppanel : panelList) {
ppanel.setPersonalLabelPreferredWidth(maxWidth);
}
}
panel.add(Box.createVerticalGlue());
personaScrollPane.setViewportView(panel);
layout.show(this, "persona");
}
}
/**
* ActionListener to handle the launching of the view dialog for the given
* persona.
*/
final private class ViewButtonHandler implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
Persona persona = personaMap.get((Component) e.getSource());
new PersonaDetailsDialog(SummaryPersonaPane.this,
PersonaDetailsMode.VIEW, persona, new PersonaViewCallbackImpl());
}
}
/**
* Callback method for the view mode of the PersonaDetailsDialog
*/
private final class PersonaViewCallbackImpl implements PersonaDetailsDialogCallback {
@Override
public void callback(Persona persona) {
// nothing to do
}
}
/**
* Callback class to handle the creation of a new person for the given
* account
*/
private final class PersonaCreateCallbackImpl implements PersonaDetailsDialogCallback {
@Override
public void callback(Persona persona) {
if (persona != null) {
List<Persona> list = new ArrayList<>();
list.add(persona);
CentralRepoAccount crAccount = null;
Collection<PersonaAccount> personaAccounts = null;
try {
personaAccounts = persona.getPersonaAccounts();
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, String.format("Failed to get cr account from person %s (%d)", persona.getName(), persona.getId()), ex);
}
if (personaAccounts != null) {
Iterator<PersonaAccount> iterator = personaAccounts.iterator();
if (iterator.hasNext()) {
crAccount = iterator.next().getAccount();
}
}
updatePersonaList(currentAccount, crAccount, list);
}
}
}
/**
* 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() {
java.awt.GridBagConstraints gridBagConstraints;
personaScrollPane = new javax.swing.JScrollPane();
messagePane = new javax.swing.JPanel();
messageLabel = new javax.swing.JLabel();
createPersonaPanel = new javax.swing.JPanel();
noPersonaLabel = new javax.swing.JLabel();
createButton = new javax.swing.JButton();
setLayout(new java.awt.CardLayout());
personaScrollPane.setBorder(null);
add(personaScrollPane, "persona");
messagePane.setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(messageLabel, org.openide.util.NbBundle.getMessage(SummaryPersonaPane.class, "SummaryPersonaPane.messageLabel.text")); // NOI18N
messageLabel.setEnabled(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weighty = 1.0;
messagePane.add(messageLabel, gridBagConstraints);
add(messagePane, "message");
createPersonaPanel.setPreferredSize(new java.awt.Dimension(200, 100));
createPersonaPanel.setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(noPersonaLabel, org.openide.util.NbBundle.getMessage(SummaryPersonaPane.class, "SummaryPersonaPane.noPersonaLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(7, 5, 0, 0);
createPersonaPanel.add(noPersonaLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(createButton, org.openide.util.NbBundle.getMessage(SummaryPersonaPane.class, "SummaryPersonaPane.createButton.text")); // NOI18N
createButton.setMargin(new java.awt.Insets(0, 5, 0, 5));
createButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
createButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridheight = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 0);
createPersonaPanel.add(createButton, gridBagConstraints);
add(createPersonaPanel, "create");
}// </editor-fold>//GEN-END:initComponents
@NbBundle.Messages({
"# {0} - accountIdentifer",
"SummaryPersonaPane_not_account_in_cr=Unable to find an account with identifier {0} in the Central Repository."
})
private void createButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createButtonActionPerformed
PersonaDetailsDialog createPersonaDialog = new PersonaDetailsDialog(SummaryPersonaPane.this,
PersonaDetailsMode.CREATE, null, new PersonaCreateCallbackImpl(), false);
// Pre populate the persona name and accounts if we have them.
PersonaDetailsPanel personaPanel = createPersonaDialog.getDetailsPanel();
if (currentCRAccount != null) {
personaPanel.addAccount(currentCRAccount, "", Persona.Confidence.HIGH);
} else {
createPersonaDialog.setStartupPopupMessage(Bundle.SummaryPersonaPane_not_account_in_cr(currentAccount.getTypeSpecificID()));
}
personaPanel.setPersonaName(currentAccount.getTypeSpecificID());
// display the dialog now
createPersonaDialog.display();
}//GEN-LAST:event_createButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton createButton;
private javax.swing.JPanel createPersonaPanel;
private javax.swing.JLabel messageLabel;
private javax.swing.JPanel messagePane;
private javax.swing.JLabel noPersonaLabel;
private javax.swing.JScrollPane personaScrollPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -265,7 +265,7 @@
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
<GridBagConstraints gridX="0" gridY="5" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
@ -281,7 +281,7 @@
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="1.0"/>
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
@ -363,5 +363,32 @@
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="personaPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Personas">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.personaPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[35, 75]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[112, 75]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new SummaryPersonaPane()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,13 +19,11 @@
package org.sleuthkit.autopsy.communications.relationships;
import java.awt.CardLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.DefaultListModel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
import org.openide.explorer.view.OutlineView;
@ -33,11 +31,10 @@ import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AccountFileInstance;
/**
* Account Summary View Panel. This panel shows a list of various counts related
@ -47,6 +44,8 @@ import org.sleuthkit.datamodel.AccountFileInstance;
*/
public class SummaryViewer extends javax.swing.JPanel implements RelationshipsViewer {
private static final long serialVersionUID = 1L;
private final Lookup lookup;
private final DefaultListModel<String> fileRefListModel;
@ -62,7 +61,8 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
"SummaryViewer_Device_Account_Description=This account was referenced by a device in the case.",
"SummaryViewer_Account_Description=This account represents a device in the case.",
"SummaryViewer_Account_Description_MuliSelect=Summary information is not available when multiple accounts are selected.",
"SummaryViewer_Country_Code=Country: "
"SummaryViewer_Country_Code=Country: ",
"SummaryViewer_Select_account_for_persona=<Select a single account to see Persona(s)>"
})
/**
@ -85,6 +85,8 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
clearControls();
caseReferencesPanel.hideOutlineView(Bundle.SummaryViewer_CentralRepository_Message());
((SummaryPersonaPane)personaPanel).setMessage(Bundle.SummaryViewer_Select_account_for_persona());
((SummaryPersonaPane)personaPanel).showMessagePanel();
}
@Override
@ -153,7 +155,7 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
caseReferencesPanel.setNode(new AbstractNode(Children.create(new CorrelationCaseChildNodeFactory(info.getAccounts()), true)));
updateFileReferences(account);
updateOtherAccountInfo(account);
setEnabled(true);
}
@ -202,24 +204,17 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
}
@Messages({
"SummaryViewer_Fetching_References=<Fetching File References>"
"SummaryViewer_Fetching_References=<Fetching File References>",
"SummaryViewer_Persona_Message=<Enable Central Repository to view Personas>"
})
private void updateFileReferences(final Account account) {
SwingWorker<List<String>, Void> worker = new SwingWorker<List<String>, Void>() {
@Override
protected List<String> doInBackground() throws Exception {
List<String> stringList = new ArrayList<>();
List<AccountFileInstance> accountFileInstanceList = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().getAccountFileInstances(account);
for (AccountFileInstance instance : accountFileInstanceList) {
stringList.add(instance.getFile().getUniquePath());
}
return stringList;
}
private void updateOtherAccountInfo(final Account account) {
SummaryPanelWorker worker = new SummaryPanelWorker(account) {
@Override
protected void done() {
try {
List<String> fileRefList = get();
SummaryPanelWorker.SummaryWorkerResults results = get();
List<String> fileRefList = results.getPaths();
fileRefList.forEach(value -> {
fileRefListModel.addElement(value);
@ -228,8 +223,18 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
CardLayout cardLayout = (CardLayout) fileRefPane.getLayout();
cardLayout.show(fileRefPane, "listPanelCard");
List<Persona> personaList = results.getPersonaList();
if (CentralRepository.isEnabled()) {
((SummaryPersonaPane) personaPanel).updatePersonaList(account, results.getCRAccount(), personaList);
} else {
((SummaryPersonaPane) personaPanel).setMessage("Bundle.SummaryViewer_Persona_Message()");
((SummaryPersonaPane) personaPanel).showMessagePanel();
}
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.WARNING, String.format(("Failed to get file references for account: %d"), account.getAccountID()), ex);
logger.log(Level.WARNING, String.format(("Failed to get data for account: %d"), account.getAccountID()), ex);
}
}
};
@ -273,6 +278,7 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
fileRefList = new javax.swing.JList<>();
javax.swing.JPanel selectAccountPane = new javax.swing.JPanel();
selectAccountFileRefLabel = new javax.swing.JLabel();
personaPanel = new SummaryPersonaPane();
setLayout(new java.awt.GridBagLayout());
@ -430,7 +436,7 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
caseReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.caseReferencesPanel.border.title"))); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 4;
gridBagConstraints.gridy = 5;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
@ -464,11 +470,19 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridy = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weighty = 1.0;
add(fileRefPane, gridBagConstraints);
personaPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.personaPanel.border.title"))); // NOI18N
personaPanel.setMinimumSize(new java.awt.Dimension(35, 75));
personaPanel.setPreferredSize(new java.awt.Dimension(112, 75));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
add(personaPanel, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
@ -489,6 +503,7 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
private javax.swing.JPanel fileRefPane;
private javax.swing.JLabel messagesDataLabel;
private javax.swing.JLabel messagesLabel;
private javax.swing.JPanel personaPanel;
private javax.swing.JLabel referencesDataLabel;
private javax.swing.JLabel referencesLabel;
private javax.swing.JLabel selectAccountFileRefLabel;

View File

@ -521,7 +521,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
if (nonNull(fxImage)) {
// We have a non-null image, so let's show it.
fxImageView.setImage(fxImage);
if (panelWidth != 0 && panelHeight != 0) {
resetView(panelWidth, panelHeight);
}
masterGroup.getChildren().add(fxImageView);
masterGroup.getChildren().add(tagsGroup);

View File

@ -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

View File

@ -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
@ -41,6 +26,8 @@ ContactArtifactViewer_cr_disabled_message=Enable Central Repository to view, cre
ContactArtifactViewer_emails_header=Email
ContactArtifactViewer_found_all_accounts_label=All accounts found.
ContactArtifactViewer_heading_Source=Source
# {0} - accountIdentifer
ContactArtifactViewer_id_not_found_in_cr=Unable to find account(s) associated with contact {0} in the Central Repository.
ContactArtifactViewer_label_datasource=Data Source
ContactArtifactViewer_missing_account_label=Missing contact account
ContactArtifactViewer_others_header=Other
@ -56,16 +43,36 @@ 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.dates.created=Created
GeneralPurposeArtifactViewer.dates.end=End
GeneralPurposeArtifactViewer.dates.start=Start
GeneralPurposeArtifactViewer.dates.time=Time
GeneralPurposeArtifactViewer.details.attrHeader=Details
GeneralPurposeArtifactViewer.details.bookmarkHeader=Bookmark Details
GeneralPurposeArtifactViewer.details.cachedHeader=Cached File
GeneralPurposeArtifactViewer.details.cookieHeader=Cookie Details
GeneralPurposeArtifactViewer.details.dataSource=Data Source
GeneralPurposeArtifactViewer.details.datesHeader=Dates
GeneralPurposeArtifactViewer.details.downloadHeader=Downloaded File
GeneralPurposeArtifactViewer.details.file=File
GeneralPurposeArtifactViewer.details.historyHeader=Visit Details
GeneralPurposeArtifactViewer.details.otherHeader=Other
GeneralPurposeArtifactViewer.details.searchHeader=Web Search
GeneralPurposeArtifactViewer.details.sourceHeader=Source
GeneralPurposeArtifactViewer.noFile.text=\ (no longer exists)
GeneralPurposeArtifactViewer.term.label=Term
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
MessageAccountPanel_contact_label=Contact:
MessageAccountPanel_copy_label=Copy
# {0} - accountIdentifer
MessageAccountPanel_id_not_found_in_cr=Unable to find an account with identifier {0} in the Central Repository.
MessageAccountPanel_no_matches=No matches found.
MessageAccountPanel_persona_label=Persona:
MessageAccountPanel_unknown_label=Unknown
@ -87,6 +94,24 @@ 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} - accountIdentifer
PersonaAccountFetcher_not_account_in_cr=Unable to find an account with identifier {0} in the Central Repository.
# {0} - Persona count
PersonaDisplayTask_persona_count_suffix=(1 of {0})

View File

@ -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

View File

@ -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.

View File

@ -40,6 +40,7 @@ import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.SwingWorker;
import org.apache.commons.lang.StringUtils;
@ -751,7 +752,9 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
}
@NbBundle.Messages({
"ContactArtifactViewer_persona_account_justification=Account found in Contact artifact"
"ContactArtifactViewer_persona_account_justification=Account found in Contact artifact",
"# {0} - accountIdentifer",
"ContactArtifactViewer_id_not_found_in_cr=Unable to find account(s) associated with contact {0} in the Central Repository."
})
@Override
@ -772,6 +775,10 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
personaPanel.addAccount(account, Bundle.ContactArtifactViewer_persona_account_justification(), Persona.Confidence.HIGH);
}
if(contactName != null && contactUniqueAccountsList.isEmpty()) {
createPersonaDialog.setStartupPopupMessage(Bundle.ContactArtifactViewer_id_not_found_in_cr(contactName));
}
// display the dialog now
createPersonaDialog.display();
}

View File

@ -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, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="DefaultTableArtifactContentViewer.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="DefaultTableArtifactContentViewer.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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>

View File

@ -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);
@ -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);
@ -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
@ -427,9 +427,9 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
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
@ -468,6 +468,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
* 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) {
@ -490,13 +491,12 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
*/
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);
}

View File

@ -0,0 +1,40 @@
<?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,0,0,0,0,0,0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="detailsPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="First"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,536 @@
/*
* 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.Arrays;
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.apache.commons.lang.StringUtils;
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.datamodel.ContentUtils;
import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TimeUtilities;
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 static final List<Integer> TYPES_WITH_DATE_SECTION = Arrays.asList(new Integer[]{BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE.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() {
addOrderings();
initComponents();
detailsPanel.setLayout(gridBagLayout);
}
/**
* Private helper method to add the orderings used for each artifact type to
* the map for lookup.
*/
private void addOrderings() {
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID(), new Integer[]{BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE.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_PROG_NAME.getTypeID()});
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID(), new Integer[]{BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.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_HEADERS.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID()});
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), new Integer[]{BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.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_PATH.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID()});
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), new Integer[]{BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.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_PROG_NAME.getTypeID()});
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(), new Integer[]{BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_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()});
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID(), new Integer[]{BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID()});
}
@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().getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get attributes for artifact " + artifact.getArtifactID(), ex);
}
updateView(artifact, attributeMap, dataSourceName, sourceFileName);
}
revalidate();
repaint();
}
/**
* Reset the panel so that it is empty.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void resetComponent() {
// clear the panel
detailsPanel.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_AS_NEEDED);
}
@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()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_ACCOUNT_TYPE.getTypeID()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL.getTypeID());
}
@NbBundle.Messages({"GeneralPurposeArtifactViewer.details.attrHeader=Details",
"GeneralPurposeArtifactViewer.details.sourceHeader=Source",
"GeneralPurposeArtifactViewer.details.dataSource=Data Source",
"GeneralPurposeArtifactViewer.details.file=File",
"GeneralPurposeArtifactViewer.details.datesHeader=Dates"})
/**
* 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() {
detailsPanel = new javax.swing.JPanel();
setLayout(new java.awt.BorderLayout());
javax.swing.GroupLayout detailsPanelLayout = new javax.swing.GroupLayout(detailsPanel);
detailsPanel.setLayout(detailsPanelLayout);
detailsPanelLayout.setHorizontalGroup(
detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
detailsPanelLayout.setVerticalGroup(
detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
add(detailsPanel, java.awt.BorderLayout.PAGE_START);
}// </editor-fold>//GEN-END:initComponents
/**
* Update the view to reflect the current artifact's details.
*
* @param artifact 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.
*/
@NbBundle.Messages({"GeneralPurposeArtifactViewer.dates.created=Created",
"GeneralPurposeArtifactViewer.dates.start=Start",
"GeneralPurposeArtifactViewer.dates.end=End",
"GeneralPurposeArtifactViewer.dates.time=Time",
"GeneralPurposeArtifactViewer.term.label=Term",
"GeneralPurposeArtifactViewer.details.otherHeader=Other",
"GeneralPurposeArtifactViewer.noFile.text= (no longer exists)"})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void updateView(BlackboardArtifact artifact, Map<Integer, List<BlackboardAttribute>> attributeMap, String dataSourceName, String sourceFilePath) {
final Integer artifactTypeId = artifact.getArtifactTypeID();
if (!(artifactTypeId < 1 || artifactTypeId >= Integer.MAX_VALUE)) {
addDetailsHeader(artifactTypeId);
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) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) {
if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()) {
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_dates_time(), TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact)));
} else {
addNameValueRow(bba.getAttributeType().getDisplayName(), TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact)));
}
} else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID() && artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()) {
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_term_label(), bba.getDisplayString());
} else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
String displayString = bba.getDisplayString();
if (!attributeMap.containsKey(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID())) {
displayString += Bundle.GeneralPurposeArtifactViewer_noFile_text();
}
addNameValueRow(bba.getAttributeType().getDisplayName(), displayString);
} else {
addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
}
}
}
if (TYPES_WITH_DATE_SECTION.contains(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID())) {
boolean headerAdded = false;
headerAdded = addDates(Bundle.GeneralPurposeArtifactViewer_dates_created(), attributeMap.remove(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID()), headerAdded);
headerAdded = addDates(Bundle.GeneralPurposeArtifactViewer_dates_start(), attributeMap.remove(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID()), headerAdded);
headerAdded = addDates(Bundle.GeneralPurposeArtifactViewer_dates_end(), attributeMap.remove(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID()), headerAdded);
addDates(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getDisplayName(), attributeMap.remove(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID()), headerAdded);
}
if (!attributeMap.keySet().isEmpty()) {
addHeader(Bundle.GeneralPurposeArtifactViewer_details_otherHeader());
for (int key : attributeMap.keySet()) {
for (BlackboardAttribute bba : attributeMap.get(key)) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) {
addNameValueRow(bba.getAttributeType().getDisplayName(), TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact)));
} else {
addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
}
}
}
addHeader(Bundle.GeneralPurposeArtifactViewer_details_sourceHeader());
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_dataSource(), dataSourceName);
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_file(), sourceFilePath);
// add veritcal glue at the end
addPageEndGlue();
}
}
/**
* Private helper method to add all dates in a given attribute list.
*
* @param label Specific String to use in place of attributes display
* name.
* @param attrList List of attributes to add dates for.
* @param headerExists If the "Dates" header has already been displayed.
*
* @return True if the "Dates" header has been displayed, false otherwise.
*/
private boolean addDates(String label, List<BlackboardAttribute> attrList, boolean headerExists) {
boolean headerAdded = headerExists;
if (attrList != null) {
if (!headerAdded) {
addHeader(Bundle.GeneralPurposeArtifactViewer_details_datesHeader());
headerAdded = true;
}
String labelToUse = label;
for (BlackboardAttribute bba : attrList) {
if (StringUtils.isBlank(label)) {
labelToUse = bba.getAttributeType().getDisplayName();
}
addNameValueRow(labelToUse, bba.getDisplayString());
}
}
return headerAdded;
}
/**
* Helper method to add an artifact specific details header.
*
* @param artifactTypeId ID of artifact type to add header for.
*/
@NbBundle.Messages({"GeneralPurposeArtifactViewer.details.bookmarkHeader=Bookmark Details",
"GeneralPurposeArtifactViewer.details.historyHeader=Visit Details",
"GeneralPurposeArtifactViewer.details.downloadHeader=Downloaded File",
"GeneralPurposeArtifactViewer.details.searchHeader=Web Search",
"GeneralPurposeArtifactViewer.details.cachedHeader=Cached File",
"GeneralPurposeArtifactViewer.details.cookieHeader=Cookie Details",})
private void addDetailsHeader(int artifactTypeId) {
String header;
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) {
header = Bundle.GeneralPurposeArtifactViewer_details_historyHeader();
} else if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID()) {
header = Bundle.GeneralPurposeArtifactViewer_details_bookmarkHeader();
} else if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID()) {
header = Bundle.GeneralPurposeArtifactViewer_details_cachedHeader();
} else if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID()) {
header = Bundle.GeneralPurposeArtifactViewer_details_cookieHeader();
} else if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()) {
header = Bundle.GeneralPurposeArtifactViewer_details_downloadHeader();
} else if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()) {
header = Bundle.GeneralPurposeArtifactViewer_details_searchHeader();
} else {
header = Bundle.GeneralPurposeArtifactViewer_details_attrHeader();
}
addHeader(header);
}
/**
* 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++;
detailsPanel.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
detailsPanel.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));
detailsPanel.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));
detailsPanel.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
detailsPanel.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);
// scroll to start of text
valueField.setCaretPosition(0);
// 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
detailsPanel.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
private javax.swing.JPanel detailsPanel;
// End of variables declaration//GEN-END:variables
}

View File

@ -562,7 +562,9 @@ final class MessageAccountPanel extends JPanel {
}
@NbBundle.Messages({
"MessageAccountPanel.account.justification=Account found in Message artifact"
"MessageAccountPanel.account.justification=Account found in Message artifact",
"# {0} - accountIdentifer",
"MessageAccountPanel_id_not_found_in_cr=Unable to find an account with identifier {0} in the Central Repository."
})
@Override
public void actionPerformed(ActionEvent e) {
@ -591,6 +593,8 @@ final class MessageAccountPanel extends JPanel {
CentralRepoAccount account = CentralRepository.getInstance().getAccount(type, accountContainer.getAccount().getTypeSpecificID());
if (account != null) {
personaPanel.addAccount(account, Bundle.MessageAccountPanel_account_justification(), Persona.Confidence.HIGH);
} else {
createPersonaDialog.setStartupPopupMessage(Bundle.MessageAccountPanel_id_not_found_in_cr(accountContainer.getAccount().getTypeSpecificID()));
}
} catch (InvalidAccountIDException ex2) {
// These are expected when the account identifier doesn't match the format of the account type.

View File

@ -180,7 +180,9 @@ class PersonaAccountFetcher extends SwingWorker<Map<String, Collection<Persona>>
}
@NbBundle.Messages({
"PersonaAccountFetcher.account.justification=Account found in Call Log artifact"
"PersonaAccountFetcher.account.justification=Account found in Call Log artifact",
"# {0} - accountIdentifer",
"PersonaAccountFetcher_not_account_in_cr=Unable to find an account with identifier {0} in the Central Repository."
})
@Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
@ -206,6 +208,11 @@ class PersonaAccountFetcher extends SwingWorker<Map<String, Collection<Persona>>
if (account != null) {
personaPanel.addAccount(account, Bundle.PersonaAccountFetcher_account_justification(), Persona.Confidence.HIGH);
}
if((personaSearcherData.getAccountIdentifer() != null &&
!personaSearcherData.getAccountIdentifer().isEmpty()) && account == null) {
dialog.setStartupPopupMessage(Bundle.PersonaAccountFetcher_not_account_in_cr(personaSearcherData.getAccountIdentifer()));
}
} catch (InvalidAccountIDException ex2) {
// These are expected when the account identifier doesn't match the format of the account type.
}

View File

@ -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;
}
/**

View File

@ -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;
}
/**

View File

@ -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());
}
/**
@ -533,6 +539,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
private javax.swing.JScrollPane jScrollPane;

View File

@ -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();

View File

@ -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;
}
}

View File

@ -21,7 +21,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

View File

@ -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

View File

@ -354,20 +354,33 @@ public class ServicesMonitor {
KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
try {
if (kwsService != null) {
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);
setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.UP.toString(), "");
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);
}
}

View File

@ -55,8 +55,12 @@ 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
@ -85,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() {
@ -326,19 +331,51 @@ 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) {
@ -632,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, "");
}
}

View File

@ -9,11 +9,6 @@
<Component class="javax.swing.ButtonGroup" name="logoSourceButtonGroup">
</Component>
</NonVisualComponents>
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="null"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -44,50 +39,22 @@
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="null"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="jPanel1">
<Container class="javax.swing.JPanel" name="mainPanel">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[648, 382]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="null"/>
</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>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Component id="logoPanel" max="32767" attributes="0"/>
<Component id="tempDirectoryPanel" alignment="0" max="32767" attributes="0"/>
<Component id="runtimePanel" min="-2" pref="631" max="-2" attributes="0"/>
</Group>
</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="runtimePanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Component id="tempDirectoryPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="logoPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="logoPanel">
<Properties>
@ -99,6 +66,11 @@
</Border>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
@ -141,7 +113,7 @@
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="agencyLogoPreview" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="12" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -234,6 +206,11 @@
</Border>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
@ -455,6 +432,11 @@
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempDirectoryPanel.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
@ -518,6 +500,103 @@
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="rdpPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Windows High DPI Scaling">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.rdpPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[33, 100]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 150]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="sizingScrollPane">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextPane" name="sizingTextPane">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.sizingTextPane.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="opaque" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Component class="javax.swing.Box$Filler" name="filler1">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 0]"/>
</Property>
</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.HorizontalGlue"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<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, 32767]"/>
</Property>
</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>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>

View File

@ -557,12 +557,13 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
fileSelectionButtonGroup = new javax.swing.ButtonGroup();
displayTimesButtonGroup = new javax.swing.ButtonGroup();
logoSourceButtonGroup = new javax.swing.ButtonGroup();
jScrollPane1 = new javax.swing.JScrollPane();
jPanel1 = new javax.swing.JPanel();
javax.swing.JPanel mainPanel = new javax.swing.JPanel();
logoPanel = new javax.swing.JPanel();
agencyLogoPathField = new javax.swing.JTextField();
browseLogosButton = new javax.swing.JButton();
@ -590,14 +591,16 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
tempDirectoryField = new javax.swing.JTextField();
tempDirectoryBrowseButton = new javax.swing.JButton();
tempDirectoryWarningLabel = new javax.swing.JLabel();
setPreferredSize(null);
rdpPanel = new javax.swing.JPanel();
javax.swing.JScrollPane sizingScrollPane = new javax.swing.JScrollPane();
javax.swing.JTextPane sizingTextPane = new javax.swing.JTextPane();
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
jScrollPane1.setBorder(null);
jScrollPane1.setPreferredSize(null);
jPanel1.setMinimumSize(new java.awt.Dimension(648, 382));
jPanel1.setPreferredSize(null);
mainPanel.setMinimumSize(new java.awt.Dimension(648, 382));
mainPanel.setLayout(new java.awt.GridBagLayout());
logoPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.logoPanel.border.title"))); // NOI18N
@ -670,9 +673,16 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addComponent(browseLogosButton)))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, logoPanelLayout.createSequentialGroup()
.addComponent(agencyLogoPreview, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 12, Short.MAX_VALUE))
.addGap(0, 0, Short.MAX_VALUE))
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
mainPanel.add(logoPanel, gridBagConstraints);
runtimePanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.runtimePanel.border.title"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(maxMemoryLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.maxMemoryLabel.text")); // NOI18N
@ -797,6 +807,13 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addContainerGap())
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
mainPanel.add(runtimePanel, gridBagConstraints);
tempDirectoryPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryPanel.border.title"))); // NOI18N
tempDirectoryPanel.setName(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryPanel.name")); // NOI18N
@ -838,32 +855,51 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addContainerGap())
);
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(logoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(tempDirectoryPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(runtimePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 631, javax.swing.GroupLayout.PREFERRED_SIZE)))
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(runtimePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(3, 3, 3)
.addComponent(tempDirectoryPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(logoPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
mainPanel.add(tempDirectoryPanel, gridBagConstraints);
tempDirectoryPanel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryPanel.AccessibleContext.accessibleName")); // NOI18N
jScrollPane1.setViewportView(jPanel1);
rdpPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.rdpPanel.border.title"))); // NOI18N
rdpPanel.setMinimumSize(new java.awt.Dimension(33, 100));
rdpPanel.setPreferredSize(new java.awt.Dimension(100, 150));
rdpPanel.setLayout(new java.awt.GridBagLayout());
sizingScrollPane.setBorder(null);
sizingTextPane.setEditable(false);
sizingTextPane.setBorder(null);
sizingTextPane.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.sizingTextPane.text")); // NOI18N
sizingTextPane.setOpaque(false);
sizingScrollPane.setViewportView(sizingTextPane);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
rdpPanel.add(sizingScrollPane, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
mainPanel.add(rdpPanel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 1.0;
mainPanel.add(filler1, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.weighty = 1.0;
mainPanel.add(filler2, gridBagConstraints);
jScrollPane1.setViewportView(mainPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
@ -902,6 +938,33 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}
}//GEN-LAST:event_tempDirectoryBrowseButtonActionPerformed
private void solrMaxHeapSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_solrMaxHeapSpinnerStateChanged
int value = (int) solrMaxHeapSpinner.getValue();
if (value == UserPreferences.getMaxSolrVMSize()) {
// if the value hasn't changed there's nothing to do.
return;
}
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_solrMaxHeapSpinnerStateChanged
private void logFileCountKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_logFileCountKeyReleased
String count = logFileCount.getText();
if (count.equals(String.valueOf(UserPreferences.getDefaultLogFileCount()))) {
//if it is still the default value don't fire change
return;
}
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_logFileCountKeyReleased
private void memFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_memFieldKeyReleased
String memText = memField.getText();
if (memText.equals(initialMemValue)) {
//if it is still the initial value don't fire change
return;
}
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_memFieldKeyReleased
private void specifyLogoRBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_specifyLogoRBActionPerformed
agencyLogoPathField.setEnabled(true);
browseLogosButton.setEnabled(true);
@ -953,33 +1016,6 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}
}//GEN-LAST:event_browseLogosButtonActionPerformed
private void solrMaxHeapSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_solrMaxHeapSpinnerStateChanged
int value = (int) solrMaxHeapSpinner.getValue();
if (value == UserPreferences.getMaxSolrVMSize()) {
// if the value hasn't changed there's nothing to do.
return;
}
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_solrMaxHeapSpinnerStateChanged
private void logFileCountKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_logFileCountKeyReleased
String count = logFileCount.getText();
if (count.equals(String.valueOf(UserPreferences.getDefaultLogFileCount()))) {
//if it is still the default value don't fire change
return;
}
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_logFileCountKeyReleased
private void memFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_memFieldKeyReleased
String memText = memField.getText();
if (memText.equals(initialMemValue)) {
//if it is still the initial value don't fire change
return;
}
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_memFieldKeyReleased
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextField agencyLogoPathField;
private javax.swing.JLabel agencyLogoPathFieldValidationLabel;
@ -988,7 +1024,6 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private javax.swing.JRadioButton defaultLogoRB;
private javax.swing.ButtonGroup displayTimesButtonGroup;
private javax.swing.ButtonGroup fileSelectionButtonGroup;
private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextField logFileCount;
private javax.swing.JTextField logNumAlert;
@ -1002,6 +1037,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private javax.swing.JLabel maxSolrMemoryLabel;
private javax.swing.JTextField memField;
private javax.swing.JLabel memFieldValidationLabel;
private javax.swing.JPanel rdpPanel;
private javax.swing.JLabel restartNecessaryWarning;
private javax.swing.JPanel runtimePanel;
private javax.swing.JLabel solrJVMHeapWarning;

View File

@ -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=
@ -90,7 +93,7 @@ DataContentViewerHex.goToOffsetTextField.msgDlg=Invalid Offset: {0}
DataContentViewerHex.setDataView.invalidOffset.negativeOffsetValue=Cannot jump to the resultant offset
MultiUserSettingsPanel.tbOops.text=
MultiUserSettingsPanel.lbTestDatabase.text=
MultiUserSettingsPanel.bnTestDatabase.text=Test
MultiUserSettingsPanel.bnTestDatabase.text=Test Connection
MultiUserSettingsPanel.tbDbHostname.toolTipText=Hostname or IP Address
MultiUserSettingsPanel.tbDbHostname.text=
MultiUserSettingsPanel.tbDbPort.toolTipText=Port Number
@ -99,12 +102,8 @@ MultiUserSettingsPanel.tbDbUsername.toolTipText=User Name
MultiUserSettingsPanel.tbDbUsername.text=
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.bnTestMessageService.text=Test Connection
MultiUserSettingsPanel.lbMessageServiceSettings.text=ActiveMQ Message Server Settings
MultiUserSettingsPanel.tbMsgPort.toolTipText=Port Number
MultiUserSettingsPanel.tbMsgPort.text=
@ -115,9 +114,11 @@ MultiUserSettingsPanel.tbMsgPassword.text=
MultiUserSettingsPanel.tbMsgHostname.toolTipText=Hostname or IP Address
MultiUserSettingsPanel.tbMsgHostname.text=
MultiUserSettingsPanel.lbTestMessageWarning.text=
MultiUserSettingsPanel.lbTestSolrWarning.text=
MultiUserSettingsPanel.lbTestDbWarning.text=
MultiUserSettingsPanel.KeywordSearchNull=Cannot find keyword search service
MultiUserSettingsPanel.Solr4ConnectionInfoMissing.error=Missing or incomplete Solr 4 connection info
MultiUserSettingsPanel.Solr8ConnectionInfoMissing.error=Missing or incomplete Solr 8 connection info
MultiUserSettingsPanel.UnableToConnectToZK=Unable to connect to ZooKeeper service
MultiUserSettingsPanel.InvalidPortNumber=Invalid port number
AutopsyOptionsPanel.agencyLogoImageLabel.toolTipText=
SortChooserDialog.label=remove
@ -175,13 +176,19 @@ DataResultViewerTable.pagesLabel.text=Pages:
DataResultViewerTable.pageNumLabel.text=
DataResultViewerTable.pageLabel.text=Page:
DataResultViewerTable.exportCSVButton.text=Save Table as CSV
AutopsyOptionsPanel.logoPanel.border.title=Logo
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text=
AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo
AutopsyOptionsPanel.defaultLogoRB.text=Use default
AutopsyOptionsPanel.agencyLogoPreview.text=<html><div style='text-align: center;'>No logo<br>selected</div></html>
AutopsyOptionsPanel.browseLogosButton.text=Browse
AutopsyOptionsPanel.agencyLogoPathField.text=
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. Embedded ZooKeeper usually run on the same host as Solr server.
MultiUserSettingsPanel.tbZkPort.toolTipText=ZooKeeper port number. Embedded ZooKeeper uses port number that is 1,000 greater than the Solr service port number. For example, if Solr service port number is 8983 then embedded ZooKeeper port number is 9983.
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=Enter Solr 8 and/or Solr 4 server settings.
MultiUserSettingsPanel.lbSolrNote2.text=New text indexing can only be done with Solr 8.
AutopsyOptionsPanel.tempDirectoryField.text=
AutopsyOptionsPanel.tempDirectoryBrowseButton.text=Browse
AutopsyOptionsPanel.a.AccessibleContext.accessibleName=Temp Directory
@ -223,3 +230,19 @@ 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
MultiUserSettingsPanel.bnTestSolr8.text=Test Connection
MultiUserSettingsPanel.lbTestSolr8.text=
MultiUserSettingsPanel.bnTestSolr4.text=Test Connection
MultiUserSettingsPanel.lbTestSolr4.text=
MultiUserSettingsPanel.bnTestZK.text=Test Connection
MultiUserSettingsPanel.lbTestZK.text=
MultiUserSettingsPanel.lbWarning.text=
AutopsyOptionsPanel.rdpPanel.border.title=Windows High DPI Scaling
AutopsyOptionsPanel.sizingTextPane.text=Make the following changes if the application is hard to navigate in High DPI systems:\n\n1. Right click on the application icon on your Desktop, Start Menu, etc.\n2. Choose Properties\n3. Go to Compatibility tab\n4. Click "Change high DPI settings" button\n5. Select "Override high DPI scaling behavior" \n6. Change the "Scaling performed by:" drop down box to "System"\n7. Restart Autopsy
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text=
AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo
AutopsyOptionsPanel.defaultLogoRB.text=Use default
AutopsyOptionsPanel.agencyLogoPreview.text=<html><div style='text-align: center;'>No logo<br>selected</div></html>
AutopsyOptionsPanel.browseLogosButton.text=Browse
AutopsyOptionsPanel.agencyLogoPathField.text=
AutopsyOptionsPanel.logoPanel.border.title=Logo

View File

@ -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=
@ -145,7 +148,7 @@ DataContentViewerHex.goToOffsetTextField.msgDlg=Invalid Offset: {0}
DataContentViewerHex.setDataView.invalidOffset.negativeOffsetValue=Cannot jump to the resultant offset
MultiUserSettingsPanel.tbOops.text=
MultiUserSettingsPanel.lbTestDatabase.text=
MultiUserSettingsPanel.bnTestDatabase.text=Test
MultiUserSettingsPanel.bnTestDatabase.text=Test Connection
MultiUserSettingsPanel.tbDbHostname.toolTipText=Hostname or IP Address
MultiUserSettingsPanel.tbDbHostname.text=
MultiUserSettingsPanel.tbDbPort.toolTipText=Port Number
@ -154,12 +157,8 @@ MultiUserSettingsPanel.tbDbUsername.toolTipText=User Name
MultiUserSettingsPanel.tbDbUsername.text=
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.bnTestMessageService.text=Test Connection
MultiUserSettingsPanel.lbMessageServiceSettings.text=ActiveMQ Message Server Settings
MultiUserSettingsPanel.tbMsgPort.toolTipText=Port Number
MultiUserSettingsPanel.tbMsgPort.text=
@ -170,9 +169,11 @@ MultiUserSettingsPanel.tbMsgPassword.text=
MultiUserSettingsPanel.tbMsgHostname.toolTipText=Hostname or IP Address
MultiUserSettingsPanel.tbMsgHostname.text=
MultiUserSettingsPanel.lbTestMessageWarning.text=
MultiUserSettingsPanel.lbTestSolrWarning.text=
MultiUserSettingsPanel.lbTestDbWarning.text=
MultiUserSettingsPanel.KeywordSearchNull=Cannot find keyword search service
MultiUserSettingsPanel.Solr4ConnectionInfoMissing.error=Missing or incomplete Solr 4 connection info
MultiUserSettingsPanel.Solr8ConnectionInfoMissing.error=Missing or incomplete Solr 8 connection info
MultiUserSettingsPanel.UnableToConnectToZK=Unable to connect to ZooKeeper service
MultiUserSettingsPanel.InvalidPortNumber=Invalid port number
AutopsyOptionsPanel.agencyLogoImageLabel.toolTipText=
SortChooserDialog.label=remove
@ -232,13 +233,19 @@ DataResultViewerTable.pagesLabel.text=Pages:
DataResultViewerTable.pageNumLabel.text=
DataResultViewerTable.pageLabel.text=Page:
DataResultViewerTable.exportCSVButton.text=Save Table as CSV
AutopsyOptionsPanel.logoPanel.border.title=Logo
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text=
AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo
AutopsyOptionsPanel.defaultLogoRB.text=Use default
AutopsyOptionsPanel.agencyLogoPreview.text=<html><div style='text-align: center;'>No logo<br>selected</div></html>
AutopsyOptionsPanel.browseLogosButton.text=Browse
AutopsyOptionsPanel.agencyLogoPathField.text=
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. Embedded ZooKeeper usually run on the same host as Solr server.
MultiUserSettingsPanel.tbZkPort.toolTipText=ZooKeeper port number. Embedded ZooKeeper uses port number that is 1,000 greater than the Solr service port number. For example, if Solr service port number is 8983 then embedded ZooKeeper port number is 9983.
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=Enter Solr 8 and/or Solr 4 server settings.
MultiUserSettingsPanel.lbSolrNote2.text=New text indexing can only be done with Solr 8.
AutopsyOptionsPanel.tempDirectoryField.text=
AutopsyOptionsPanel.tempDirectoryBrowseButton.text=Browse
AutopsyOptionsPanel.a.AccessibleContext.accessibleName=Temp Directory
@ -280,3 +287,20 @@ 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
MultiUserSettingsPanel.bnTestSolr8.text=Test Connection
MultiUserSettingsPanel.lbTestSolr8.text=
MultiUserSettingsPanel.bnTestSolr4.text=Test Connection
MultiUserSettingsPanel.lbTestSolr4.text=
MultiUserSettingsPanel.bnTestZK.text=Test Connection
MultiUserSettingsPanel.lbTestZK.text=
MultiUserSettingsPanel.lbWarning.text=
AutopsyOptionsPanel.rdpPanel.border.title=Windows High DPI Scaling
AutopsyOptionsPanel.sizingTextPane.text=Make the following changes if the application is hard to navigate in High DPI systems:\n\n1. Right click on the application icon on your Desktop, Start Menu, etc.\n2. Choose Properties\n3. Go to Compatibility tab\n4. Click "Change high DPI settings" button\n5. Select "Override high DPI scaling behavior" \n6. Change the "Scaling performed by:" drop down box to "System"\n7. Restart Autopsy
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text=
AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo
AutopsyOptionsPanel.defaultLogoRB.text=Use default
AutopsyOptionsPanel.agencyLogoPreview.text=<html><div style='text-align: center;'>No logo<br>selected</div></html>
AutopsyOptionsPanel.browseLogosButton.text=Browse
AutopsyOptionsPanel.agencyLogoPathField.text=
AutopsyOptionsPanel.logoPanel.border.title=Logo

View File

@ -10,19 +10,13 @@ AddExternalViewerRulePanel.nameTextField.text=
AdvancedConfigurationDialog.applyButton.text=OK
AdvancedConfigurationDialog.cancelButton.text=\u53d6\u308a\u6d88\u3057
AutopsyOptionsPanel.agencyLogoImageLabel.toolTipText=
AutopsyOptionsPanel.agencyLogoPathField.text=
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.invalidImageSpecified.text=\u7121\u52b9\u306e\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u304c\u6307\u5b9a\u3055\u308c\u307e\u3057\u305f\u3002
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.invalidPath.text=\u30d1\u30b9\u306f\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.pathNotSet.text=\u30a8\u30fc\u30b8\u30a7\u30f3\u30b7\u30fc\u30ed\u30b4\u30d1\u30b9\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text=
AutopsyOptionsPanel.agencyLogoPreview.text=<html><div style\='text-align\: center;'>\u30ed\u30b4\u304c\u3042\u308a\u307e\u305b\u3093<br>selected</div></html>
AutopsyOptionsPanel.browseLogosButton.text=\u53c2\u7167
AutopsyOptionsPanel.defaultLogoRB.text=\u30c7\u30d5\u30a9\u30eb\u30c8\u3092\u4f7f\u7528
AutopsyOptionsPanel.invalidImageFile.msg=\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u3092\u30a8\u30fc\u30b8\u30a7\u30f3\u30b7\u30fc\u30ed\u30b4\u3068\u3057\u3066\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
AutopsyOptionsPanel.invalidImageFile.title=\u7121\u52b9\u306a\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u3067\u3059
AutopsyOptionsPanel.logNumAlert.invalidInput.text=\u3053\u3053\u3067\u306f\u6b63\u306e\u6574\u6570\u304c\u5fc5\u8981\u3067\u3059\u3002
AutopsyOptionsPanel.logNumAlert.text=
AutopsyOptionsPanel.logoPanel.border.title=\u30ed\u30b4
AutopsyOptionsPanel.maxLogFileCount.text=\u6700\u5927\u30ed\u30b0\u30d5\u30a1\u30a4\u30eb\u6570\:
AutopsyOptionsPanel.maxMemoryLabel.text=\u6700\u5927JVM\u30e1\u30e2\u30ea\u30fc\:
AutopsyOptionsPanel.maxMemoryUnitsLabel.text=GB
@ -37,7 +31,6 @@ AutopsyOptionsPanel.memFieldValidationLabel.underMinMemory.text=\u5024\u306f\u5c
AutopsyOptionsPanel.restartNecessaryWarning.text=\u30e1\u30e2\u30ea\u30fc\u5909\u66f4\u3092\u6709\u52b9\u306b\u3059\u308b\u306b\u306f\u518d\u8d77\u52d5\u304c\u5fc5\u8981\u3067\u3059\u3002
AutopsyOptionsPanel.runtimePanel.border.title=\u30e9\u30f3\u30bf\u30a4\u30e0
AutopsyOptionsPanel.solrJVMHeapWarning.text=\u6ce8\: \u3053\u308c\u3092\u3042\u307e\u308a\u306b\u3082\u5927\u304d\u304f\u8a2d\u5b9a\u3059\u308b\u3068\u3001\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u5168\u4f53\u306b\u5f71\u97ff\u3059\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002
AutopsyOptionsPanel.specifyLogoRB.text=\u30ed\u30b4\u3092\u6307\u5b9a
AutopsyOptionsPanel.totalMemoryLabel.text=\u5408\u8a08\u30b7\u30b9\u30c6\u30e0\u30e1\u30e2\u30ea\u30fc\:
CTL_CustomAboutAction=\u6982\u8981
CTL_DataContentAction=DataContent
@ -170,6 +163,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&trade; \u306f The Sleuth Kit&trade; \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 &copy; 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
@ -186,7 +232,6 @@ MultiUserSettingsPanel.lbTestDbWarning.text=
MultiUserSettingsPanel.lbTestMessageService.text=
MultiUserSettingsPanel.lbTestMessageWarning.text=
MultiUserSettingsPanel.lbTestSolr.text=
MultiUserSettingsPanel.lbTestSolrWarning.text=
MultiUserSettingsPanel.nonWindowsOs.msg=\u30de\u30eb\u30c1\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9\u306fWindows\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u4e0a\u3067\u306e\u307f\u5229\u7528\u3067\u304d\u307e\u3059
MultiUserSettingsPanel.tbDbHostname.text=
MultiUserSettingsPanel.tbDbHostname.toolTipText=\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9
@ -195,6 +240,12 @@ 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.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 +290,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:
@ -259,3 +322,17 @@ ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=\u305f\u3068\u3048\u30
ViewPreferencesPanel.useBestViewerRadioButton.text=\u6700\u3082\u56fa\u6709\u306e\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u306b\u5207\u308a\u66ff\u3048\u308b
ViewPreferencesPanel.selectFileLabel.text=\u30d5\u30a1\u30a4\u30eb\u9078\u629e\u6642:
ViewPreferencesPanel.globalSettingsPanel.border.title=\u30b0\u30ed\u30fc\u30d0\u30eb\u8a2d\u5b9a
MultiUserSettingsPanel.bnTestSolr8.text=\u30c6\u30b9\u30c8
MultiUserSettingsPanel.lbTestSolr8.text=
MultiUserSettingsPanel.bnTestSolr4.text=\u30c6\u30b9\u30c8
MultiUserSettingsPanel.lbTestSolr4.text=
MultiUserSettingsPanel.bnTestZK.text=\u30c6\u30b9\u30c8
MultiUserSettingsPanel.lbTestZK.text=
MultiUserSettingsPanel.lbWarning.text=
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text=
AutopsyOptionsPanel.specifyLogoRB.text=\u30ed\u30b4\u3092\u6307\u5b9a
AutopsyOptionsPanel.defaultLogoRB.text=\u30c7\u30d5\u30a9\u30eb\u30c8\u3092\u4f7f\u7528
AutopsyOptionsPanel.agencyLogoPreview.text=<html><div style='text-align: center;'>\u30ed\u30b4\u304c\u3042\u308a\u307e\u305b\u3093<br>selected</div></html>
AutopsyOptionsPanel.browseLogosButton.text=\u53c2\u7167
AutopsyOptionsPanel.agencyLogoPathField.text=
AutopsyOptionsPanel.logoPanel.border.title=\u30ed\u30b4

View File

@ -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();
}
/**

View File

@ -460,7 +460,6 @@ 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) {
@ -468,18 +467,20 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
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;
}
}
((TableFilterNode) rootNode).setChildNodeSelectionInfo(null);
}
}
});
/*
* The table setup is done, so any added/removed events can now be

View File

@ -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="1250" 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"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="102" 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"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="pnMessagingSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="39" max="32767" attributes="0"/>
</Group>
<Component id="pnSolrSettings" max="32767" attributes="0"/>
</Group>
<EmptySpace pref="234" 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,39 @@
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="tbSolrHostname" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="lbSolrSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="bnTestSolr" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="lbTestSolr" min="-2" pref="16" 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="lbSolr4Settings" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="bnTestSolr4" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="lbTestSolr4" min="-2" pref="16" max="-2" attributes="0"/>
</Group>
<Component id="tbSolr8Hostname" max="32767" 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" alignment="0" attributes="0">
<Component id="lbZkSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="164" max="32767" attributes="0"/>
<Component id="bnTestZK" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="lbTestZK" min="-2" pref="16" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="lbSolr8Settings" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="bnTestSolr8" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="lbTestSolr8" min="-2" pref="16" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lbSolrNote1" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbSolrNote2" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbWarning" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
@ -273,89 +299,263 @@
<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"/>
<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">
<Group type="102" attributes="0">
<Component id="lbSolrNote2" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
<Component id="lbSolr8Settings" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lbTestSolr" alignment="1" min="-2" pref="23" max="-2" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<Component id="lbTestSolr8" min="-2" pref="23" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
</Group>
<Component id="bnTestSolr8" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="6" 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"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbSolr4Settings" alignment="3" min="-2" pref="21" max="-2" attributes="0"/>
<Component id="bnTestSolr4" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="tbSolrHostname" min="-2" 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"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbZkSettings" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnTestZK" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lbTestZK" alignment="1" min="-2" pref="23" max="-2" attributes="0"/>
</Group>
<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="tbSolrPort" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lbTestSolrWarning" min="-2" pref="16" max="-2" attributes="0"/>
<Component id="lbWarning" min="-2" pref="16" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="lbTestSolr4" min="-2" pref="23" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</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, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbSolr8Settings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbSolr8Hostname.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.tbSolr8Port.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnTestSolr">
<Component class="javax.swing.JButton" name="bnTestSolr8">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.bnTestSolr.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.bnTestSolr8.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnTestSolrActionPerformed"/>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnTestSolr8ActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lbTestSolr">
<Component class="javax.swing.JLabel" name="lbTestSolr8">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbTestSolr.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbTestSolr8.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbTestSolrWarning">
<Component class="javax.swing.JLabel" name="lbWarning">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</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.lbTestSolrWarning.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbWarning.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/warning16.png"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnTestSolr4">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.bnTestSolr4.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnTestSolr4ActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lbTestSolr4">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbTestSolr4.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbTestZK">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.lbTestZK.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnTestZK">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MultiUserSettingsPanel.bnTestZK.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnTestZKActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pnMessagingSettings">
@ -376,7 +576,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 +598,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>

View File

@ -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", lbTestSolr8);
tbSolr8Port.getDocument().putProperty("statusIcon", lbTestSolr8);
tbSolr4Hostname.getDocument().putProperty("statusIcon", lbTestSolr4);
tbSolr4Port.getDocument().putProperty("statusIcon", lbTestSolr4);
tbZkHostname.getDocument().putProperty("statusIcon", lbTestZK);
tbZkPort.getDocument().putProperty("statusIcon", lbTestZK);
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,24 @@ 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();
bnTestSolr = new javax.swing.JButton();
lbTestSolr = new javax.swing.JLabel();
lbTestSolrWarning = new javax.swing.JLabel();
lbSolr8Settings = new javax.swing.JLabel();
tbSolr8Hostname = new javax.swing.JTextField();
tbSolr8Port = new javax.swing.JTextField();
bnTestSolr8 = new javax.swing.JButton();
lbTestSolr8 = new javax.swing.JLabel();
lbWarning = 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();
bnTestSolr4 = new javax.swing.JButton();
lbTestSolr4 = new javax.swing.JLabel();
lbTestZK = new javax.swing.JLabel();
bnTestZK = new javax.swing.JButton();
pnMessagingSettings = new javax.swing.JPanel();
lbMessageServiceSettings = new javax.swing.JLabel();
tbMsgHostname = new javax.swing.JTextField();
@ -241,7 +283,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 +302,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,26 +318,69 @@ 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() {
org.openide.awt.Mnemonics.setLocalizedText(bnTestSolr8, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.bnTestSolr8.text")); // NOI18N
bnTestSolr8.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnTestSolrActionPerformed(evt);
bnTestSolr8ActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(lbTestSolr, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestSolr.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbTestSolr8, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestSolr8.text")); // NOI18N
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
lbWarning.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(lbWarning, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbWarning.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));
lbSolrNote1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/warning16.png"))); // NOI18N
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
org.openide.awt.Mnemonics.setLocalizedText(bnTestSolr4, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.bnTestSolr4.text")); // NOI18N
bnTestSolr4.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnTestSolr4ActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(lbTestSolr4, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestSolr4.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbTestZK, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestZK.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(bnTestZK, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.bnTestZK.text")); // NOI18N
bnTestZK.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnTestZKActionPerformed(evt);
}
});
javax.swing.GroupLayout pnSolrSettingsLayout = new javax.swing.GroupLayout(pnSolrSettings);
pnSolrSettings.setLayout(pnSolrSettingsLayout);
@ -304,37 +389,87 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
.addContainerGap()
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(tbSolrHostname)
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
.addComponent(lbSolrSettings)
.addComponent(lbSolr4Settings)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(bnTestSolr)
.addComponent(bnTestSolr4)
.addGap(18, 18, 18)
.addComponent(lbTestSolr, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(tbSolrPort)
.addComponent(lbTestSolr4, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(tbSolr8Hostname)
.addComponent(tbSolr8Port)
.addComponent(tbSolr4Hostname)
.addComponent(tbSolr4Port)
.addComponent(tbZkHostname)
.addComponent(tbZkPort)
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
.addComponent(lbTestSolrWarning)
.addComponent(lbZkSettings)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 164, Short.MAX_VALUE)
.addComponent(bnTestZK)
.addGap(18, 18, 18)
.addComponent(lbTestZK, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
.addComponent(lbSolr8Settings)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(bnTestSolr8)
.addGap(18, 18, 18)
.addComponent(lbTestSolr8, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lbSolrNote1)
.addComponent(lbSolrNote2)
.addComponent(lbWarning))
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
pnSolrSettingsLayout.setVerticalGroup(
pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
.addContainerGap()
.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))
.addGap(8, 8, 8)
.addComponent(lbSolrNote1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tbSolrHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
.addComponent(lbSolrNote2)
.addGap(28, 28, 28)
.addComponent(lbSolr8Settings))
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
.addComponent(lbTestSolr8, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(5, 5, 5))
.addComponent(bnTestSolr8))
.addGap(6, 6, 6)
.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)
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbSolr4Settings, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(bnTestSolr4))
.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)
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnSolrSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbZkSettings)
.addComponent(bnTestZK))
.addComponent(lbTestZK, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
.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)
.addComponent(lbWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(pnSolrSettingsLayout.createSequentialGroup()
.addComponent(lbTestSolr4, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE)))
.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 +513,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 +530,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 +571,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 +584,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)
.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)
.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))
.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(234, Short.MAX_VALUE))
);
jScrollPane.setViewportView(pnOverallPanel);
@ -462,13 +599,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, 1250, 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
@ -490,12 +627,16 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
tbOops.setText("");
bnTestDatabase.setEnabled(false);
lbTestDatabase.setIcon(null);
bnTestSolr.setEnabled(false);
lbTestSolr.setIcon(null);
bnTestSolr8.setEnabled(false);
lbTestSolr8.setIcon(null);
bnTestSolr4.setEnabled(false);
lbTestSolr4.setIcon(null);
bnTestZK.setEnabled(false);
lbTestZK.setIcon(null);
bnTestMessageService.setEnabled(false);
lbTestMessageService.setIcon(null);
lbTestDbWarning.setText("");
lbTestSolrWarning.setText("");
lbWarning.setText("");
lbTestMessageWarning.setText("");
}
enableMultiUserComponents(textBoxes, cbEnableMultiUser.isSelected());
@ -560,41 +701,119 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
}
}//GEN-LAST:event_bnTestMessageServiceActionPerformed
private void bnTestSolrActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestSolrActionPerformed
lbTestSolr.setIcon(null);
lbTestSolr.paintImmediately(lbTestSolr.getVisibleRect());
lbTestSolrWarning.setText("");
lbTestSolrWarning.paintImmediately(lbTestSolrWarning.getVisibleRect());
private void bnTestSolr8ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestSolr8ActionPerformed
lbTestSolr8.setIcon(null);
lbTestSolr8.paintImmediately(lbTestSolr8.getVisibleRect());
lbWarning.setText("");
lbWarning.paintImmediately(lbWarning.getVisibleRect());
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
try {
if (kwsService != null) {
int port = Integer.parseInt(tbSolrPort.getText().trim());
kwsService.tryConnect(tbSolrHostname.getText().trim(), port);
lbTestSolr.setIcon(goodIcon);
lbTestSolrWarning.setText("");
// test Solr 8 connectivity
if (tbSolr8Port.getText().trim().isEmpty() || tbSolr8Hostname.getText().trim().isEmpty()) {
lbTestSolr8.setIcon(badIcon);
lbWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.Solr8ConnectionInfoMissing.error"));
return;
}
int port = Integer.parseInt(tbSolr8Port.getText().trim());
kwsService.tryConnect(tbSolr8Hostname.getText().trim(), port);
lbTestSolr8.setIcon(goodIcon);
lbWarning.setText("");
} else {
lbTestSolr.setIcon(badIcon);
lbTestSolrWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.KeywordSearchNull"));
lbTestSolr8.setIcon(badIcon);
lbWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.KeywordSearchNull"));
}
} catch (NumberFormatException ex) {
lbTestSolr.setIcon(badIcon);
lbTestSolrWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.InvalidPortNumber"));
lbTestSolr8.setIcon(badIcon);
lbWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.InvalidPortNumber"));
} catch (KeywordSearchServiceException ex) {
lbTestSolr.setIcon(badIcon);
lbTestSolrWarning.setText(ex.getMessage());
lbTestSolr8.setIcon(badIcon);
lbWarning.setText(ex.getMessage());
} finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}//GEN-LAST:event_bnTestSolrActionPerformed
}//GEN-LAST:event_bnTestSolr8ActionPerformed
private void bnTestSolr4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestSolr4ActionPerformed
lbTestSolr4.setIcon(null);
lbTestSolr4.paintImmediately(lbTestSolr4.getVisibleRect());
lbWarning.setText("");
lbWarning.paintImmediately(lbWarning.getVisibleRect());
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
try {
if (kwsService != null) {
// test Solr 4 conenctivity
if (tbSolr4Port.getText().trim().isEmpty() || tbSolr4Hostname.getText().trim().isEmpty()) {
lbTestSolr4.setIcon(badIcon);
lbWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.Solr4ConnectionInfoMissing.error"));
return;
}
int port = Integer.parseInt(tbSolr4Port.getText().trim());
kwsService.tryConnect(tbSolr4Hostname.getText().trim(), port);
lbTestSolr4.setIcon(goodIcon);
lbWarning.setText("");
} else {
lbTestSolr4.setIcon(badIcon);
lbWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.KeywordSearchNull"));
}
} catch (NumberFormatException ex) {
lbTestSolr4.setIcon(badIcon);
lbWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.InvalidPortNumber"));
} catch (KeywordSearchServiceException ex) {
lbTestSolr4.setIcon(badIcon);
lbWarning.setText(ex.getMessage());
} finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}//GEN-LAST:event_bnTestSolr4ActionPerformed
private void bnTestZKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestZKActionPerformed
lbTestZK.setIcon(null);
lbTestZK.paintImmediately(lbTestZK.getVisibleRect());
lbWarning.setText("");
lbWarning.paintImmediately(lbWarning.getVisibleRect());
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
// test ZooKeeper connectivity (ZK settings are mandatory)
if (tbZkPort.getText().trim().isEmpty() || tbZkHostname.getText().trim().isEmpty()) {
lbTestZK.setIcon(badIcon);
lbWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.UnableToConnectToZK"));
return;
}
if (false == CoordinationServiceUtils.isZooKeeperAccessible(tbZkHostname.getText().trim(), tbZkPort.getText().trim())) {
lbTestZK.setIcon(badIcon);
lbWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.UnableToConnectToZK"));
return;
}
lbTestZK.setIcon(goodIcon);
lbWarning.setText("");
} catch (NumberFormatException ex) {
lbTestZK.setIcon(badIcon);
lbWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.InvalidPortNumber"));
} catch (InterruptedException | IOException ex) {
// ZK exceptions
lbTestZK.setIcon(badIcon);
lbWarning.setText(ex.getMessage());
} finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}//GEN-LAST:event_bnTestZKActionPerformed
void load() {
lbTestDatabase.setIcon(null);
lbTestSolr.setIcon(null);
lbTestSolr8.setIcon(null);
lbTestSolr4.setIcon(null);
lbTestZK.setIcon(null);
lbTestMessageService.setIcon(null);
lbTestDbWarning.setText("");
lbTestSolrWarning.setText("");
lbWarning.setText("");
lbTestMessageWarning.setText("");
try {
@ -617,27 +836,66 @@ 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);
}
lbTestDatabase.setIcon(null);
lbTestSolr.setIcon(null);
lbTestMessageService.setIcon(null);
populateSolrAndZkSettings();
bnTestDatabase.setEnabled(false);
bnTestSolr.setEnabled(false);
bnTestSolr8.setEnabled(false);
bnTestSolr4.setEnabled(false);
bnTestZK.setEnabled(false);
bnTestMessageService.setEnabled(false);
cbEnableMultiUser.setSelected(UserPreferences.getIsMultiUserModeEnabled());
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 +910,49 @@ 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.
*
* @return True or false.
*/
private boolean solrFieldsArePopulated() {
return !tbSolrHostname.getText().trim().isEmpty()
&& !tbSolrPort.getText().trim().isEmpty();
private boolean solr4FieldsArePopulated() {
// check if Solr 4 settings are set
if (!tbSolr4Hostname.getText().trim().isEmpty()
&& !tbSolr4Port.getText().trim().isEmpty()) {
return true;
} else {
return false;
}
}
/**
* Tests whether or not values have been entered in all of the mandatory
* Solr settings text fields.
*
* @return True or false.
*/
private boolean solr8FieldsArePopulated() {
// check if Solr 8 settings are set
if (!tbSolr8Hostname.getText().trim().isEmpty()
&& !tbSolr8Port.getText().trim().isEmpty()) {
return true;
} else {
return false;
}
}
/**
* Tests whether or not values have been entered in all of the mandatory
* ZK settings text fields.
*
* @return True or false.
*/
private boolean ZooKeeperFieldsArePopulated() {
// check if ZK settings are set
return (!tbZkHostname.getText().trim().isEmpty()
&& !tbZkPort.getText().trim().isEmpty());
}
/**
@ -681,8 +974,6 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
return (isUserSet == isPwSet);
}
void store() {
boolean prevSelected = UserPreferences.getIsMultiUserModeEnabled();
CaseDbConnectionInfo prevConn = null;
@ -699,6 +990,13 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
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,20 +1033,48 @@ 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);
}
}
@ -758,15 +1084,13 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
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.
*
@ -795,19 +1119,25 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
boolean result = true;
boolean dbPopulated = databaseFieldsArePopulated();
boolean solrPopulated = solrFieldsArePopulated();
boolean solr4Populated = solr4FieldsArePopulated();
boolean solr8Populated = solr8FieldsArePopulated();
boolean zkPopulated = ZooKeeperFieldsArePopulated();
boolean messageServicePopulated = messageServiceFieldsArePopulated();
// PostgreSQL Database
bnTestDatabase.setEnabled(dbPopulated);
// Solr Indexing
bnTestSolr.setEnabled(solrPopulated);
bnTestSolr8.setEnabled(solr8Populated);
bnTestSolr4.setEnabled(solr4Populated);
bnTestZK.setEnabled(zkPopulated);
// ActiveMQ Messaging
bnTestMessageService.setEnabled(messageServicePopulated);
if (!dbPopulated || !solrPopulated || !messageServicePopulated) {
if (dbPopulated && messageServicePopulated && zkPopulated && (solr8Populated || solr4Populated)) {
result = true;
} else {
// We don't even have everything filled out
result = false;
tbOops.setText(INCOMPLETE_SETTINGS_MSG);
@ -849,11 +1179,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;
}
@ -880,18 +1254,26 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton bnTestDatabase;
private javax.swing.JButton bnTestMessageService;
private javax.swing.JButton bnTestSolr;
private javax.swing.JButton bnTestSolr4;
private javax.swing.JButton bnTestSolr8;
private javax.swing.JButton bnTestZK;
private javax.swing.JCheckBox cbEnableMultiUser;
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 lbTestSolr4;
private javax.swing.JLabel lbTestSolr8;
private javax.swing.JLabel lbTestZK;
private javax.swing.JLabel lbWarning;
private javax.swing.JLabel lbZkSettings;
private javax.swing.JPanel pnDatabaseSettings;
private javax.swing.JPanel pnMessagingSettings;
private javax.swing.JPanel pnOverallPanel;
@ -905,8 +1287,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 +1328,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());
}
};
}

View 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);
}
}
}

View File

@ -120,6 +120,33 @@ public final class ExecUtil {
}
}
/**
* This class takes a list of ProcessTerminators checking all of them
* during shouldTerminateProcess.
*/
public static class HybridTerminator implements ProcessTerminator {
private final List<ProcessTerminator> terminatorList;
/**
* Constructs a new instance of the terminator.
*
* @param terminators A list of terminators.
*/
public HybridTerminator(List<ProcessTerminator> terminators) {
this.terminatorList = terminators;
}
@Override
public boolean shouldTerminateProcess() {
for(ProcessTerminator terminator: terminatorList) {
if(terminator.shouldTerminateProcess()) {
return true;
}
}
return false;
}
}
/**
* Runs a process without a process terminator. This method should be used
* with caution because there is nothing to stop the process from running

View File

@ -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() {
}
@ -70,24 +74,13 @@ 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 "";
@ -102,9 +95,8 @@ public class NetworkUtils {
}
/**
* 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,19 +105,21 @@ 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;
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2018 Basis Technology Corp.
* Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -40,7 +40,7 @@ public final class PathValidator {
*
* @return - boolean true for valid path, false for invalid path
*/
public static boolean isValidForMultiUserCase(String path, Case.CaseType caseType) {
public static boolean isValidForCaseType(String path, Case.CaseType caseType) {
if (caseType == Case.CaseType.MULTI_USER_CASE) {
// check that path is not on "C:" drive
@ -48,7 +48,10 @@ public final class PathValidator {
return false;
}
} else {
// single user case - no validation needed
// check that path is not a UNC path. Solr 8 does not allow UNC paths for indexes.
if (UNCPathUtilities.isUNC(path)) {
return false;
}
}
return true;
@ -113,6 +116,19 @@ public final class PathValidator {
*/
@Deprecated
public static boolean isValid(String path, Case.CaseType caseType) {
return isValidForMultiUserCase(path, caseType);
return isValidForCaseType(path, caseType);
}
/**
* Checks if the provided path is valid given the case type.
*
* @param path - the path to validate
* @param caseType - the type of case which the path is being validated for
*
* @return - boolean true for valid path, false for invalid path
*/
@Deprecated
public static boolean isValidForMultiUserCase(String path, Case.CaseType caseType) {
return isValidForCaseType(path, caseType);
}
}

View File

@ -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));

View File

@ -72,11 +72,18 @@ public class TextUtil {
* 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
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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) {
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
content = null;
}
if (content == null) {
return Lookups.fixed(artifact);
} else {
return Lookups.fixed(artifact, content);
}
} 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);
}
/**
* 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 {
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) {

View File

@ -121,6 +121,10 @@ 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_WEB_CATEGORIZATION.getTypeID()) {
imageFile = "domain-16.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
}

View File

@ -292,7 +292,7 @@ final class RawDSInputPanel extends JPanel implements DocumentListener {
"RawDSInputPanel.noOpenCase.errMsg=Exception while getting open case."})
private void warnIfPathIsInvalid(String path) {
try {
if (!PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) {
if (!PathValidator.isValidForCaseType(path, Case.getCurrentCaseThrows().getCaseType())) {
errorLabel.setVisible(true);
errorLabel.setText(Bundle.RawDSInputPanel_error_text());
}

View File

@ -0,0 +1,108 @@
/*
* 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.util.Objects;
import org.sleuthkit.autopsy.geolocation.KdTree;
/**
* A record for a particular city including country and location.
*/
public class CityRecord extends KdTree.XYZPoint {
private final String cityName;
private final String country;
/**
* Main constructor.
*
* @param cityName The name of the city.
* @param country The country of that city.
* @param latitude Latitude for the city.
* @param longitude Longitude for the city.
*/
CityRecord(String cityName, String country, double latitude, double longitude) {
super(latitude, longitude);
this.cityName = cityName;
this.country = country;
}
/**
* @return The name of the city.
*/
public String getCityName() {
return cityName;
}
/**
* @return The country of that city.
*/
public String getCountry() {
return country;
}
/**
* @return Latitude for the city.
*/
public double getLatitude() {
return getY();
}
/**
* @return Longitude for the city.
*/
public double getLongitude() {
return getX();
}
@Override
public int hashCode() {
int hash = super.hashCode();
hash = 37 * hash + Objects.hashCode(this.cityName);
hash = 37 * hash + Objects.hashCode(this.country);
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 CityRecord other = (CityRecord) obj;
if (!Objects.equals(this.cityName, other.cityName)) {
return false;
}
if (!Objects.equals(this.country, other.country)) {
return false;
}
return super.equals(obj);
}
@Override
public String toString() {
return "CityRecord{" + "cityName=" + cityName + ", country=" + country + ", lat=" + getX() + ", lng=" + getY() + '}';
}
}

View File

@ -0,0 +1,254 @@
/*
* 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.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Class that provides the closest city within a tolerance to a provided point.
* This class relies on worldcities.csv and loads the file on first use.
*/
class ClosestCityMapper {
// class resource for cities lat/lng taken from https://simplemaps.com/data/world-cities
private static final String CITIES_CSV_FILENAME = "worldcities.csv";
// index within a csv row of pertinent data
private static final int CITY_NAME_IDX = 0;
private static final int COUNTRY_NAME_IDX = 4;
private static final int LAT_IDX = 2;
private static final int LONG_IDX = 3;
// regex for parsing csv value from a row. This assumes values are in quotes and no escape sequence is used. Also performs a trim.
private static final Pattern CSV_NAIVE_REGEX = Pattern.compile("\"\\s*(([^\"]+?)?)\\s*\"");
// Identifies if cities are in last, first format like "Korea, South"
private static final Pattern COUNTRY_WITH_COMMA = Pattern.compile("^\\s*([^,]*)\\s*,\\s*([^,]*)\\s*$");
private static final int MAX_IDX = Stream.of(CITY_NAME_IDX, COUNTRY_NAME_IDX, LAT_IDX, LONG_IDX)
.max(Integer::compare)
.get();
// singleton instance of this class
private static ClosestCityMapper instance = null;
/**
* Retrieves singleton instance of this class.
*
* @return The singleton instance of this class.
* @throws IOException
*/
static ClosestCityMapper getInstance() throws IOException {
if (instance == null) {
instance = new ClosestCityMapper();
}
return instance;
}
// data structure housing cities
private LatLngMap<CityRecord> latLngMap = null;
// the logger
private final java.util.logging.Logger logger;
/**
* Main Constructor.
*
* @throws IOException
*/
private ClosestCityMapper() throws IOException {
this(
GeolocationSummary.class.getResourceAsStream(CITIES_CSV_FILENAME),
Logger.getLogger(ClosestCityMapper.class.getName()));
}
/**
* Main Constructor loading from an input stream.
*
* @param citiesInputStream The input stream for the csv text file
* containing the cities.
* @param logger The logger to be used with this.
* @throws IOException
*/
private ClosestCityMapper(InputStream citiesInputStream, java.util.logging.Logger logger) throws IOException {
this.logger = logger;
latLngMap = new LatLngMap<CityRecord>(parseCsvLines(citiesInputStream, true));
}
/**
* Finds the closest city to the given point. Null is returned if no close
* city can be determined.
*
* @param point The point to locate.
* @return The closest city or null if no close city can be found.
*/
CityRecord findClosest(CityRecord point) {
return latLngMap.findClosest(point);
}
/**
* Tries to parse a string to a double. If can't be parsed, null is
* returned.
*
* @param s The string to parse.
* @return The double value or null if value cannot be parsed.
*/
private Double tryParse(String s) {
if (s == null) {
return null;
}
try {
return Double.parseDouble(s);
} catch (NumberFormatException ex) {
return null;
}
}
/**
* Parses a country name and transforms values like "last, first" to "first
* last" (i.e. "Korea, South" becomes "South Korea").
*
* @param orig The original string value.
* @param lineNum The line number that this country was found.
* @return The country name.
*/
private String parseCountryName(String orig, int lineNum) {
if (StringUtils.isBlank(orig)) {
logger.log(Level.WARNING, String.format("No country name determined for line %d.", lineNum));
return null;
}
Matcher m = COUNTRY_WITH_COMMA.matcher(orig);
if (m.find()) {
return String.format("%s %s", m.group(1), m.group(2));
}
return orig;
}
/**
* Parses a row from the csv creating a city record.
*
* @param csvRow The row of data where each item in the list is each column
* in the row.
* @param lineNum The line number for this csv row.
* @return The parsed CityRecord or null if none can be determined.
*/
private CityRecord getCsvCityRecord(List<String> csvRow, int lineNum) {
if (csvRow == null || csvRow.size() <= MAX_IDX) {
logger.log(Level.WARNING, String.format("Row at line number %d is required to have at least %d elements and does not.", lineNum, (MAX_IDX + 1)));
return null;
}
String cityName = csvRow.get(CITY_NAME_IDX);
if (StringUtils.isBlank(cityName)) {
logger.log(Level.WARNING, String.format("No city name determined for line %d.", lineNum));
return null;
}
String countryName = parseCountryName(csvRow.get(COUNTRY_NAME_IDX), lineNum);
Double lattitude = tryParse(csvRow.get(LAT_IDX));
if (lattitude == null) {
logger.log(Level.WARNING, String.format("No lattitude determined for line %d.", lineNum));
return null;
}
Double longitude = tryParse(csvRow.get(LONG_IDX));
if (longitude == null) {
logger.log(Level.WARNING, String.format("No longitude determined for line %d.", lineNum));
return null;
}
return new CityRecord(cityName, countryName, lattitude, longitude);
}
/**
* Parses a row of the csv into individual column values.
*
* @param line The line to parse.
* @param lineNum The line number in the csv where this line is.
* @return The list of column values.
*/
private List<String> parseCsvLine(String line, int lineNum) {
if (line == null || line.length() <= 0) {
logger.log(Level.INFO, String.format("Line at %d had no content", lineNum));
return null;
}
List<String> allMatches = new ArrayList<String>();
Matcher m = CSV_NAIVE_REGEX.matcher(line);
while (m.find()) {
allMatches.add(m.group(1));
}
return allMatches;
}
/**
* Parses all lines in the csv file input stream into a list of city
* records.
*
* @param csvInputStream The csv file input stream.
* @param ignoreHeaderRow Whether or not there is a header row in the csv
* file.
* @return The list of city records.
* @throws IOException
*/
private List<CityRecord> parseCsvLines(InputStream csvInputStream, boolean ignoreHeaderRow) throws IOException {
List<CityRecord> cityRecords = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(csvInputStream, "UTF-8"))) {
int lineNum = 1;
String line = reader.readLine();
if (line != null && ignoreHeaderRow) {
line = reader.readLine();
lineNum++;
}
while (line != null) {
// read next line
List<String> rowElements = parseCsvLine(line, lineNum);
if (rowElements != null) {
cityRecords.add(getCsvCityRecord(rowElements, lineNum));
}
line = reader.readLine();
lineNum++;
}
}
return cityRecords;
}
}

View File

@ -0,0 +1,426 @@
/*
* 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.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import org.sleuthkit.autopsy.geolocation.AbstractWaypointFetcher;
import org.sleuthkit.autopsy.geolocation.GeoFilter;
import org.sleuthkit.autopsy.geolocation.MapWaypoint;
import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.DataSource;
/**
* Gathers summary data about Geolocation information for a data source.
*/
public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/**
* A count of hits for a particular city.
*/
public static class CityRecordCount {
private final CityRecord cityRecord;
private final int count;
/**
* Main constructor.
*
* @param cityRecord The record for the city including name, country,
* and location.
* @param count The number of hits in proximity to that city.
*/
CityRecordCount(CityRecord cityRecord, int count) {
this.cityRecord = cityRecord;
this.count = count;
}
/**
* @return The record for the city including name, country, and
* location.
*/
public CityRecord getCityRecord() {
return cityRecord;
}
/**
* @return The number of hits in proximity to that city.
*/
public int getCount() {
return count;
}
}
/**
* Returned data providing counts of most common cities seen and most recent
* cities seen.
*/
public static class CityData {
private final CityCountsList mostCommon;
private final CityCountsList mostRecent;
private final Long mostRecentSeen;
/**
* Main constructor.
*
* @param mostCommon The list of most common cities seen.
* @param mostRecent The list of most recent cities seen.
* @param mostRecentSeen
*/
CityData(CityCountsList mostCommon, CityCountsList mostRecent, Long mostRecentSeen) {
this.mostCommon = mostCommon;
this.mostRecent = mostRecent;
this.mostRecentSeen = mostRecentSeen;
}
/**
* @return The list of most common cities found in the data source.
*/
public CityCountsList getMostCommon() {
return mostCommon;
}
/**
* @return The list of cities seen in most recent use of data source.
*/
public CityCountsList getMostRecent() {
return mostRecent;
}
/**
* @return The time stamp in seconds from epoch of the most recently
* seen city
*/
public Long getMostRecentSeen() {
return mostRecentSeen;
}
}
/**
* Indicates a list of cities to which way points are closest. Also includes
* the count of way points where no closest city was determined due to not
* being close enough.
*/
public static class CityCountsList {
private final List<CityRecordCount> counts;
private final int otherCount;
/**
* Main constructor.
*
* @param counts The list of cities and the count of how many points are
* closest to that city.
* @param otherCount The count of points where no closest city was
* determined due to not being close enough.
*/
CityCountsList(List<CityRecordCount> counts, int otherCount) {
this.counts = Collections.unmodifiableList(new ArrayList<>(counts));
this.otherCount = otherCount;
}
/**
* @return The list of cities and the count of how many points are
* closest to that city.
*/
public List<CityRecordCount> getCounts() {
return counts;
}
/**
* @return The count of points where no closest city was determined due
* to not being close enough.
*/
public int getOtherCount() {
return otherCount;
}
}
// taken from GeoFilterPanel: all of the GPS artifact types.
@SuppressWarnings("deprecation")
private static final List<ARTIFACT_TYPE> GPS_ARTIFACT_TYPES = Arrays.asList(
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK,
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION,
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE,
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH,
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK,
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT,
BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF
);
// all GPS types
private static final Set<Integer> GPS_ARTIFACT_TYPE_IDS = GPS_ARTIFACT_TYPES.stream()
.map(artifactType -> artifactType.getTypeID())
.collect(Collectors.toSet());
private final SleuthkitCaseProvider provider;
private final java.util.logging.Logger logger;
private final SupplierWithException<ClosestCityMapper, IOException> cityMapper;
/**
* A supplier of an item T that can throw an exception of type E.
*/
public interface SupplierWithException<T, E extends Throwable> {
/**
* A supplier method that can throw an exception of E.
*
* @return The object type.
* @throws E The exception type.
*/
T get() throws E;
}
/**
* Default constructor.
*/
public GeolocationSummary() {
this(() -> ClosestCityMapper.getInstance(), SleuthkitCaseProvider.DEFAULT, Logger.getLogger(GeolocationSummary.class.getName()));
}
/**
* Main constructor.
*
* @param cityMapper A means of acquiring a ClosestCityMapper that can throw
* an IOException.
* @param provider A means of acquiring a SleuthkitCaseProvider.
* @param logger The logger.
*/
public GeolocationSummary(SupplierWithException<ClosestCityMapper, IOException> cityMapper, SleuthkitCaseProvider provider, java.util.logging.Logger logger) {
this.cityMapper = cityMapper;
this.provider = provider;
this.logger = logger;
}
/**
* @return Returns all the geolocation artifact types.
*/
public List<ARTIFACT_TYPE> getGeoTypes() {
return GPS_ARTIFACT_TYPES;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return GPS_ARTIFACT_TYPE_IDS;
}
/**
* Returns whether or not the time is >= the provided minimum time handling
* the event where either time is null.
*
* @param minTime The minimum time. If null is provided, this function will
* return true.
* @param time The time to check. If null is provided and the min time is
* non-null, then this function will return false.
* @return If minTime == null then true. If minTime != null && time == null
* then false. Otherwise time >= minTime.
*/
private boolean greaterThanOrEqual(Long minTime, Long time) {
if ((minTime == null) || (time != null && time >= minTime)) {
return true;
} else {
return false;
}
}
private static final Pair<Integer, Integer> EMPTY_COUNT = Pair.of(0, 0);
/**
* Based on a set of waypoints, determines the count of total waypoints and
* a total of waypoints whose time stamp is greater than or equal to
* minTime.
*
* @param points The list of way points.
* @param minTime The minimum time for most recent points count.
* @return A pair where the left value is the total count of way points and
* the right is the total list of way points that are >= minTime.
*/
private Pair<Integer, Integer> getCounts(List<MapWaypoint> points, Long minTime) {
if (points == null) {
return EMPTY_COUNT;
}
return points.stream().reduce(
EMPTY_COUNT,
(total, w) -> Pair.of(total.getLeft() + 1, total.getRight() + (greaterThanOrEqual(minTime, w.getTimestamp()) ? 1 : 0)),
(pair1, pair2) -> Pair.of(pair1.getLeft() + pair2.getLeft(), pair1.getRight() + pair2.getRight()));
}
private static final long DAY_SECS = 24 * 60 * 60;
/**
* Get this list of hits per city where the list is sorted descending by
* number of found hits (i.e. most hits is first index).
*
* @param dataSource The data source.
* @param daysCount Number of days to go back.
* @param maxCount Maximum number of results.
*
* @return The sorted list.
*
* @throws SleuthkitCaseProviderException
* @throws GeoLocationDataException
* @throws InterruptedException
*/
public CityData getCityCounts(DataSource dataSource, int daysCount, int maxCount)
throws SleuthkitCaseProviderException, GeoLocationDataException, InterruptedException, IOException {
ClosestCityMapper closestCityMapper = ClosestCityMapper.getInstance();
List<MapWaypoint> dataSourcePoints = getPoints(dataSource);
Map<CityRecord, List<MapWaypoint>> allCityPoints = new HashMap<>();
List<MapWaypoint> others = new ArrayList<>();
Long mostRecent = null;
for (MapWaypoint pt : dataSourcePoints) {
CityRecord city = closestCityMapper.findClosest(new CityRecord(null, null, pt.getX(), pt.getY()));
Long curTime = pt.getTimestamp();
if (curTime != null && (mostRecent == null || curTime > mostRecent)) {
mostRecent = curTime;
}
if (city == null) {
others.add(pt);
} else {
List<MapWaypoint> cityPoints = allCityPoints.get(city);
if (cityPoints == null) {
cityPoints = new ArrayList<>();
allCityPoints.put(city, cityPoints);
}
cityPoints.add(pt);
}
}
final Long mostRecentMinTime = (mostRecent == null) ? null : mostRecent - daysCount * DAY_SECS;
// pair left is total count and right is count within range (or mostRecent is null)
Map<CityRecord, Pair<Integer, Integer>> allCityCounts = allCityPoints.entrySet().stream()
.collect(Collectors.toMap((e) -> e.getKey(), (e) -> getCounts(e.getValue(), mostRecentMinTime)));
List<CityRecordCount> mostCommonCounts = allCityCounts.entrySet().stream()
.map(e -> new CityRecordCount(e.getKey(), e.getValue().getLeft()))
.sorted((a, b) -> -Integer.compare(a.getCount(), b.getCount()))
.limit(maxCount)
.collect(Collectors.toList());
List<CityRecordCount> mostRecentCounts = allCityCounts.entrySet().stream()
.map(e -> new CityRecordCount(e.getKey(), e.getValue().getRight()))
.sorted((a, b) -> -Integer.compare(a.getCount(), b.getCount()))
.limit(maxCount)
.collect(Collectors.toList());
Pair<Integer, Integer> otherCounts = getCounts(others, mostRecentMinTime);
int otherMostCommonCount = otherCounts.getLeft();
int otherMostRecentCount = otherCounts.getRight();
return new CityData(
new CityCountsList(mostCommonCounts, otherMostCommonCount),
new CityCountsList(mostRecentCounts, otherMostRecentCount),
mostRecentMinTime);
}
/**
* Means of fetching points from geolocation.
*/
private static class PointFetcher extends AbstractWaypointFetcher {
private final BlockingQueue<List<MapWaypoint>> asyncResult;
/**
* Main constructor.
*
* @param asyncResult Geolocation fetches results in a callback which is
* already handled by other mechanisms in data source summary. The
* BlockingQueue blocks until a result is received from geolocation.
* @param filters The applicable filters for geolocation.
*/
public PointFetcher(BlockingQueue<List<MapWaypoint>> asyncResult, GeoFilter filters) {
super(filters);
this.asyncResult = asyncResult;
}
@Override
public void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks, List<Set<MapWaypoint>> areas, boolean wasEntirelySuccessful) {
Stream<List<Set<MapWaypoint>>> stream = Stream.of(
Arrays.asList(mapWaypoints),
tracks == null ? Collections.emptyList() : tracks,
areas == null ? Collections.emptyList() : areas);
List<MapWaypoint> wayPoints = stream
.flatMap((List<Set<MapWaypoint>> list) -> list.stream())
.flatMap((Set<MapWaypoint> set) -> set.stream())
.collect(Collectors.toList());
// push to blocking queue to continue
try {
asyncResult.put(wayPoints);
} catch (InterruptedException ignored) {
// ignored cancellations
}
}
}
/**
* Fetches all GPS data for the data source from the current case.
*
* @param dataSource The data source.
* @return The GPS data pertaining to the data source.
* @throws SleuthkitCaseProviderException
* @throws GeoLocationDataException
* @throws InterruptedException
*/
private List<MapWaypoint> getPoints(DataSource dataSource) throws SleuthkitCaseProviderException, GeoLocationDataException, InterruptedException {
// make asynchronous callback synchronous (the callback nature will be handled in a different level)
// see the following: https://stackoverflow.com/questions/20659961/java-synchronous-callback
final BlockingQueue<List<MapWaypoint>> asyncResult = new ArrayBlockingQueue<>(1);
GeoFilter geoFilter = new GeoFilter(true, false, 0, Arrays.asList(dataSource), GPS_ARTIFACT_TYPES);
WaypointBuilder.getAllWaypoints(provider.get(),
Arrays.asList(dataSource),
GPS_ARTIFACT_TYPES,
true,
-1,
false,
new PointFetcher(asyncResult, geoFilter));
return asyncResult.take();
}
}

View File

@ -0,0 +1,184 @@
/*
* 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.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.geolocation.KdTree;
import org.sleuthkit.autopsy.geolocation.KdTree.XYZPoint;
/**
* Divides map into grid and places each grid square in separate index in a
* hashmap.
*/
class LatLngMap<E extends KdTree.XYZPoint> {
// radius of Earth in kilometers
private static final double EARTH_RADIUS = 6371;
// 300 km buckets with 150km accuracy
private static final double DEFAULT_BUCKET_SIZE = 300;
// maps the determined pair of (north/south index, east/west index) to the KdTree containing all items within that bucket.
private final Map<Pair<Integer, Integer>, KdTree<E>> latLngMap;
private final double bucketSize;
// calculates the bucket for a specific point provided.
private final Function<XYZPoint, Pair<Integer, Integer>> bucketCalculator = (point) -> {
Pair<Double, Double> dPair = getBucketLocation(point);
return Pair.of((int) (double) dPair.getLeft(), (int) (double) dPair.getRight());
};
/**
* Contructor.
*
* @param pointsToAdd The points to be added to the data structure.
*/
LatLngMap(List<E> pointsToAdd) {
this(pointsToAdd, DEFAULT_BUCKET_SIZE);
}
/**
* Main contructor.
*
* @param pointsToAdd The points to be added to the data structure.
* @param bucketSize The size of a grid square in kilometers. So, if this
* value is 100, each sqaure will be a 100 x 100 km.
*/
LatLngMap(List<E> pointsToAdd, double bucketSize) {
this.bucketSize = bucketSize;
Map<Pair<Integer, Integer>, List<E>> latLngBuckets = pointsToAdd.stream()
.collect(Collectors.groupingBy((pt) -> bucketCalculator.apply(pt)));
this.latLngMap = latLngBuckets.entrySet().stream()
.map(e -> Pair.of(e.getKey(), new KdTree<E>(e.getValue())))
.collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
}
/**
* Calculates the bucket-normalized pair of (north/south index, east/west
* index) as a double value. For instance, for bucket size 300Km, if a value
* was 450Km east and 150Km north of lat/lng: (0,0), that will translate to
* (1.5, 0.5). This is used to determine the bucket to search in and the
* closest neighboring buckets.
*
* @param point The point to calculate the bucket location pair.
* @return The pair that was determined.
*/
private Pair<Double, Double> getBucketLocation(XYZPoint point) {
double y = euclideanDistance(new XYZPoint(0D, 0D), new XYZPoint(0D, point.getY())) / bucketSize;
if (point.getY() < 0) {
y = -y;
}
double x = euclideanDistance(new XYZPoint(0D, point.getY()), new XYZPoint(point.getX(), point.getY())) / bucketSize;
if (point.getX() < 0) {
x = -x;
}
return Pair.of(y, x);
}
/**
* Finds closest point within (.5 * bucketSize) distance.
*
* @param point The point for which to find closest.
* @return Returns the found point.
*/
E findClosest(E point) {
Pair<Double, Double> calculated = getBucketLocation(point);
// search 2x2 grid around point for closest item. This is done so that if a point is on the
// edge of a grid square and a point in another square is actually closer.
int latBucket = (int) (double) calculated.getLeft();
int latBucket2 = Math.round(calculated.getLeft()) == latBucket ? latBucket - 1 : latBucket + 1;
int lngBucket = (int) (double) calculated.getRight();
int lngBucket2 = Math.round(calculated.getRight()) == lngBucket ? lngBucket - 1 : lngBucket + 1;
E closest1 = findClosestInBucket(latBucket, lngBucket, point);
E closest2 = findClosestInBucket(latBucket2, lngBucket, point);
E closest3 = findClosestInBucket(latBucket, lngBucket2, point);
E closest4 = findClosestInBucket(latBucket2, lngBucket2, point);
return Stream.of(closest1, closest2, closest3, closest4)
.filter(c -> c != null && euclideanDistance(point, c) <= bucketSize / 2)
.min((a, b) -> Double.compare(euclideanDistance(point, a), euclideanDistance(point, b)))
.orElse(null);
}
/**
* Within the specific bucket, finds the closest point if any exists.
*
* @param x The x axis bucket.
* @param y The y axis bucket.
* @param point The point to search for.
* @return The point, if any, that was found.
*/
private E findClosestInBucket(int x, int y, E point) {
KdTree<E> thisLatLngMap = latLngMap.get(Pair.of(x, y));
if (thisLatLngMap == null) {
return null;
}
Collection<E> closest = thisLatLngMap.nearestNeighbourSearch(1, point);
if (closest != null && closest.size() > 0) {
return closest.iterator().next();
} else {
return null;
}
}
/**
* Computes the Euclidean distance from one point to the other.
*
* Source for the distance calculation:
* https://www.movable-type.co.uk/scripts/latlong.html
*
* @param o1 first point.
* @param o2 second point.
*
* @return euclidean distance.
*/
private static double euclideanDistance(KdTree.XYZPoint o1, KdTree.XYZPoint o2) {
if (o1.equals(o2)) {
return 0;
}
double lat1Radians = Math.toRadians(o1.getY());
double lat2Radians = Math.toRadians(o2.getY());
double deltaLatRadians = Math.toRadians(o2.getY() - o1.getY());
double deltaLongRadians = Math.toRadians(o2.getX() - o1.getX());
double a = Math.sin(deltaLatRadians / 2) * Math.sin(deltaLatRadians / 2)
+ Math.cos(lat1Radians) * Math.cos(lat2Radians)
* Math.sin(deltaLongRadians / 2) * Math.sin(deltaLongRadians / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return EARTH_RADIUS * c;
}
}

View File

@ -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,13 +94,53 @@ 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(artifact, 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.
@ -112,38 +153,47 @@ 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,
maxCount);
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();
/**
* 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(artifact, path, accessedTime, domain);
}
}
if (accessedTime != null && accessedTime != 0) {
fileDetails.add(new RecentFileDetails(path, accessedTime));
/**
* 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.");
}
}
return fileDetails;
}
/**
* Return a list of the most recent downloads based on the value of the the
* artifact TSK_DATETIME_ACCESSED attribute.
@ -163,38 +213,15 @@ 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 && accessedTime != 0L) {
fileDetails.add(new RecentDownloadDetails(path, accessedTime, domain));
}
}
return fileDetails;
return getSortedLimited(details, maxCount);
}
/**
@ -214,109 +241,66 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList();
}
if (maxCount < 0) {
throw new IllegalArgumentException("Invalid maxCount passed to getRecentAttachments, value must be equal to or greater than 0");
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 createListFromMap(buildAttachmentMap(dataSource), maxCount);
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) {
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) {
continue;
return null;
}
// get associated message artifact if exists or return no result
BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong());
if (messageArtifact != null && 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 = "";
if (messageArtifact == null || !isMessageArtifact(messageArtifact)) {
return null;
}
senderAttribute = messageArtifact.getAttribute(MSG_DATEIME_SENT_ATT);
if (senderAttribute != null) {
date = senderAttribute.getValueLong();
// get abstract file if exists or return no result
Content content = artifact.getParent();
if (!(content instanceof AbstractFile)) {
return null;
}
AbstractFile abstractFile = (AbstractFile) content;
path = Paths.get(abstractFile.getParentPath(), abstractFile.getName()).toString();
// 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) {
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);
if (date == null || date == 0 || StringUtils.isBlank(path)) {
return null;
} else {
break;
return new RecentAttachmentDetails(messageArtifact, path, date, sender);
}
}
}
return fileList;
}
/**
* Is the given artifact a message.
@ -330,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();
}
/**
@ -339,14 +324,17 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
private final String path;
private final long date;
private final BlackboardArtifact artifact;
/**
* Constructor for files with just a path and date.
*
* @param artifact The relevant artifact.
* @param path File path.
* @param date File access date\time in seconds with java epoch
*/
RecentFileDetails(String path, long date) {
RecentFileDetails(BlackboardArtifact artifact, String path, long date) {
this.artifact = artifact;
this.path = path;
this.date = date;
}
@ -379,6 +367,12 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return path;
}
/**
* @return The pertinent artifact for this recent file hit.
*/
public BlackboardArtifact getArtifact() {
return artifact;
}
}
/**
@ -391,12 +385,13 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
/**
* Constructor for files with just a path and date.
*
* @param artifact The relevant artifact.
* @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) {
super(path, date);
RecentDownloadDetails(BlackboardArtifact artifact, String path, long date, String webDomain) {
super(artifact, path, date);
this.webDomain = webDomain;
}
@ -422,13 +417,14 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* Constructor for recent download files which have a path, date and
* domain value.
*
* @param artifact The relevant artifact.
* @param path File path.
* @param date File crtime.
* @param sender The sender of the message from which the file was
* attached.
*/
RecentAttachmentDetails(String path, long date, String sender) {
super(path, date);
RecentAttachmentDetails(BlackboardArtifact artifact, String path, long date, String sender) {
super(artifact, path, date);
this.sender = sender;
}

View File

@ -0,0 +1,95 @@
/*
* 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.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.TimeLineModule;
import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState;
import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TimelineFilter;
import org.sleuthkit.datamodel.TimelineFilter.DataSourceFilter;
import org.sleuthkit.datamodel.TimelineFilter.RootFilter;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Utilities for interacting with Timeline in relation to data sources.
*/
public class TimelineDataSourceUtils {
private static TimelineDataSourceUtils instance = null;
/**
* @return Singleton instance of this class.
*/
public static TimelineDataSourceUtils getInstance() {
if (instance == null) {
instance = new TimelineDataSourceUtils();
}
return instance;
}
/**
* Main constructor. Should be instantiated through getInstance().
*/
private TimelineDataSourceUtils() {
}
/**
* Retrieves a RootFilter based on the default filter state but only the
* specified dataSource is selected.
*
* @param dataSource The data source.
* @return The root filter representing a default filter with only this data
* source selected.
* @throws NoCurrentCaseException
* @throws TskCoreException
*/
public RootFilter getDataSourceFilter(DataSource dataSource) throws NoCurrentCaseException, TskCoreException {
RootFilterState filterState = getDataSourceFilterState(dataSource);
return filterState == null ? null : filterState.getActiveFilter();
}
/**
* Retrieves a TimeLineController based on the default filter state but only
* the specified dataSource is selected.
*
* @param dataSource The data source.
* @return The root filter state representing a default filter with only
* this data source selected.
* @throws NoCurrentCaseException
* @throws TskCoreException
*/
public RootFilterState getDataSourceFilterState(DataSource dataSource) throws NoCurrentCaseException, TskCoreException {
TimeLineController controller = TimeLineModule.getController();
RootFilterState dataSourceState = controller.getEventsModel().getDefaultEventFilterState().copyOf();
for (FilterState<? extends TimelineFilter.DataSourceFilter> filterState : dataSourceState.getDataSourcesFilterState().getSubFilterStates()) {
DataSourceFilter dsFilter = filterState.getFilter();
if (dsFilter != null) {
filterState.setSelected(dsFilter.getDataSourceID() == dataSource.getId());
}
}
return dataSourceState;
}
}

View File

@ -0,0 +1,342 @@
/*
* 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.RootFilter;
import org.sleuthkit.datamodel.TimelineManager;
import org.sleuthkit.datamodel.TskCoreException;
import java.util.function.Supplier;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
/**
* Provides data source summary information pertaining to Timeline data.
*/
public class TimelineSummary implements DefaultUpdateGovernor {
/**
* A function for obtaining a Timeline RootFilter filtered to the specific
* data source.
*/
public interface DataSourceFilterFunction {
/**
* Obtains a Timeline RootFilter filtered to the specific data source.
*
* @param dataSource The data source.
* @return The timeline root filter.
* @throws NoCurrentCaseException
* @throws TskCoreException
*/
RootFilter apply(DataSource dataSource) throws NoCurrentCaseException, TskCoreException;
}
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;
private final DataSourceFilterFunction filterFunction;
/**
* Default constructor.
*/
public TimelineSummary() {
this(SleuthkitCaseProvider.DEFAULT,
() -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()),
(ds) -> TimelineDataSourceUtils.getInstance().getDataSourceFilter(ds));
}
/**
* Construct object with given SleuthkitCaseProvider
*
* @param caseProvider SleuthkitCaseProvider provider; cannot be null.
* @param timeZoneProvider The timezone provider; cannot be null.
* @param filterFunction Provides the default root filter function filtered
* to the data source; cannot be null.
*/
public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier<TimeZone> timeZoneProvider, DataSourceFilterFunction filterFunction) {
this.caseProvider = caseProvider;
this.timeZoneProvider = timeZoneProvider;
this.filterFunction = filterFunction;
}
@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
* @throws NoCurrentCaseException
*/
public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException, NoCurrentCaseException {
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, dataSource);
}
/**
* 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
* @throws NoCurrentCaseException
*/
private Map<Long, DailyActivityAmount> getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone)
throws TskCoreException, NoCurrentCaseException {
RootFilter rootFilter = this.filterFunction.apply(dataSource);
// get events for data source
long curRunTime = System.currentTimeMillis();
List<TimelineEvent> events = timelineManager.getEvents(new Interval(1, curRunTime), rootFilter);
// 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;
private final DataSource dataSource;
/**
* 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
* max date sorted by min to max date.
* @param dataSource The data source for which this data applies. the
* latest usage date by day.
*/
TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity, DataSource dataSource) {
this.minDate = minDate;
this.maxDate = maxDate;
this.histogramActivity = (recentDaysActivity == null) ? Collections.emptyList() : Collections.unmodifiableList(recentDaysActivity);
this.dataSource = dataSource;
}
/**
* @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 sorted min to max date.
*/
public List<DailyActivityAmount> getMostRecentDaysActivity() {
return histogramActivity;
}
/**
* @return The data source that this data applies to.
*/
public DataSource getDataSource() {
return dataSource;
}
}
/**
* 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;
}
}
}

View File

@ -108,8 +108,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
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());
private static final Comparator<TopAccountResult> TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccessed().compareTo(b.getLastAccessed());
private static final Comparator<TopWebSearchResult> TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccessed().compareTo(b.getLastAccessed());
/**
* Sorts TopProgramsResults pushing highest run time count then most recent
@ -126,8 +126,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
// 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());
a.getLastAccessed() == null ? null : a.getLastAccessed().getTime(),
b.getLastAccessed() == null ? null : b.getLastAccessed().getTime());
if (lastRunCompare != 0) {
return -lastRunCompare;
@ -219,14 +219,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList();
}
Pair<Long, Map<String, List<Long>>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource);
Pair<Long, Map<String, List<Pair<BlackboardArtifact, Long>>>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource);
// if no recent domains, return accordingly
if (mostRecentAndGroups.getKey() == null || mostRecentAndGroups.getValue().size() == 0) {
return Collections.emptyList();
}
final long mostRecentMs = mostRecentAndGroups.getLeft();
Map<String, List<Long>> groups = mostRecentAndGroups.getRight();
Map<String, List<Pair<BlackboardArtifact, Long>>> groups = mostRecentAndGroups.getRight();
return groups.entrySet().stream()
.map(entry -> getDomainsResult(entry.getKey(), entry.getValue(), mostRecentMs))
@ -243,24 +243,32 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* within DOMAIN_WINDOW_MS of mostRecentMs.
*
* @param domain The domain.
* @param visits The number of visits.
* @param visits The list of the artifact and its associated time in
* milliseconds.
* @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.
*/
private TopDomainsResult getDomainsResult(String domain, List<Long> visits, long mostRecentMs) {
private TopDomainsResult getDomainsResult(String domain, List<Pair<BlackboardArtifact, Long>> visits, long mostRecentMs) {
long visitCount = 0;
Long thisMostRecentMs = null;
BlackboardArtifact thisMostRecentArtifact = null;
for (Long visitMs : visits) {
for (Pair<BlackboardArtifact, Long> visitInstance : visits) {
BlackboardArtifact artifact = visitInstance.getLeft();
Long visitMs = visitInstance.getRight();
// make sure that visit is within window of mostRecentMS; otherwise skip it.
if (visitMs + DOMAIN_WINDOW_MS < mostRecentMs) {
if (visitMs == null || visitMs + DOMAIN_WINDOW_MS < mostRecentMs) {
continue;
}
// if visit is within window, increment the count and get most recent
visitCount++;
if (thisMostRecentMs == null || visitMs > thisMostRecentMs) {
thisMostRecentMs = visitMs;
thisMostRecentArtifact = artifact;
}
thisMostRecentMs = getMax(thisMostRecentMs, visitMs);
}
@ -269,7 +277,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return null;
} else {
// create a top domain result with the domain, count, and most recent visit date
return new TopDomainsResult(domain, visitCount, new Date(thisMostRecentMs));
return new TopDomainsResult(domain, visitCount, new Date(thisMostRecentMs), thisMostRecentArtifact);
}
}
@ -280,19 +288,19 @@ 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 and the relevant
* artifact.
*
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
*/
private Pair<Long, Map<String, List<Long>>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException {
private Pair<Long, Map<String, List<Pair<BlackboardArtifact, Long>>>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException {
List<BlackboardArtifact> artifacts = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_WEB_HISTORY,
dataSource, TYPE_DATETIME_ACCESSED, DataSourceInfoUtilities.SortOrder.DESCENDING, 0);
Long mostRecentMs = null;
Map<String, List<Long>> domainVisits = new HashMap<>();
Map<String, List<Pair<BlackboardArtifact, Long>>> domainVisits = new HashMap<>();
for (BlackboardArtifact art : artifacts) {
Long artifactDateSecs = DataSourceInfoUtilities.getLongOrNull(art, TYPE_DATETIME_ACCESSED);
@ -313,13 +321,13 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
domain = domain.toLowerCase().trim();
// add this visit date to the list of dates for the domain
List<Long> domainVisitList = domainVisits.get(domain);
List<Pair<BlackboardArtifact, Long>> domainVisitList = domainVisits.get(domain);
if (domainVisitList == null) {
domainVisitList = new ArrayList<>();
domainVisits.put(domain, domainVisitList);
}
domainVisitList.add(artifactDateMs);
domainVisitList.add(Pair.of(art, artifactDateMs));
}
return Pair.of(mostRecentMs, domainVisits);
@ -355,7 +363,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
Date dateAccessed = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME_ACCESSED);
return (StringUtils.isNotBlank(searchString) && dateAccessed != null)
? new TopWebSearchResult(searchString, dateAccessed)
? new TopWebSearchResult(searchString, dateAccessed, artifact)
: null;
}
@ -364,8 +372,7 @@ 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.
@ -386,21 +393,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)
@ -448,12 +456,31 @@ 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.getLastAccessed()== null) {
return r1;
}
if (r1.getLastAccessed() == null) {
return r2;
}
return r1.getLastAccessed().compareTo(r2.getLastAccessed()) >= 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.
@ -469,7 +496,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 -> {
@ -477,14 +504,20 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_ID),
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME),
DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MAKE),
DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MODEL)
DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MODEL),
artifact
);
})
// 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());
}
@ -501,7 +534,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE);
Date date = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME);
return (StringUtils.isNotBlank(type) && date != null)
? new TopAccountResult(type, date)
? new TopAccountResult(type, date, artifact)
: null;
}
@ -529,7 +562,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
}
return (StringUtils.isNotBlank(type) && latestDate != null)
? new TopAccountResult(type, latestDate)
? new TopAccountResult(type, latestDate, artifact)
: null;
}
@ -538,8 +571,7 @@ 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.
@ -585,18 +617,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
@ -667,7 +700,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
programName,
path,
longCount,
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME)
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME),
artifact
);
}
@ -721,7 +755,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
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
@ -732,8 +765,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
*
* @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.
* @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.
@ -759,14 +792,20 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
// 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(), res.getProgramPath()),
res -> Pair.of(
res.getProgramName() == null ? null : res.getProgramName().toUpperCase(),
res.getProgramPath() == null ? null : res.getProgramPath().toUpperCase()),
res -> res,
(res1, res2) -> {
Long maxRunTimes = getMax(res1.getRunTimes(), res2.getRunTimes());
Date maxDate = getMax(res1.getLastAccessed(), res2.getLastAccessed());
TopProgramsResult maxResult = TOP_PROGRAMS_RESULT_COMPARE.compare(res1, res2) >= 0 ? res1 : res2;
return new TopProgramsResult(
res1.getProgramName(),
res1.getProgramPath(),
getMax(res1.getRunTimes(), res2.getRunTimes()),
getMax(res1.getLastRun(), res2.getLastRun()));
maxResult.getProgramName(),
maxResult.getProgramPath(),
maxRunTimes,
maxDate,
maxResult.getArtifact());
})).values();
List<TopProgramsResult> orderedResults = results.stream()
@ -779,7 +818,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
// 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()))) {
|| (topResult.getLastAccessed() != null && isPositiveNum(topResult.getLastAccessed().getTime()))) {
return orderedResults.stream().limit(count).collect(Collectors.toList());
}
}
@ -788,13 +827,47 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return orderedResults;
}
/**
* Base class including date of last access and the relevant blackboard
* artifact.
*/
public static class LastAccessedArtifact {
private final Date lastAccessed;
private final BlackboardArtifact artifact;
/**
* Main constructor.
*
* @param lastAccessed The date of last access.
* @param artifact The relevant blackboard artifact.
*/
public LastAccessedArtifact(Date lastAccessed, BlackboardArtifact artifact) {
this.lastAccessed = lastAccessed;
this.artifact = artifact;
}
/**
* @return The date of last access.
*/
public Date getLastAccessed() {
return lastAccessed;
}
/**
* @return The associated artifact.
*/
public BlackboardArtifact getArtifact() {
return artifact;
}
}
/**
* Object containing information about a web search artifact.
*/
public static class TopWebSearchResult {
public static class TopWebSearchResult extends LastAccessedArtifact {
private final String searchString;
private final Date dateAccessed;
private String translatedResult;
/**
@ -802,10 +875,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
*
* @param searchString The search string.
* @param dateAccessed The latest date searched.
* @param artifact The relevant blackboard artifact.
*/
public TopWebSearchResult(String searchString, Date dateAccessed) {
public TopWebSearchResult(String searchString, Date dateAccessed, BlackboardArtifact artifact) {
super(dateAccessed, artifact);
this.searchString = searchString;
this.dateAccessed = dateAccessed;
}
/**
@ -830,22 +904,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
public String getSearchString() {
return searchString;
}
/**
* @return The date for the search.
*/
public Date getDateAccessed() {
return dateAccessed;
}
}
/**
* A record of a device attached.
*/
public static class TopDeviceAttachedResult {
public static class TopDeviceAttachedResult extends LastAccessedArtifact {
private final String deviceId;
private final Date dateAccessed;
private final String deviceMake;
private final String deviceModel;
@ -856,10 +922,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param dateAccessed The date last attached.
* @param deviceMake The device make.
* @param deviceModel The device model.
* @param artifact The relevant blackboard artifact.
*/
public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel) {
public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact) {
super(dateAccessed, artifact);
this.deviceId = deviceId;
this.dateAccessed = dateAccessed;
this.deviceMake = deviceMake;
this.deviceModel = deviceModel;
}
@ -871,13 +938,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return deviceId;
}
/**
* @return The date last attached.
*/
public Date getDateAccessed() {
return dateAccessed;
}
/**
* @return The device make.
*/
@ -897,20 +957,20 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* A record of an account and the last time it was used determined by
* messages.
*/
public static class TopAccountResult {
public static class TopAccountResult extends LastAccessedArtifact {
private final String accountType;
private final Date lastAccess;
/**
* Main constructor.
*
* @param accountType The account type.
* @param lastAccess The date the account was last accessed.
* @param artifact The artifact indicating last access.
*/
public TopAccountResult(String accountType, Date lastAccess) {
public TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact) {
super(lastAccess, artifact);
this.accountType = accountType;
this.lastAccess = lastAccess;
}
/**
@ -919,23 +979,15 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
public String getAccountType() {
return accountType;
}
/**
* @return The date the account was last accessed.
*/
public Date getLastAccess() {
return lastAccess;
}
}
/**
* Describes a result of a program run on a datasource.
*/
public static class TopDomainsResult {
public static class TopDomainsResult extends LastAccessedArtifact {
private final String domain;
private final Long visitTimes;
private final Date lastVisit;
/**
* Describes a top domain result.
@ -943,11 +995,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param domain The domain.
* @param visitTimes The number of times it was visited.
* @param lastVisit The date of the last visit.
* @param artifact The relevant blackboard artifact.
*/
public TopDomainsResult(String domain, Long visitTimes, Date lastVisit) {
public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) {
super(lastVisit, artifact);
this.domain = domain;
this.visitTimes = visitTimes;
this.lastVisit = lastVisit;
}
/**
@ -963,24 +1016,16 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
public Long getVisitTimes() {
return visitTimes;
}
/**
* @return The date of the last visit.
*/
public Date getLastVisit() {
return lastVisit;
}
}
/**
* Describes a result of a program run on a datasource.
*/
public static class TopProgramsResult {
public static class TopProgramsResult extends LastAccessedArtifact {
private final String programName;
private final String programPath;
private final Long runTimes;
private final Date lastRun;
/**
* Main constructor.
@ -988,12 +1033,13 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param programName The name of the program.
* @param programPath The path of the program.
* @param runTimes The number of runs.
* @param artifact The relevant blackboard artifact.
*/
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) {
super(lastRun, artifact);
this.programName = programName;
this.programPath = programPath;
this.runTimes = runTimes;
this.lastRun = lastRun;
}
/**
@ -1016,12 +1062,5 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
public Long getRunTimes() {
return runTimes;
}
/**
* @return The last time the program was run or null if not present.
*/
public Date getLastRun() {
return lastRun;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -19,20 +19,29 @@
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.beans.PropertyChangeEvent;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
@ -41,6 +50,8 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialExecutor;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
import org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
@ -53,6 +64,8 @@ import org.sleuthkit.datamodel.TskCoreException;
/**
* Base class from which other tabs in data source summary derive.
*/
@Messages({"BaseDataSourceSummaryPanel_goToArtifact=View Source Result",
"BaseDataSourceSummaryPanel_goToFile=View Source File in Directory"})
abstract class BaseDataSourceSummaryPanel extends JPanel {
private static final long serialVersionUID = 1L;
@ -64,6 +77,8 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
private final EventUpdateHandler updateHandler;
private final List<UpdateGovernor> governors;
private Runnable notifyParentClose = null;
private DataSource dataSource;
/**
@ -227,6 +242,104 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
this.updateHandler.register();
}
/**
* Given the relevant artifact, navigates to the artifact in the tree and
* closes data source summary dialog if open.
*
* @param artifact The artifact.
* @return The menu item for a go to artifact menu item.
*/
protected CellModelTableCellRenderer.MenuItem getArtifactNavigateItem(BlackboardArtifact artifact) {
if (artifact == null) {
return null;
}
return new CellModelTableCellRenderer.DefaultMenuItem(
Bundle.BaseDataSourceSummaryPanel_goToArtifact(),
() -> {
final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
// Navigate to the source context artifact.
if (dtc != null && artifact != null) {
dtc.viewArtifact(artifact);
}
notifyParentClose();
});
}
private static final Pattern windowsDrivePattern = Pattern.compile("^[A-Za-z]\\:(.*)$");
/**
* Normalizes the path for lookup in the sleuthkit database (unix endings;
* remove C:\).
*
* @param path The path to normalize.
* @return The normalized path.
*/
private String normalizePath(String path) {
if (path == null) {
return null;
}
String trimmed = path.trim();
Matcher match = windowsDrivePattern.matcher(trimmed);
if (match.find()) {
return FilenameUtils.normalize(match.group(1), true);
} else {
return FilenameUtils.normalize(trimmed, true);
}
}
/**
* Creates a menu item to navigate to a file.
*
* @param path The path to the file.
* @return The menu item or null if file cannot be found in data source.
*/
protected CellModelTableCellRenderer.MenuItem getFileNavigateItem(String path) {
if (StringUtils.isNotBlank(path)) {
Path p = Paths.get(path);
String fileName = normalizePath(p.getFileName().toString());
String directory = normalizePath(p.getParent().toString());
if (fileName != null && directory != null) {
try {
List<AbstractFile> files = Case.getCurrentCaseThrows().getSleuthkitCase().findFiles(getDataSource(), fileName, directory);
if (CollectionUtils.isNotEmpty(files)) {
return getFileNavigateItem(files.get(0));
}
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, "There was an error fetching file for path: " + path, ex);
}
}
}
return null;
}
/**
* Given the relevant file, navigates to the file in the
* tree and closes data source summary dialog if open.
*
* @param file The file.
* @return The menu item list for a go to artifact menu item.
*/
protected CellModelTableCellRenderer.MenuItem getFileNavigateItem(AbstractFile file) {
if (file == null) {
return null;
}
return new CellModelTableCellRenderer.DefaultMenuItem(
Bundle.BaseDataSourceSummaryPanel_goToFile(),
() -> {
new ViewContextAction(Bundle.BaseDataSourceSummaryPanel_goToFile(), file)
.actionPerformed(null);
notifyParentClose();
});
}
/**
* Closes listeners and resources.
*/
@ -246,6 +359,24 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
onNewDataSource(this.dataSource);
}
/**
* Sends event that parent should close.
*/
protected void notifyParentClose() {
if (notifyParentClose != null) {
notifyParentClose.run();
}
}
/**
* Sets the listener for parent close events.
*
* @param action The action to run when parent is to close.
*/
void setParentCloseListener(Runnable action) {
notifyParentClose = action;
}
/**
* @return The current data source.
*/
@ -375,8 +506,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
* @param result The data result.
* @param factoryClass The fully qualified class name of the relevant
* factory.
* @param moduleName The name of the ingest module (i.e. 'Keyword
* Search').
* @param moduleName The name of the ingest module (i.e. 'Keyword Search').
*/
protected <T> void showResultWithModuleCheck(LoadableComponent<List<T>> component, DataFetchResult<List<T>> result, String factoryClass, String moduleName) {
Predicate<List<T>> hasResults = (lst) -> lst != null && !lst.isEmpty();
@ -395,8 +525,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
* data contains any actual results.
* @param factoryClass The fully qualified class name of the relevant
* factory.
* @param moduleName The name of the ingest module (i.e. 'Keyword
* Search').
* @param moduleName The name of the ingest module (i.e. 'Keyword Search').
*/
protected <T> void showResultWithModuleCheck(LoadableComponent<T> component, DataFetchResult<T> result,
Predicate<T> hasResults, String factoryClass, String moduleName) {

View File

@ -42,3 +42,19 @@ 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
GeolocationPanel.withinDistanceLabel.text=Locations further than 150Km from a city are not included
GeolocationPanel.mostRecentLabel.text=Recent Cities from Geolocation Results
GeolocationPanel.withinDistanceLabel1.text=Locations further than 150Km from a city are not included
GeolocationPanel.mostCommonLabel.text=Most Common Cities from Geolocation Results
GeolocationPanel.recentViewInGeolocationBtn.text=View in Map
GeolocationPanel.commonViewInGeolocationBtn.text=View in Map
RecentFilesPanel.rightClickForMoreOptions1.text=Right click on row for more options
RecentFilesPanel.rightClickForMoreOptions2.text=Right click on row for more options
RecentFilesPanel.rightClickForMoreOptions3.text=Right click on row for more options
UserActivityPanel.rightClickForMoreOptions1.text=Right click on row for more options
UserActivityPanel.rightClickForMoreOptions2.text=Right click on row for more options
UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more options
UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more options
UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options
TimelinePanel.viewInTimelineBtn.text=View in Timeline

View File

@ -3,6 +3,9 @@ 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.
BaseDataSourceSummaryPanel_goToArtifact=View Source Result
BaseDataSourceSummaryPanel_goToFile=View Source File in Directory
ContainerPanel_setFieldsForNonImageDataSource_na=N/A
CTL_DataSourceSummaryAction=Data Source Summary
DataSourceSummaryDialog.closeButton.text=Close
ContainerPanel.displayNameLabel.text=Display Name:
@ -44,11 +47,17 @@ DataSourceSummaryNode.column.type.header=Type
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source
DataSourceSummaryTabbedPane_analysisTab_title=Analysis
DataSourceSummaryTabbedPane_detailsTab_title=Container
DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation
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
GeolocationPanel_cityColumn_title=Closest City
GeolocationPanel_countColumn_title=Count
GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX Parser was not run.
GeolocationPanel_unknownRow_title=Unknown
PastCasesPanel_caseColumn_title=Case
PastCasesPanel_countColumn_title=Count
PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run.
@ -64,6 +73,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=Result 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 +109,22 @@ 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
GeolocationPanel.withinDistanceLabel.text=Locations further than 150Km from a city are not included
GeolocationPanel.mostRecentLabel.text=Recent Cities from Geolocation Results
GeolocationPanel.withinDistanceLabel1.text=Locations further than 150Km from a city are not included
GeolocationPanel.mostCommonLabel.text=Most Common Cities from Geolocation Results
GeolocationPanel.recentViewInGeolocationBtn.text=View in Map
GeolocationPanel.commonViewInGeolocationBtn.text=View in Map
RecentFilesPanel.rightClickForMoreOptions1.text=Right click on row for more options
RecentFilesPanel.rightClickForMoreOptions2.text=Right click on row for more options
RecentFilesPanel.rightClickForMoreOptions3.text=Right click on row for more options
UserActivityPanel.rightClickForMoreOptions1.text=Right click on row for more options
UserActivityPanel.rightClickForMoreOptions2.text=Right click on row for more options
UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more options
UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more options
UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options
TimelinePanel.viewInTimelineBtn.text=View in Timeline
UserActivityPanel_noDataExists=No communication data exists
UserActivityPanel_tab_title=User Activity
UserActivityPanel_TopAccountTableModel_accountType_header=Account Type

View File

@ -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;
@ -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());
@ -174,28 +173,52 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
try {
acquisitionDetailsTextArea.setText(selectedDataSource.getAcquisitionDetails());
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get aquisition details for selected data source", ex);
logger.log(Level.WARNING, "Unable to get acquisition details for selected data source", ex);
}
if (selectedDataSource instanceof Image) {
setFieldsForImage((Image) selectedDataSource);
setFieldsForImage((Image) selectedDataSource, unallocatedFilesSize);
} else {
setFieldsForNonImageDataSource();
}
}
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});

View File

@ -61,6 +61,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
Map<Long, Long> fileCountsMap = CaseDataSourcesSummary.getCountsOfFiles();
dataSourcesPanel = new DataSourceBrowser(usageMap, fileCountsMap);
dataSourceSummaryTabbedPane = new DataSourceSummaryTabbedPane();
dataSourceSummaryTabbedPane.setParentCloseListener(() -> DataSourceSummaryDialog.this.dispose());
initComponents();
dataSourceSummarySplitPane.setLeftComponent(dataSourcesPanel);
dataSourcesPanel.addListSelectionListener((ListSelectionEvent e) -> {

View File

@ -20,10 +20,13 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.awt.CardLayout;
import java.awt.Component;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.function.Consumer;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
import org.sleuthkit.datamodel.DataSource;
@ -38,7 +41,9 @@ 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_geolocationTab_title=Geolocation",
"DataSourceSummaryTabbedPane_timelineTab_title=Timeline"
})
public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
@ -46,7 +51,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
* Records of tab information (i.e. title, component, function to call on
* new data source).
*/
private static class DataSourceTab {
private class DataSourceTab {
private final String tabTitle;
private final Component component;
@ -73,8 +78,12 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
*
* @param tabTitle The title of the tab.
* @param panel The component to be displayed in the tab.
* @param notifyParentClose Notifies parent to trigger a close.
*/
DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) {
panel.setParentCloseListener(() -> notifyParentClose());
this.tabTitle = tabTitle;
this.component = panel;
this.onDataSource = panel::setDataSource;
@ -115,6 +124,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
private static final String TABBED_PANE = "tabbedPane";
private static final String NO_DATASOURCE_PANE = "noDataSourcePane";
private Runnable notifyParentClose = null;
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
private final List<DataSourceTab> tabs = Arrays.asList(
@ -123,6 +133,8 @@ 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_geolocationTab_title(), new GeolocationPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_timelineTab_title(), new TimelinePanel()),
// do nothing on closing
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> {
}),
@ -132,12 +144,40 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
private DataSource dataSource = null;
private CardLayout cardLayout;
/**
* On case close, clear the currently held data source summary node.
*/
private final PropertyChangeListener caseEventsListener = (evt) -> {
if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString()) && evt.getNewValue() == null) {
setDataSource(null);
}
};
/**
* Creates new form TabPane
*/
public DataSourceSummaryTabbedPane() {
initComponents();
postInit();
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), caseEventsListener);
}
/**
* Sends event that parent should close.
*/
private void notifyParentClose() {
if (notifyParentClose != null) {
notifyParentClose.run();
}
}
/**
* Sets the listener for parent close events.
*
* @param parentCloseAction The observer.
*/
void setParentCloseListener(Runnable parentCloseAction) {
notifyParentClose = parentCloseAction;
}
/**
@ -172,12 +212,14 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
if (this.dataSource == null) {
cardLayout.show(this, NO_DATASOURCE_PANE);
} else {
for (DataSourceTab tab : tabs) {
tab.getOnDataSource().accept(dataSource);
}
if (this.dataSource == null) {
cardLayout.show(this, NO_DATASOURCE_PANE);
} else {
cardLayout.show(this, TABBED_PANE);
}
}
@ -189,6 +231,8 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
for (DataSourceTab tab : tabs) {
tab.getOnClose().run();
}
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), caseEventsListener);
}
/**

View File

@ -0,0 +1,339 @@
<?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="746" 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="mostRecentLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="GeolocationPanel.mostRecentLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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="mostRecentPanel">
<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, 106]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 106]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 106]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="mostRecentTable"/>
<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, 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>
<Component class="javax.swing.JLabel" name="withinDistanceLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="GeolocationPanel.withinDistanceLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<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="filler3">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 5]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 5]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 5]"/>
</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>
<Component class="javax.swing.JButton" name="recentViewInGeolocationBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="GeolocationPanel.recentViewInGeolocationBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="recentViewInGeolocationBtnActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.Box$Filler" name="filler8">
<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>
<Component class="javax.swing.JLabel" name="mostCommonLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="GeolocationPanel.mostCommonLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<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="filler7">
<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="mostCommonPanel">
<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, 106]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 106]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 106]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="mostCommonTable"/>
<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="filler6">
<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>
<Component class="javax.swing.JLabel" name="withinDistanceLabel1">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="GeolocationPanel.withinDistanceLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<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="filler4">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 5]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 5]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 5]"/>
</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>
<Component class="javax.swing.JButton" name="commonViewInGeolocationBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="GeolocationPanel.commonViewInGeolocationBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="commonViewInGeolocationBtnActionPerformed"/>
</Events>
</Component>
<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>

View File

@ -0,0 +1,412 @@
/*
* 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.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.JButton;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.CallableSystemAction;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityCountsList;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityData;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityRecordCount;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.CityRecord;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
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.JTablePanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
import org.sleuthkit.autopsy.geolocation.GeoFilter;
import org.sleuthkit.autopsy.geolocation.GeoLocationUIException;
import org.sleuthkit.autopsy.geolocation.GeolocationTopComponent;
import org.sleuthkit.autopsy.geolocation.OpenGeolocationAction;
import org.sleuthkit.datamodel.DataSource;
/**
* A tab shown in data source summary displaying information about a data
* source's geolocation data.
*/
@Messages({
"GeolocationPanel_cityColumn_title=Closest City",
"GeolocationPanel_countColumn_title=Count",
"GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX Parser was not run.",
"GeolocationPanel_unknownRow_title=Unknown",})
public class GeolocationPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final String GPX_FACTORY = "org.python.proxies.GPX_Parser_Module$GPXParserFileIngestModuleFactory";
private static final String GPX_NAME = "GPX Parser";
private static final int DAYS_COUNT = 30;
private static final int MAX_COUNT = 10;
// The column indicating the city
private static final ColumnModel<Pair<String, Integer>> CITY_COL = new ColumnModel<>(
Bundle.GeolocationPanel_cityColumn_title(),
(pair) -> new DefaultCellModel(pair.getLeft()),
300
);
// The column indicating the count of points seen close to that city
private static final ColumnModel<Pair<String, Integer>> COUNT_COL = new ColumnModel<>(
Bundle.GeolocationPanel_countColumn_title(),
(pair) -> new DefaultCellModel(Integer.toString(pair.getRight())),
100
);
// tables displaying city and number of hits for that city
private final JTablePanel<Pair<String, Integer>> mostCommonTable = JTablePanel.getJTablePanel(Arrays.asList(CITY_COL, COUNT_COL))
.setKeyFunction((pair) -> pair.getLeft());
private final JTablePanel<Pair<String, Integer>> mostRecentTable = JTablePanel.getJTablePanel(Arrays.asList(CITY_COL, COUNT_COL))
.setKeyFunction((pair) -> pair.getLeft());
// loadable components on this tab
private final List<JTablePanel<?>> tables = Arrays.asList(mostCommonTable, mostRecentTable);
private final Logger logger = Logger.getLogger(GeolocationPanel.class.getName());
// means of fetching and displaying data
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final GeolocationSummary whereUsedData;
/**
* Main constructor.
*/
public GeolocationPanel() {
this(new GeolocationSummary());
}
/**
* Main constructor.
*
* @param whereUsedData The GeolocationSummary instance to use.
*/
public GeolocationPanel(GeolocationSummary whereUsedData) {
this.whereUsedData = whereUsedData;
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> whereUsedData.getCityCounts(dataSource, DAYS_COUNT, MAX_COUNT),
(result) -> handleData(result)));
initComponents();
}
/**
* Means of rendering data to be shown in the tables.
*
* @param result The result of fetching data for a data source.
*/
private void handleData(DataFetchResult<CityData> result) {
showCityContent(DataFetchResult.getSubResult(result, (dr) -> dr.getMostCommon()), mostCommonTable, commonViewInGeolocationBtn);
showCityContent(DataFetchResult.getSubResult(result, (dr) -> dr.getMostRecent()), mostRecentTable, recentViewInGeolocationBtn);
}
/**
* Retrieves the city name to display from the record.
*
* @param record The record for the city to display.
* @return The display name (city, country).
*/
private static String getCityName(CityRecord record) {
if (record == null) {
return null;
}
if (StringUtils.isBlank(record.getCountry())) {
return record.getCityName();
}
return String.format("%s, %s", record.getCityName(), record.getCountry());
}
/**
* Formats one record to be displayed as a row in the table (specifically,
* formats the city name).
*
* @param cityCount The CityRecordCount representing a row.
* @return The city/count pair to be displayed as a row.
*/
private Pair<String, Integer> formatRecord(CityRecordCount cityCount) {
if (cityCount == null) {
return null;
}
String cityName = getCityName(cityCount.getCityRecord());
int count = cityCount.getCount();
return Pair.of(cityName, count);
}
/**
* Formats a list of records to be displayed in a table (specifically,
* includes the count of points where no closest city could be determined as
* 'unknown').
*
* @param countsList The CityCountsList object representing the data to be
* displayed in the table.
* @return The list of city/count tuples to be displayed as a row.
*/
private List<Pair<String, Integer>> formatList(CityCountsList countsList) {
if (countsList == null) {
return null;
}
Stream<CityRecordCount> countsStream = ((countsList.getCounts() == null)
? new ArrayList<CityRecordCount>()
: countsList.getCounts()).stream();
Stream<Pair<String, Integer>> pairStream = countsStream.map((r) -> formatRecord(r));
Pair<String, Integer> unknownRecord = Pair.of(Bundle.GeolocationPanel_unknownRow_title(), countsList.getOtherCount());
return Stream.concat(pairStream, Stream.of(unknownRecord))
.filter((p) -> p != null && p.getRight() != null && p.getRight() > 0)
.sorted((a, b) -> -Integer.compare(a.getRight(), b.getRight()))
.limit(MAX_COUNT)
.collect(Collectors.toList());
}
/**
* Shows data in a particular table.
*
* @param result The result to be displayed in the table.
* @param table The table where the data will be displayed.
* @param goToGeolocation The corresponding geolocation navigation button.
*/
private void showCityContent(DataFetchResult<CityCountsList> result, JTablePanel<Pair<String, Integer>> table, JButton goToGeolocation) {
DataFetchResult<List<Pair<String, Integer>>> convertedData = DataFetchResult.getSubResult(result, (countsList) -> formatList(countsList));
if (convertedData != null && convertedData.getResultType() == DataFetchResult.ResultType.SUCCESS && CollectionUtils.isNotEmpty(convertedData.getData())) {
goToGeolocation.setEnabled(true);
}
showResultWithModuleCheck(table, convertedData, GPX_FACTORY, GPX_NAME);
}
/**
* Action to open the geolocation window.
*
* @param dataSource The data source for which the window should filter.
* @param daysLimit The limit for how recently the waypoints should be (for
* most recent table) or null for most recent filter to not be set (for most
* common table).
*/
private void openGeolocationWindow(DataSource dataSource, Integer daysLimit) {
// notify dialog (if in dialog) should close.
notifyParentClose();
// set the filter
TopComponent topComponent = WindowManager.getDefault().findTopComponent(GeolocationTopComponent.class.getSimpleName());
if (topComponent instanceof GeolocationTopComponent) {
GeolocationTopComponent geoComponent = (GeolocationTopComponent) topComponent;
GeoFilter filter = (daysLimit == null)
? new GeoFilter(true, false, 0, Arrays.asList(dataSource), whereUsedData.getGeoTypes())
: new GeoFilter(false, false, DAYS_COUNT, Arrays.asList(dataSource), whereUsedData.getGeoTypes());
try {
geoComponent.setFilterState(filter);
} catch (GeoLocationUIException ex) {
logger.log(Level.WARNING, "There was an error setting filters in the GeoLocationTopComponent.", ex);
}
}
// open the window
OpenGeolocationAction action = CallableSystemAction.get(OpenGeolocationAction.class);
if (action == null) {
logger.log(Level.WARNING, "Unable to obtain an OpenGeolocationAction instance from CallableSystemAction.");
} else {
action.performAction();
}
}
/**
* Disables navigation buttons.
*/
private void disableNavButtons() {
commonViewInGeolocationBtn.setEnabled(false);
recentViewInGeolocationBtn.setEnabled(false);
}
@Override
protected void fetchInformation(DataSource dataSource) {
disableNavButtons();
fetchInformation(dataFetchComponents, dataSource);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
disableNavButtons();
onNewDataSource(dataFetchComponents, tables, 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 mostRecentLabel = 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 mostRecentPanel = mostRecentTable;
javax.swing.Box.Filler filler2 = 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.JLabel withinDistanceLabel = new javax.swing.JLabel();
javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 5), new java.awt.Dimension(0, 5), new java.awt.Dimension(0, 5));
recentViewInGeolocationBtn = new javax.swing.JButton();
javax.swing.Box.Filler filler8 = 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.JLabel mostCommonLabel = new javax.swing.JLabel();
javax.swing.Box.Filler filler7 = 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 mostCommonPanel = mostCommonTable;
javax.swing.Box.Filler filler6 = 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.JLabel withinDistanceLabel1 = new javax.swing.JLabel();
javax.swing.Box.Filler filler4 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 5), new java.awt.Dimension(0, 5), new java.awt.Dimension(0, 5));
commonViewInGeolocationBtn = new javax.swing.JButton();
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);
org.openide.awt.Mnemonics.setLocalizedText(mostRecentLabel, org.openide.util.NbBundle.getMessage(GeolocationPanel.class, "GeolocationPanel.mostRecentLabel.text")); // NOI18N
mainContentPanel.add(mostRecentLabel);
mostRecentLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(GeolocationPanel.class, "PastCasesPanel.notableFileLabel.text")); // NOI18N
filler1.setAlignmentX(0.0F);
mainContentPanel.add(filler1);
mostRecentPanel.setAlignmentX(0.0F);
mostRecentPanel.setMaximumSize(new java.awt.Dimension(32767, 106));
mostRecentPanel.setMinimumSize(new java.awt.Dimension(100, 106));
mostRecentPanel.setPreferredSize(new java.awt.Dimension(100, 106));
mainContentPanel.add(mostRecentPanel);
filler2.setAlignmentX(0.0F);
mainContentPanel.add(filler2);
org.openide.awt.Mnemonics.setLocalizedText(withinDistanceLabel, org.openide.util.NbBundle.getMessage(GeolocationPanel.class, "GeolocationPanel.withinDistanceLabel.text")); // NOI18N
mainContentPanel.add(withinDistanceLabel);
filler3.setAlignmentX(0.0F);
mainContentPanel.add(filler3);
org.openide.awt.Mnemonics.setLocalizedText(recentViewInGeolocationBtn, org.openide.util.NbBundle.getMessage(GeolocationPanel.class, "GeolocationPanel.recentViewInGeolocationBtn.text")); // NOI18N
recentViewInGeolocationBtn.setEnabled(false);
recentViewInGeolocationBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
recentViewInGeolocationBtnActionPerformed(evt);
}
});
mainContentPanel.add(recentViewInGeolocationBtn);
filler8.setAlignmentX(0.0F);
mainContentPanel.add(filler8);
org.openide.awt.Mnemonics.setLocalizedText(mostCommonLabel, org.openide.util.NbBundle.getMessage(GeolocationPanel.class, "GeolocationPanel.mostCommonLabel.text")); // NOI18N
mainContentPanel.add(mostCommonLabel);
filler7.setAlignmentX(0.0F);
mainContentPanel.add(filler7);
mostCommonPanel.setAlignmentX(0.0F);
mostCommonPanel.setMaximumSize(new java.awt.Dimension(32767, 106));
mostCommonPanel.setMinimumSize(new java.awt.Dimension(100, 106));
mostCommonPanel.setPreferredSize(new java.awt.Dimension(100, 106));
mainContentPanel.add(mostCommonPanel);
filler6.setAlignmentX(0.0F);
mainContentPanel.add(filler6);
org.openide.awt.Mnemonics.setLocalizedText(withinDistanceLabel1, org.openide.util.NbBundle.getMessage(GeolocationPanel.class, "GeolocationPanel.withinDistanceLabel1.text")); // NOI18N
mainContentPanel.add(withinDistanceLabel1);
filler4.setAlignmentX(0.0F);
mainContentPanel.add(filler4);
org.openide.awt.Mnemonics.setLocalizedText(commonViewInGeolocationBtn, org.openide.util.NbBundle.getMessage(GeolocationPanel.class, "GeolocationPanel.commonViewInGeolocationBtn.text")); // NOI18N
commonViewInGeolocationBtn.setEnabled(false);
commonViewInGeolocationBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
commonViewInGeolocationBtnActionPerformed(evt);
}
});
mainContentPanel.add(commonViewInGeolocationBtn);
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, 746, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
private void recentViewInGeolocationBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_recentViewInGeolocationBtnActionPerformed
openGeolocationWindow(getDataSource(), DAYS_COUNT);
}//GEN-LAST:event_recentViewInGeolocationBtnActionPerformed
private void commonViewInGeolocationBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_commonViewInGeolocationBtnActionPerformed
openGeolocationWindow(getDataSource(), null);
}//GEN-LAST:event_commonViewInGeolocationBtnActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton commonViewInGeolocationBtn;
private javax.swing.JButton recentViewInGeolocationBtn;
// End of variables declaration//GEN-END:variables
}

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More