merge from develop

This commit is contained in:
Greg DiCristofaro 2020-11-20 15:03:53 -05:00
commit 05e2fd17cd
152 changed files with 7816 additions and 1217 deletions

12
.gitignore vendored
View File

@ -89,3 +89,15 @@ hs_err_pid*.log
*.img *.img
*.vhd *.vhd
*.E01 *.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

@ -98,6 +98,11 @@
<copy file="${thirdparty.dir}/jdom/jdom-2.0.5.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/jdom/jdom-2.0.5.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/jdom/jdom-2.0.5-contrib.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/jdom/jdom-2.0.5-contrib.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/DatCon/3.6.9/DatCon.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/DatCon/3.6.9/DatCon.jar" todir="${ext.dir}" />
<!--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> </target>
@ -197,7 +202,7 @@
</target> </target>
<!--sets up integration test system properties, calls underlying test-init and then sets up the pathing jar--> <!--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 The paths specified in 'module.run.classpath' are incorporated into the manifest of a jar and then the path to the
@ -238,4 +243,67 @@
</path> </path>
</sequential> </sequential>
</target> </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> </project>

View File

@ -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.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.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.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 file.reference.zookeeper-3.4.6.jar=release\\modules\\ext\\zookeeper-3.4.6.jar
javac.source=1.8 javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial javac.compilerargs=-Xlint -Xlint:-serial

View File

@ -548,18 +548,10 @@
<runtime-relative-path>ext/checker-compat-qual-2.5.3.jar</runtime-relative-path> <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> <binary-origin>release\modules\ext\checker-compat-qual-2.5.3.jar</binary-origin>
</class-path-extension> </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> <class-path-extension>
<runtime-relative-path>ext/animal-sniffer-annotations-1.17.jar</runtime-relative-path> <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> <binary-origin>release\modules\ext\animal-sniffer-annotations-1.17.jar</binary-origin>
</class-path-extension> </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> <class-path-extension>
<runtime-relative-path>ext/gax-1.44.0.jar</runtime-relative-path> <runtime-relative-path>ext/gax-1.44.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\gax-1.44.0.jar</binary-origin> <binary-origin>release\modules\ext\gax-1.44.0.jar</binary-origin>
@ -568,6 +560,10 @@
<runtime-relative-path>ext/jsoup-1.10.3.jar</runtime-relative-path> <runtime-relative-path>ext/jsoup-1.10.3.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jsoup-1.10.3.jar</binary-origin> <binary-origin>release\modules\ext\jsoup-1.10.3.jar</binary-origin>
</class-path-extension> </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> <class-path-extension>
<runtime-relative-path>ext/grpc-context-1.19.0.jar</runtime-relative-path> <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> <binary-origin>release\modules\ext\grpc-context-1.19.0.jar</binary-origin>
@ -672,6 +668,10 @@
<runtime-relative-path>ext/grpc-alts-1.19.0.jar</runtime-relative-path> <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> <binary-origin>release\modules\ext\grpc-alts-1.19.0.jar</binary-origin>
</class-path-extension> </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> <class-path-extension>
<runtime-relative-path>ext/jdom-2.0.5.jar</runtime-relative-path> <runtime-relative-path>ext/jdom-2.0.5.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jdom-2.0.5.jar</binary-origin> <binary-origin>release\modules\ext\jdom-2.0.5.jar</binary-origin>
@ -796,6 +796,10 @@
<runtime-relative-path>ext/sevenzipjbinding-AllPlatforms.jar</runtime-relative-path> <runtime-relative-path>ext/sevenzipjbinding-AllPlatforms.jar</runtime-relative-path>
<binary-origin>release\modules\ext\sevenzipjbinding-AllPlatforms.jar</binary-origin> <binary-origin>release\modules\ext\sevenzipjbinding-AllPlatforms.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/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> <class-path-extension>
<runtime-relative-path>ext/jutf7-1.0.0.jar</runtime-relative-path> <runtime-relative-path>ext/jutf7-1.0.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jutf7-1.0.0.jar</binary-origin> <binary-origin>release\modules\ext\jutf7-1.0.0.jar</binary-origin>

View File

@ -383,6 +383,31 @@ public class CentralRepoDbManager {
saveNewCentralRepo(); 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 * This method returns if changes to the central repository configuration
* were successfully applied. * were successfully applied.

View File

@ -121,10 +121,12 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbConnectiv
* @return * @return
*/ */
String getConnectionURL(boolean usePostgresDb) { String getConnectionURL(boolean usePostgresDb) {
StringBuilder url = new StringBuilder(); StringBuilder url = new StringBuilder()
url.append(getJDBCBaseURI()); .append(getJDBCBaseURI())
url.append(getHost()); .append(getHost())
url.append("/"); // NON-NLS .append(":") // NON-NLS
.append(getPort())
.append("/"); // NON-NLS
if (usePostgresDb) { if (usePostgresDb) {
url.append("postgres"); // NON-NLS url.append("postgres"); // NON-NLS
} else { } else {
@ -153,7 +155,7 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbConnectiv
} catch (ClassNotFoundException | SQLException ex) { } catch (ClassNotFoundException | SQLException ex) {
// TODO: Determine why a connection failure (ConnectionException) re-throws // TODO: Determine why a connection failure (ConnectionException) re-throws
// the SQLException and does not print this log message? // 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; conn = null;
} }
return conn; return conn;

View File

@ -1,4 +1,10 @@
caseeventlistener.evidencetag=Evidence 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.ingestmodule.name=Central Repository
IngestEventsListener.prevCaseComment.text=Previous Case: IngestEventsListener.prevCaseComment.text=Previous Case:
# {0} - typeName # {0} - typeName
@ -7,6 +13,3 @@ IngestEventsListener.prevCount.text=Number of previous {0}: {1}
IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository) IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository)
IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository) IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)
Installer.centralRepoUpgradeFailed.title=Central repository disabled 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 javax.swing.SwingUtilities;
import org.openide.modules.ModuleInstall; import org.openide.modules.ModuleInstall;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.Version;
/** /**
* Adds/removes application event listeners responsible for adding data to the * 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 * the org.sleuthkit.autopsy.core package when the already installed
* Autopsy-Core module is restored (during application startup). * 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 @Override
public void restored() { public void restored() {
addApplicationEventListeners(); addApplicationEventListeners();
if (Version.getBuildType() == Version.Type.RELEASE) {
setupDefaultCentralRepository(); setupDefaultCentralRepository();
} }
}
/** /**
* Adds the application event listeners responsible for adding data to the * 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, * 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 * does the set up unconditionally. If the application is running with a
* unconditionally (not running with a GUI, e.g., in an automated ingest * GUI, a notification will be displayed to the user if the mode is RELEASE
* node). * (in other words, developers are exempt from seeing the notification).
*/ */
private void setupDefaultCentralRepository() { private void setupDefaultCentralRepository() {
Map<String, String> centralRepoSettings = ModuleSettings.getConfigSettings("CentralRepository"); 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 (!initialized) { return; // Nothing to do
// if running with a GUI, prompt the user }
if (RuntimeProperties.runningWithGUI()) {
try {
SwingUtilities.invokeAndWait(() -> {
try {
String dialogText
= "<html><body>"
+ "<div style='width: 400px;'>"
+ "<p>" + NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.messageHeader") + "</p>"
+ "<p style='margin-top: 10px'>" + NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.messageDesc") + "</p>"
+ "</div>"
+ "</body></html>";
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), if (CentralRepositoryNotificationDialog.shouldDisplay()) {
dialogText, CentralRepositoryNotificationDialog.display();
NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.title"), }
JOptionPane.YES_NO_OPTION)) {
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) { } catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex); logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex);
doMessageBoxIfRunningInGUI(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"); 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 * Display a central repository exception in a message box if running with a

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.ccLabel.text=CC:
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageArtifactViewer.toText.text=to list goes here MessageArtifactViewer.toText.text=to list goes here
@ -32,3 +15,19 @@ MessageArtifactViewer.subjectLabel.text=Subject:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageArtifactViewer.ccText.text=cc list goes here MessageArtifactViewer.ccText.text=cc list goes here
MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text 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_cr_disabled_message=Enable Central Repository to view, create and edit personas.
CallLogArtifactViewer_heading_metadata=Metadata CallLogArtifactViewer_heading_metadata=Metadata
CallLogArtifactViewer_heading_others=Other Attributes CallLogArtifactViewer_heading_others=Other Attributes
@ -56,11 +41,15 @@ ContactArtifactViewer_persona_unknown=Unknown
ContactArtifactViewer_phones_header=Phone ContactArtifactViewer_phones_header=Phone
DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database 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 DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database
DefaultArtifactContentViewer.attrsTableHeader.sources=Source(s) DefaultTableArtifactContentViewer.attrsTableHeader.sources=Source(s)
DefaultArtifactContentViewer.attrsTableHeader.type=Type DefaultTableArtifactContentViewer.attrsTableHeader.type=Type
DefaultArtifactContentViewer.attrsTableHeader.value=Value DefaultTableArtifactContentViewer.attrsTableHeader.value=Value
DefaultArtifactContentViewer.copyMenuItem.text=Copy GeneralPurposeArtifactViewer.details.attrHeader=Attributes
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All GeneralPurposeArtifactViewer.details.dataSource=Data Source
GeneralPurposeArtifactViewer.details.file=File
GeneralPurposeArtifactViewer.details.sourceHeader=Source
GeneralPurposeArtifactViewer.unknown.text=Unknown
GeneralPurposeArtifactViewer_menuitem_copy=Copy
MessageAccountPanel.account.justification=Account found in Message artifact MessageAccountPanel.account.justification=Account found in Message artifact
MessageAccountPanel_button_create_label=Create MessageAccountPanel_button_create_label=Create
MessageAccountPanel_button_view_label=View MessageAccountPanel_button_view_label=View
@ -87,6 +76,22 @@ MessageArtifactViewer.subjectLabel.text=Subject:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageArtifactViewer.ccText.text=cc list goes here MessageArtifactViewer.ccText.text=cc list goes here
MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text 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 PersonaAccountFetcher.account.justification=Account found in Call Log artifact
# {0} - Persona count # {0} - Persona count
PersonaDisplayTask_persona_count_suffix=(1 of {0}) 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.sources=\u30bd\u30fc\u30b9
DefaultArtifactContentViewer.attrsTableHeader.type=\u30bf\u30a4\u30d7 DefaultArtifactContentViewer.attrsTableHeader.type=\u30bf\u30a4\u30d7
DefaultArtifactContentViewer.attrsTableHeader.value=\u5024 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_create_label=\u4f5c\u6210
MessageAccountPanel_button_view_label=\u8868\u793a MessageAccountPanel_button_view_label=\u8868\u793a
MessageAccountPanel_no_matches=\u4e00\u81f4\u3059\u308b\u3082\u306e\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002 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.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 MessageArtifactViewer.viewInNewWindowButton.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a
PersonaDisplayTask_persona_count_suffix=\uff081/{0}\uff09 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 panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
* @param headerString Heading string to display. * @param headerString Heading string to display.
* *
* @return JLabel Heading label added. * @return JLabel Heading label added.
@ -109,6 +109,23 @@ final class CommunicationArtifactViewerHelper {
return headingLabel; 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. * Adds the given component to the panel.
* *
@ -116,7 +133,7 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
* @param component Component to add. * @param component Component to add.
*/ */
static void addComponent(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, JComponent component) { static void addComponent(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, JComponent component) {
@ -132,7 +149,7 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
*/ */
static void addLineEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) { static void addLineEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
// Place the filler just past the last column. // Place the filler just past the last column.
@ -159,7 +176,7 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
*/ */
static void addPageEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) { static void addPageEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
@ -185,7 +202,7 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
*/ */
static void addBlankLine(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) { static void addBlankLine(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
constraints.gridy++; constraints.gridy++;
@ -203,7 +220,7 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
* @param keyString Key name to display. * @param keyString Key name to display.
* *
* @return Label added. * @return Label added.
@ -217,7 +234,7 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
* @param keyString Key name to display. * @param keyString Key name to display.
* @param gridx column index, must be less than MAX_COLS - 1. * @param gridx column index, must be less than MAX_COLS - 1.
* *
@ -246,8 +263,8 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
* @param keyString Value string to display. * @param valueString Value string to display.
* *
* @return Label added. * @return Label added.
*/ */
@ -260,7 +277,7 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
* @param keyString Value string to display. * @param keyString Value string to display.
* @param gridx Column index, must be less than MAX_COLS; * @param gridx Column index, must be less than MAX_COLS;
* *
@ -367,7 +384,7 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
* @param accountIdentifier Account identifier to search the persona. * @param accountIdentifier Account identifier to search the persona.
* *
* @return List of AccountPersonaSearcherData objects. * @return List of AccountPersonaSearcherData objects.
@ -435,7 +452,7 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constraints to use.
* @param contactId Contact name to display. * @param contactId Contact name to display.
* *
* @return A JLabel with the contact information. * @return A JLabel with the contact information.

View File

@ -11,14 +11,14 @@
<MenuItem class="javax.swing.JMenuItem" name="copyMenuItem"> <MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <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> </Property>
</Properties> </Properties>
</MenuItem> </MenuItem>
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem"> <MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <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> </Property>
</Properties> </Properties>
</MenuItem> </MenuItem>
@ -27,7 +27,7 @@
</NonVisualComponents> </NonVisualComponents>
<Properties> <Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 58]"/> <Dimension value="[0, 0]"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
@ -59,8 +59,11 @@
<Properties> <Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="32"/> <Property name="horizontalScrollBarPolicy" type="int" value="32"/>
<Property name="verticalScrollBarPolicy" type="int" value="22"/> <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"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[620, 34]"/> <Dimension value="[0, 0]"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>

View File

@ -54,37 +54,36 @@ import com.google.gson.JsonArray;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel;
//import org.sleuthkit.autopsy.contentviewers.Bundle; //import org.sleuthkit.autopsy.contentviewers.Bundle;
/** /**
* This class displays a Blackboard artifact as a table listing all it's * This class displays a Blackboard artifact as a table of its attributes.
* attributes names and values.
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @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({ @NbBundle.Messages({
"DefaultArtifactContentViewer.attrsTableHeader.type=Type", "DefaultTableArtifactContentViewer.attrsTableHeader.type=Type",
"DefaultArtifactContentViewer.attrsTableHeader.value=Value", "DefaultTableArtifactContentViewer.attrsTableHeader.value=Value",
"DefaultArtifactContentViewer.attrsTableHeader.sources=Source(s)", "DefaultTableArtifactContentViewer.attrsTableHeader.sources=Source(s)",
"DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database", "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" "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 long serialVersionUID = 1L;
private static final String[] COLUMN_HEADERS = { private static final String[] COLUMN_HEADERS = {
Bundle.DefaultArtifactContentViewer_attrsTableHeader_type(), Bundle.DefaultTableArtifactContentViewer_attrsTableHeader_type(),
Bundle.DefaultArtifactContentViewer_attrsTableHeader_value(), Bundle.DefaultTableArtifactContentViewer_attrsTableHeader_value(),
Bundle.DefaultArtifactContentViewer_attrsTableHeader_sources()}; Bundle.DefaultTableArtifactContentViewer_attrsTableHeader_sources()};
private static final int[] COLUMN_WIDTHS = {100, 800, 100}; private static final int[] COLUMN_WIDTHS = {100, 800, 100};
private static final int CELL_BOTTOM_MARGIN = 5; private static final int CELL_BOTTOM_MARGIN = 5;
private static final int CELL_RIGHT_MARGIN = 1; private static final int CELL_RIGHT_MARGIN = 1;
public DefaultArtifactContentViewer() { public DefaultTableArtifactContentViewer() {
initResultsTable(); initResultsTable();
initComponents(); initComponents();
resultsTableScrollPane.setViewportView(resultsTable); resultsTableScrollPane.setViewportView(resultsTable);
@ -202,17 +201,18 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
selectAllMenuItem = new javax.swing.JMenuItem(); selectAllMenuItem = new javax.swing.JMenuItem();
resultsTableScrollPane = new javax.swing.JScrollPane(); 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); 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); 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.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_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); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
@ -279,7 +279,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
@Override @Override
public void setArtifact(BlackboardArtifact artifact) { public void setArtifact(BlackboardArtifact artifact) {
try { try {
ResultsTableArtifact resultsTableArtifact = new ResultsTableArtifact(artifact, artifact.getParent()); ResultsTableArtifact resultsTableArtifact = artifact == null ? null : new ResultsTableArtifact(artifact, artifact.getParent());
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
@ -427,9 +427,9 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
return returnString; 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 jsonKey
* @param jsonElement * @param jsonElement
@ -468,6 +468,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
* Converts epoch time to readable string. * Converts epoch time to readable string.
* *
* @param epochTime epoch time value to be converted to string. * @param epochTime epoch time value to be converted to string.
*
* @return String with human readable time. * @return String with human readable time.
*/ */
private String epochTimeToString(long epochTime) { private String epochTimeToString(long epochTime) {
@ -490,13 +491,12 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
*/ */
private void updateView(ResultsTableArtifact resultsTableArtifact) { private void updateView(ResultsTableArtifact resultsTableArtifact) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel()); 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(); updateColumnSizes();
updateRowHeights(); updateRowHeights();
resultsTable.clearSelection(); resultsTable.clearSelection();
this.setCursor(null); this.setCursor(null);
} }

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Form>

View File

@ -0,0 +1,383 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel to display the details for an Artifact.
*/
@ServiceProvider(service = ArtifactContentViewer.class)
public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel implements ArtifactContentViewer {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(GeneralPurposeArtifactViewer.class.getName());
// Number of columns in the gridbag layout.
private final static int MAX_COLS = 4;
private final static Insets ROW_INSETS = new java.awt.Insets(0, 12, 0, 0);
private final static Insets HEADER_INSETS = new java.awt.Insets(0, 0, 0, 0);
private final static double GLUE_WEIGHT_X = 1.0;
private final static double TEXT_WEIGHT_X = 0.0;
private final static int LABEL_COLUMN = 0;
private final static int VALUE_COLUMN = 1;
private final static int VALUE_WIDTH = 2;
private final static int LABEL_WIDTH = 1;
private static final Integer[] DEFAULT_ORDERING = new Integer[]{BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_REFERRER.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()};
private final GridBagLayout gridBagLayout = new GridBagLayout();
private final GridBagConstraints gridBagConstraints = new GridBagConstraints();
private final Map<Integer, Integer[]> orderingMap = new HashMap<>();
/**
* Creates new form GeneralPurposeArtifactViewer.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public GeneralPurposeArtifactViewer() {
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID(), DEFAULT_ORDERING);
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID(), DEFAULT_ORDERING);
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), DEFAULT_ORDERING);
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), DEFAULT_ORDERING);
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(), DEFAULT_ORDERING);
orderingMap.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID(), DEFAULT_ORDERING);
initComponents();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"GeneralPurposeArtifactViewer.unknown.text=Unknown"})
@Override
public void setArtifact(BlackboardArtifact artifact) {
resetComponent();
if (artifact != null) {
String dataSourceName = Bundle.GeneralPurposeArtifactViewer_unknown_text();
String sourceFileName = Bundle.GeneralPurposeArtifactViewer_unknown_text();
Map<Integer, List<BlackboardAttribute>> attributeMap = new HashMap<>();
try {
// Get all the attributes and group them by the attributeType
for (BlackboardAttribute bba : artifact.getAttributes()) {
List<BlackboardAttribute> attrList = attributeMap.get(bba.getAttributeType().getTypeID());
if (attrList == null) {
attrList = new ArrayList<>();
}
attrList.add(bba);
attributeMap.put(bba.getAttributeType().getTypeID(), attrList);
}
dataSourceName = artifact.getDataSource().getName();
sourceFileName = artifact.getParent().getName();
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get attributes for artifact " + artifact.getArtifactID(), ex);
}
updateView(artifact.getArtifactTypeID(), attributeMap, dataSourceName, sourceFileName);
}
this.setLayout(this.gridBagLayout);
this.revalidate();
this.repaint();
}
/**
* Reset the panel so that it is empty.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void resetComponent() {
// clear the panel
this.removeAll();
gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridx = LABEL_COLUMN;
gridBagConstraints.weighty = 0.0;
gridBagConstraints.weightx = TEXT_WEIGHT_X; // keep components fixed horizontally.
gridBagConstraints.fill = GridBagConstraints.NONE;
gridBagConstraints.insets = ROW_INSETS;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public Component getComponent() {
// Slap a vertical scrollbar on the panel.
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public boolean isSupported(BlackboardArtifact artifact) {
return (artifact != null)
&& (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID());
}
@NbBundle.Messages({"GeneralPurposeArtifactViewer.details.attrHeader=Attributes",
"GeneralPurposeArtifactViewer.details.sourceHeader=Source",
"GeneralPurposeArtifactViewer.details.dataSource=Data Source",
"GeneralPurposeArtifactViewer.details.file=File"})
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
setPreferredSize(new java.awt.Dimension(0, 0));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
/**
* Update the view to reflect the current artifact's details.
*
* @param artifactTypeId The BlackboardArtifact type id for the artifact
* being displayed.
* @param attributeMap The map of attributes that exist for the artifact.
* @param dataSourceName The name of the datasource that caused the creation
* of the artifact.
* @param sourceFileName The name of the file that caused the creation of
* the artifact.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void updateView(Integer artifactTypeId, Map<Integer, List<BlackboardAttribute>> attributeMap, String dataSourceName, String sourceFileName) {
if (!(artifactTypeId < 1 || artifactTypeId >= Integer.MAX_VALUE)) {
addHeader(Bundle.GeneralPurposeArtifactViewer_details_attrHeader());
Integer[] orderingArray = orderingMap.get(artifactTypeId);
if (orderingArray == null) {
orderingArray = DEFAULT_ORDERING;
}
for (Integer attrId : orderingArray) {
List<BlackboardAttribute> attrList = attributeMap.remove(attrId);
if (attrList != null) {
for (BlackboardAttribute bba : attrList) {
addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
}
}
for (int key : attributeMap.keySet()) {
for (BlackboardAttribute bba : attributeMap.get(key)) {
addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
}
addHeader(Bundle.GeneralPurposeArtifactViewer_details_sourceHeader());
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_dataSource(), dataSourceName);
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_file(), sourceFileName);
// add veritcal glue at the end
addPageEndGlue();
}
}
/**
* Adds a new heading to the panel.
*
* @param headerString Heading string to display.
*
* @return JLabel Heading label added.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private JLabel addHeader(String headerString) {
// create label for heading
javax.swing.JLabel headingLabel = new javax.swing.JLabel();
// add a blank line before the start of new section, unless it's
// the first section
if (gridBagConstraints.gridy != 0) {
gridBagConstraints.gridy++;
add(new javax.swing.JLabel(" "), gridBagConstraints);
addLineEndGlue();
}
gridBagConstraints.gridy++;
gridBagConstraints.gridx = LABEL_COLUMN;;
// let the header span all of the row
gridBagConstraints.gridwidth = MAX_COLS;
gridBagConstraints.insets = HEADER_INSETS;
// set text
headingLabel.setText(headerString);
// make it large and bold
headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2));
// add to panel
add(headingLabel, gridBagConstraints);
// reset constraints to normal
gridBagConstraints.gridwidth = LABEL_WIDTH;
// add line end glue
addLineEndGlue();
gridBagConstraints.insets = ROW_INSETS;
return headingLabel;
}
/**
* Add a key value row to the specified panel with the specified layout and
* constraints.
*
* @param keyString Key name to display.
* @param valueString Value string to display.
*/
private void addNameValueRow(String keyString, String valueString) {
addKeyAtCol(keyString);
addValueAtCol(valueString);
}
/**
* Adds a filler/glue at the end of the line to keep the other columns
* aligned, in case the panel is resized.
*/
private void addLineEndGlue() {
// Place the filler just past the last column.
gridBagConstraints.gridx = MAX_COLS;
gridBagConstraints.weightx = GLUE_WEIGHT_X; // take up all the horizontal space
gridBagConstraints.fill = GridBagConstraints.BOTH;
javax.swing.Box.Filler horizontalFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0));
add(horizontalFiller, gridBagConstraints);
// restore fill & weight
gridBagConstraints.fill = GridBagConstraints.NONE;
gridBagConstraints.weightx = TEXT_WEIGHT_X;
}
/**
* Adds a filler/glue at the bottom of the panel to keep the data rows
* aligned, in case the panel is resized.
*/
private void addPageEndGlue() {
gridBagConstraints.weighty = 1.0; // take up all the vertical space
gridBagConstraints.fill = GridBagConstraints.VERTICAL;
javax.swing.Box.Filler vertFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(0, 32767));
add(vertFiller, gridBagConstraints);
}
/**
* Adds a label/key to the panel.
*
* @param keyString Key name to display.
*
* @return Label added.
*/
private JLabel addKeyAtCol(String keyString) {
// create label
javax.swing.JLabel keyLabel = new javax.swing.JLabel();
gridBagConstraints.gridy++;
gridBagConstraints.gridx = LABEL_COLUMN;
gridBagConstraints.gridwidth = LABEL_WIDTH;
// set text
keyLabel.setText(keyString + ": ");
// add to panel
add(keyLabel, gridBagConstraints);
return keyLabel;
}
/**
* Adds a value string to the panel at specified column.
*
* @param valueString Value string to display.
*
* @return Label added.
*/
private JTextPane addValueAtCol(String valueString) {
// create label,
JTextPane valueField = new JTextPane();
valueField.setEditable(false);
valueField.setOpaque(false);
gridBagConstraints.gridx = VALUE_COLUMN;
GridBagConstraints cloneConstraints = (GridBagConstraints) gridBagConstraints.clone();
// let the value span 2 cols
cloneConstraints.gridwidth = VALUE_WIDTH;
cloneConstraints.fill = GridBagConstraints.BOTH;
// set text
valueField.setText(valueString);
// attach a right click menu with Copy option
valueField.addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mouseClicked(java.awt.event.MouseEvent evt) {
valueLabelMouseClicked(evt, valueField);
}
});
// add label to panel
add(valueField, cloneConstraints);
// end the line
addLineEndGlue();
return valueField;
}
/**
* Event handler for mouse click event. Attaches a 'Copy' menu item to right
* click.
*
* @param evt Event to check.
* @param valueLabel Label to attach the menu item to.
*/
@NbBundle.Messages({
"GeneralPurposeArtifactViewer_menuitem_copy=Copy"
})
private void valueLabelMouseClicked(java.awt.event.MouseEvent evt, JTextPane valueLabel) {
if (SwingUtilities.isRightMouseButton(evt)) {
JPopupMenu popup = new JPopupMenu();
JMenuItem copyMenu = new JMenuItem(Bundle.CommunicationArtifactViewerHelper_menuitem_copy()); // NON-NLS
copyMenu.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(valueLabel.getText()), null);
}
});
popup.add(copyMenu);
popup.show(valueLabel, evt.getX(), evt.getY());
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -41,7 +41,7 @@ import org.sleuthkit.datamodel.TskException;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.ArtifactContentViewer; 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 * 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_INTERESTING_FILE_HIT.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID()) || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.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; return 3;
} else { } else {
return 6; return 6;
@ -370,7 +372,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
return viewer; return viewer;
} }
} }
return new DefaultArtifactContentViewer(); return new DefaultTableArtifactContentViewer();
} }
/** /**

View File

@ -284,17 +284,49 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
*/ */
private static Lookup createLookup(BlackboardArtifact artifact) { private static Lookup createLookup(BlackboardArtifact artifact) {
final long objectID = artifact.getObjectID(); final long objectID = artifact.getObjectID();
Content content = null;
try { 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) { if (content == null) {
return Lookups.fixed(artifact); return Lookups.fixed(artifact);
} else { } else {
return Lookups.fixed(artifact, content); 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;
} }
/** /**

View File

@ -28,10 +28,11 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.SortedMap; import java.util.stream.Collectors;
import java.util.TreeMap; import org.apache.commons.lang.StringUtils;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -93,13 +94,53 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return ARTIFACT_UPDATE_TYPE_IDS; return ARTIFACT_UPDATE_TYPE_IDS;
} }
/**
* Removes fileDetails entries with redundant paths, sorts by date
* descending and limits to the limit provided.
*
* @param fileDetails The file details list.
* @param limit The maximum number of entries to return.
* @return The sorted limited list with unique paths.
*/
private <T extends RecentFileDetails> List<T> getSortedLimited(List<T> fileDetails, int limit) {
Map<String, T> fileDetailsMap = fileDetails.stream()
.filter(details -> details != null)
.collect(Collectors.toMap(
d -> d.getPath().toUpperCase(),
d -> d,
(d1, d2) -> Long.compare(d1.getDateAsLong(), d2.getDateAsLong()) > 0 ? d1 : d2));
return fileDetailsMap.values().stream()
.sorted((a, b) -> -Long.compare(a.getDateAsLong(), b.getDateAsLong()))
.limit(limit)
.collect(Collectors.toList());
}
/**
* Returns a RecentFileDetails object as derived from the recent document
* artifact or null if no appropriate object can be made.
*
* @param artifact The artifact.
* @return The derived object or null if artifact is invalid.
*/
private RecentFileDetails getRecentlyOpenedDocument(BlackboardArtifact artifact) {
String path = DataSourceInfoUtilities.getStringOrNull(artifact, PATH_ATT);
Long lastOpened = DataSourceInfoUtilities.getLongOrNull(artifact, DATETIME_ATT);
if (StringUtils.isBlank(path) || lastOpened == null || lastOpened == 0) {
return null;
} else {
return new RecentFileDetails(path, lastOpened);
}
}
/** /**
* Return a list of the most recently opened documents based on the * Return a list of the most recently opened documents based on the
* TSK_RECENT_OBJECT artifact. * TSK_RECENT_OBJECT artifact.
* *
* @param dataSource The data source to query. * @param dataSource The data source to query.
* @param maxCount The maximum number of results to return, pass 0 to get * @param maxCount The maximum number of results to return, pass 0 to get a
* a list of all results. * list of all results.
* *
* @return A list RecentFileDetails representing the most recently opened * @return A list RecentFileDetails representing the most recently opened
* documents or an empty list if none were found. * documents or an empty list if none were found.
@ -112,38 +153,47 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList(); return Collections.emptyList();
} }
List<BlackboardArtifact> artifactList throwOnNonPositiveCount(maxCount);
= DataSourceInfoUtilities.getArtifacts(provider.get(),
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_RECENT_OBJECT),
dataSource,
DATETIME_ATT,
DataSourceInfoUtilities.SortOrder.DESCENDING,
maxCount);
List<RecentFileDetails> fileDetails = new ArrayList<>(); List<RecentFileDetails> details = provider.get().getBlackboard()
for (BlackboardArtifact artifact : artifactList) { .getArtifacts(ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID(), dataSource.getId()).stream()
Long accessedTime = null; .map(art -> getRecentlyOpenedDocument(art))
String path = ""; .filter(d -> d != null)
.collect(Collectors.toList());
// Get all the attributes in one call. return getSortedLimited(details, maxCount);
List<BlackboardAttribute> attributeList = artifact.getAttributes(); }
for (BlackboardAttribute attribute : attributeList) {
if (attribute.getAttributeType().equals(DATETIME_ATT)) { /**
accessedTime = attribute.getValueLong(); * Returns a RecentDownloadDetails object as derived from the recent
} else if (attribute.getAttributeType().equals(PATH_ATT)) { * download artifact or null if no appropriate object can be made.
path = attribute.getValueString(); *
* @param artifact The artifact.
* @return The derived object or null if artifact is invalid.
*/
private RecentDownloadDetails getRecentDownload(BlackboardArtifact artifact) {
Long accessedTime = DataSourceInfoUtilities.getLongOrNull(artifact, DATETIME_ACCESSED_ATT);
String domain = DataSourceInfoUtilities.getStringOrNull(artifact, DOMAIN_ATT);
String path = DataSourceInfoUtilities.getStringOrNull(artifact, PATH_ATT);
if (StringUtils.isBlank(path) || accessedTime == null || accessedTime == 0) {
return null;
} else {
return new RecentDownloadDetails(path, accessedTime, domain);
} }
} }
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 * Return a list of the most recent downloads based on the value of the the
* artifact TSK_DATETIME_ACCESSED attribute. * artifact TSK_DATETIME_ACCESSED attribute.
@ -163,38 +213,15 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList(); return Collections.emptyList();
} }
List<BlackboardArtifact> artifactList throwOnNonPositiveCount(maxCount);
= DataSourceInfoUtilities.getArtifacts(provider.get(),
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD),
dataSource,
DATETIME_ACCESSED_ATT,
DataSourceInfoUtilities.SortOrder.DESCENDING,
maxCount);
List<RecentDownloadDetails> fileDetails = new ArrayList<>(); List<RecentDownloadDetails> details = provider.get().getBlackboard()
for (BlackboardArtifact artifact : artifactList) { .getArtifacts(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), dataSource.getId()).stream()
// Get all the attributes in one call. .map(art -> getRecentDownload(art))
Long accessedTime = null; .filter(d -> d != null)
String domain = ""; .collect(Collectors.toList());
String path = "";
List<BlackboardAttribute> attributeList = artifact.getAttributes(); return getSortedLimited(details, maxCount);
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;
} }
/** /**
@ -214,109 +241,66 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList(); return Collections.emptyList();
} }
if (maxCount < 0) { throwOnNonPositiveCount(maxCount);
throw new IllegalArgumentException("Invalid maxCount passed to getRecentAttachments, value must be equal to or greater than 0");
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. * @param artifact The associated object artifact.
* * @param skCase The current case.
* @return Returns a SortedMap of details objects returned in descending * @return The derived object or null.
* order.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException * @throws TskCoreException
*/ */
private SortedMap<Long, List<RecentAttachmentDetails>> buildAttachmentMap(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { private RecentAttachmentDetails getRecentAttachment(BlackboardArtifact artifact, SleuthkitCase skCase) throws TskCoreException {
SleuthkitCase skCase = provider.get(); // get associated artifact or return no result
TreeMap<Long, List<RecentAttachmentDetails>> sortedMap = new TreeMap<>();
List<BlackboardArtifact> associatedArtifacts = skCase.getBlackboard().getArtifacts(ASSOCATED_OBJ_ART.getTypeID(), dataSource.getId());
for (BlackboardArtifact artifact : associatedArtifacts) {
BlackboardAttribute attribute = artifact.getAttribute(ASSOCATED_ATT); BlackboardAttribute attribute = artifact.getAttribute(ASSOCATED_ATT);
if (attribute == null) { if (attribute == null) {
continue; return null;
} }
// get associated message artifact if exists or return no result
BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong()); BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong());
if (messageArtifact != null && isMessageArtifact(messageArtifact)) { if (messageArtifact == null || !isMessageArtifact(messageArtifact)) {
Content content = artifact.getParent(); return null;
if (content instanceof AbstractFile) {
String sender;
Long date = null;
String path;
BlackboardAttribute senderAttribute = messageArtifact.getAttribute(EMAIL_FROM_ATT);
if (senderAttribute != null) {
sender = senderAttribute.getValueString();
} else {
sender = "";
} }
senderAttribute = messageArtifact.getAttribute(MSG_DATEIME_SENT_ATT);
if (senderAttribute != null) { // get abstract file if exists or return no result
date = senderAttribute.getValueLong(); Content content = artifact.getParent();
if (!(content instanceof AbstractFile)) {
return null;
} }
AbstractFile abstractFile = (AbstractFile) content; 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) { if (date == null || date == 0 || StringUtils.isBlank(path)) {
List<RecentAttachmentDetails> list = sortedMap.get(date); return null;
if (list == null) {
list = new ArrayList<>();
sortedMap.put(date, list);
}
RecentAttachmentDetails details = new RecentAttachmentDetails(path, date, sender);
if (!list.contains(details)) {
list.add(details);
}
}
}
}
}
return sortedMap.descendingMap();
}
/**
* Create a list of detail objects from the given sorted map of the max
* size.
*
* @param sortedMap A Map of attachment details sorted by date.
* @param maxCount Maximum number of values to return.
*
* @return A list of the details of the most recent attachments or empty
* list if none where found.
*/
private List<RecentAttachmentDetails> createListFromMap(SortedMap<Long, List<RecentAttachmentDetails>> sortedMap, int maxCount) {
List<RecentAttachmentDetails> fileList = new ArrayList<>();
for (List<RecentAttachmentDetails> mapList : sortedMap.values()) {
if (maxCount == 0 || fileList.size() + mapList.size() <= maxCount) {
fileList.addAll(mapList);
continue;
}
if (maxCount == fileList.size()) {
break;
}
for (RecentAttachmentDetails details : mapList) {
if (fileList.size() < maxCount) {
fileList.add(details);
} else { } else {
break; return new RecentAttachmentDetails(path, date, sender);
} }
} }
}
return fileList;
}
/** /**
* Is the given artifact a message. * Is the given artifact a message.
@ -330,6 +314,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
final int artifactTypeID = nodeArtifact.getArtifactTypeID(); final int artifactTypeID = nodeArtifact.getArtifactTypeID();
return artifactTypeID == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() return artifactTypeID == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(); || artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID();
} }
/** /**

View File

@ -0,0 +1,318 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.joda.time.Interval;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TimelineEvent;
import org.sleuthkit.datamodel.TimelineEventType;
import org.sleuthkit.datamodel.TimelineFilter;
import org.sleuthkit.datamodel.TimelineFilter.DataSourcesFilter;
import org.sleuthkit.datamodel.TimelineFilter.RootFilter;
import org.sleuthkit.datamodel.TimelineManager;
import org.sleuthkit.datamodel.TskCoreException;
import java.util.function.Supplier;
import org.sleuthkit.autopsy.core.UserPreferences;
/**
* Provides data source summary information pertaining to Timeline data.
*/
public class TimelineSummary implements DefaultUpdateGovernor {
private static final long DAY_SECS = 24 * 60 * 60;
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
private static final Set<TimelineEventType> FILE_SYSTEM_EVENTS
= new HashSet<>(Arrays.asList(
TimelineEventType.FILE_MODIFIED,
TimelineEventType.FILE_ACCESSED,
TimelineEventType.FILE_CREATED,
TimelineEventType.FILE_CHANGED));
private final SleuthkitCaseProvider caseProvider;
private final Supplier<TimeZone> timeZoneProvider;
/**
* Default constructor.
*/
public TimelineSummary() {
this(SleuthkitCaseProvider.DEFAULT, () -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()));
}
/**
* Construct object with given SleuthkitCaseProvider
*
* @param caseProvider SleuthkitCaseProvider provider, cannot be null.
* @param timeZoneProvider The timezone provider, cannot be null.
*/
public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier<TimeZone> timeZoneProvider) {
this.caseProvider = caseProvider;
this.timeZoneProvider = timeZoneProvider;
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
}
@Override
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return INGEST_JOB_EVENTS;
}
/**
* Retrieves timeline summary data.
*
* @param dataSource The data source for which timeline data will be
* retrieved.
* @param recentDaysNum The maximum number of most recent days' activity to
* include.
* @return The retrieved data.
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException {
TimeZone timeZone = this.timeZoneProvider.get();
TimelineManager timelineManager = this.caseProvider.get().getTimelineManager();
// get a mapping of days from epoch to the activity for that day
Map<Long, DailyActivityAmount> dateCounts = getTimelineEventsByDay(dataSource, timelineManager, timeZone);
// get minimum and maximum usage date by iterating through
Long minDay = null;
Long maxDay = null;
for (long daysFromEpoch : dateCounts.keySet()) {
minDay = (minDay == null) ? daysFromEpoch : Math.min(minDay, daysFromEpoch);
maxDay = (maxDay == null) ? daysFromEpoch : Math.max(maxDay, daysFromEpoch);
}
// if no min date or max date, no usage; return null.
if (minDay == null || maxDay == null) {
return null;
}
Date minDate = new Date(minDay * 1000 * DAY_SECS);
Date maxDate = new Date(maxDay * 1000 * DAY_SECS);
// The minimum recent day will be within recentDaysNum from the maximum day
// (+1 since maxDay included) or the minimum day of activity
long minRecentDay = Math.max(maxDay - recentDaysNum + 1, minDay);
// get most recent days activity
List<DailyActivityAmount> mostRecentActivityAmt = getMostRecentActivityAmounts(dateCounts, minRecentDay, maxDay);
return new TimelineSummaryData(minDate, maxDate, mostRecentActivityAmt);
}
/**
* Given activity by day, converts to most recent days' activity handling
* empty values.
*
* @param dateCounts The day from epoch mapped to activity amounts for that
* day.
* @param minRecentDay The minimum recent day in days from epoch.
* @param maxDay The maximum recent day in days from epoch;
* @return The most recent daily activity amounts.
*/
private List<DailyActivityAmount> getMostRecentActivityAmounts(Map<Long, DailyActivityAmount> dateCounts, long minRecentDay, long maxDay) {
List<DailyActivityAmount> mostRecentActivityAmt = new ArrayList<>();
for (long curRecentDay = minRecentDay; curRecentDay <= maxDay; curRecentDay++) {
DailyActivityAmount prevCounts = dateCounts.get(curRecentDay);
DailyActivityAmount countsHandleNotFound = prevCounts != null
? prevCounts
: new DailyActivityAmount(new Date(curRecentDay * DAY_SECS * 1000), 0, 0);
mostRecentActivityAmt.add(countsHandleNotFound);
}
return mostRecentActivityAmt;
}
/**
* Fetches timeline events per day for a particular data source.
*
* @param dataSource The data source.
* @param timelineManager The timeline manager to use while fetching the
* data.
* @param timeZone The time zone to use to determine which day activity
* belongs.
* @return A Map mapping days from epoch to the activity for that day.
* @throws TskCoreException
*/
private Map<Long, DailyActivityAmount> getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone) throws TskCoreException {
DataSourcesFilter dataSourceFilter = new DataSourcesFilter();
dataSourceFilter.addSubFilter(new TimelineFilter.DataSourceFilter(dataSource.getName(), dataSource.getId()));
RootFilter dataSourceRootFilter = new RootFilter(
null,
null,
null,
null,
null,
dataSourceFilter,
null,
Collections.emptySet());
// get events for data source
long curRunTime = System.currentTimeMillis();
List<TimelineEvent> events = timelineManager.getEvents(new Interval(1, curRunTime), dataSourceRootFilter);
// get counts of events per day (left is file system events, right is everything else)
Map<Long, DailyActivityAmount> dateCounts = new HashMap<>();
for (TimelineEvent evt : events) {
long curSecondsFromEpoch = evt.getTime();
long curDaysFromEpoch = Instant.ofEpochMilli(curSecondsFromEpoch * 1000)
.atZone(timeZone.toZoneId())
.toLocalDate()
.toEpochDay();
DailyActivityAmount prevAmt = dateCounts.get(curDaysFromEpoch);
long prevFileEvtCount = prevAmt == null ? 0 : prevAmt.getFileActivityCount();
long prevArtifactEvtCount = prevAmt == null ? 0 : prevAmt.getArtifactActivityCount();
Date thisDay = prevAmt == null ? new Date(curDaysFromEpoch * 1000 * DAY_SECS) : prevAmt.getDay();
boolean isFileEvt = FILE_SYSTEM_EVENTS.contains(evt.getEventType());
long curFileEvtCount = prevFileEvtCount + (isFileEvt ? 1 : 0);
long curArtifactEvtCount = prevArtifactEvtCount + (isFileEvt ? 0 : 1);
dateCounts.put(curDaysFromEpoch, new DailyActivityAmount(thisDay, curFileEvtCount, curArtifactEvtCount));
}
return dateCounts;
}
/**
* All the data to be represented in the timeline summary tab.
*/
public static class TimelineSummaryData {
private final Date minDate;
private final Date maxDate;
private final List<DailyActivityAmount> histogramActivity;
/**
* Main constructor.
*
* @param minDate Earliest usage date recorded for the data source.
* @param maxDate Latest usage date recorded for the data source.
* @param recentDaysActivity A list of activity prior to and including
* the latest usage date by day.
*/
TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity) {
this.minDate = minDate;
this.maxDate = maxDate;
this.histogramActivity = (recentDaysActivity == null) ? Collections.emptyList() : Collections.unmodifiableList(recentDaysActivity);
}
/**
* @return Earliest usage date recorded for the data source.
*/
public Date getMinDate() {
return minDate;
}
/**
* @return Latest usage date recorded for the data source.
*/
public Date getMaxDate() {
return maxDate;
}
/**
* @return A list of activity prior to and including the latest usage
* date by day.
*/
public List<DailyActivityAmount> getMostRecentDaysActivity() {
return histogramActivity;
}
}
/**
* Represents the amount of usage based on timeline events for a day.
*/
public static class DailyActivityAmount {
private final Date day;
private final long fileActivityCount;
private final long artifactActivityCount;
/**
* Main constructor.
*
* @param day The day for which activity is being measured.
* @param fileActivityCount The amount of file activity timeline events.
* @param artifactActivityCount The amount of artifact timeline events.
*/
DailyActivityAmount(Date day, long fileActivityCount, long artifactActivityCount) {
this.day = day;
this.fileActivityCount = fileActivityCount;
this.artifactActivityCount = artifactActivityCount;
}
/**
* @return The day for which activity is being measured.
*/
public Date getDay() {
return day;
}
/**
* @return The amount of file activity timeline events.
*/
public long getFileActivityCount() {
return fileActivityCount;
}
/**
* @return The amount of artifact timeline events.
*/
public long getArtifactActivityCount() {
return artifactActivityCount;
}
}
}

View File

@ -280,9 +280,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param dataSource The datasource. * @param dataSource The datasource.
* *
* @return A tuple where the first value is the latest web history accessed * @return A tuple where the first value is the latest web history accessed
* date in milliseconds and the second value maps normalized * date in milliseconds and the second value maps normalized (lowercase;
* (lowercase; trimmed) domain names to when those domains were * trimmed) domain names to when those domains were visited.
* visited.
* *
* @throws TskCoreException * @throws TskCoreException
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
@ -364,8 +363,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* term. * term.
* *
* @param dataSource The data source. * @param dataSource The data source.
* @param count The maximum number of records to be shown (must be > * @param count The maximum number of records to be shown (must be > 0).
* 0).
* *
* @return The list of most recent web searches where most recent search * @return The list of most recent web searches where most recent search
* appears first. * appears first.
@ -386,21 +384,22 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
.getArtifacts(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), dataSource.getId()); .getArtifacts(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), dataSource.getId());
// group by search string (case insensitive) // group by search string (case insensitive)
Collection<List<TopWebSearchResult>> resultGroups = webSearchArtifacts Collection<TopWebSearchResult> resultGroups = webSearchArtifacts
.stream() .stream()
// get items where search string and date is not null // get items where search string and date is not null
.map(UserActivitySummary::getWebSearchResult) .map(UserActivitySummary::getWebSearchResult)
// remove null records // remove null records
.filter(result -> result != null) .filter(result -> result != null)
// get these messages grouped by search to string // get the latest message for each search string
.collect(Collectors.groupingBy((result) -> result.getSearchString().toUpperCase())) .collect(Collectors.toMap(
(result) -> result.getSearchString().toUpperCase(),
result -> result,
(result1, result2) -> TOP_WEBSEARCH_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
.values(); .values();
// get the most recent date for each search term // get the most recent date for each search term
List<TopWebSearchResult> results = resultGroups List<TopWebSearchResult> results = resultGroups
.stream() .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 // get most recent searches first
.sorted(TOP_WEBSEARCH_RESULT_DATE_COMPARE.reversed()) .sorted(TOP_WEBSEARCH_RESULT_DATE_COMPARE.reversed())
.limit(count) .limit(count)
@ -448,12 +447,31 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return translated; return translated;
} }
/**
* Gives the most recent TopDeviceAttachedResult. If one is null, the other
* is returned.
*
* @param r1 A result.
* @param r2 Another result.
* @return The most recent one with a non-null date.
*/
private TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2) {
if (r2.getDateAccessed() == null) {
return r1;
}
if (r1.getDateAccessed() == null) {
return r2;
}
return r1.getDateAccessed().compareTo(r2.getDateAccessed()) >= 0 ? r1 : r2;
}
/** /**
* Retrieves most recent devices used by most recent date attached. * Retrieves most recent devices used by most recent date attached.
* *
* @param dataSource The data source. * @param dataSource The data source.
* @param count The maximum number of records to be shown (must be > * @param count The maximum number of records to be shown (must be > 0).
* 0).
* *
* @return The list of most recent devices attached where most recent device * @return The list of most recent devices attached where most recent device
* attached appears first. * attached appears first.
@ -469,7 +487,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList(); 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) dataSource, TYPE_DATETIME, DataSourceInfoUtilities.SortOrder.DESCENDING, 0)
.stream() .stream()
.map(artifact -> { .map(artifact -> {
@ -482,9 +500,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
}) })
// remove Root Hub identifier // remove Root Hub identifier
.filter(result -> { .filter(result -> {
return result.getDeviceModel() == null return result.getDeviceId() == null
|| result.getDeviceModel() == null
|| !DEVICE_EXCLUDE_LIST.contains(result.getDeviceModel().trim().toUpperCase()); || !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) .limit(count)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -538,8 +561,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* sent. * sent.
* *
* @param dataSource The data source. * @param dataSource The data source.
* @param count The maximum number of records to be shown (must be > * @param count The maximum number of records to be shown (must be > 0).
* 0).
* *
* @return The list of most recent accounts used where the most recent * @return The list of most recent accounts used where the most recent
* account by last message sent occurs first. * account by last message sent occurs first.
@ -585,18 +607,19 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
Stream<TopAccountResult> allResults = Stream.concat(messageResults, Stream.concat(emailResults, calllogResults)); Stream<TopAccountResult> allResults = Stream.concat(messageResults, Stream.concat(emailResults, calllogResults));
// get them grouped by account type // get them grouped by account type
Collection<List<TopAccountResult>> groupedResults = allResults Collection<TopAccountResult> groupedResults = allResults
// remove null records // remove null records
.filter(result -> result != null) .filter(result -> result != null)
// get these messages grouped by account type // get these messages grouped by account type and get the most recent of each type
.collect(Collectors.groupingBy(TopAccountResult::getAccountType)) .collect(Collectors.toMap(
result -> result.getAccountType(),
result -> result,
(result1, result2) -> TOP_ACCOUNT_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
.values(); .values();
// get account type sorted by most recent date // get account type sorted by most recent date
return groupedResults return groupedResults
.stream() .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 // get most recent accounts accessed
.sorted(TOP_ACCOUNT_RESULT_DATE_COMPARE.reversed()) .sorted(TOP_ACCOUNT_RESULT_DATE_COMPARE.reversed())
// limit to count // limit to count
@ -721,7 +744,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return longNum != null && longNum > 0; return longNum != null && longNum > 0;
} }
/** /**
* Retrieves the top programs results for the given data source limited to * 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 * the count provided as a parameter. The highest run times are at the top
@ -732,8 +754,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* *
* @param dataSource The datasource. If the datasource is null, an empty * @param dataSource The datasource. If the datasource is null, an empty
* list will be returned. * list will be returned.
* @param count The number of results to return. This value must be > 0 * @param count The number of results to return. This value must be > 0 or
* or an IllegalArgumentException will be thrown. * an IllegalArgumentException will be thrown.
* *
* @return The sorted list and limited to the count if last run or run count * @return The sorted list and limited to the count if last run or run count
* information is available on any item. * information is available on any item.
@ -759,7 +781,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
// The value will be a TopProgramsResult with the max run times // The value will be a TopProgramsResult with the max run times
// and most recent last run date for each program name / program path pair. // and most recent last run date for each program name / program path pair.
.collect(Collectors.toMap( .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, res -> res,
(res1, res2) -> { (res1, res2) -> {
return new TopProgramsResult( return new TopProgramsResult(

View File

@ -44,3 +44,4 @@ PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs
DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected. DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected.
WhereUsedPanel.cityCountsLabel.text=Cities WhereUsedPanel.cityCountsLabel.text=Cities
WhereUsedPanel.viewInGeolocationBtn.text=View In Geolocation WhereUsedPanel.viewInGeolocationBtn.text=View In Geolocation
TimelinePanel.activityRangeLabel.text=Activity Range

View File

@ -3,6 +3,7 @@ AnalysisPanel_keyColumn_title=Name
AnalysisPanel_keywordSearchModuleName=Keyword Search AnalysisPanel_keywordSearchModuleName=Keyword Search
# {0} - module name # {0} - module name
BaseDataSourceSummaryPanel_defaultNotIngestMessage=The {0} ingest module has not been run on this data source. BaseDataSourceSummaryPanel_defaultNotIngestMessage=The {0} ingest module has not been run on this data source.
ContainerPanel_setFieldsForNonImageDataSource_na=N/A
CTL_DataSourceSummaryAction=Data Source Summary CTL_DataSourceSummaryAction=Data Source Summary
DataSourceSummaryDialog.closeButton.text=Close DataSourceSummaryDialog.closeButton.text=Close
ContainerPanel.displayNameLabel.text=Display Name: ContainerPanel.displayNameLabel.text=Display Name:
@ -48,6 +49,7 @@ DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases
DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files
DataSourceSummaryTabbedPane_timelineTab_title=Timeline
DataSourceSummaryTabbedPane_typesTab_title=Types DataSourceSummaryTabbedPane_typesTab_title=Types
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
GeolocationPanel_cityColumn_title=City GeolocationPanel_cityColumn_title=City
@ -68,6 +70,11 @@ SizeRepresentationUtil_units_kilobytes=\ kB
SizeRepresentationUtil_units_megabytes=\ MB SizeRepresentationUtil_units_megabytes=\ MB
SizeRepresentationUtil_units_petabytes=\ PB SizeRepresentationUtil_units_petabytes=\ PB
SizeRepresentationUtil_units_terabytes=\ TB SizeRepresentationUtil_units_terabytes=\ TB
TimelinePanel_earliestLabel_title=Earliest
TimelinePanel_latestLabel_title=Latest
TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events
TimlinePanel_last30DaysChart_fileEvts_title=File Events
TimlinePanel_last30DaysChart_title=Last 30 Days
TypesPanel_artifactsTypesPieChart_title=Artifact Types TypesPanel_artifactsTypesPieChart_title=Artifact Types
TypesPanel_fileMimeTypesChart_audio_title=Audio TypesPanel_fileMimeTypesChart_audio_title=Audio
TypesPanel_fileMimeTypesChart_documents_title=Documents TypesPanel_fileMimeTypesChart_documents_title=Documents
@ -100,6 +107,7 @@ PastCasesPanel.notableFileLabel.text=Cases with Common Items That Were Tagged as
PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs
DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected. DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected.
GeolocationPanel.cityCountsLabel.text=Cities GeolocationPanel.cityCountsLabel.text=Cities
TimelinePanel.activityRangeLabel.text=Activity Range
UserActivityPanel_noDataExists=No communication data exists UserActivityPanel_noDataExists=No communication data exists
UserActivityPanel_tab_title=User Activity UserActivityPanel_tab_title=User Activity
UserActivityPanel_TopAccountTableModel_accountType_header=Account Type UserActivityPanel_TopAccountTableModel_accountType_header=Account Type

View File

@ -26,6 +26,7 @@ import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.table.DefaultTableModel; import javax.swing.table.DefaultTableModel;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
@ -165,8 +166,6 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize) { private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize) {
clearTableValues(); clearTableValues();
if (selectedDataSource != null) { if (selectedDataSource != null) {
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(unallocatedFilesSize));
timeZoneValue.setText(selectedDataSource.getTimeZone());
displayNameValue.setText(selectedDataSource.getName()); displayNameValue.setText(selectedDataSource.getName());
originalNameValue.setText(selectedDataSource.getName()); originalNameValue.setText(selectedDataSource.getName());
deviceIdValue.setText(selectedDataSource.getDeviceId()); deviceIdValue.setText(selectedDataSource.getDeviceId());
@ -178,24 +177,48 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
} }
if (selectedDataSource instanceof Image) { if (selectedDataSource instanceof Image) {
setFieldsForImage((Image) selectedDataSource); setFieldsForImage((Image) selectedDataSource, unallocatedFilesSize);
} else {
setFieldsForNonImageDataSource();
} }
} }
this.repaint(); 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 * Sets text fields for an image. This should be called after
* clearTableValues and before updateFieldVisibility to ensure the proper * clearTableValues and before updateFieldVisibility to ensure the proper
* rendering. * rendering.
* *
* @param selectedImage The selected image. * @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()); imageTypeValue.setText(selectedImage.getType().getName());
sizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSize())); sizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSize()));
sectorSizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSsize())); sectorSizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSsize()));
timeZoneValue.setText(selectedImage.getTimeZone());
for (String path : selectedImage.getPaths()) { for (String path : selectedImage.getPaths()) {
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path}); ((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path});

View File

@ -39,7 +39,8 @@ import org.sleuthkit.datamodel.DataSource;
"DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files", "DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files",
"DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases", "DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases",
"DataSourceSummaryTabbedPane_analysisTab_title=Analysis", "DataSourceSummaryTabbedPane_analysisTab_title=Analysis",
"DataSourceSummaryTabbedPane_whereUsedTab_title=Where Used" "DataSourceSummaryTabbedPane_whereUsedTab_title=Where Used",
"DataSourceSummaryTabbedPane_timelineTab_title=Timeline"
}) })
public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
@ -125,6 +126,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()), new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_pastCasesTab_title(), new PastCasesPanel()), new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_pastCasesTab_title(), new PastCasesPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_whereUsedTab_title(), new WhereUsedPanel()), new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_whereUsedTab_title(), new WhereUsedPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_timelineTab_title(), new TimelinePanel()),
// do nothing on closing // do nothing on closing
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> { new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> {
}), }),

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Function;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory; 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.datamodel.PastCasesSummary.PastCasesResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; 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;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
@ -103,31 +101,8 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
* @param result The result. * @param result The result.
*/ */
private void handleResult(DataFetchResult<PastCasesResult> result) { private void handleResult(DataFetchResult<PastCasesResult> result) {
showResultWithModuleCheck(notableFileTable, getSubResult(result, (res) -> res.getTaggedNotable()), CR_FACTORY, CR_NAME); showResultWithModuleCheck(notableFileTable, DataFetchResult.getSubResult(result, (res) -> res.getTaggedNotable()), CR_FACTORY, CR_NAME);
showResultWithModuleCheck(sameIdTable, getSubResult(result, (res) -> res.getSameIdsResults()), CR_FACTORY, CR_NAME); showResultWithModuleCheck(sameIdTable, DataFetchResult.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());
}
} }
@Override @Override

View File

@ -0,0 +1,214 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="mainScrollPane" alignment="0" pref="400" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="mainScrollPane" alignment="0" pref="300" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="mainContentPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
<EmptyBorder bottom="10" left="10" right="10" top="10"/>
</Border>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="3"/>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="ingestRunningPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 25]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="ingestRunningLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.JLabel" name="activityRangeLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Segoe UI" size="12" style="1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="TimelinePanel.activityRangeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &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="earliestLabelPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 20]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="earliestLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="latestLabelPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 20]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="latestLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler2">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
<Property name="alignmentX" type="float" value="0.0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
</AuxValues>
</Component>
<Container class="javax.swing.JPanel" name="sameIdPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[600, 300]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[600, 300]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[600, 300]"/>
</Property>
<Property name="verifyInputWhenFocusTarget" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="last30DaysChart"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler5">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 32767]"/>
</Property>
<Property name="alignmentX" type="float" value="0.0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalGlue"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,263 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.awt.Color;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.apache.commons.collections.CollectionUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.BarChartItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.BarChartSeries;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.OrderedKey;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
import org.sleuthkit.datamodel.DataSource;
/**
* A tab shown in data source summary displaying information about a data
* source's timeline events.
*/
@Messages({
"TimelinePanel_earliestLabel_title=Earliest",
"TimelinePanel_latestLabel_title=Latest",
"TimlinePanel_last30DaysChart_title=Last 30 Days",
"TimlinePanel_last30DaysChart_fileEvts_title=File Events",
"TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events",})
public class TimelinePanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy");
private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d");
private static final int MOST_RECENT_DAYS_COUNT = 30;
/**
* Creates a DateFormat formatter that uses UTC for time zone.
*
* @param formatString The date format string.
* @return The data format.
*/
private static DateFormat getUtcFormat(String formatString) {
return new SimpleDateFormat(formatString, Locale.getDefault());
}
// components displayed in the tab
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title());
private final LoadableLabel latestLabel = new LoadableLabel(Bundle.TimelinePanel_latestLabel_title());
private final BarChartPanel last30DaysChart = new BarChartPanel(Bundle.TimlinePanel_last30DaysChart_title(), "", "");
// all loadable components on this tab
private final List<LoadableComponent<?>> loadableComponents = Arrays.asList(earliestLabel, latestLabel, last30DaysChart);
// actions to load data for this tab
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
public TimelinePanel() {
this(new TimelineSummary());
}
/**
* Creates new form PastCasesPanel
*/
public TimelinePanel(TimelineSummary timelineData) {
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT),
(result) -> handleResult(result))
);
initComponents();
}
/**
* Formats a date using a DateFormat. In the event that the date is null,
* returns a null string.
*
* @param date The date to format.
* @param formatter The DateFormat to use to format the date.
* @return The formatted string generated from the formatter or null if the
* date is null.
*/
private static String formatDate(Date date, DateFormat formatter) {
return date == null ? null : formatter.format(date);
}
private static final Color FILE_EVT_COLOR = new Color(228, 22, 28);
private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100);
/**
* Converts DailyActivityAmount data retrieved from TimelineSummary into
* data to be displayed as a bar chart.
*
* @param recentDaysActivity The data retrieved from TimelineSummary.
* @return The data to be displayed in the BarChart.
*/
private List<BarChartSeries> parseChartData(List<DailyActivityAmount> recentDaysActivity) {
// if no data, return null indicating no result.
if (CollectionUtils.isEmpty(recentDaysActivity)) {
return null;
}
// Create a bar chart item for each recent days activity item
List<BarChartItem> fileEvtCounts = new ArrayList<>();
List<BarChartItem> artifactEvtCounts = new ArrayList<>();
for (int i = 0; i < recentDaysActivity.size(); i++) {
DailyActivityAmount curItem = recentDaysActivity.get(i);
long fileAmt = curItem.getFileActivityCount();
long artifactAmt = curItem.getArtifactActivityCount() * 100;
String formattedDate = (i == 0 || i == recentDaysActivity.size() - 1)
? formatDate(curItem.getDay(), CHART_FORMAT) : "";
OrderedKey thisKey = new OrderedKey(formattedDate, i);
fileEvtCounts.add(new BarChartItem(thisKey, fileAmt));
artifactEvtCounts.add(new BarChartItem(thisKey, artifactAmt));
}
return Arrays.asList(
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_fileEvts_title(), FILE_EVT_COLOR, fileEvtCounts),
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts));
}
/**
* Handles displaying the result for each displayable item in the
* TimelinePanel by breaking the TimelineSummaryData result into its
* constituent parts and then sending each data item to the pertinent
* component.
*
* @param result The result to be displayed on this tab.
*/
private void handleResult(DataFetchResult<TimelineSummaryData> result) {
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity())));
}
@Override
protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
onNewDataSource(dataFetchComponents, loadableComponents, dataSource);
}
@Override
public void close() {
ingestRunningLabel.unregister();
super.close();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JScrollPane mainScrollPane = new javax.swing.JScrollPane();
javax.swing.JPanel mainContentPanel = new javax.swing.JPanel();
javax.swing.JPanel ingestRunningPanel = ingestRunningLabel;
javax.swing.JLabel activityRangeLabel = new javax.swing.JLabel();
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2));
javax.swing.JPanel earliestLabelPanel = earliestLabel;
javax.swing.JPanel latestLabelPanel = latestLabel;
javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20));
javax.swing.JPanel sameIdPanel = last30DaysChart;
javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
mainContentPanel.setLayout(new javax.swing.BoxLayout(mainContentPanel, javax.swing.BoxLayout.PAGE_AXIS));
ingestRunningPanel.setAlignmentX(0.0F);
ingestRunningPanel.setMaximumSize(new java.awt.Dimension(32767, 25));
ingestRunningPanel.setMinimumSize(new java.awt.Dimension(10, 25));
ingestRunningPanel.setPreferredSize(new java.awt.Dimension(10, 25));
mainContentPanel.add(ingestRunningPanel);
activityRangeLabel.setFont(new java.awt.Font("Segoe UI", 1, 12)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(activityRangeLabel, org.openide.util.NbBundle.getMessage(TimelinePanel.class, "TimelinePanel.activityRangeLabel.text")); // NOI18N
mainContentPanel.add(activityRangeLabel);
activityRangeLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(TimelinePanel.class, "PastCasesPanel.notableFileLabel.text")); // NOI18N
filler1.setAlignmentX(0.0F);
mainContentPanel.add(filler1);
earliestLabelPanel.setAlignmentX(0.0F);
earliestLabelPanel.setMaximumSize(new java.awt.Dimension(32767, 20));
earliestLabelPanel.setMinimumSize(new java.awt.Dimension(100, 20));
earliestLabelPanel.setPreferredSize(new java.awt.Dimension(100, 20));
mainContentPanel.add(earliestLabelPanel);
latestLabelPanel.setAlignmentX(0.0F);
latestLabelPanel.setMaximumSize(new java.awt.Dimension(32767, 20));
latestLabelPanel.setMinimumSize(new java.awt.Dimension(100, 20));
latestLabelPanel.setPreferredSize(new java.awt.Dimension(100, 20));
mainContentPanel.add(latestLabelPanel);
filler2.setAlignmentX(0.0F);
mainContentPanel.add(filler2);
sameIdPanel.setAlignmentX(0.0F);
sameIdPanel.setMaximumSize(new java.awt.Dimension(600, 300));
sameIdPanel.setMinimumSize(new java.awt.Dimension(600, 300));
sameIdPanel.setPreferredSize(new java.awt.Dimension(600, 300));
sameIdPanel.setVerifyInputWhenFocusTarget(false);
mainContentPanel.add(sameIdPanel);
filler5.setAlignmentX(0.0F);
mainContentPanel.add(filler5);
mainScrollPane.setViewportView(mainContentPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -18,7 +18,6 @@
*/ */
package org.sleuthkit.autopsy.datasourcesummary.ui; package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.sql.SQLException; import java.sql.SQLException;
import java.text.DecimalFormat; import java.text.DecimalFormat;
@ -30,7 +29,6 @@ import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.swing.JLabel;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory; import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
@ -40,13 +38,13 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil; import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.AbstractLoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel.PieChartItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel.PieChartItem;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory;
@ -78,46 +76,6 @@ import org.sleuthkit.datamodel.TskCoreException;
"TypesPanel_sizeLabel_title=Size"}) "TypesPanel_sizeLabel_title=Size"})
class TypesPanel extends BaseDataSourceSummaryPanel { class TypesPanel extends BaseDataSourceSummaryPanel {
/**
* A label that allows for displaying loading messages and can be used with
* a DataFetchResult. Text displays as "<key>:<value | message>".
*/
private static class LoadableLabel extends AbstractLoadableComponent<String> {
private static final long serialVersionUID = 1L;
private final JLabel label = new JLabel();
private final String key;
/**
* Main constructor for the label.
*
* @param key The key to be displayed.
*/
LoadableLabel(String key) {
this.key = key;
setLayout(new BorderLayout());
add(label, BorderLayout.CENTER);
this.showResults(null);
}
private void setValue(String value) {
String formattedKey = StringUtils.isBlank(key) ? "" : key;
String formattedValue = StringUtils.isBlank(value) ? "" : value;
label.setText(String.format("%s: %s", formattedKey, formattedValue));
}
@Override
protected void setMessage(boolean visible, String message) {
setValue(message);
}
@Override
protected void setResults(String data) {
setValue(data);
}
}
/** /**
* Data for types pie chart. * Data for types pie chart.
*/ */

View File

@ -0,0 +1,307 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.util.Collections;
import java.util.List;
import javax.swing.JLabel;
import org.apache.commons.collections4.CollectionUtils;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.data.category.DefaultCategoryDataset;
/**
* A bar chart panel.
*/
public class BarChartPanel extends AbstractLoadableComponent<List<BarChartPanel.BarChartSeries>> {
/**
* Represents a series in a bar chart where all items pertain to one
* category.
*/
public static class BarChartSeries {
private final Comparable<?> key;
private final Color color;
private final List<BarChartItem> items;
/**
* Main constructor.
*
* @param color The color for this series.
* @param items The bars to be displayed for this series.
*/
public BarChartSeries(Comparable<?> key, Color color, List<BarChartItem> items) {
this.key = key;
this.color = color;
this.items = (items == null) ? Collections.emptyList() : Collections.unmodifiableList(items);
}
/**
* @return The color for this series.
*/
public Color getColor() {
return color;
}
/**
* @return The bars to be displayed for this series.
*/
public List<BarChartItem> getItems() {
return items;
}
/**
* @return The key for this item.
*/
public Comparable<?> getKey() {
return key;
}
}
/**
* An individual bar to be displayed in the bar chart.
*/
public static class BarChartItem {
private final Comparable<?> key;
private final double value;
/**
* Main constructor.
*
* @param label The key for this bar. Also serves as the label using
* toString().
* @param value The value for this item.
*/
public BarChartItem(Comparable<?> key, double value) {
this.key = key;
this.value = value;
}
/**
* @return The key for this item.
*/
public Comparable<?> getKey() {
return key;
}
/**
* @return The value for this item.
*/
public double getValue() {
return value;
}
}
/**
* JFreeChart bar charts don't preserve the order of bars provided to the
* chart, but instead uses the comparable nature to order items. This
* provides order using a provided index as well as the value for the axis.
*/
public static class OrderedKey implements Comparable<OrderedKey> {
private final Object keyValue;
private final int keyIndex;
/**
* Main constructor.
*
* @param keyValue The value for the key to be displayed in the domain
* axis.
* @param keyIndex The index at which it will be displayed.
*/
public OrderedKey(Object keyValue, int keyIndex) {
this.keyValue = keyValue;
this.keyIndex = keyIndex;
}
/**
* @return The value for the key to be displayed in the domain axis.
*/
Object getKeyValue() {
return keyValue;
}
/**
* @return The index at which it will be displayed.
*/
int getKeyIndex() {
return keyIndex;
}
@Override
public int compareTo(OrderedKey o) {
// this will have a higher value than null.
if (o == null) {
return 1;
}
// compare by index
return Integer.compare(this.getKeyIndex(), o.getKeyIndex());
}
@Override
public int hashCode() {
int hash = 3;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final OrderedKey other = (OrderedKey) obj;
if (this.keyIndex != other.keyIndex) {
return false;
}
return true;
}
@Override
public String toString() {
// use toString on the key.
return this.getKeyValue() == null ? null : this.getKeyValue().toString();
}
}
private static final long serialVersionUID = 1L;
private static final Font DEFAULT_FONT = new JLabel().getFont();
private static final Font DEFAULT_HEADER_FONT = new Font(DEFAULT_FONT.getName(), DEFAULT_FONT.getStyle(), (int) (DEFAULT_FONT.getSize() * 1.5));
private final ChartMessageOverlay overlay = new ChartMessageOverlay();
private final DefaultCategoryDataset dataset = new DefaultCategoryDataset();
private final JFreeChart chart;
private final CategoryPlot plot;
/**
* Main constructor assuming null values for all items.
*/
public BarChartPanel() {
this(null, null, null);
}
/**
* Main constructor for the pie chart.
*
* @param title The title for this pie chart.
* @param categoryLabel The x-axis label.
* @param valueLabel The y-axis label.
*/
public BarChartPanel(String title, String categoryLabel, String valueLabel) {
this.chart = ChartFactory.createStackedBarChart(
title,
categoryLabel,
valueLabel,
dataset,
PlotOrientation.VERTICAL,
true, false, false);
// set style to match autopsy components
chart.setBackgroundPaint(null);
chart.getTitle().setFont(DEFAULT_HEADER_FONT);
this.plot = ((CategoryPlot) chart.getPlot());
this.plot.getRenderer().setBaseItemLabelFont(DEFAULT_FONT);
plot.setBackgroundPaint(null);
plot.setOutlinePaint(null);
// hide y axis labels
ValueAxis range = plot.getRangeAxis();
range.setVisible(false);
// make sure x axis labels don't get cut off
plot.getDomainAxis().setMaximumCategoryLabelWidthRatio(10);
((BarRenderer) plot.getRenderer()).setBarPainter(new StandardBarPainter());
// Create Panel
ChartPanel panel = new ChartPanel(chart);
panel.addOverlay(overlay);
panel.setPopupMenu(null);
this.setLayout(new BorderLayout());
this.add(panel, BorderLayout.CENTER);
}
/**
* @return The title for this chart if one exists.
*/
public String getTitle() {
return (this.chart == null || this.chart.getTitle() == null)
? null
: this.chart.getTitle().getText();
}
/**
* Sets the title for this pie chart.
*
* @param title The title.
*
* @return As a utility, returns this.
*/
public BarChartPanel setTitle(String title) {
this.chart.getTitle().setText(title);
return this;
}
@Override
protected void setMessage(boolean visible, String message) {
this.overlay.setVisible(visible);
this.overlay.setMessage(message);
}
@Override
protected void setResults(List<BarChartPanel.BarChartSeries> data) {
this.dataset.clear();
if (CollectionUtils.isNotEmpty(data)) {
for (int s = 0; s < data.size(); s++) {
BarChartPanel.BarChartSeries series = data.get(s);
if (series != null && CollectionUtils.isNotEmpty(series.getItems())) {
if (series.getColor() != null) {
this.plot.getRenderer().setSeriesPaint(s, series.getColor());
}
for (int i = 0; i < series.getItems().size(); i++) {
BarChartItem bar = series.getItems().get(i);
this.dataset.setValue(bar.getValue(), series.getKey(), bar.getKey());
}
}
}
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import java.awt.Graphics2D;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.panel.AbstractOverlay;
import org.jfree.chart.panel.Overlay;
/**
* A JFreeChart message overlay that can show a message for the purposes of the
* LoadableComponent.
*/
class ChartMessageOverlay extends AbstractOverlay implements Overlay {
private static final long serialVersionUID = 1L;
private final BaseMessageOverlay overlay = new BaseMessageOverlay();
// multiply this value by the smaller dimension (height or width) of the component
// to determine width of text to be displayed.
private static final double MESSAGE_WIDTH_FACTOR = .6;
/**
* Sets this layer visible when painted. In order to be shown in UI, this
* component needs to be repainted.
*
* @param visible Whether or not it is visible.
*/
void setVisible(boolean visible) {
overlay.setVisible(visible);
}
/**
* Sets the message to be displayed in the child jlabel.
*
* @param message The message to be displayed.
*/
void setMessage(String message) {
overlay.setMessage(message);
}
@Override
public void paintOverlay(Graphics2D gd, ChartPanel cp) {
int labelWidth = (int) (Math.min(cp.getWidth(), cp.getHeight()) * MESSAGE_WIDTH_FACTOR);
overlay.paintOverlay(gd, cp.getWidth(), cp.getHeight(), labelWidth);
}
}

View File

@ -18,6 +18,8 @@
*/ */
package org.sleuthkit.autopsy.datasourcesummary.uiutils; package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import java.util.function.Function;
/** /**
* The result of a loading process. * The result of a loading process.
*/ */
@ -30,6 +32,29 @@ public final class DataFetchResult<R> {
SUCCESS, ERROR SUCCESS, ERROR
} }
/**
* A utility method that, given an input data fetch result, creates an error
* result if the original is an error. Otherwise, uses the getSubResult
* function on the underlying data to create a new DataFetchResult.
*
* @param inputResult The input result.
* @param getSubComponent The means of getting the data given the original
* data.
*
* @return The new result with the error of the original or the processed
* data.
*/
public static <I, O> DataFetchResult<O> getSubResult(DataFetchResult<I> inputResult, Function<I, O> getSubResult) {
if (inputResult == null) {
return null;
} else if (inputResult.getResultType() == ResultType.SUCCESS) {
O innerData = (inputResult.getData() == null) ? null : getSubResult.apply(inputResult.getData());
return DataFetchResult.getSuccessResult(innerData);
} else {
return DataFetchResult.getErrorResult(inputResult.getException());
}
}
/** /**
* Creates a DataFetchResult of loaded data including the data. * Creates a DataFetchResult of loaded data including the data.
* *
@ -60,8 +85,7 @@ public final class DataFetchResult<R> {
* Main constructor for the DataLoadingResult. * Main constructor for the DataLoadingResult.
* *
* @param state The state of the result. * @param state The state of the result.
* @param data If the result is SUCCESS, the data related to this * @param data If the result is SUCCESS, the data related to this result.
* result.
* @param exception If the result is ERROR, the related exception. * @param exception If the result is ERROR, the related exception.
*/ */
private DataFetchResult(ResultType state, R data, Throwable exception) { private DataFetchResult(ResultType state, R data, Throwable exception) {

View File

@ -0,0 +1,63 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import java.awt.BorderLayout;
import javax.swing.JLabel;
import org.apache.commons.lang3.StringUtils;
/**
* A label that allows for displaying loading messages and can be used with a
* DataFetchResult. Text displays as "<key>:<value | message>".
*/
public class LoadableLabel extends AbstractLoadableComponent<String> {
private static final long serialVersionUID = 1L;
private final JLabel label = new JLabel();
private final String key;
/**
* Main constructor for the label.
*
* @param key The key to be displayed.
*/
public LoadableLabel(String key) {
this.key = key;
setLayout(new BorderLayout());
add(label, BorderLayout.CENTER);
this.showResults(null);
}
private void setValue(String value) {
String formattedKey = StringUtils.isBlank(key) ? "" : key;
String formattedValue = StringUtils.isBlank(value) ? "" : value;
label.setText(String.format("%s: %s", formattedKey, formattedValue));
}
@Override
protected void setMessage(boolean visible, String message) {
setValue(message);
}
@Override
protected void setResults(String data) {
setValue(data);
}
}

View File

@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Font; import java.awt.Font;
import java.awt.Graphics2D;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.List; import java.util.List;
import javax.swing.JLabel; import javax.swing.JLabel;
@ -30,8 +29,6 @@ import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart; import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.PieSectionLabelGenerator; import org.jfree.chart.labels.PieSectionLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator; import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.panel.AbstractOverlay;
import org.jfree.chart.panel.Overlay;
import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.PiePlot;
import org.jfree.data.general.DefaultPieDataset; import org.jfree.data.general.DefaultPieDataset;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
@ -89,46 +86,6 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
} }
} }
/**
* A JFreeChart message overlay that can show a message for the purposes of
* the LoadableComponent.
*/
private static class MessageOverlay extends AbstractOverlay implements Overlay {
private static final long serialVersionUID = 1L;
private final BaseMessageOverlay overlay = new BaseMessageOverlay();
// multiply this value by the smaller dimension (height or width) of the component
// to determine width of text to be displayed.
private static final double MESSAGE_WIDTH_FACTOR = .6;
/**
* Sets this layer visible when painted. In order to be shown in UI,
* this component needs to be repainted.
*
* @param visible Whether or not it is visible.
*/
void setVisible(boolean visible) {
overlay.setVisible(visible);
}
/**
* Sets the message to be displayed in the child jlabel.
*
* @param message The message to be displayed.
*/
void setMessage(String message) {
overlay.setMessage(message);
}
@Override
public void paintOverlay(Graphics2D gd, ChartPanel cp) {
int labelWidth = (int) (Math.min(cp.getWidth(), cp.getHeight()) * MESSAGE_WIDTH_FACTOR);
overlay.paintOverlay(gd, cp.getWidth(), cp.getHeight(), labelWidth);
}
}
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Font DEFAULT_FONT = new JLabel().getFont(); private static final Font DEFAULT_FONT = new JLabel().getFont();
@ -146,7 +103,7 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
= new StandardPieSectionLabelGenerator( = new StandardPieSectionLabelGenerator(
"{0}: {1} ({2})", new DecimalFormat("#,###"), new DecimalFormat("0.0%")); "{0}: {1} ({2})", new DecimalFormat("#,###"), new DecimalFormat("0.0%"));
private final MessageOverlay overlay = new MessageOverlay(); private final ChartMessageOverlay overlay = new ChartMessageOverlay();
private final DefaultPieDataset dataset = new DefaultPieDataset(); private final DefaultPieDataset dataset = new DefaultPieDataset();
private final JFreeChart chart; private final JFreeChart chart;
private final PiePlot plot; private final PiePlot plot;

View File

@ -19,12 +19,14 @@
package org.sleuthkit.autopsy.discovery.search; package org.sleuthkit.autopsy.discovery.search;
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.EventBus;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey; import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
import org.sleuthkit.autopsy.discovery.search.SearchData.Type; import org.sleuthkit.autopsy.discovery.search.SearchData.Type;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
/** /**
* Class to handle event bus and events for discovery tool. * Class to handle event bus and events for discovery tool.
@ -90,8 +92,8 @@ public final class DiscoveryEventUtils {
} }
/** /**
* Event to signal that any background tasks currently running should * Event to signal that any background tasks currently running should be
* be cancelled. * cancelled.
*/ */
public static final class CancelBackgroundTasksEvent { public static final class CancelBackgroundTasksEvent {
@ -124,6 +126,30 @@ public final class DiscoveryEventUtils {
} }
} }
/**
* Event to signal that the list should be populated.
*/
public static final class PopulateDomainTabsEvent {
private final String domain;
/**
* Construct a new PopulateDomainTabsEvent.
*/
public PopulateDomainTabsEvent(String domain) {
this.domain = domain;
}
/**
* Get the domain for the details area.
*
* @return The the domain for the details area.
*/
public String getDomain() {
return domain;
}
}
/** /**
* Event to signal the completion of a search being performed. * Event to signal the completion of a search being performed.
*/ */
@ -203,6 +229,47 @@ public final class DiscoveryEventUtils {
} }
/**
* Event to signal the completion of a search being performed.
*/
public static final class ArtifactSearchResultEvent {
private final List<BlackboardArtifact> listOfArtifacts = new ArrayList<>();
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
/**
* Construct a new ArtifactSearchResultEvent with a list of specified
* artifacts and an artifact type.
*
* @param artifactType The type of artifacts in the list.
* @param listOfArtifacts The list of artifacts retrieved.
*/
public ArtifactSearchResultEvent(BlackboardArtifact.ARTIFACT_TYPE artifactType, List<BlackboardArtifact> listOfArtifacts) {
if (listOfArtifacts != null) {
this.listOfArtifacts.addAll(listOfArtifacts);
}
this.artifactType = artifactType;
}
/**
* Get the list of artifacts included in the event.
*
* @return The list of artifacts retrieved.
*/
public List<BlackboardArtifact> getListOfArtifacts() {
return Collections.unmodifiableList(listOfArtifacts);
}
/**
* Get the type of BlackboardArtifact type of which exist in the list.
*
* @return The BlackboardArtifact type of which exist in the list.
*/
public BlackboardArtifact.ARTIFACT_TYPE getArtifactType() {
return artifactType;
}
}
/** /**
* Event to signal the completion of page retrieval and include the page * Event to signal the completion of page retrieval and include the page
* contents. * contents.

View File

@ -33,6 +33,7 @@ import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -217,6 +218,15 @@ public class SearchFiltering {
this.types = types; this.types = types;
} }
/**
* Get the list of artifact types specified by the filter.
*
* @return The list of artifact types specified by the filter.
*/
public List<ARTIFACT_TYPE> getTypes() {
return Collections.unmodifiableList(types);
}
@Override @Override
public String getWhereClause() { public String getWhereClause() {
StringJoiner joiner = new StringJoiner(","); StringJoiner joiner = new StringJoiner(",");

View File

@ -0,0 +1,41 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.discovery.ui;
import javax.swing.JPanel;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Class for ensuring all ArtifactDetailsPanels have a setArtifact method.
*
*/
public abstract class AbstractArtifactDetailsPanel extends JPanel {
private static final long serialVersionUID = 1L;
/**
* Called to display the contents of the given artifact.
*
* @param artifact the artifact to display.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract public void setArtifact(BlackboardArtifact artifact);
}

View File

@ -24,6 +24,7 @@ import javax.swing.JCheckBox;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/** /**
* Abstract class extending JPanel for filter controls. * Abstract class extending JPanel for filter controls.
@ -41,6 +42,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* selected, null to indicate leaving selected items * selected, null to indicate leaving selected items
* unchanged or that there are no items to select. * unchanged or that there are no items to select.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract void configurePanel(boolean selected, int[] indicesSelected); abstract void configurePanel(boolean selected, int[] indicesSelected);
/** /**
@ -48,6 +50,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* *
* @return The JCheckBox which enables and disables this filter. * @return The JCheckBox which enables and disables this filter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract JCheckBox getCheckbox(); abstract JCheckBox getCheckbox();
/** /**
@ -57,6 +60,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* @return The JList which contains the values available for selection for * @return The JList which contains the values available for selection for
* this filter. * this filter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract JList<?> getList(); abstract JList<?> getList();
/** /**
@ -65,6 +69,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* *
* @return The JLabel to display under the JCheckBox. * @return The JLabel to display under the JCheckBox.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract JLabel getAdditionalLabel(); abstract JLabel getAdditionalLabel();
/** /**
@ -73,6 +78,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* @return If the settings are invalid returns the error that has occurred, * @return If the settings are invalid returns the error that has occurred,
* otherwise returns empty string. * otherwise returns empty string.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract String checkForError(); abstract String checkForError();
/** /**
@ -82,6 +88,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* @param actionlistener The listener for the checkbox selection events. * @param actionlistener The listener for the checkbox selection events.
* @param listListener The listener for the list selection events. * @param listListener The listener for the list selection events.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addListeners(ActionListener actionListener, ListSelectionListener listListener) { void addListeners(ActionListener actionListener, ListSelectionListener listListener) {
if (getCheckbox() != null) { if (getCheckbox() != null) {
getCheckbox().addActionListener(actionListener); getCheckbox().addActionListener(actionListener);
@ -97,11 +104,13 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* @return The AbstractFilter for the selected settings, null if the * @return The AbstractFilter for the selected settings, null if the
* settings are not in use. * settings are not in use.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract AbstractFilter getFilter(); abstract AbstractFilter getFilter();
/** /**
* Remove listeners from the checkbox and the list if they exist. * Remove listeners from the checkbox and the list if they exist.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void removeListeners() { void removeListeners() {
if (getCheckbox() != null) { if (getCheckbox() != null) {
for (ActionListener listener : getCheckbox().getActionListeners()) { for (ActionListener listener : getCheckbox().getActionListeners()) {

View File

@ -32,6 +32,7 @@ import javax.swing.JSplitPane;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes.GroupingAttributeType; import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes.GroupingAttributeType;
import org.sleuthkit.autopsy.discovery.search.Group; import org.sleuthkit.autopsy.discovery.search.Group;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter.SortingMethod; import org.sleuthkit.autopsy.discovery.search.ResultsSorter.SortingMethod;
@ -65,6 +66,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
/** /**
* Setup necessary for implementations of this abstract class. * Setup necessary for implementations of this abstract class.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
AbstractFiltersPanel() { AbstractFiltersPanel() {
firstColumnPanel.setLayout(new GridBagLayout()); firstColumnPanel.setLayout(new GridBagLayout());
secondColumnPanel.setLayout(new GridBagLayout()); secondColumnPanel.setLayout(new GridBagLayout());
@ -75,6 +77,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* *
* @return The type of results this panel filters. * @return The type of results this panel filters.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract SearchData.Type getType(); abstract SearchData.Type getType();
/** /**
@ -88,7 +91,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* list, null if none are selected. * list, null if none are selected.
* @param column The column to add the DiscoveryFilterPanel to. * @param column The column to add the DiscoveryFilterPanel to.
*/ */
final synchronized void addFilter(AbstractDiscoveryFilterPanel filterPanel, boolean isSelected, int[] indicesSelected, int column) { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void addFilter(AbstractDiscoveryFilterPanel filterPanel, boolean isSelected, int[] indicesSelected, int column) {
if (!isInitialized) { if (!isInitialized) {
constraints.gridy = 0; constraints.gridy = 0;
constraints.anchor = GridBagConstraints.FIRST_LINE_START; constraints.anchor = GridBagConstraints.FIRST_LINE_START;
@ -132,6 +136,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* *
* @param splitPane The JSplitPane which the columns are added to. * @param splitPane The JSplitPane which the columns are added to.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void addPanelsToScrollPane(JSplitPane splitPane) { final void addPanelsToScrollPane(JSplitPane splitPane) {
splitPane.setLeftComponent(firstColumnPanel); splitPane.setLeftComponent(firstColumnPanel);
splitPane.setRightComponent(secondColumnPanel); splitPane.setRightComponent(secondColumnPanel);
@ -142,7 +147,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
/** /**
* Clear the filters from the panel * Clear the filters from the panel
*/ */
final synchronized void clearFilters() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void clearFilters() {
for (AbstractDiscoveryFilterPanel filterPanel : filters) { for (AbstractDiscoveryFilterPanel filterPanel : filters) {
filterPanel.removeListeners(); filterPanel.removeListeners();
} }
@ -159,6 +165,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* column. * column.
* @param columnIndex The column to add the Component to. * @param columnIndex The column to add the Component to.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void addToGridBagLayout(Component componentToAdd, Component additionalComponentToAdd, int columnIndex) { private void addToGridBagLayout(Component componentToAdd, Component additionalComponentToAdd, int columnIndex) {
addToColumn(componentToAdd, columnIndex); addToColumn(componentToAdd, columnIndex);
if (additionalComponentToAdd != null) { if (additionalComponentToAdd != null) {
@ -174,6 +181,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* @param component The Component to add. * @param component The Component to add.
* @param columnNumber The column to add the Component to. * @param columnNumber The column to add the Component to.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void addToColumn(Component component, int columnNumber) { private void addToColumn(Component component, int columnNumber) {
if (columnNumber == 0) { if (columnNumber == 0) {
firstColumnPanel.add(component, constraints); firstColumnPanel.add(component, constraints);
@ -186,7 +194,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* Check if the fields are valid, and fire a property change event to * Check if the fields are valid, and fire a property change event to
* indicate any errors. * indicate any errors.
*/ */
synchronized void validateFields() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void validateFields() {
String errorString = null; String errorString = null;
for (AbstractDiscoveryFilterPanel filterPanel : filters) { for (AbstractDiscoveryFilterPanel filterPanel : filters) {
errorString = filterPanel.checkForError(); errorString = filterPanel.checkForError();
@ -197,6 +206,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
firePropertyChange("FilterError", null, errorString); firePropertyChange("FilterError", null, errorString);
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
validateFields(); validateFields();
@ -209,6 +219,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* *
* @return True if the ObjectsDetectedFilter is supported, false otherwise. * @return True if the ObjectsDetectedFilter is supported, false otherwise.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
boolean isObjectsFilterSupported() { boolean isObjectsFilterSupported() {
for (AbstractDiscoveryFilterPanel filter : filters) { for (AbstractDiscoveryFilterPanel filter : filters) {
if (filter instanceof ObjectDetectedFilterPanel) { if (filter instanceof ObjectDetectedFilterPanel) {
@ -223,6 +234,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* *
* @return True if the HashSetFilter is supported, false otherwise. * @return True if the HashSetFilter is supported, false otherwise.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
boolean isHashSetFilterSupported() { boolean isHashSetFilterSupported() {
for (AbstractDiscoveryFilterPanel filter : filters) { for (AbstractDiscoveryFilterPanel filter : filters) {
if (filter instanceof HashSetFilterPanel) { if (filter instanceof HashSetFilterPanel) {
@ -237,6 +249,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* *
* @return True if the InterestingItemsFilter is supported, false otherwise. * @return True if the InterestingItemsFilter is supported, false otherwise.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
boolean isInterestingItemsFilterSupported() { boolean isInterestingItemsFilterSupported() {
for (AbstractDiscoveryFilterPanel filter : filters) { for (AbstractDiscoveryFilterPanel filter : filters) {
if (filter instanceof InterestingItemsFilterPanel) { if (filter instanceof InterestingItemsFilterPanel) {
@ -251,8 +264,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* *
* @return The list of filters selected by the user. * @return The list of filters selected by the user.
*/ */
synchronized List<AbstractFilter> getFilters() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
List<AbstractFilter> getFilters() {
List<AbstractFilter> filtersToUse = new ArrayList<>(); List<AbstractFilter> filtersToUse = new ArrayList<>();
if (getType() != SearchData.Type.DOMAIN) { //Domain type does not have a file type if (getType() != SearchData.Type.DOMAIN) { //Domain type does not have a file type
filtersToUse.add(new SearchFiltering.FileTypeFilter(getType())); filtersToUse.add(new SearchFiltering.FileTypeFilter(getType()));
@ -268,6 +281,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
return filtersToUse; return filtersToUse;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public void valueChanged(ListSelectionEvent evt) { public void valueChanged(ListSelectionEvent evt) {
if (!evt.getValueIsAdjusting()) { if (!evt.getValueIsAdjusting()) {
@ -282,6 +296,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* *
* @return The most recently used sorting method. * @return The most recently used sorting method.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
SortingMethod getLastSortingMethod() { SortingMethod getLastSortingMethod() {
return lastSortingMethod; return lastSortingMethod;
} }
@ -291,6 +306,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* *
* @param lastSortingMethod The most recently used sorting method. * @param lastSortingMethod The most recently used sorting method.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void setLastSortingMethod(SortingMethod lastSortingMethod) { final void setLastSortingMethod(SortingMethod lastSortingMethod) {
this.lastSortingMethod = lastSortingMethod; this.lastSortingMethod = lastSortingMethod;
} }
@ -300,6 +316,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* *
* @return The most recently used grouping attribute. * @return The most recently used grouping attribute.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
GroupingAttributeType getLastGroupingAttributeType() { GroupingAttributeType getLastGroupingAttributeType() {
return lastGroupingAttributeType; return lastGroupingAttributeType;
} }
@ -310,6 +327,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* @param lastGroupingAttributeType The most recently used grouping * @param lastGroupingAttributeType The most recently used grouping
* attribute. * attribute.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void setLastGroupingAttributeType(GroupingAttributeType lastGroupingAttributeType) { final void setLastGroupingAttributeType(GroupingAttributeType lastGroupingAttributeType) {
this.lastGroupingAttributeType = lastGroupingAttributeType; this.lastGroupingAttributeType = lastGroupingAttributeType;
} }
@ -319,6 +337,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* *
* @return The most recently used group sorting algorithm. * @return The most recently used group sorting algorithm.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
Group.GroupSortingAlgorithm getLastGroupSortingAlg() { Group.GroupSortingAlgorithm getLastGroupSortingAlg() {
return lastGroupSortingAlg; return lastGroupSortingAlg;
} }
@ -329,6 +348,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* @param lastGroupSortingAlg The most recently used group sorting * @param lastGroupSortingAlg The most recently used group sorting
* algorithm. * algorithm.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void setLastGroupSortingAlg(Group.GroupSortingAlgorithm lastGroupSortingAlg) { final void setLastGroupSortingAlg(Group.GroupSortingAlgorithm lastGroupSortingAlg) {
this.lastGroupSortingAlg = lastGroupSortingAlg; this.lastGroupSortingAlg = lastGroupSortingAlg;
} }

View File

@ -26,6 +26,7 @@ import javax.swing.JCheckBox;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData; import org.sleuthkit.autopsy.discovery.search.SearchData;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter; import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
@ -40,6 +41,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Creates new form ArtifactTypeFilterPanel * Creates new form ArtifactTypeFilterPanel
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ArtifactTypeFilterPanel() { ArtifactTypeFilterPanel() {
initComponents(); initComponents();
setUpArtifactTypeFilter(); setUpArtifactTypeFilter();
@ -49,6 +51,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Initialize the data source filter. * Initialize the data source filter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpArtifactTypeFilter() { private void setUpArtifactTypeFilter() {
int count = 0; int count = 0;
DefaultListModel<ArtifactTypeItem> artifactTypeModel = (DefaultListModel<ArtifactTypeItem>) artifactList.getModel(); DefaultListModel<ArtifactTypeItem> artifactTypeModel = (DefaultListModel<ArtifactTypeItem>) artifactList.getModel();
@ -104,6 +107,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
artifactList.setEnabled(artifactTypeCheckbox.isSelected()); artifactList.setEnabled(artifactTypeCheckbox.isSelected());
}//GEN-LAST:event_artifactTypeCheckboxActionPerformed }//GEN-LAST:event_artifactTypeCheckboxActionPerformed
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void configurePanel(boolean selected, int[] indicesSelected) { void configurePanel(boolean selected, int[] indicesSelected) {
artifactTypeCheckbox.setSelected(selected); artifactTypeCheckbox.setSelected(selected);
@ -119,11 +123,13 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JCheckBox getCheckbox() { JCheckBox getCheckbox() {
return artifactTypeCheckbox; return artifactTypeCheckbox;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JList<?> getList() { JList<?> getList() {
return artifactList; return artifactList;
@ -134,6 +140,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
return null; return null;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"ArtifactTypeFilterPanel.selectionNeeded.text=At least one Result type must be selected."}) @NbBundle.Messages({"ArtifactTypeFilterPanel.selectionNeeded.text=At least one Result type must be selected."})
@Override @Override
String checkForError() { String checkForError() {
@ -143,6 +150,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
return ""; return "";
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
AbstractFilter getFilter() { AbstractFilter getFilter() {
if (artifactTypeCheckbox.isSelected() && !artifactList.getSelectedValuesList().isEmpty()) { if (artifactTypeCheckbox.isSelected() && !artifactList.getSelectedValuesList().isEmpty()) {

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="opaque" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" pref="400" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" alignment="0" pref="607" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="jTable1">
<Properties>
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="tableModel" type="code"/>
</Property>
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
<JTableSelectionModel selectionMode="0"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,352 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JPanel;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel to display list of artifacts for selected domain.
*
*/
class ArtifactsListPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DomainArtifactTableModel tableModel;
private static final Logger logger = Logger.getLogger(ArtifactsListPanel.class.getName());
/**
* Creates new form ArtifactsListPanel.
*
* @param artifactType The type of artifact displayed in this table.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ArtifactsListPanel(BlackboardArtifact.ARTIFACT_TYPE artifactType) {
tableModel = new DomainArtifactTableModel(artifactType);
initComponents();
jTable1.getRowSorter().toggleSortOrder(0);
jTable1.getRowSorter().toggleSortOrder(0);
}
/**
* Add a listener to the table of artifacts to perform actions when an
* artifact is selected.
*
* @param listener The listener to add to the table of artifacts.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addSelectionListener(ListSelectionListener listener) {
jTable1.getSelectionModel().addListSelectionListener(listener);
}
/**
* Remove a listener from the table of artifacts.
*
* @param listener The listener to remove from the table of artifacts.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void removeListSelectionListener(ListSelectionListener listener) {
jTable1.getSelectionModel().removeListSelectionListener(listener);
}
/**
* The artifact which is currently selected, null if no artifact is
* selected.
*
* @return The currently selected BlackboardArtifact or null if none is
* selected.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
BlackboardArtifact getSelectedArtifact() {
int selectedIndex = jTable1.getSelectionModel().getLeadSelectionIndex();
if (selectedIndex < jTable1.getSelectionModel().getMinSelectionIndex() || jTable1.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > jTable1.getSelectionModel().getMaxSelectionIndex()) {
return null;
}
return tableModel.getArtifactByRow(jTable1.convertRowIndexToModel(selectedIndex));
}
/**
* Whether the list of artifacts is empty.
*
* @return true if the list of artifacts is empty, false if there are
* artifacts.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
boolean isEmpty() {
return tableModel.getRowCount() <= 0;
}
/**
* Select the first available artifact in the list if it is not empty to
* populate the panel to the right.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void selectFirst() {
if (!isEmpty()) {
jTable1.setRowSelectionInterval(0, 0);
} else {
jTable1.clearSelection();
}
}
/**
* Add the specified list of artifacts to the list of artifacts which should
* be displayed.
*
* @param artifactList The list of artifacts to display.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addArtifacts(List<BlackboardArtifact> artifactList) {
tableModel.setContents(artifactList);
jTable1.validate();
jTable1.repaint();
tableModel.fireTableDataChanged();
}
/**
* Remove all artifacts from the list of artifacts displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void clearArtifacts() {
tableModel.setContents(new ArrayList<>());
tableModel.fireTableDataChanged();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
jTable1 = new javax.swing.JTable();
setOpaque(false);
setPreferredSize(new java.awt.Dimension(300, 0));
jScrollPane1.setBorder(null);
jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0));
jScrollPane1.setPreferredSize(new java.awt.Dimension(0, 0));
jTable1.setAutoCreateRowSorter(true);
jTable1.setModel(tableModel);
jTable1.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
jScrollPane1.setViewportView(jTable1);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 607, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
/**
* Table model which allows the artifact table in this panel to mimic a list
* of artifacts.
*/
private class DomainArtifactTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private final List<BlackboardArtifact> artifactList = new ArrayList<>();
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
/**
* Construct a new DomainArtifactTableModel.
*
* @param artifactType The type of artifact displayed in this table.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainArtifactTableModel(BlackboardArtifact.ARTIFACT_TYPE artifactType) {
this.artifactType = artifactType;
}
/**
* Set the list of artifacts which should be represented by this table
* model.
*
* @param artifacts The list of BlackboardArtifacts to represent.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void setContents(List<BlackboardArtifact> artifacts) {
jTable1.clearSelection();
artifactList.clear();
artifactList.addAll(artifacts);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public int getRowCount() {
return artifactList.size();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public int getColumnCount() {
if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE) {
return 3;
} else {
return 2;
}
}
/**
* Get the BlackboardArtifact at the specified row.
*
* @param rowIndex The row the artifact to return is at.
*
* @return The BlackboardArtifact at the specified row.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
BlackboardArtifact getArtifactByRow(int rowIndex) {
return artifactList.get(rowIndex);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"ArtifactsListPanel.value.noValue=No value available."})
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex < 2 || artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE) {
try {
for (BlackboardAttribute bba : getArtifactByRow(rowIndex).getAttributes()) {
if (!StringUtils.isBlank(bba.getDisplayString())) {
String stringFromAttribute = getStringForColumn(bba, columnIndex);
if (!StringUtils.isBlank(stringFromAttribute)) {
return stringFromAttribute;
}
}
}
return getFallbackValue(rowIndex, columnIndex);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting attributes for artifact " + getArtifactByRow(rowIndex).getArtifactID(), ex);
}
}
return Bundle.ArtifactsListPanel_value_noValue();
}
/**
* Get the appropriate String for the specified column from the
* BlackboardAttribute.
*
* @param bba The BlackboardAttribute which may contain a value.
* @param columnIndex The column the value will be displayed in.
*
* @return The value from the specified attribute which should be
* displayed in the specified column, null if the specified
* attribute does not contain a value for that column.
*
* @throws TskCoreException When unable to get abstract files based on
* the TSK_PATH_ID.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private String getStringForColumn(BlackboardAttribute bba, int columnIndex) throws TskCoreException {
if (columnIndex == 0 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID()) {
return bba.getDisplayString();
} else if (columnIndex == 1) {
if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD || artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE) {
if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) {
return Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(bba.getValueLong()).getName();
} else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
return FilenameUtils.getName(bba.getDisplayString());
}
} else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE.getTypeID()) {
return bba.getDisplayString();
}
} else if (columnIndex == 2 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) {
return Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(bba.getValueLong()).getMIMEType();
}
return null;
}
/**
* Private helper method to use when the value we want for either date
* or title is not available.
*
*
* @param rowIndex The row the artifact to return is at.
* @param columnIndex The column index the attribute will be displayed
* at.
*
* @return A string that can be used in place of the accessed date time
* attribute title when they are not available.
*
* @throws TskCoreException
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private String getFallbackValue(int rowIndex, int columnIndex) throws TskCoreException {
for (BlackboardAttribute bba : getArtifactByRow(rowIndex).getAttributes()) {
if (columnIndex == 0 && bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME") && !StringUtils.isBlank(bba.getDisplayString())) {
return bba.getDisplayString();
} else if (columnIndex == 1 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.getTypeID() && !StringUtils.isBlank(bba.getDisplayString())) {
return bba.getDisplayString();
} else if (columnIndex == 1 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID() && !StringUtils.isBlank(bba.getDisplayString())) {
return bba.getDisplayString();
} else if (columnIndex == 1 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID() && !StringUtils.isBlank(bba.getDisplayString())) {
return bba.getDisplayString();
}
}
return Bundle.ArtifactsListPanel_value_noValue();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"ArtifactsListPanel.titleColumn.name=Title",
"ArtifactsListPanel.fileNameColumn.name=Name",
"ArtifactsListPanel.dateColumn.name=Date/Time",
"ArtifactsListPanel.mimeTypeColumn.name=MIME Type"})
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return Bundle.ArtifactsListPanel_dateColumn_name();
case 1:
if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE || artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD) {
return Bundle.ArtifactsListPanel_fileNameColumn_name();
} else {
return Bundle.ArtifactsListPanel_titleColumn_name();
}
case 2:
return Bundle.ArtifactsListPanel_mimeTypeColumn_name();
default:
return "";
}
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTable jTable1;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,79 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.DomainSearch;
import org.sleuthkit.autopsy.discovery.search.DomainSearchArtifactsRequest;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* SwingWorker to retrieve a list of artifacts for a specified type and domain.
*/
class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
private final static Logger logger = Logger.getLogger(ArtifactsWorker.class.getName());
private final String domain;
/**
* Construct a new ArtifactsWorker.
*
* @param artifactType The type of artifact being retrieved.
* @param domain The domain the artifacts should have as an attribute.
*/
ArtifactsWorker(BlackboardArtifact.ARTIFACT_TYPE artifactType, String domain) {
this.artifactType = artifactType;
this.domain = domain;
}
@Override
protected List<BlackboardArtifact> doInBackground() throws Exception {
if (artifactType != null && !StringUtils.isBlank(domain)) {
DomainSearch domainSearch = new DomainSearch();
return domainSearch.getArtifacts(new DomainSearchArtifactsRequest(Case.getCurrentCase().getSleuthkitCase(), domain, artifactType));
}
return new ArrayList<>();
}
@Override
protected void done() {
List<BlackboardArtifact> listOfArtifacts = new ArrayList<>();
if (!isCancelled()) {
try {
listOfArtifacts.addAll(get());
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Exception while trying to get list of artifacts for Domain details for artifact type: "
+ artifactType.getDisplayName() + " and domain: " + domain, ex);
} catch (CancellationException ignored) {
//Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging
}
}
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.ArtifactSearchResultEvent(artifactType, listOfArtifacts));
}
}

View File

@ -51,10 +51,12 @@ HashSetFilterPanel.hashSetCheckbox.text=Hash Set:
PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences: PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences:
DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show
ObjectDetectedFilterPanel.text=Object Detected: ObjectDetectedFilterPanel.text=Object Detected:
DetailsPanel.instancesList.border.title=Instances
DateFilterPanel.mostRecentRadioButton.text=Only last: DateFilterPanel.mostRecentRadioButton.text=Only last:
DateFilterPanel.dateFilterCheckBox.text=Date Filter: DateFilterPanel.dateFilterCheckBox.text=Date Filter:
DomainSummaryPanel.activityLabel.text= DomainSummaryPanel.activityLabel.text=
DomainSummaryPanel.pagesLabel.text= DomainSummaryPanel.pagesLabel.text=
DomainSummaryPanel.filesDownloadedLabel.text= DomainSummaryPanel.filesDownloadedLabel.text=
DomainSummaryPanel.totalVisitsLabel.text= DomainSummaryPanel.totalVisitsLabel.text=
FileDetailsPanel.instancesList.border.title=Instances
CookieDetailsPanel.jLabel1.text=Artifact:
CookieDetailsPanel.jLabel2.text=

View File

@ -1,3 +1,8 @@
ArtifactsListPanel.dateColumn.name=Date/Time
ArtifactsListPanel.fileNameColumn.name=Name
ArtifactsListPanel.mimeTypeColumn.name=MIME Type
ArtifactsListPanel.titleColumn.name=Title
ArtifactsListPanel.value.noValue=No value available.
ArtifactTypeFilterPanel.selectionNeeded.text=At least one Result type must be selected. ArtifactTypeFilterPanel.selectionNeeded.text=At least one Result type must be selected.
CTL_OpenDiscoveryAction=Discovery CTL_OpenDiscoveryAction=Discovery
DataSourceFilterPanel.error.text=At least one data source must be selected. DataSourceFilterPanel.error.text=At least one data source must be selected.
@ -126,13 +131,15 @@ HashSetFilterPanel.hashSetCheckbox.text=Hash Set:
PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences: PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences:
DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show
ObjectDetectedFilterPanel.text=Object Detected: ObjectDetectedFilterPanel.text=Object Detected:
DetailsPanel.instancesList.border.title=Instances
DateFilterPanel.mostRecentRadioButton.text=Only last: DateFilterPanel.mostRecentRadioButton.text=Only last:
DateFilterPanel.dateFilterCheckBox.text=Date Filter: DateFilterPanel.dateFilterCheckBox.text=Date Filter:
DomainSummaryPanel.activityLabel.text= DomainSummaryPanel.activityLabel.text=
DomainSummaryPanel.pagesLabel.text= DomainSummaryPanel.pagesLabel.text=
DomainSummaryPanel.filesDownloadedLabel.text= DomainSummaryPanel.filesDownloadedLabel.text=
DomainSummaryPanel.totalVisitsLabel.text= DomainSummaryPanel.totalVisitsLabel.text=
FileDetailsPanel.instancesList.border.title=Instances
CookieDetailsPanel.jLabel1.text=Artifact:
CookieDetailsPanel.jLabel2.text=
VideoThumbnailPanel.bytes.text=bytes VideoThumbnailPanel.bytes.text=bytes
VideoThumbnailPanel.deleted.text=All instances of file are deleted. VideoThumbnailPanel.deleted.text=All instances of file are deleted.
VideoThumbnailPanel.gigaBytes.text=GB VideoThumbnailPanel.gigaBytes.text=GB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
</Form>

View File

@ -0,0 +1,69 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.discovery.ui;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Details panel for displaying the collection of content viewers.
*/
final class ContentViewerDetailsPanel extends AbstractArtifactDetailsPanel {
private static final long serialVersionUID = 1L;
private final DataContentPanel contentViewer = DataContentPanel.createInstance();
/**
* Creates new form ContentViewerDetailsPanel
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ContentViewerDetailsPanel() {
initComponents();
add(contentViewer);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
setLayout(new java.awt.BorderLayout());
}// </editor-fold>//GEN-END:initComponents
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void setArtifact(BlackboardArtifact artifact) {
Node node = Node.EMPTY;
if (artifact != null) {
node = new BlackboardArtifactNode(artifact);
}
contentViewer.setNode(node);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -30,6 +30,7 @@ import javax.swing.JList;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering; import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -45,6 +46,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Creates new form DataSourceFilterPanel. * Creates new form DataSourceFilterPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DataSourceFilterPanel() { DataSourceFilterPanel() {
initComponents(); initComponents();
setUpDataSourceFilter(); setUpDataSourceFilter();
@ -109,6 +111,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JScrollPane dataSourceScrollPane; private javax.swing.JScrollPane dataSourceScrollPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void configurePanel(boolean selected, int[] indicesSelected) { void configurePanel(boolean selected, int[] indicesSelected) {
dataSourceCheckbox.setSelected(selected); dataSourceCheckbox.setSelected(selected);
@ -124,6 +127,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JCheckBox getCheckbox() { JCheckBox getCheckbox() {
return dataSourceCheckbox; return dataSourceCheckbox;
@ -137,6 +141,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Initialize the data source filter. * Initialize the data source filter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpDataSourceFilter() { private void setUpDataSourceFilter() {
int count = 0; int count = 0;
try { try {
@ -156,6 +161,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JList<?> getList() { JList<?> getList() {
return dataSourceList; return dataSourceList;
@ -193,6 +199,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"DataSourceFilterPanel.error.text=At least one data source must be selected."}) @NbBundle.Messages({"DataSourceFilterPanel.error.text=At least one data source must be selected."})
@Override @Override
String checkForError() { String checkForError() {
@ -202,6 +209,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
return ""; return "";
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
AbstractFilter getFilter() { AbstractFilter getFilter() {
if (dataSourceCheckbox.isSelected()) { if (dataSourceCheckbox.isSelected()) {

View File

@ -33,6 +33,7 @@ import javax.swing.JSpinner;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.communications.Utils; import org.sleuthkit.autopsy.communications.Utils;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering; import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
/** /**
@ -48,6 +49,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
*/ */
@NbBundle.Messages({"# {0} - timeZone", @NbBundle.Messages({"# {0} - timeZone",
"DateFilterPanel.dateRange.text=Date Range ({0}):"}) "DateFilterPanel.dateRange.text=Date Range ({0}):"})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DateFilterPanel() { DateFilterPanel() {
initComponents(); initComponents();
rangeRadioButton.setText(Bundle.DateFilterPanel_dateRange_text(Utils.getUserPreferredZoneId().toString())); rangeRadioButton.setText(Bundle.DateFilterPanel_dateRange_text(Utils.getUserPreferredZoneId().toString()));
@ -225,6 +227,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
endCheckBox.firePropertyChange("EndButtonChange", true, false); endCheckBox.firePropertyChange("EndButtonChange", true, false);
}//GEN-LAST:event_rangeRadioButtonStateChanged }//GEN-LAST:event_rangeRadioButtonStateChanged
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void configurePanel(boolean selected, int[] indicesSelected) { void configurePanel(boolean selected, int[] indicesSelected) {
dateFilterCheckBox.setSelected(selected); dateFilterCheckBox.setSelected(selected);
@ -238,6 +241,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JCheckBox getCheckbox() { JCheckBox getCheckbox() {
return dateFilterCheckBox; return dateFilterCheckBox;
@ -253,6 +257,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
return null; return null;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void addListeners(ActionListener actionListener, ListSelectionListener listListener) { void addListeners(ActionListener actionListener, ListSelectionListener listListener) {
dateFilterCheckBox.addActionListener(actionListener); dateFilterCheckBox.addActionListener(actionListener);
@ -274,6 +279,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
}); });
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void removeListeners() { void removeListeners() {
for (ActionListener listener : dateFilterCheckBox.getActionListeners()) { for (ActionListener listener : dateFilterCheckBox.getActionListeners()) {
@ -302,6 +308,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"DateFilterPanel.invalidRange.text=Range or Only Last must be selected.", @NbBundle.Messages({"DateFilterPanel.invalidRange.text=Range or Only Last must be selected.",
"DateFilterPanel.startOrEndNeeded.text=A start or end date must be specified to use the range filter.", "DateFilterPanel.startOrEndNeeded.text=A start or end date must be specified to use the range filter.",
"DateFilterPanel.startAfterEnd.text=Start date should be before the end date when both are enabled."}) "DateFilterPanel.startAfterEnd.text=Start date should be before the end date when both are enabled."})
@ -320,6 +327,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
return ""; return "";
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
AbstractFilter getFilter() { AbstractFilter getFilter() {
if (dateFilterCheckBox.isSelected()) { if (dateFilterCheckBox.isSelected()) {

View File

@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes; import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils; import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.Group; import org.sleuthkit.autopsy.discovery.search.Group;
@ -99,6 +100,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/** /**
* Private constructor to construct a new DiscoveryDialog * Private constructor to construct a new DiscoveryDialog
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Messages("DiscoveryDialog.name.text=Discovery") @Messages("DiscoveryDialog.name.text=Discovery")
private DiscoveryDialog() { private DiscoveryDialog() {
super(WindowManager.getDefault().getMainWindow(), Bundle.DiscoveryDialog_name_text(), true); super(WindowManager.getDefault().getMainWindow(), Bundle.DiscoveryDialog_name_text(), true);
@ -116,10 +118,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
@Override @Override
public void itemStateChanged(ItemEvent event) { public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.SELECTED) { if (event.getStateChange() == ItemEvent.SELECTED) {
SwingUtilities.invokeLater(() -> {
getSelectedFilterPanel().setLastGroupingAttributeType(groupByCombobox.getItemAt(groupByCombobox.getSelectedIndex())); getSelectedFilterPanel().setLastGroupingAttributeType(groupByCombobox.getItemAt(groupByCombobox.getSelectedIndex()));
});
} }
} }
}); });
@ -127,9 +126,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
@Override @Override
public void itemStateChanged(ItemEvent event) { public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.SELECTED) { if (event.getStateChange() == ItemEvent.SELECTED) {
SwingUtilities.invokeLater(() -> {
getSelectedFilterPanel().setLastSortingMethod(orderByCombobox.getItemAt(orderByCombobox.getSelectedIndex())); getSelectedFilterPanel().setLastSortingMethod(orderByCombobox.getItemAt(orderByCombobox.getSelectedIndex()));
});
} }
} }
}); });
@ -137,9 +134,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
@Override @Override
public void itemStateChanged(ItemEvent event) { public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.SELECTED) { if (event.getStateChange() == ItemEvent.SELECTED) {
SwingUtilities.invokeLater(() -> {
getSelectedFilterPanel().setLastGroupSortingAlg(groupSortingComboBox.getItemAt(groupSortingComboBox.getSelectedIndex())); getSelectedFilterPanel().setLastGroupSortingAlg(groupSortingComboBox.getItemAt(groupSortingComboBox.getSelectedIndex()));
});
} }
} }
}); });
@ -151,6 +146,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/** /**
* Update the search settings to a default state. * Update the search settings to a default state.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void updateSearchSettings() { void updateSearchSettings() {
removeAllPanels(); removeAllPanels();
imageFilterPanel = null; imageFilterPanel = null;
@ -176,6 +172,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/** /**
* Set the type buttons to a default state where none are selected. * Set the type buttons to a default state where none are selected.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void unselectAllButtons() { private void unselectAllButtons() {
imagesButton.setSelected(false); imagesButton.setSelected(false);
imagesButton.setEnabled(true); imagesButton.setEnabled(true);
@ -194,6 +191,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/** /**
* Private helper method to perform update of comboboxes update. * Private helper method to perform update of comboboxes update.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void updateComboBoxes() { private void updateComboBoxes() {
// Set up the grouping attributes // Set up the grouping attributes
List<GroupingAttributeType> groupingAttrs = new ArrayList<>(); List<GroupingAttributeType> groupingAttrs = new ArrayList<>();
@ -230,6 +228,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
* *
* @return The panel that corresponds to the currently selected type. * @return The panel that corresponds to the currently selected type.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private AbstractFiltersPanel getSelectedFilterPanel() { private AbstractFiltersPanel getSelectedFilterPanel() {
switch (type) { switch (type) {
case IMAGE: case IMAGE:
@ -251,6 +250,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
* *
* @param type The Type of GroupingAttribute to add. * @param type The Type of GroupingAttribute to add.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void addTypeToGroupByComboBox(GroupingAttributeType type) { private void addTypeToGroupByComboBox(GroupingAttributeType type) {
switch (type) { switch (type) {
case FREQUENCY: case FREQUENCY:
@ -282,7 +282,8 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/** /**
* Validate the filter settings for File type filters. * Validate the filter settings for File type filters.
*/ */
synchronized void validateDialog() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void validateDialog() {
AbstractFiltersPanel panel = getSelectedFilterPanel(); AbstractFiltersPanel panel = getSelectedFilterPanel();
if (panel != null) { if (panel != null) {
panel.validateFields(); panel.validateFields();
@ -551,6 +552,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/** /**
* Helper method to remove all filter panels and their listeners * Helper method to remove all filter panels and their listeners
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void removeAllPanels() { private void removeAllPanels() {
if (imageFilterPanel != null) { if (imageFilterPanel != null) {
remove(imageFilterPanel); remove(imageFilterPanel);
@ -635,6 +637,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
repaint(); repaint();
}//GEN-LAST:event_domainsButtonActionPerformed }//GEN-LAST:event_domainsButtonActionPerformed
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public void dispose() { public void dispose() {
setVisible(false); setVisible(false);
@ -643,6 +646,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/** /**
* Cancel the searchWorker if it exists. * Cancel the searchWorker if it exists.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void cancelSearch() { void cancelSearch() {
if (searchWorker != null) { if (searchWorker != null) {
searchWorker.cancel(true); searchWorker.cancel(true);
@ -656,6 +660,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
* @param error The error message to display, empty string if there is no * @param error The error message to display, empty string if there is no
* error. * error.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setValid(String error) { private void setValid(String error) {
if (StringUtils.isBlank(error)) { if (StringUtils.isBlank(error)) {
errorLabel.setText(""); errorLabel.setText("");

View File

@ -27,6 +27,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
@ -42,28 +43,27 @@ class DiscoveryThumbnailChildren extends Children.Keys<AbstractFile> {
/* /*
* Creates the list of thumbnails from the given list of AbstractFiles. * Creates the list of thumbnails from the given list of AbstractFiles.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DiscoveryThumbnailChildren(List<AbstractFile> files) { DiscoveryThumbnailChildren(List<AbstractFile> files) {
super(false); super(false);
this.files = files; this.files = files;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
protected Node[] createNodes(AbstractFile t) { protected Node[] createNodes(AbstractFile t) {
return new Node[]{new ThumbnailNode(t)}; return new Node[]{new ThumbnailNode(t)};
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
protected void addNotify() { protected void addNotify() {
super.addNotify(); super.addNotify();
Set<AbstractFile> thumbnails = new TreeSet<>((AbstractFile file1, AbstractFile file2) -> { Set<AbstractFile> thumbnails = new TreeSet<>((AbstractFile file1, AbstractFile file2) -> {
int result = Long.compare(file1.getSize(), file2.getSize()); int result = Long.compare(file1.getSize(), file2.getSize());
if (result == 0) { if (result == 0) {
result = file1.getName().compareTo(file2.getName()); result = file1.getName().compareTo(file2.getName());
} }
return result; return result;
}); });
thumbnails.addAll(files); thumbnails.addAll(files);
@ -75,10 +75,12 @@ class DiscoveryThumbnailChildren extends Children.Keys<AbstractFile> {
*/ */
static class ThumbnailNode extends FileNode { static class ThumbnailNode extends FileNode {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ThumbnailNode(AbstractFile file) { ThumbnailNode(AbstractFile file) {
super(file, false); super(file, false);
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet sheet = super.createSheet(); Sheet sheet = super.createSheet();

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties> <Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[199, 200]"/> <Dimension value="[199, 200]"/>

View File

@ -28,6 +28,7 @@ import java.beans.PropertyChangeListener;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.JSplitPane; import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI; import javax.swing.plaf.basic.BasicSplitPaneUI;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -40,6 +41,8 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils; import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.SearchData.Type; import org.sleuthkit.autopsy.discovery.search.SearchData.Type;
import static org.sleuthkit.autopsy.discovery.search.SearchData.Type.DOMAIN; import static org.sleuthkit.autopsy.discovery.search.SearchData.Type.DOMAIN;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
/** /**
* Create a dialog for displaying the Discovery results. * Create a dialog for displaying the Discovery results.
@ -53,12 +56,12 @@ public final class DiscoveryTopComponent extends TopComponent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String PREFERRED_ID = "DiscoveryTc"; // NON-NLS private static final String PREFERRED_ID = "DiscoveryTc"; // NON-NLS
private static final int ANIMATION_INCREMENT = 30; private static final int ANIMATION_INCREMENT = 30;
private volatile static int resultsAreaSize = 250; private volatile static int previousDividerLocation = 250;
private final GroupListPanel groupListPanel; private final GroupListPanel groupListPanel;
private final DetailsPanel detailsPanel;
private final ResultsPanel resultsPanel; private final ResultsPanel resultsPanel;
private String selectedDomainTabName;
private Type searchType; private Type searchType;
private int dividerLocation = -1; private int dividerLocation = JSplitPane.UNDEFINED_CONDITION;
private SwingAnimator animator = null; private SwingAnimator animator = null;
/** /**
@ -70,10 +73,7 @@ public final class DiscoveryTopComponent extends TopComponent {
setName(Bundle.DiscoveryTopComponent_name()); setName(Bundle.DiscoveryTopComponent_name());
groupListPanel = new GroupListPanel(); groupListPanel = new GroupListPanel();
resultsPanel = new ResultsPanel(); resultsPanel = new ResultsPanel();
detailsPanel = new DetailsPanel();
mainSplitPane.setLeftComponent(groupListPanel); mainSplitPane.setLeftComponent(groupListPanel);
rightSplitPane.setTopComponent(resultsPanel);
rightSplitPane.setBottomComponent(detailsPanel);
//set color of divider //set color of divider
rightSplitPane.setUI(new BasicSplitPaneUI() { rightSplitPane.setUI(new BasicSplitPaneUI() {
@Override @Override
@ -87,14 +87,19 @@ public final class DiscoveryTopComponent extends TopComponent {
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)) { if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)) {
//Only change the saved location when it was a manual change by the user and not the animation or the window opening initially //Only change the saved location when it was a manual change by the user and not the animation or the window opening initially
if ((animator == null || !animator.isRunning()) && evt.getNewValue() instanceof Integer if ((animator == null || !animator.isRunning())
&& ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize())) { && evt.getNewValue() instanceof Integer
resultsAreaSize = (int) evt.getNewValue(); && evt.getOldValue() instanceof Integer
&& ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize())
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())
&& ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION)) {
previousDividerLocation = (int) evt.getNewValue();
} }
} }
} }
}); });
rightSplitPane.setTopComponent(resultsPanel);
} }
/** /**
@ -108,6 +113,7 @@ public final class DiscoveryTopComponent extends TopComponent {
* @param ui The component which contains the split pane this divider is * @param ui The component which contains the split pane this divider is
* in. * in.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
BasicSplitPaneDividerImpl(BasicSplitPaneUI ui) { BasicSplitPaneDividerImpl(BasicSplitPaneUI ui) {
super(ui); super(ui);
this.setLayout(new BorderLayout()); this.setLayout(new BorderLayout());
@ -129,11 +135,13 @@ public final class DiscoveryTopComponent extends TopComponent {
/** /**
* Reset the top component so it isn't displaying any results. * Reset the top component so it isn't displaying any results.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void resetTopComponent() { public void resetTopComponent() {
resultsPanel.resetResultViewer(); resultsPanel.resetResultViewer();
groupListPanel.resetGroupList(); groupListPanel.resetGroupList();
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public void componentOpened() { public void componentOpened() {
super.componentOpened(); super.componentOpened();
@ -141,9 +149,9 @@ public final class DiscoveryTopComponent extends TopComponent {
DiscoveryEventUtils.getDiscoveryEventBus().register(this); DiscoveryEventUtils.getDiscoveryEventBus().register(this);
DiscoveryEventUtils.getDiscoveryEventBus().register(resultsPanel); DiscoveryEventUtils.getDiscoveryEventBus().register(resultsPanel);
DiscoveryEventUtils.getDiscoveryEventBus().register(groupListPanel); DiscoveryEventUtils.getDiscoveryEventBus().register(groupListPanel);
DiscoveryEventUtils.getDiscoveryEventBus().register(detailsPanel);
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
protected void componentClosed() { protected void componentClosed() {
DiscoveryDialog.getDiscoveryDialogInstance().cancelSearch(); DiscoveryDialog.getDiscoveryDialogInstance().cancelSearch();
@ -152,7 +160,11 @@ public final class DiscoveryTopComponent extends TopComponent {
DiscoveryEventUtils.getDiscoveryEventBus().unregister(this); DiscoveryEventUtils.getDiscoveryEventBus().unregister(this);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel); DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel); DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(detailsPanel); DiscoveryEventUtils.getDiscoveryEventBus().unregister(rightSplitPane.getBottomComponent());
if (rightSplitPane.getBottomComponent() instanceof DomainDetailsPanel) {
selectedDomainTabName = ((DomainDetailsPanel) rightSplitPane.getBottomComponent()).getSelectedTabName();
}
rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION);
super.componentClosed(); super.componentClosed();
} }
@ -262,7 +274,7 @@ public final class DiscoveryTopComponent extends TopComponent {
*/ */
@Subscribe @Subscribe
void handleDetailsVisibleEvent(DiscoveryEventUtils.DetailsVisibleEvent detailsVisibleEvent) { void handleDetailsVisibleEvent(DiscoveryEventUtils.DetailsVisibleEvent detailsVisibleEvent) {
if (resultsPanel.getActiveType() != DOMAIN) { SwingUtilities.invokeLater(() -> {
if (animator != null && animator.isRunning()) { if (animator != null && animator.isRunning()) {
animator.stop(); animator.stop();
animator = null; animator = null;
@ -274,7 +286,7 @@ public final class DiscoveryTopComponent extends TopComponent {
animator = new SwingAnimator(new HideDetailsAreaCallback()); animator = new SwingAnimator(new HideDetailsAreaCallback());
} }
animator.start(); animator.start();
} });
} }
/** /**
@ -289,12 +301,12 @@ public final class DiscoveryTopComponent extends TopComponent {
"DiscoveryTopComponent.searchError.text=Error no type specified for search."}) "DiscoveryTopComponent.searchError.text=Error no type specified for search."})
@Subscribe @Subscribe
void handleSearchStartedEvent(DiscoveryEventUtils.SearchStartedEvent searchStartedEvent) { void handleSearchStartedEvent(DiscoveryEventUtils.SearchStartedEvent searchStartedEvent) {
SwingUtilities.invokeLater(() -> {
newSearchButton.setText(Bundle.DiscoveryTopComponent_cancelButton_text()); newSearchButton.setText(Bundle.DiscoveryTopComponent_cancelButton_text());
progressMessageTextArea.setForeground(Color.red); progressMessageTextArea.setForeground(Color.red);
searchType = searchStartedEvent.getType(); searchType = searchStartedEvent.getType();
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchInProgress_text(searchType.name())); progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchInProgress_text(searchType.name()));
rightSplitPane.getComponent(1).setVisible(searchStartedEvent.getType() != DOMAIN); });
rightSplitPane.getComponent(2).setVisible(searchStartedEvent.getType() != DOMAIN);
} }
/** /**
@ -310,6 +322,7 @@ public final class DiscoveryTopComponent extends TopComponent {
"DiscoveryTopComponent.domainSearch.text=Type: Domain", "DiscoveryTopComponent.domainSearch.text=Type: Domain",
"DiscoveryTopComponent.additionalFilters.text=; "}) "DiscoveryTopComponent.additionalFilters.text=; "})
void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) { void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
SwingUtilities.invokeLater(() -> {
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text()); newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
progressMessageTextArea.setForeground(Color.black); progressMessageTextArea.setForeground(Color.black);
String descriptionText = ""; String descriptionText = "";
@ -319,10 +332,42 @@ public final class DiscoveryTopComponent extends TopComponent {
if (!searchCompleteEvent.getFilters().isEmpty()) { if (!searchCompleteEvent.getFilters().isEmpty()) {
descriptionText += Bundle.DiscoveryTopComponent_additionalFilters_text(); descriptionText += Bundle.DiscoveryTopComponent_additionalFilters_text();
} }
selectedDomainTabName = validateLastSelectedType(searchCompleteEvent);
rightSplitPane.setBottomComponent(new DomainDetailsPanel(selectedDomainTabName));
} else {
rightSplitPane.setBottomComponent(new FileDetailsPanel());
} }
DiscoveryEventUtils.getDiscoveryEventBus().register(rightSplitPane.getBottomComponent());
descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; ")); descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; "));
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText)); progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText));
progressMessageTextArea.setCaretPosition(0); progressMessageTextArea.setCaretPosition(0);
});
}
/**
* Get the name of the tab which was last selected unless the tab last
* selected would not be included in the types currently being displayed or
* was not previously set.
*
* @return The name of the tab which should be selected in the new
* DomainDetailsPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private String validateLastSelectedType(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
String typeFilteredOn = selectedDomainTabName;
for (AbstractFilter filter : searchCompleteEvent.getFilters()) {
if (filter instanceof ArtifactTypeFilter) {
for (ARTIFACT_TYPE type : ((ArtifactTypeFilter) filter).getTypes()) {
typeFilteredOn = type.getDisplayName();
if (selectedDomainTabName == null || typeFilteredOn.equalsIgnoreCase(selectedDomainTabName)) {
break;
}
}
break;
}
}
return typeFilteredOn;
} }
/** /**
@ -334,9 +379,11 @@ public final class DiscoveryTopComponent extends TopComponent {
@Messages({"DiscoveryTopComponent.searchCancelled.text=Search has been cancelled."}) @Messages({"DiscoveryTopComponent.searchCancelled.text=Search has been cancelled."})
@Subscribe @Subscribe
void handleSearchCancelledEvent(DiscoveryEventUtils.SearchCancelledEvent searchCancelledEvent) { void handleSearchCancelledEvent(DiscoveryEventUtils.SearchCancelledEvent searchCancelledEvent) {
SwingUtilities.invokeLater(() -> {
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text()); newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
progressMessageTextArea.setForeground(Color.red); progressMessageTextArea.setForeground(Color.red);
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text()); progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text());
});
} }
@ -345,16 +392,18 @@ public final class DiscoveryTopComponent extends TopComponent {
*/ */
private final class ShowDetailsAreaCallback implements SwingAnimatorCallback { private final class ShowDetailsAreaCallback implements SwingAnimatorCallback {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public void callback(Object caller) { public void callback(Object caller) {
dividerLocation -= ANIMATION_INCREMENT; dividerLocation -= ANIMATION_INCREMENT;
repaint(); repaint();
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public boolean hasTerminated() { public boolean hasTerminated() {
if (dividerLocation != JSplitPane.UNDEFINED_CONDITION && dividerLocation < resultsAreaSize) { if (dividerLocation != JSplitPane.UNDEFINED_CONDITION && dividerLocation < previousDividerLocation) {
dividerLocation = resultsAreaSize; dividerLocation = previousDividerLocation;
animator = null; animator = null;
return true; return true;
} }
@ -368,12 +417,14 @@ public final class DiscoveryTopComponent extends TopComponent {
*/ */
private final class HideDetailsAreaCallback implements SwingAnimatorCallback { private final class HideDetailsAreaCallback implements SwingAnimatorCallback {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public void callback(Object caller) { public void callback(Object caller) {
dividerLocation += ANIMATION_INCREMENT; dividerLocation += ANIMATION_INCREMENT;
repaint(); repaint();
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public boolean hasTerminated() { public boolean hasTerminated() {
if (dividerLocation > rightSplitPane.getHeight() || dividerLocation == JSplitPane.UNDEFINED_CONDITION) { if (dividerLocation > rightSplitPane.getHeight() || dividerLocation == JSplitPane.UNDEFINED_CONDITION) {
@ -399,10 +450,11 @@ public final class DiscoveryTopComponent extends TopComponent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
if (animator != null && animator.isRunning() && (dividerLocation == JSplitPane.UNDEFINED_CONDITION if (animator != null && animator.isRunning() && (dividerLocation == JSplitPane.UNDEFINED_CONDITION
|| (dividerLocation <= getHeight() && dividerLocation >= resultsAreaSize))) { || (dividerLocation <= getHeight() && dividerLocation >= previousDividerLocation))) {
setDividerLocation(dividerLocation); setDividerLocation(dividerLocation);
} }
super.paintComponent(g); super.paintComponent(g);

View File

@ -51,6 +51,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corelibs.ScalrWrapper; import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import static org.sleuthkit.autopsy.coreutils.VideoUtils.getVideoFileInTempDir; import static org.sleuthkit.autopsy.coreutils.VideoUtils.getVideoFileInTempDir;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.discovery.search.ResultFile; import org.sleuthkit.autopsy.discovery.search.ResultFile;
@ -173,6 +174,7 @@ final class DiscoveryUiUtils {
* *
* @return True if the point is over the icon, false otherwise. * @return True if the point is over the icon, false otherwise.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
static boolean isPointOnIcon(Component comp, Point point) { static boolean isPointOnIcon(Component comp, Point point) {
return comp instanceof JComponent && point.x >= comp.getX() && point.x <= comp.getX() + ICON_SIZE && point.y >= comp.getY() && point.y <= comp.getY() + ICON_SIZE; return comp instanceof JComponent && point.x >= comp.getX() && point.x <= comp.getX() + ICON_SIZE && point.y >= comp.getY() && point.y <= comp.getY() + ICON_SIZE;
} }
@ -186,6 +188,7 @@ final class DiscoveryUiUtils {
* @param isDeletedLabel The label to set the icon and tooltip for. * @param isDeletedLabel The label to set the icon and tooltip for.
*/ */
@NbBundle.Messages({"DiscoveryUiUtils.isDeleted.text=All instances of file are deleted."}) @NbBundle.Messages({"DiscoveryUiUtils.isDeleted.text=All instances of file are deleted."})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
static void setDeletedIcon(boolean isDeleted, javax.swing.JLabel isDeletedLabel) { static void setDeletedIcon(boolean isDeleted, javax.swing.JLabel isDeletedLabel) {
if (isDeleted) { if (isDeleted) {
isDeletedLabel.setIcon(DELETED_ICON); isDeletedLabel.setIcon(DELETED_ICON);
@ -203,6 +206,7 @@ final class DiscoveryUiUtils {
* score of. * score of.
* @param scoreLabel The label to set the icon and tooltip for. * @param scoreLabel The label to set the icon and tooltip for.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
static void setScoreIcon(ResultFile resultFile, javax.swing.JLabel scoreLabel) { static void setScoreIcon(ResultFile resultFile, javax.swing.JLabel scoreLabel) {
switch (resultFile.getScore()) { switch (resultFile.getScore()) {
case NOTABLE_SCORE: case NOTABLE_SCORE:
@ -232,6 +236,7 @@ final class DiscoveryUiUtils {
* Helper method to display an error message when the results of the * Helper method to display an error message when the results of the
* Discovery Top component may be incomplete. * Discovery Top component may be incomplete.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"DiscoveryUiUtils.resultsIncomplete.text=Discovery results may be incomplete"}) @NbBundle.Messages({"DiscoveryUiUtils.resultsIncomplete.text=Discovery results may be incomplete"})
static void displayErrorMessage(DiscoveryDialog dialog) { static void displayErrorMessage(DiscoveryDialog dialog) {
//check if modules run and assemble message //check if modules run and assemble message

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData; import org.sleuthkit.autopsy.discovery.search.SearchData;
/** /**
@ -32,6 +33,7 @@ final class DocumentFilterPanel extends AbstractFiltersPanel {
/** /**
* Constructs a new DocumentFilterPanel. * Constructs a new DocumentFilterPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DocumentFilterPanel() { DocumentFilterPanel() {
super(); super();
initComponents(); initComponents();

View File

@ -79,9 +79,6 @@
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/file-icon-deleted.png"/> <Image iconType="3" name="/org/sleuthkit/autopsy/images/file-icon-deleted.png"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DocumentPanel.isDeletedLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/> <Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property> </Property>
@ -91,6 +88,9 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/> <Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DocumentPanel.isDeletedLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="scoreLabel"> <Component class="javax.swing.JLabel" name="scoreLabel">
@ -98,7 +98,6 @@
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/red-circle-exclamation.png"/> <Image iconType="3" name="/org/sleuthkit/autopsy/images/red-circle-exclamation.png"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/> <Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property> </Property>
@ -108,6 +107,7 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/> <Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="fileSizeLabel"> <Component class="javax.swing.JLabel" name="fileSizeLabel">

View File

@ -29,6 +29,7 @@ import javax.swing.JList;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane; import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData; import org.sleuthkit.autopsy.discovery.search.SearchData;
/** /**
@ -43,6 +44,7 @@ class DocumentPanel extends javax.swing.JPanel implements ListCellRenderer<Docum
/** /**
* Creates new form DocumentPanel. * Creates new form DocumentPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DocumentPanel() { DocumentPanel() {
initComponents(); initComponents();
} }
@ -150,7 +152,7 @@ class DocumentPanel extends javax.swing.JPanel implements ListCellRenderer<Docum
"DocumentPanel.numberOfImages.text=1 of {0} images", "DocumentPanel.numberOfImages.text=1 of {0} images",
"DocumentPanel.numberOfImages.noImages=No images", "DocumentPanel.numberOfImages.noImages=No images",
"DocumentPanel.noImageExtraction.text=0 of ? images"}) "DocumentPanel.noImageExtraction.text=0 of ? images"})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public Component getListCellRendererComponent(JList<? extends DocumentWrapper> list, DocumentWrapper value, int index, boolean isSelected, boolean cellHasFocus) { public Component getListCellRendererComponent(JList<? extends DocumentWrapper> list, DocumentWrapper value, int index, boolean isSelected, boolean cellHasFocus) {
fileSizeLabel.setText(DiscoveryUiUtils.getFileSizeString(value.getResultFile().getFirstInstance().getSize())); fileSizeLabel.setText(DiscoveryUiUtils.getFileSizeString(value.getResultFile().getFirstInstance().getSize()));
@ -180,6 +182,7 @@ class DocumentPanel extends javax.swing.JPanel implements ListCellRenderer<Docum
return this; return this;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public String getToolTipText(MouseEvent event) { public String getToolTipText(MouseEvent event) {
if (event != null) { if (event != null) {

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.DefaultListModel; import javax.swing.DefaultListModel;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
/** /**
@ -35,6 +36,7 @@ final class DocumentPreviewViewer extends javax.swing.JPanel {
/** /**
* Creates new form DocumentViewer. * Creates new form DocumentViewer.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DocumentPreviewViewer() { DocumentPreviewViewer() {
initComponents(); initComponents();
} }
@ -75,12 +77,11 @@ final class DocumentPreviewViewer extends javax.swing.JPanel {
/** /**
* Clear the list of documents being displayed. * Clear the list of documents being displayed.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void clearViewer() { void clearViewer() {
synchronized (this) {
documentListModel.removeAllElements(); documentListModel.removeAllElements();
documentScrollPane.getVerticalScrollBar().setValue(0); documentScrollPane.getVerticalScrollBar().setValue(0);
} }
}
/** /**
* Add a selection listener to the list of document previews being * Add a selection listener to the list of document previews being
@ -88,6 +89,7 @@ final class DocumentPreviewViewer extends javax.swing.JPanel {
* *
* @param listener The ListSelectionListener to add to the selection model. * @param listener The ListSelectionListener to add to the selection model.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addListSelectionListener(ListSelectionListener listener) { void addListSelectionListener(ListSelectionListener listener) {
documentList.getSelectionModel().addListSelectionListener(listener); documentList.getSelectionModel().addListSelectionListener(listener);
} }
@ -99,15 +101,14 @@ final class DocumentPreviewViewer extends javax.swing.JPanel {
* @return The list of AbstractFiles which are represented by the selected * @return The list of AbstractFiles which are represented by the selected
* document preview. * document preview.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
List<AbstractFile> getInstancesForSelected() { List<AbstractFile> getInstancesForSelected() {
synchronized (this) {
if (documentList.getSelectedIndex() == -1) { if (documentList.getSelectedIndex() == -1) {
return new ArrayList<>(); return new ArrayList<>();
} else { } else {
return documentListModel.getElementAt(documentList.getSelectedIndex()).getResultFile().getAllInstances(); return documentListModel.getElementAt(documentList.getSelectedIndex()).getResultFile().getAllInstances();
} }
} }
}
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JList<DocumentWrapper> documentList; private javax.swing.JList<DocumentWrapper> documentList;
@ -120,9 +121,8 @@ final class DocumentPreviewViewer extends javax.swing.JPanel {
* @param documentWrapper The object which contains the document preview * @param documentWrapper The object which contains the document preview
* which will be displayed. * which will be displayed.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addDocument(DocumentWrapper documentWrapper) { void addDocument(DocumentWrapper documentWrapper) {
synchronized (this) {
documentListModel.addElement(documentWrapper); documentListModel.addElement(documentWrapper);
} }
} }
}

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="jSplitPane1">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,192 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer;
import com.google.common.eventbus.Subscribe;
import java.util.logging.Level;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultTableArtifactContentViewer;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* JPanel which should be used as a tab in the domain artifacts details area.
*/
final class DomainArtifactsTabPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final static Logger logger = Logger.getLogger(DomainArtifactsTabPanel.class.getName());
private final ArtifactsListPanel listPanel;
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
private AbstractArtifactDetailsPanel rightPanel = null;
private volatile ArtifactRetrievalStatus status = ArtifactRetrievalStatus.UNPOPULATED;
private final ListSelectionListener listener = new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent event) {
if (!event.getValueIsAdjusting()) {
rightPanel.setArtifact(listPanel.getSelectedArtifact());
}
}
};
/**
* Creates new form CookiesPanel
*
* @param type The type of Artifact this tab is displaying information for.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainArtifactsTabPanel(BlackboardArtifact.ARTIFACT_TYPE type) {
initComponents();
this.artifactType = type;
listPanel = new ArtifactsListPanel(artifactType);
jSplitPane1.setLeftComponent(listPanel);
setRightComponent();
listPanel.addSelectionListener(listener);
}
/**
* Set the right component of the tab panel, which will display the details
* for the artifact.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setRightComponent() {
switch (artifactType) {
case TSK_WEB_HISTORY:
case TSK_WEB_COOKIE:
case TSK_WEB_SEARCH_QUERY:
case TSK_WEB_BOOKMARK:
rightPanel = new GeneralPurposeArtifactViewer();
break;
case TSK_WEB_DOWNLOAD:
case TSK_WEB_CACHE:
rightPanel = new ContentViewerDetailsPanel();
break;
default:
rightPanel = new DefaultTableArtifactContentViewer();
break;
}
if (rightPanel != null) {
jSplitPane1.setRightComponent(new JScrollPane(rightPanel));
}
}
/**
* Get the status of the panel which indicates if it is populated.
*
* @return The ArtifactRetrievalStatus of the panel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ArtifactRetrievalStatus getStatus() {
return status;
}
/**
* Manually set the status of the panel.
*
* @param status The ArtifactRetrievalStatus of the panel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void setStatus(ArtifactRetrievalStatus status) {
this.status = status;
if (status == ArtifactRetrievalStatus.UNPOPULATED && rightPanel != null) {
rightPanel.setArtifact(null);
}
}
/**
* Handle the event which indicates the artifacts have been retrieved.
*
* @param artifactresultEvent The event which indicates the artifacts have
* been retrieved.
*/
@Subscribe
void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) {
if (artifactType == artifactresultEvent.getArtifactType()) {
SwingUtilities.invokeLater(() -> {
listPanel.removeListSelectionListener(listener);
listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts());
status = ArtifactRetrievalStatus.POPULATED;
setEnabled(!listPanel.isEmpty());
listPanel.addSelectionListener(listener);
listPanel.selectFirst();
revalidate();
repaint();
try {
DiscoveryEventUtils.getDiscoveryEventBus().unregister(this);
} catch (IllegalArgumentException notRegistered) {
logger.log(Level.INFO, "Attempting to unregister tab which was not registered");
// attempting to remove a tab that was never registered
}
});
}
}
/**
* Get the type of Artifact the panel exists for.
*
* @return The ARTIFACT_TYPE of the BlackboardArtifact being displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
BlackboardArtifact.ARTIFACT_TYPE getArtifactType() {
return artifactType;
}
/**
* 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() {
jSplitPane1 = new javax.swing.JSplitPane();
setMinimumSize(new java.awt.Dimension(0, 0));
setPreferredSize(new java.awt.Dimension(0, 0));
setLayout(new java.awt.BorderLayout());
jSplitPane1.setMinimumSize(new java.awt.Dimension(0, 0));
jSplitPane1.setPreferredSize(new java.awt.Dimension(0, 0));
add(jSplitPane1, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JSplitPane jSplitPane1;
// End of variables declaration//GEN-END:variables
/**
* Enum to keep track of the populated state of this panel.
*/
enum ArtifactRetrievalStatus {
UNPOPULATED(),
POPULATING(),
POPULATED();
}
}

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="enabled" type="boolean" value="false"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JTabbedPane" name="jTabbedPane1">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,186 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.discovery.ui;
import com.google.common.eventbus.Subscribe;
import java.awt.Component;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.commons.lang.StringUtils;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.autopsy.discovery.search.SearchData;
/**
* Panel to display details area for domain discovery results.
*
*/
final class DomainDetailsPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ArtifactsWorker detailsWorker;
private String domain;
private String selectedTabName;
/**
* Creates new form ArtifactDetailsPanel.
*
* @param selectedTabName The name of the tab to select initially.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainDetailsPanel(String selectedTabName) {
initComponents();
addArtifactTabs(selectedTabName);
}
/**
* Add the tabs for each of the artifact types which we will be displaying.
*
* @param tabName The name of the tab to select initially.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void addArtifactTabs(String tabName) {
for (BlackboardArtifact.ARTIFACT_TYPE type : SearchData.Type.DOMAIN.getArtifactTypes()) {
jTabbedPane1.add(type.getDisplayName(), new DomainArtifactsTabPanel(type));
}
selectedTabName = tabName;
selectTab();
jTabbedPane1.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (jTabbedPane1.getSelectedIndex() >= 0) {
String newTabTitle = jTabbedPane1.getTitleAt(jTabbedPane1.getSelectedIndex());
if (selectedTabName == null || !selectedTabName.equals(newTabTitle)) {
selectedTabName = newTabTitle;
runDomainWorker();
}
}
}
});
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
/**
* Set the selected tab index to be the previously selected tab if a
* previously selected tab exists.
*/
private void selectTab() {
for (int i = 0; i < jTabbedPane1.getTabCount(); i++) {
if (!StringUtils.isBlank(selectedTabName) && selectedTabName.equals(jTabbedPane1.getTitleAt(i))) {
jTabbedPane1.setSelectedIndex(i);
return;
}
}
}
/**
* Run the worker which retrieves the list of artifacts for the domain to
* populate the details area.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void runDomainWorker() {
Component selectedComponent = jTabbedPane1.getSelectedComponent();
if (selectedComponent instanceof DomainArtifactsTabPanel) {
if (detailsWorker != null && !detailsWorker.isDone()) {
detailsWorker.cancel(true);
}
DomainArtifactsTabPanel selectedTab = (DomainArtifactsTabPanel) selectedComponent;
if (selectedTab.getStatus() == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) {
DiscoveryEventUtils.getDiscoveryEventBus().register(selectedTab);
selectedTab.setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING);
detailsWorker = new ArtifactsWorker(selectedTab.getArtifactType(), domain);
detailsWorker.execute();
}
}
}
/**
* Populate the the details tabs.
*
* @param populateEvent The PopulateDomainTabsEvent which indicates which
* domain the details tabs should be populated for.
*/
@Subscribe
void handlePopulateDomainTabsEvent(DiscoveryEventUtils.PopulateDomainTabsEvent populateEvent) {
domain = populateEvent.getDomain();
SwingUtilities.invokeLater(() -> {
resetTabsStatus();
selectTab();
runDomainWorker();
if (StringUtils.isBlank(domain)) {
//send fade out event
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false));
} else {
//send fade in event
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true));
}
});
}
/**
* Private helper method to ensure tabs will re-populate after a new domain
* is selected.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void resetTabsStatus() {
for (Component comp : jTabbedPane1.getComponents()) {
if (comp instanceof DomainArtifactsTabPanel) {
((DomainArtifactsTabPanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED);
}
}
}
/**
* Get the name of the tab that was most recently selected.
*
* @return The name of the tab that was most recently selected.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
String getSelectedTabName() {
return selectedTabName;
}
/**
* 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() {
jTabbedPane1 = new javax.swing.JTabbedPane();
setEnabled(false);
setMinimumSize(new java.awt.Dimension(0, 0));
setPreferredSize(new java.awt.Dimension(0, 0));
setLayout(new java.awt.BorderLayout());
jTabbedPane1.setMinimumSize(new java.awt.Dimension(0, 0));
jTabbedPane1.setPreferredSize(new java.awt.Dimension(0, 0));
add(jTabbedPane1, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTabbedPane jTabbedPane1;
// End of variables declaration//GEN-END:variables
}

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes; import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter; import org.sleuthkit.autopsy.discovery.search.ResultsSorter;
import org.sleuthkit.autopsy.discovery.search.SearchData; import org.sleuthkit.autopsy.discovery.search.SearchData;
@ -34,6 +35,7 @@ public class DomainFilterPanel extends AbstractFiltersPanel {
/** /**
* Creates new form DomainFilterPanel. * Creates new form DomainFilterPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public DomainFilterPanel() { public DomainFilterPanel() {
super(); super();
initComponents(); initComponents();

View File

@ -30,6 +30,7 @@ import javax.swing.JComponent;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/** /**
* Class which displays a preview and details about a domain. * Class which displays a preview and details about a domain.
@ -43,6 +44,7 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
/** /**
* Creates new form DomainPanel. * Creates new form DomainPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainSummaryPanel() { DomainSummaryPanel() {
initComponents(); initComponents();
domainNameLabel.setFont(domainNameLabel.getFont().deriveFont(domainNameLabel.getFont().getStyle(), domainNameLabel.getFont().getSize() + 6)); domainNameLabel.setFont(domainNameLabel.getFont().deriveFont(domainNameLabel.getFont().getStyle(), domainNameLabel.getFont().getSize() + 6));
@ -136,6 +138,7 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
private javax.swing.JLabel totalVisitsLabel; private javax.swing.JLabel totalVisitsLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"# {0} - startDate", @NbBundle.Messages({"# {0} - startDate",
"# {1} - endDate", "# {1} - endDate",
"DomainSummaryPanel.activity.text=Activity: {0} to {1}", "DomainSummaryPanel.activity.text=Activity: {0} to {1}",
@ -163,6 +166,7 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
return this; return this;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public String getToolTipText(MouseEvent event) { public String getToolTipText(MouseEvent event) {
if (event != null) { if (event != null) {

View File

@ -39,8 +39,6 @@
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;DomainWrapper&gt;"/> <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;DomainWrapper&gt;"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues> </AuxValues>
</Component> </Component>
</SubComponents> </SubComponents>

View File

@ -19,6 +19,9 @@
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import javax.swing.DefaultListModel; import javax.swing.DefaultListModel;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
/** /**
* A JPanel to display domain summaries. * A JPanel to display domain summaries.
@ -30,20 +33,20 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
private final DefaultListModel<DomainWrapper> domainListModel = new DefaultListModel<>(); private final DefaultListModel<DomainWrapper> domainListModel = new DefaultListModel<>();
/** /**
* Clear the list of documents being displayed. * Creates new form DomainSummaryPanel
*/ */
void clearViewer() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
synchronized (this) { public DomainSummaryViewer() {
domainListModel.removeAllElements(); initComponents();
domainScrollPane.getVerticalScrollBar().setValue(0);
}
} }
/** /**
* Creates new form DomainSummaryPanel * Clear the list of documents being displayed.
*/ */
public DomainSummaryViewer() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
initComponents(); void clearViewer() {
domainListModel.removeAllElements();
domainScrollPane.getVerticalScrollBar().setValue(0);
} }
/** /**
@ -56,7 +59,7 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
private void initComponents() { private void initComponents() {
domainScrollPane = new javax.swing.JScrollPane(); domainScrollPane = new javax.swing.JScrollPane();
javax.swing.JList<DomainWrapper> domainList = new javax.swing.JList<>(); domainList = new javax.swing.JList<>();
setLayout(new java.awt.BorderLayout()); setLayout(new java.awt.BorderLayout());
@ -69,6 +72,7 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JList<DomainWrapper> domainList;
private javax.swing.JScrollPane domainScrollPane; private javax.swing.JScrollPane domainScrollPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ -78,9 +82,39 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
* @param domainWrapper The object which contains the domain summary which * @param domainWrapper The object which contains the domain summary which
* will be displayed. * will be displayed.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addDomain(DomainWrapper domainWrapper) { void addDomain(DomainWrapper domainWrapper) {
synchronized (this) {
domainListModel.addElement(domainWrapper); domainListModel.addElement(domainWrapper);
} }
/**
* Send an event to perform the population of the domain details tabs to
* reflect the currently selected domain. Will populate the list with
* nothing when a domain is not used.
*
* @param useDomain If the currently selected domain should be used to
* retrieve a list.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void sendPopulateEvent(boolean useDomain) {
String domain = "";
if (useDomain) {
if (domainList.getSelectedIndex() != -1) {
domain = domainListModel.getElementAt(domainList.getSelectedIndex()).getResultDomain().getDomain();
}
}
//send populateMesage
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.PopulateDomainTabsEvent(domain));
}
/**
* Add a selection listener to the list of document previews being
* displayed.
*
* @param listener The ListSelectionListener to add to the selection model.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addListSelectionListener(ListSelectionListener listener) {
domainList.getSelectionModel().addListSelectionListener(listener);
} }
} }

View File

@ -105,8 +105,8 @@
<Properties> <Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo"> <Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Instances"> <TitledBorder title="&lt;FileDetailsPanel.instancesList.border.title&gt;">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DetailsPanel.instancesList.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="FileDetailsPanel.instancesList.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder> </TitledBorder>
</Border> </Border>
</Property> </Property>

View File

@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.corecomponents.DataContentPanel; import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ViewContextAction; import org.sleuthkit.autopsy.directorytree.ViewContextAction;
@ -48,7 +49,7 @@ import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Panel to display the details of the selected result. * Panel to display the details of the selected result.
*/ */
final class DetailsPanel extends javax.swing.JPanel { final class FileDetailsPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -59,7 +60,8 @@ final class DetailsPanel extends javax.swing.JPanel {
/** /**
* Creates new form DetailsPanel. * Creates new form DetailsPanel.
*/ */
DetailsPanel() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
FileDetailsPanel() {
initComponents(); initComponents();
dataContentPanel = DataContentPanel.createInstance(); dataContentPanel = DataContentPanel.createInstance();
detailsSplitPane.setBottomComponent(dataContentPanel); detailsSplitPane.setBottomComponent(dataContentPanel);
@ -68,7 +70,6 @@ final class DetailsPanel extends javax.swing.JPanel {
@Override @Override
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) { if (SwingUtilities.isRightMouseButton(e)) {
SwingUtilities.invokeLater(() -> {
instancesList.setSelectedIndex(instancesList.locationToIndex(e.getPoint())); instancesList.setSelectedIndex(instancesList.locationToIndex(e.getPoint()));
Set<AbstractFile> files = new HashSet<>(); Set<AbstractFile> files = new HashSet<>();
files.add(instancesList.getSelectedValue()); files.add(instancesList.getSelectedValue());
@ -81,7 +82,6 @@ final class DetailsPanel extends javax.swing.JPanel {
menu.add(DeleteFileContentTagAction.getInstance().getMenuForFiles(files)); menu.add(DeleteFileContentTagAction.getInstance().getMenuForFiles(files));
menu.add(AddContentToHashDbAction.getInstance().getMenuForFiles(files)); menu.add(AddContentToHashDbAction.getInstance().getMenuForFiles(files));
menu.show(instancesList, e.getPoint().x, e.getPoint().y); menu.show(instancesList, e.getPoint().x, e.getPoint().y);
});
} }
} }
}); });
@ -89,14 +89,12 @@ final class DetailsPanel extends javax.swing.JPanel {
@Override @Override
public void valueChanged(ListSelectionEvent e) { public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) { if (!e.getValueIsAdjusting()) {
SwingUtilities.invokeLater(() -> {
AbstractFile file = getSelectedFile(); AbstractFile file = getSelectedFile();
if (file != null) { if (file != null) {
dataContentPanel.setNode(new TableFilterNode(new FileNode(file), false)); dataContentPanel.setNode(new TableFilterNode(new FileNode(file), false));
} else { } else {
dataContentPanel.setNode(null); dataContentPanel.setNode(null);
} }
});
} }
} }
}; };
@ -112,7 +110,9 @@ final class DetailsPanel extends javax.swing.JPanel {
*/ */
@Subscribe @Subscribe
void handleClearSelectionListener(DiscoveryEventUtils.ClearInstanceSelectionEvent clearEvent) { void handleClearSelectionListener(DiscoveryEventUtils.ClearInstanceSelectionEvent clearEvent) {
SwingUtilities.invokeLater(() -> {
instancesList.clearSelection(); instancesList.clearSelection();
});
} }
/** /**
@ -122,9 +122,9 @@ final class DetailsPanel extends javax.swing.JPanel {
* instances list should be populated * instances list should be populated
*/ */
@Subscribe @Subscribe
synchronized void handlePopulateInstancesListEvent(DiscoveryEventUtils.PopulateInstancesListEvent populateEvent) { void handlePopulateInstancesListEvent(DiscoveryEventUtils.PopulateInstancesListEvent populateEvent) {
SwingUtilities.invokeLater(() -> {
List<AbstractFile> files = populateEvent.getInstances(); List<AbstractFile> files = populateEvent.getInstances();
SwingUtilities.invokeLater(() -> {
if (files.isEmpty()) { if (files.isEmpty()) {
//if there are no files currently remove the current items without removing listener to cause content viewer to reset //if there are no files currently remove the current items without removing listener to cause content viewer to reset
instancesListModel.removeAllElements(); instancesListModel.removeAllElements();
@ -154,7 +154,8 @@ final class DetailsPanel extends javax.swing.JPanel {
* *
* @return The AbstractFile which is currently selected. * @return The AbstractFile which is currently selected.
*/ */
synchronized AbstractFile getSelectedFile() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
AbstractFile getSelectedFile() {
if (instancesList.getSelectedIndex() == -1) { if (instancesList.getSelectedIndex() == -1) {
return null; return null;
} else { } else {
@ -186,7 +187,7 @@ final class DetailsPanel extends javax.swing.JPanel {
instancesScrollPane.setPreferredSize(new java.awt.Dimension(775, 60)); instancesScrollPane.setPreferredSize(new java.awt.Dimension(775, 60));
instancesList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(DetailsPanel.class, "DetailsPanel.instancesList.border.title"))); // NOI18N instancesList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(FileDetailsPanel.class, "FileDetailsPanel.instancesList.border.title"))); // NOI18N
instancesList.setModel(instancesListModel); instancesList.setModel(instancesListModel);
instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
instancesList.setCellRenderer(new InstancesCellRenderer()); instancesList.setCellRenderer(new InstancesCellRenderer());
@ -241,6 +242,7 @@ final class DetailsPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);

View File

@ -31,6 +31,7 @@ import javax.swing.JList;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes; import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils; import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey; import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
@ -56,6 +57,7 @@ final class GroupListPanel extends javax.swing.JPanel {
/** /**
* Creates new form GroupListPanel. * Creates new form GroupListPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
GroupListPanel() { GroupListPanel() {
initComponents(); initComponents();
} }
@ -168,10 +170,9 @@ final class GroupListPanel extends javax.swing.JPanel {
/** /**
* Reset the group list to be empty. * Reset the group list to be empty.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void resetGroupList() { void resetGroupList() {
SwingUtilities.invokeLater(() -> {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
});
groupKeyList.setListData(new GroupKey[0]); groupKeyList.setListData(new GroupKey[0]);
} }
@ -211,6 +212,7 @@ final class GroupListPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public java.awt.Component getListCellRendererComponent( public java.awt.Component getListCellRendererComponent(
JList<?> list, JList<?> list,

View File

@ -27,6 +27,7 @@ import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering; import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -43,6 +44,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Creates new form HashSetFilterPaenl. * Creates new form HashSetFilterPaenl.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
HashSetFilterPanel() { HashSetFilterPanel() {
initComponents(); initComponents();
setUpHashFilter(); setUpHashFilter();
@ -51,6 +53,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Initialize the hash filter. * Initialize the hash filter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpHashFilter() { private void setUpHashFilter() {
int count = 0; int count = 0;
try { try {
@ -123,6 +126,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JScrollPane hashSetScrollPane; private javax.swing.JScrollPane hashSetScrollPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void configurePanel(boolean selected, int[] indicesSelected) { void configurePanel(boolean selected, int[] indicesSelected) {
boolean hasHashSets = hashSetList.getModel().getSize() > 0; boolean hasHashSets = hashSetList.getModel().getSize() > 0;
@ -140,6 +144,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JCheckBox getCheckbox() { JCheckBox getCheckbox() {
return hashSetCheckbox; return hashSetCheckbox;
@ -150,6 +155,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
return null; return null;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"HashSetFilterPanel.error.text=At least one hash set name must be selected."}) @NbBundle.Messages({"HashSetFilterPanel.error.text=At least one hash set name must be selected."})
@Override @Override
String checkForError() { String checkForError() {
@ -159,11 +165,13 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
return ""; return "";
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JList<?> getList() { JList<?> getList() {
return hashSetList; return hashSetList;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
AbstractFilter getFilter() { AbstractFilter getFilter() {
if (hashSetCheckbox.isSelected()) { if (hashSetCheckbox.isSelected()) {

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData; import org.sleuthkit.autopsy.discovery.search.SearchData;
/** /**
@ -32,6 +33,7 @@ final class ImageFilterPanel extends AbstractFiltersPanel {
/** /**
* Creates new form ImageFilterPanel. * Creates new form ImageFilterPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ImageFilterPanel() { ImageFilterPanel() {
super(); super();
initComponents(); initComponents();

View File

@ -84,7 +84,6 @@
</Component> </Component>
<Component class="javax.swing.JLabel" name="nameLabel"> <Component class="javax.swing.JLabel" name="nameLabel">
<Properties> <Properties>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[159, 12]"/> <Dimension value="[159, 12]"/>
</Property> </Property>
@ -94,6 +93,7 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[159, 12]"/> <Dimension value="[159, 12]"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="isDeletedLabel"> <Component class="javax.swing.JLabel" name="isDeletedLabel">
@ -101,9 +101,6 @@
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/file-icon-deleted.png"/> <Image iconType="3" name="/org/sleuthkit/autopsy/images/file-icon-deleted.png"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="ImageThumbnailPanel.isDeletedLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/> <Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property> </Property>
@ -113,6 +110,9 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/> <Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="ImageThumbnailPanel.isDeletedLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="scoreLabel"> <Component class="javax.swing.JLabel" name="scoreLabel">
@ -120,7 +120,6 @@
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/red-circle-exclamation.png"/> <Image iconType="3" name="/org/sleuthkit/autopsy/images/red-circle-exclamation.png"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/> <Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property> </Property>
@ -130,6 +129,7 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/> <Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties> </Properties>
</Component> </Component>
</SubComponents> </SubComponents>

View File

@ -28,6 +28,7 @@ import javax.swing.JComponent;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/** /**
* Class which displays a thumbnail and information for an image file. * Class which displays a thumbnail and information for an image file.
@ -41,6 +42,7 @@ final class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellRe
/** /**
* Creates new form ImageThumbnailPanel * Creates new form ImageThumbnailPanel
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ImageThumbnailPanel() { ImageThumbnailPanel() {
initComponents(); initComponents();
} }
@ -129,6 +131,7 @@ final class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellRe
private javax.swing.JLabel thumbnailLabel; private javax.swing.JLabel thumbnailLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - otherInstanceCount", "# {0} - otherInstanceCount",
"ImageThumbnailPanel.nameLabel.more.text= and {0} more", "ImageThumbnailPanel.nameLabel.more.text= and {0} more",
@ -152,6 +155,7 @@ final class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellRe
return this; return this;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public String getToolTipText(MouseEvent event) { public String getToolTipText(MouseEvent event) {
if (event != null) { if (event != null) {

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.DefaultListModel; import javax.swing.DefaultListModel;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
/** /**
@ -37,6 +38,7 @@ final class ImageThumbnailViewer extends javax.swing.JPanel {
/** /**
* Creates new form ImageThumbnailViewer. * Creates new form ImageThumbnailViewer.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ImageThumbnailViewer() { ImageThumbnailViewer() {
initComponents(); initComponents();
@ -77,6 +79,7 @@ final class ImageThumbnailViewer extends javax.swing.JPanel {
* *
* @param listener The ListSelectionListener to add to the selection model. * @param listener The ListSelectionListener to add to the selection model.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addListSelectionListener(ListSelectionListener listener) { void addListSelectionListener(ListSelectionListener listener) {
thumbnailList.getSelectionModel().addListSelectionListener(listener); thumbnailList.getSelectionModel().addListSelectionListener(listener);
} }
@ -88,24 +91,23 @@ final class ImageThumbnailViewer extends javax.swing.JPanel {
* @return The list of AbstractFiles which are represented by the selected * @return The list of AbstractFiles which are represented by the selected
* image thumbnail. * image thumbnail.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
List<AbstractFile> getInstancesForSelected() { List<AbstractFile> getInstancesForSelected() {
synchronized (this) {
if (thumbnailList.getSelectedIndex() == -1) { if (thumbnailList.getSelectedIndex() == -1) {
return new ArrayList<>(); return new ArrayList<>();
} else { } else {
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances(); return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances();
} }
} }
}
/** /**
* Clear the list of thumbnails being displayed. * Clear the list of thumbnails being displayed.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void clearViewer() { void clearViewer() {
synchronized (this) {
thumbnailListModel.removeAllElements(); thumbnailListModel.removeAllElements();
thumbnailListScrollPane.getVerticalScrollBar().setValue(0); thumbnailListScrollPane.getVerticalScrollBar().setValue(0);
}
} }
/** /**
@ -114,9 +116,8 @@ final class ImageThumbnailViewer extends javax.swing.JPanel {
* @param thumbnailWrapper The object which contains the thumbnail which * @param thumbnailWrapper The object which contains the thumbnail which
* will be displayed. * will be displayed.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addImage(ImageThumbnailWrapper thumbnailWrapper) { void addImage(ImageThumbnailWrapper thumbnailWrapper) {
synchronized (this) {
thumbnailListModel.addElement(thumbnailWrapper); thumbnailListModel.addElement(thumbnailWrapper);
} }
} }
}

View File

@ -27,6 +27,7 @@ import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering; import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -43,6 +44,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Creates new form InterestingItemsFilterPanel. * Creates new form InterestingItemsFilterPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
InterestingItemsFilterPanel() { InterestingItemsFilterPanel() {
initComponents(); initComponents();
setUpInterestingItemsFilter(); setUpInterestingItemsFilter();
@ -51,6 +53,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Initialize the interesting items filter. * Initialize the interesting items filter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpInterestingItemsFilter() { private void setUpInterestingItemsFilter() {
int count = 0; int count = 0;
try { try {
@ -118,6 +121,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
interestingItemsList.setEnabled(interestingItemsCheckbox.isSelected()); interestingItemsList.setEnabled(interestingItemsCheckbox.isSelected());
}//GEN-LAST:event_interestingItemsCheckboxActionPerformed }//GEN-LAST:event_interestingItemsCheckboxActionPerformed
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void configurePanel(boolean selected, int[] indicesSelected) { void configurePanel(boolean selected, int[] indicesSelected) {
boolean hasInterestingItems = interestingItemsList.getModel().getSize() > 0; boolean hasInterestingItems = interestingItemsList.getModel().getSize() > 0;
@ -135,6 +139,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JCheckBox getCheckbox() { JCheckBox getCheckbox() {
return interestingItemsCheckbox; return interestingItemsCheckbox;
@ -145,6 +150,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
return null; return null;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"InterestingItemsFilterPanel.error.text=At least one interesting file set name must be selected."}) @NbBundle.Messages({"InterestingItemsFilterPanel.error.text=At least one interesting file set name must be selected."})
@Override @Override
String checkForError() { String checkForError() {
@ -161,11 +167,13 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JScrollPane interestingItemsScrollPane; private javax.swing.JScrollPane interestingItemsScrollPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JList<?> getList() { JList<?> getList() {
return interestingItemsList; return interestingItemsList;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
AbstractFilter getFilter() { AbstractFilter getFilter() {
if (interestingItemsCheckbox.isSelected()) { if (interestingItemsCheckbox.isSelected()) {

View File

@ -27,6 +27,7 @@ import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering; import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -43,6 +44,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Creates new form ObjectDetectedFilter. * Creates new form ObjectDetectedFilter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ObjectDetectedFilterPanel() { ObjectDetectedFilterPanel() {
initComponents(); initComponents();
setUpObjectFilter(); setUpObjectFilter();
@ -51,6 +53,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Initialize the object filter. * Initialize the object filter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpObjectFilter() { private void setUpObjectFilter() {
int count = 0; int count = 0;
try { try {
@ -129,6 +132,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JScrollPane objectsScrollPane; private javax.swing.JScrollPane objectsScrollPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void configurePanel(boolean selected, int[] indicesSelected) { void configurePanel(boolean selected, int[] indicesSelected) {
boolean hasObjects = objectsList.getModel().getSize() > 0; boolean hasObjects = objectsList.getModel().getSize() > 0;
@ -146,6 +150,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JCheckBox getCheckbox() { JCheckBox getCheckbox() {
return objectsCheckbox; return objectsCheckbox;
@ -155,6 +160,8 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
JLabel getAdditionalLabel() { JLabel getAdditionalLabel() {
return null; return null;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"ObjectDetectedFilterPanel.error.text=At least one object type name must be selected."}) @NbBundle.Messages({"ObjectDetectedFilterPanel.error.text=At least one object type name must be selected."})
@Override @Override
String checkForError() { String checkForError() {
@ -164,11 +171,13 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
return ""; return "";
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JList<?> getList() { JList<?> getList() {
return objectsList; return objectsList;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
AbstractFilter getFilter() { AbstractFilter getFilter() {
if (objectsCheckbox.isSelected()) { if (objectsCheckbox.isSelected()) {

View File

@ -31,6 +31,7 @@ import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.CallableSystemAction;
import org.openide.util.actions.Presenter; import org.openide.util.actions.Presenter;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/** /**
* Class to open the Discovery dialog. Allows the user to run searches and see * Class to open the Discovery dialog. Allows the user to run searches and see
@ -76,6 +77,7 @@ public final class OpenDiscoveryAction extends CallableSystemAction implements P
* *
* @return The toolbar button * @return The toolbar button
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public Component getToolbarPresenter() { public Component getToolbarPresenter() {
ImageIcon icon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/discovery-icon-24.png")); //NON-NLS ImageIcon icon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/discovery-icon-24.png")); //NON-NLS

View File

@ -26,6 +26,7 @@ import javax.swing.JCheckBox;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering; import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ParentSearchTerm; import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ParentSearchTerm;
@ -41,6 +42,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Creates new form ParentFolderFilterPanel. * Creates new form ParentFolderFilterPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ParentFolderFilterPanel() { ParentFolderFilterPanel() {
initComponents(); initComponents();
setUpParentPathFilter(); setUpParentPathFilter();
@ -49,6 +51,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Initialize the parent path filter. * Initialize the parent path filter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpParentPathFilter() { private void setUpParentPathFilter() {
fullRadioButton.setSelected(true); fullRadioButton.setSelected(true);
includeRadioButton.setSelected(true); includeRadioButton.setSelected(true);
@ -239,6 +242,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JRadioButton substringRadioButton; private javax.swing.JRadioButton substringRadioButton;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void configurePanel(boolean selected, int[] indicesSelected) { void configurePanel(boolean selected, int[] indicesSelected) {
parentCheckbox.setSelected(selected); parentCheckbox.setSelected(selected);
@ -270,16 +274,19 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JCheckBox getCheckbox() { JCheckBox getCheckbox() {
return parentCheckbox; return parentCheckbox;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JLabel getAdditionalLabel() { JLabel getAdditionalLabel() {
return parentLabel; return parentLabel;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"ParentFolderFilterPanel.error.text=At least one parent path must be entered."}) @NbBundle.Messages({"ParentFolderFilterPanel.error.text=At least one parent path must be entered."})
@Override @Override
String checkForError() { String checkForError() {
@ -290,6 +297,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
return ""; return "";
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
/** /**
* Utility method to get the parent path objects out of the JList. * Utility method to get the parent path objects out of the JList.
* *
@ -303,11 +311,13 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
return results; return results;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JList<?> getList() { JList<?> getList() {
return parentList; return parentList;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
AbstractFilter getFilter() { AbstractFilter getFilter() {
if (parentCheckbox.isSelected()) { if (parentCheckbox.isSelected()) {

View File

@ -24,6 +24,7 @@ import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter; import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import org.sleuthkit.autopsy.discovery.search.SearchData; import org.sleuthkit.autopsy.discovery.search.SearchData;
import org.sleuthkit.autopsy.discovery.search.SearchData.Frequency; import org.sleuthkit.autopsy.discovery.search.SearchData.Frequency;
@ -41,6 +42,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Creates new form PastOccurrencesFilterPanel. * Creates new form PastOccurrencesFilterPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
PastOccurrencesFilterPanel(Type type) { PastOccurrencesFilterPanel(Type type) {
initComponents(); initComponents();
this.type = type; this.type = type;
@ -101,6 +103,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Initialize the frequency filter. * Initialize the frequency filter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpFrequencyFilter() { private void setUpFrequencyFilter() {
int count = 0; int count = 0;
DefaultListModel<SearchData.Frequency> frequencyListModel = (DefaultListModel<SearchData.Frequency>) crFrequencyList.getModel(); DefaultListModel<SearchData.Frequency> frequencyListModel = (DefaultListModel<SearchData.Frequency>) crFrequencyList.getModel();
@ -126,6 +129,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JCheckBox pastOccurrencesCheckbox; private javax.swing.JCheckBox pastOccurrencesCheckbox;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void configurePanel(boolean selected, int[] indicesSelected) { void configurePanel(boolean selected, int[] indicesSelected) {
boolean canBeFilteredOn = type != Type.DOMAIN || CentralRepository.isEnabled(); boolean canBeFilteredOn = type != Type.DOMAIN || CentralRepository.isEnabled();
@ -144,6 +148,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JCheckBox getCheckbox() { JCheckBox getCheckbox() {
return pastOccurrencesCheckbox; return pastOccurrencesCheckbox;
@ -154,6 +159,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
return null; return null;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"PastOccurrencesFilterPanel.error.text=At least one value in the past occurrence filter must be selected."}) @NbBundle.Messages({"PastOccurrencesFilterPanel.error.text=At least one value in the past occurrence filter must be selected."})
@Override @Override
String checkForError() { String checkForError() {
@ -163,11 +169,13 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
return ""; return "";
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JList<?> getList() { JList<?> getList() {
return crFrequencyList; return crFrequencyList;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
AbstractFilter getFilter() { AbstractFilter getFilter() {
if (pastOccurrencesCheckbox.isSelected()) { if (pastOccurrencesCheckbox.isSelected()) {

View File

@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes; import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils; import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey; import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
@ -85,6 +86,7 @@ final class ResultsPanel extends javax.swing.JPanel {
*/ */
@Messages({"ResultsPanel.viewFileInDir.name=View File in Directory", @Messages({"ResultsPanel.viewFileInDir.name=View File in Directory",
"ResultsPanel.openInExternalViewer.name=Open in External Viewer"}) "ResultsPanel.openInExternalViewer.name=Open in External Viewer"})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ResultsPanel() { ResultsPanel() {
initComponents(); initComponents();
imageThumbnailViewer = new ImageThumbnailViewer(); imageThumbnailViewer = new ImageThumbnailViewer();
@ -125,9 +127,14 @@ final class ResultsPanel extends javax.swing.JPanel {
} }
} }
}); });
//JIRA-TODO 6307 Add listener for domainSummaryViewer when 6782, 6773, and the other details area related stories are done domainSummaryViewer.addListSelectionListener((e) -> {
if (resultType == SearchData.Type.DOMAIN) {
domainSummaryViewer.sendPopulateEvent(!e.getValueIsAdjusting());
}
});
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
SearchData.Type getActiveType() { SearchData.Type getActiveType() {
return resultType; return resultType;
} }
@ -139,6 +146,7 @@ final class ResultsPanel extends javax.swing.JPanel {
* @return The list of AbstractFiles which are represented by the item * @return The list of AbstractFiles which are represented by the item
* selected in the results viewer area. * selected in the results viewer area.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private List<AbstractFile> getInstancesForSelected() { private List<AbstractFile> getInstancesForSelected() {
if (null != resultType) { if (null != resultType) {
switch (resultType) { switch (resultType) {
@ -174,12 +182,12 @@ final class ResultsPanel extends javax.swing.JPanel {
*/ */
@Subscribe @Subscribe
void handlePageRetrievedEvent(DiscoveryEventUtils.PageRetrievedEvent pageRetrievedEvent) { void handlePageRetrievedEvent(DiscoveryEventUtils.PageRetrievedEvent pageRetrievedEvent) {
SwingUtilities.invokeLater(() -> {
//send populateMesage //send populateMesage
if (pageRetrievedEvent.getType() != DOMAIN) { if (pageRetrievedEvent.getType() != DOMAIN) {
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.PopulateInstancesListEvent(getInstancesForSelected())); DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.PopulateInstancesListEvent(getInstancesForSelected()));
} }
currentPage = pageRetrievedEvent.getPageNumber(); currentPage = pageRetrievedEvent.getPageNumber();
SwingUtilities.invokeLater(() -> {
updateControls(); updateControls();
resetResultViewer(); resetResultViewer();
if (null != pageRetrievedEvent.getType()) { if (null != pageRetrievedEvent.getType()) {
@ -206,8 +214,7 @@ final class ResultsPanel extends javax.swing.JPanel {
} }
resultsViewerPanel.revalidate(); resultsViewerPanel.revalidate();
resultsViewerPanel.repaint(); resultsViewerPanel.repaint();
} });
);
} }
@Subscribe @Subscribe
@ -217,7 +224,6 @@ final class ResultsPanel extends javax.swing.JPanel {
thumbWorker.cancel(true); thumbWorker.cancel(true);
} }
} }
resultContentWorkers.clear(); resultContentWorkers.clear();
} }
@ -225,7 +231,8 @@ final class ResultsPanel extends javax.swing.JPanel {
* Reset the result viewer and any associate workers to a default empty * Reset the result viewer and any associate workers to a default empty
* state. * state.
*/ */
synchronized void resetResultViewer() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void resetResultViewer() {
resultsViewerPanel.remove(imageThumbnailViewer); resultsViewerPanel.remove(imageThumbnailViewer);
resultsViewerPanel.remove(videoThumbnailViewer); resultsViewerPanel.remove(videoThumbnailViewer);
resultsViewerPanel.remove(documentPreviewViewer); resultsViewerPanel.remove(documentPreviewViewer);
@ -250,7 +257,8 @@ final class ResultsPanel extends javax.swing.JPanel {
* *
* @param results The list of ResultFiles to populate the video viewer with. * @param results The list of ResultFiles to populate the video viewer with.
*/ */
synchronized void populateVideoViewer(List<Result> results) { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void populateVideoViewer(List<Result> results) {
for (Result result : results) { for (Result result : results) {
VideoThumbnailWorker thumbWorker = new VideoThumbnailWorker((ResultFile) result); VideoThumbnailWorker thumbWorker = new VideoThumbnailWorker((ResultFile) result);
thumbWorker.execute(); thumbWorker.execute();
@ -265,7 +273,8 @@ final class ResultsPanel extends javax.swing.JPanel {
* *
* @param results The list of ResultFiles to populate the image viewer with. * @param results The list of ResultFiles to populate the image viewer with.
*/ */
synchronized void populateImageViewer(List<Result> results) { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void populateImageViewer(List<Result> results) {
for (Result result : results) { for (Result result : results) {
ImageThumbnailWorker thumbWorker = new ImageThumbnailWorker((ResultFile) result); ImageThumbnailWorker thumbWorker = new ImageThumbnailWorker((ResultFile) result);
thumbWorker.execute(); thumbWorker.execute();
@ -281,7 +290,8 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param results The list of ResultFiles to populate the document viewer * @param results The list of ResultFiles to populate the document viewer
* with. * with.
*/ */
synchronized void populateDocumentViewer(List<Result> results) { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void populateDocumentViewer(List<Result> results) {
for (Result result : results) { for (Result result : results) {
DocumentPreviewWorker documentWorker = new DocumentPreviewWorker((ResultFile) result); DocumentPreviewWorker documentWorker = new DocumentPreviewWorker((ResultFile) result);
documentWorker.execute(); documentWorker.execute();
@ -297,7 +307,8 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param results The list of ResultDomains to populate the domain summary * @param results The list of ResultDomains to populate the domain summary
* viewer with. * viewer with.
*/ */
synchronized void populateDomainViewer(List<Result> results) { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void populateDomainViewer(List<Result> results) {
SleuthkitCase currentCase; SleuthkitCase currentCase;
try { try {
currentCase = Case.getCurrentCaseThrows().getSleuthkitCase(); currentCase = Case.getCurrentCaseThrows().getSleuthkitCase();
@ -322,7 +333,6 @@ final class ResultsPanel extends javax.swing.JPanel {
*/ */
@Subscribe @Subscribe
void handleGroupSelectedEvent(DiscoveryEventUtils.GroupSelectedEvent groupSelectedEvent) { void handleGroupSelectedEvent(DiscoveryEventUtils.GroupSelectedEvent groupSelectedEvent) {
SwingUtilities.invokeLater(() -> {
searchFilters = groupSelectedEvent.getFilters(); searchFilters = groupSelectedEvent.getFilters();
groupingAttribute = groupSelectedEvent.getGroupingAttr(); groupingAttribute = groupSelectedEvent.getGroupingAttr();
groupSort = groupSelectedEvent.getGroupSort(); groupSort = groupSelectedEvent.getGroupSort();
@ -330,6 +340,7 @@ final class ResultsPanel extends javax.swing.JPanel {
selectedGroupKey = groupSelectedEvent.getGroupKey(); selectedGroupKey = groupSelectedEvent.getGroupKey();
resultType = groupSelectedEvent.getResultType(); resultType = groupSelectedEvent.getResultType();
groupSize = groupSelectedEvent.getGroupSize(); groupSize = groupSelectedEvent.getGroupSize();
SwingUtilities.invokeLater(() -> {
resetResultViewer(); resetResultViewer();
setPage(0); setPage(0);
}); });
@ -343,9 +354,9 @@ final class ResultsPanel extends javax.swing.JPanel {
*/ */
@Subscribe @Subscribe
void handleNoResultsEvent(DiscoveryEventUtils.NoResultsEvent noResultsEvent) { void handleNoResultsEvent(DiscoveryEventUtils.NoResultsEvent noResultsEvent) {
SwingUtilities.invokeLater(() -> {
groupSize = 0; groupSize = 0;
currentPage = 0; currentPage = 0;
SwingUtilities.invokeLater(() -> {
updateControls(); updateControls();
videoThumbnailViewer.clearViewer(); videoThumbnailViewer.clearViewer();
imageThumbnailViewer.clearViewer(); imageThumbnailViewer.clearViewer();
@ -374,11 +385,9 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param startingEntry The index of the first file in the group to include * @param startingEntry The index of the first file in the group to include
* in this page. * in this page.
*/ */
@Subscribe @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private synchronized void setPage(int startingEntry private void setPage(int startingEntry) {
) {
int pageSize = pageSizeComboBox.getItemAt(pageSizeComboBox.getSelectedIndex()); int pageSize = pageSizeComboBox.getItemAt(pageSizeComboBox.getSelectedIndex());
synchronized (this) {
if (pageWorker != null && !pageWorker.isDone()) { if (pageWorker != null && !pageWorker.isDone()) {
pageWorker.cancel(true); pageWorker.cancel(true);
} }
@ -395,16 +404,14 @@ final class ResultsPanel extends javax.swing.JPanel {
pageWorker = new PageWorker(searchFilters, groupingAttribute, groupSort, fileSortMethod, selectedGroupKey, startingEntry, pageSize, resultType, centralRepo); pageWorker = new PageWorker(searchFilters, groupingAttribute, groupSort, fileSortMethod, selectedGroupKey, startingEntry, pageSize, resultType, centralRepo);
pageWorker.execute(); pageWorker.execute();
} else { } else {
SwingUtilities.invokeLater(() -> {
pageSizeComboBox.setEnabled(true); pageSizeComboBox.setEnabled(true);
});
}
} }
} }
/** /**
* Enable the paging controls based on what exists in the page. * Enable the paging controls based on what exists in the page.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Messages({"# {0} - currentPage", @Messages({"# {0} - currentPage",
"# {1} - totalPages", "# {1} - totalPages",
"ResultsPanel.currentPage.displayValue=Page: {0} of {1}"}) "ResultsPanel.currentPage.displayValue=Page: {0} of {1}"})
@ -678,6 +685,7 @@ final class ResultsPanel extends javax.swing.JPanel {
/** /**
* Disable all the paging controls. * Disable all the paging controls.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void disablePagingControls() { private void disablePagingControls() {
nextPageButton.setEnabled(false); nextPageButton.setEnabled(false);
previousPageButton.setEnabled(false); previousPageButton.setEnabled(false);
@ -708,6 +716,7 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param file The ResultFile which represents the video file thumbnails * @param file The ResultFile which represents the video file thumbnails
* are being retrieved for. * are being retrieved for.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
VideoThumbnailWorker(ResultFile file) { VideoThumbnailWorker(ResultFile file) {
thumbnailWrapper = new VideoThumbnailsWrapper(file); thumbnailWrapper = new VideoThumbnailsWrapper(file);
videoThumbnailViewer.addVideo(thumbnailWrapper); videoThumbnailViewer.addVideo(thumbnailWrapper);
@ -746,6 +755,7 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param file The ResultFile which represents the image file thumbnails * @param file The ResultFile which represents the image file thumbnails
* are being retrieved for. * are being retrieved for.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ImageThumbnailWorker(ResultFile file) { ImageThumbnailWorker(ResultFile file) {
thumbnailWrapper = new ImageThumbnailWrapper(file); thumbnailWrapper = new ImageThumbnailWrapper(file);
imageThumbnailViewer.addImage(thumbnailWrapper); imageThumbnailViewer.addImage(thumbnailWrapper);
@ -788,6 +798,7 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param file The ResultFile which represents the document file a * @param file The ResultFile which represents the document file a
* preview is being retrieved for. * preview is being retrieved for.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DocumentPreviewWorker(ResultFile file) { DocumentPreviewWorker(ResultFile file) {
documentWrapper = new DocumentWrapper(file); documentWrapper = new DocumentWrapper(file);
documentPreviewViewer.addDocument(documentWrapper); documentPreviewViewer.addDocument(documentWrapper);
@ -836,6 +847,7 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param file The ResultFile which represents the domain attribute the * @param file The ResultFile which represents the domain attribute the
* preview is being retrieved for. * preview is being retrieved for.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainThumbnailWorker(SleuthkitCase caseDb, ResultDomain domain) { DomainThumbnailWorker(SleuthkitCase caseDb, ResultDomain domain) {
this.caseDb = caseDb; this.caseDb = caseDb;
domainWrapper = new DomainWrapper(domain); domainWrapper = new DomainWrapper(domain);

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import java.awt.Cursor; import java.awt.Cursor;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils; import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
@ -32,6 +33,7 @@ final class ResultsSplitPaneDivider extends javax.swing.JPanel {
/** /**
* Creates new form LabeledSplitPaneDivider. * Creates new form LabeledSplitPaneDivider.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ResultsSplitPaneDivider() { ResultsSplitPaneDivider() {
initComponents(); initComponents();
} }

View File

@ -26,6 +26,7 @@ import javax.swing.JCheckBox;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData; import org.sleuthkit.autopsy.discovery.search.SearchData;
import org.sleuthkit.autopsy.discovery.search.SearchData.FileSize; import org.sleuthkit.autopsy.discovery.search.SearchData.FileSize;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering; import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
@ -42,6 +43,7 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
* *
* @param type The type of result being searched for. * @param type The type of result being searched for.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
SizeFilterPanel(SearchData.Type type) { SizeFilterPanel(SearchData.Type type) {
initComponents(); initComponents();
setUpSizeFilter(type); setUpSizeFilter(type);
@ -109,6 +111,7 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JScrollPane sizeScrollPane; private javax.swing.JScrollPane sizeScrollPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void configurePanel(boolean selected, int[] indicesSelected) { void configurePanel(boolean selected, int[] indicesSelected) {
sizeCheckbox.setSelected(selected); sizeCheckbox.setSelected(selected);
@ -124,6 +127,7 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JCheckBox getCheckbox() { JCheckBox getCheckbox() {
return sizeCheckbox; return sizeCheckbox;
@ -137,6 +141,7 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Initialize the file size filter. * Initialize the file size filter.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpSizeFilter(SearchData.Type fileType) { private void setUpSizeFilter(SearchData.Type fileType) {
int count = 0; int count = 0;
DefaultListModel<FileSize> sizeListModel = (DefaultListModel<FileSize>) sizeList.getModel(); DefaultListModel<FileSize> sizeListModel = (DefaultListModel<FileSize>) sizeList.getModel();
@ -169,6 +174,7 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
@NbBundle.Messages({"SizeFilterPanel.error.text=At least one size must be selected."}) @NbBundle.Messages({"SizeFilterPanel.error.text=At least one size must be selected."})
@Override @Override
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
String checkForError() { String checkForError() {
if (sizeCheckbox.isSelected() && sizeList.getSelectedValuesList().isEmpty()) { if (sizeCheckbox.isSelected() && sizeList.getSelectedValuesList().isEmpty()) {
return Bundle.SizeFilterPanel_error_text(); return Bundle.SizeFilterPanel_error_text();
@ -177,11 +183,13 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JList<?> getList() { JList<?> getList() {
return sizeList; return sizeList;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
AbstractFilter getFilter() { AbstractFilter getFilter() {
if (sizeCheckbox.isSelected()) { if (sizeCheckbox.isSelected()) {

View File

@ -22,6 +22,7 @@ import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering; import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
/** /**
@ -34,6 +35,7 @@ final class UserCreatedFilterPanel extends AbstractDiscoveryFilterPanel {
/** /**
* Creates new form UserCreatedFilterPanel. * Creates new form UserCreatedFilterPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
UserCreatedFilterPanel() { UserCreatedFilterPanel() {
initComponents(); initComponents();
} }
@ -69,11 +71,13 @@ final class UserCreatedFilterPanel extends AbstractDiscoveryFilterPanel {
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void configurePanel(boolean selected, int[] indicesSelected) { void configurePanel(boolean selected, int[] indicesSelected) {
userCreatedCheckbox.setSelected(selected); userCreatedCheckbox.setSelected(selected);
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
JCheckBox getCheckbox() { JCheckBox getCheckbox() {
return userCreatedCheckbox; return userCreatedCheckbox;
@ -99,6 +103,7 @@ final class UserCreatedFilterPanel extends AbstractDiscoveryFilterPanel {
return null; return null;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
AbstractFilter getFilter() { AbstractFilter getFilter() {
if (userCreatedCheckbox.isSelected()) { if (userCreatedCheckbox.isSelected()) {

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData; import org.sleuthkit.autopsy.discovery.search.SearchData;
/** /**
@ -32,6 +33,7 @@ final class VideoFilterPanel extends AbstractFiltersPanel {
/** /**
* Creates new form VideoFilterPanel. * Creates new form VideoFilterPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
VideoFilterPanel() { VideoFilterPanel() {
super(); super();
initComponents(); initComponents();

View File

@ -32,6 +32,7 @@ import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/** /**
* Class which displays thumbnails and information for a video file. * Class which displays thumbnails and information for a video file.
@ -47,6 +48,7 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe
/** /**
* Creates new form VideoThumbnailPanel. * Creates new form VideoThumbnailPanel.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
VideoThumbnailPanel() { VideoThumbnailPanel() {
initComponents(); initComponents();
this.setFocusable(true); this.setFocusable(true);
@ -58,6 +60,7 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe
* @param thumbnailWrapper The object which contains the video thumbnails to * @param thumbnailWrapper The object which contains the video thumbnails to
* add. * add.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void addThumbnails(VideoThumbnailsWrapper thumbnailWrapper) { private void addThumbnails(VideoThumbnailsWrapper thumbnailWrapper) {
imagePanel.removeAll(); imagePanel.removeAll();
GridBagConstraints gridBagConstraints = new GridBagConstraints(); GridBagConstraints gridBagConstraints = new GridBagConstraints();
@ -164,6 +167,7 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe
private javax.swing.JLabel scoreLabel; private javax.swing.JLabel scoreLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Messages({ @Messages({
"# {0} - otherInstanceCount", "# {0} - otherInstanceCount",
"VideoThumbnailPanel.nameLabel.more.text= and {0} more", "VideoThumbnailPanel.nameLabel.more.text= and {0} more",
@ -231,6 +235,7 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe
return Bundle.VideoThumbnailPanel_sizeLabel_text(size, units); return Bundle.VideoThumbnailPanel_sizeLabel_text(size, units);
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public String getToolTipText(MouseEvent event) { public String getToolTipText(MouseEvent event) {
if (event != null) { if (event != null) {

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.DefaultListModel; import javax.swing.DefaultListModel;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
/** /**
@ -36,6 +37,7 @@ final class VideoThumbnailViewer extends javax.swing.JPanel {
/** /**
* Creates new form VideoThumbnailViewer. * Creates new form VideoThumbnailViewer.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
VideoThumbnailViewer() { VideoThumbnailViewer() {
initComponents(); initComponents();
} }
@ -45,6 +47,7 @@ final class VideoThumbnailViewer extends javax.swing.JPanel {
* *
* @param listener The ListSelectionListener to add to the selection model. * @param listener The ListSelectionListener to add to the selection model.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addListSelectionListener(ListSelectionListener listener) { void addListSelectionListener(ListSelectionListener listener) {
thumbnailList.getSelectionModel().addListSelectionListener(listener); thumbnailList.getSelectionModel().addListSelectionListener(listener);
} }
@ -56,25 +59,23 @@ final class VideoThumbnailViewer extends javax.swing.JPanel {
* @return The list of AbstractFiles which are represented by the selected * @return The list of AbstractFiles which are represented by the selected
* Video thumbnails. * Video thumbnails.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
List<AbstractFile> getInstancesForSelected() { List<AbstractFile> getInstancesForSelected() {
synchronized (this) {
if (thumbnailList.getSelectedIndex() == -1) { if (thumbnailList.getSelectedIndex() == -1) {
return new ArrayList<>(); return new ArrayList<>();
} else { } else {
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances(); return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances();
} }
} }
}
/** /**
* Clear the list of thumbnails being displayed. * Clear the list of thumbnails being displayed.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void clearViewer() { void clearViewer() {
synchronized (this) {
thumbnailListModel.removeAllElements(); thumbnailListModel.removeAllElements();
thumbnailListScrollPane.getVerticalScrollBar().setValue(0); thumbnailListScrollPane.getVerticalScrollBar().setValue(0);
} }
}
/** /**
* Add thumbnails for a video to the panel. * Add thumbnails for a video to the panel.
@ -82,11 +83,10 @@ final class VideoThumbnailViewer extends javax.swing.JPanel {
* @param thumbnailWrapper The object which contains the thumbnails which * @param thumbnailWrapper The object which contains the thumbnails which
* will be displayed. * will be displayed.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addVideo(VideoThumbnailsWrapper thumbnailWrapper) { void addVideo(VideoThumbnailsWrapper thumbnailWrapper) {
synchronized (this) {
thumbnailListModel.addElement(thumbnailWrapper); thumbnailListModel.addElement(thumbnailWrapper);
} }
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.

View File

@ -0,0 +1,51 @@
/*
* 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.guicomponeontutils;
import javax.swing.Icon;
import org.sleuthkit.autopsy.guiutils.CheckBoxJList;
/**
* An abstract implementation of CheckBoxJList.CheckboxListItem so that
* implementing classes have default implementation.
*/
public abstract class AbstractCheckboxListItem implements CheckBoxJList.CheckboxListItem {
private boolean checked = false;
@Override
public boolean isChecked() {
return checked;
}
@Override
public void setChecked(boolean checked) {
this.checked = checked;
}
@Override
public boolean hasIcon() {
return false;
}
@Override
public Icon getIcon() {
return null;
}
}

View File

@ -32,8 +32,10 @@ import javax.swing.ListSelectionModel;
/** /**
* A JList that renders the list items as check boxes. * A JList that renders the list items as check boxes.
*
* @param <T> An object that implements CheckboxListItem
*/ */
final class CheckBoxJList<T extends CheckBoxJList.CheckboxListItem> extends JList<T> { public final class CheckBoxJList<T extends CheckBoxJList.CheckboxListItem> extends JList<T> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -42,7 +44,7 @@ final class CheckBoxJList<T extends CheckBoxJList.CheckboxListItem> extends JLis
* a checkbox in CheckBoxJList. * a checkbox in CheckBoxJList.
* *
*/ */
interface CheckboxListItem { public interface CheckboxListItem {
/** /**
* Returns the checkbox state. * Returns the checkbox state.
@ -83,7 +85,7 @@ final class CheckBoxJList<T extends CheckBoxJList.CheckboxListItem> extends JLis
/** /**
* Construct a new JCheckBoxList. * Construct a new JCheckBoxList.
*/ */
CheckBoxJList() { public CheckBoxJList() {
initalize(); initalize();
} }
@ -134,12 +136,15 @@ final class CheckBoxJList<T extends CheckBoxJList.CheckboxListItem> extends JLis
checkbox.setSelected(value.isChecked()); checkbox.setSelected(value.isChecked());
checkbox.setBackground(list.getBackground()); checkbox.setBackground(list.getBackground());
checkbox.setEnabled(list.isEnabled()); checkbox.setEnabled(list.isEnabled());
checkbox.setOpaque(list.isOpaque());
label.setText(value.getDisplayName()); label.setText(value.getDisplayName());
label.setEnabled(list.isEnabled()); label.setEnabled(list.isEnabled());
label.setOpaque(list.isOpaque());
if (value.hasIcon()) { if (value.hasIcon()) {
label.setIcon(value.getIcon()); label.setIcon(value.getIcon());
} }
setOpaque(list.isOpaque());
setEnabled(list.isEnabled()); setEnabled(list.isEnabled());
return this; return this;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,6 +1,6 @@
DATExtractor_process_message=Processing DJI DAT file: %s DATExtractor_process_message=Processing DJI DAT file: %s
DATFileExtractor_Extractor_Name=DAT File Extractor DATFileExtractor_Extractor_Name=DAT File Extractor
DroneIngestModule_Description=Analyzes files generated by drones. DroneIngestModule_Description=Analyzes files generated by some DJI drones.
DroneIngestModule_Name=Drone Analyzer DroneIngestModule_Name=DJI Drone Analyzer
# {0} - AbstractFileName # {0} - AbstractFileName
DroneIngestModule_process_start=Started {0} DroneIngestModule_process_start=Started {0}

View File

@ -33,8 +33,8 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
public class DroneIngestModuleFactory extends IngestModuleFactoryAdapter { public class DroneIngestModuleFactory extends IngestModuleFactoryAdapter {
@Messages({ @Messages({
"DroneIngestModule_Name=Drone Analyzer", "DroneIngestModule_Name=DJI Drone Analyzer",
"DroneIngestModule_Description=Analyzes files generated by drones." "DroneIngestModule_Description=Analyzes files generated by some DJI drones."
}) })
/** /**

View File

@ -1,3 +1,4 @@
ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}. ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
ILeappAnalyzerIngestModule.processing.file=Processing file {0} ILeappAnalyzerIngestModule.processing.file=Processing file {0}
ILeappAnalyzerIngestModule.parsing.file=Parsing file {0} ILeappAnalyzerIngestModule.parsing.file=Parsing file {0}
ILeappAnalyzerIngestModule.processing.filesystem=Processing filesystem

View File

@ -8,6 +8,7 @@ ILeappAnalyzerIngestModule.iLeapp.cancelled=iLeapp run was canceled
ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}. ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
ILeappAnalyzerIngestModule.processing.file=Processing file {0} ILeappAnalyzerIngestModule.processing.file=Processing file {0}
ILeappAnalyzerIngestModule.parsing.file=Parsing file {0} ILeappAnalyzerIngestModule.parsing.file=Parsing file {0}
ILeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
ILeappAnalyzerIngestModule.report.name=iLeapp Html Report ILeappAnalyzerIngestModule.report.name=iLeapp Html Report
ILeappAnalyzerIngestModule.requires.windows=iLeapp module requires windows. ILeappAnalyzerIngestModule.requires.windows=iLeapp module requires windows.
ILeappAnalyzerIngestModule.running.iLeapp=Running iLeapp ILeappAnalyzerIngestModule.running.iLeapp=Running iLeapp

View File

@ -18,8 +18,10 @@
*/ */
package org.sleuthkit.autopsy.modules.ileappanalyzer; package org.sleuthkit.autopsy.modules.ileappanalyzer;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -32,14 +34,17 @@ import java.util.Locale;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.commons.io.FilenameUtils;
import org.openide.modules.InstalledFileLocator; import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase; import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.ExecUtil; import org.sleuthkit.autopsy.coreutils.ExecUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
@ -50,6 +55,7 @@ import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -61,7 +67,9 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
private static final String MODULE_NAME = ILeappAnalyzerModuleFactory.getModuleName(); private static final String MODULE_NAME = ILeappAnalyzerModuleFactory.getModuleName();
private static final String ILEAPP = "iLeapp"; //NON-NLS private static final String ILEAPP = "iLeapp"; //NON-NLS
private static final String ILEAPP_FS = "fs_"; //NON-NLS
private static final String ILEAPP_EXECUTABLE = "ileapp.exe";//NON-NLS private static final String ILEAPP_EXECUTABLE = "ileapp.exe";//NON-NLS
private static final String ILEAPP_PATHS_FILE = "iLeapp_paths.txt"; //NON-NLS
private File iLeappExecutable; private File iLeappExecutable;
@ -87,7 +95,7 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
try { try {
iLeappFileProcessor = new ILeappFileProcessor(); iLeappFileProcessor = new ILeappFileProcessor();
} catch (IOException | IngestModuleException ex) { } catch (IOException | IngestModuleException | NoCurrentCaseException ex) {
throw new IngestModuleException(Bundle.ILeappAnalyzerIngestModule_error_ileapp_file_processor_init(), ex); throw new IngestModuleException(Bundle.ILeappAnalyzerIngestModule_error_ileapp_file_processor_init(), ex);
} }
@ -112,59 +120,50 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
@Override @Override
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) { public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) {
if (!(context.getDataSource() instanceof LocalFilesDataSource)) { Case currentCase = Case.getCurrentCase();
return ProcessResult.OK; Path tempOutputPath = Paths.get(currentCase.getTempDirectory(), ILEAPP, ILEAPP_FS + dataSource.getId());
try {
Files.createDirectories(tempOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", tempOutputPath.toString()), ex);
return ProcessResult.ERROR;
}
List<String> iLeappPathsToProcess = new ArrayList<>();
ProcessBuilder iLeappCommand = buildiLeappListCommand(tempOutputPath);
try {
int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
if (result != 0) {
logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program getting file paths to search for result is %d", result));
return ProcessResult.ERROR;
}
iLeappPathsToProcess = loadIleappPathFile(tempOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program getting file paths to search"), ex);
return ProcessResult.ERROR;
} }
statusHelper.progress(Bundle.ILeappAnalyzerIngestModule_starting_iLeapp(), 0); statusHelper.progress(Bundle.ILeappAnalyzerIngestModule_starting_iLeapp(), 0);
List<AbstractFile> iLeappFilesToProcess = findiLeappFilesToProcess(dataSource); List<AbstractFile> iLeappFilesToProcess = new ArrayList<>();
if (!(context.getDataSource() instanceof LocalFilesDataSource)) {
extractFilesFromImage(dataSource, iLeappPathsToProcess, tempOutputPath);
statusHelper.switchToDeterminate(iLeappFilesToProcess.size());
processILeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
} else {
iLeappFilesToProcess = findiLeappFilesToProcess(dataSource);
statusHelper.switchToDeterminate(iLeappFilesToProcess.size()); statusHelper.switchToDeterminate(iLeappFilesToProcess.size());
Integer filesProcessedCount = 0; Integer filesProcessedCount = 0;
Case currentCase = Case.getCurrentCase();
for (AbstractFile iLeappFile : iLeappFilesToProcess) { for (AbstractFile iLeappFile : iLeappFilesToProcess) {
processILeappFile(dataSource, currentCase, statusHelper, filesProcessedCount, iLeappFile);
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ILEAPP, currentTime);
try {
Files.createDirectories(moduleOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", moduleOutputPath.toString()), ex);
return ProcessResult.ERROR;
}
statusHelper.progress(NbBundle.getMessage(this.getClass(), "ILeappAnalyzerIngestModule.processing.file", iLeappFile.getName()), filesProcessedCount);
ProcessBuilder iLeappCommand = buildiLeappCommand(moduleOutputPath, iLeappFile.getLocalAbsPath(), iLeappFile.getNameExtension());
try {
int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
if (result != 0) {
// ignore if there is an error and continue to try and process the next file if there is one
continue;
}
addILeappReportToReports(moduleOutputPath, currentCase);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program against file %s", iLeappFile.getLocalAbsPath()), ex);
return ProcessResult.ERROR;
}
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
return ProcessResult.OK;
}
ProcessResult fileProcessorResult = iLeappFileProcessor.processFiles(dataSource, moduleOutputPath, iLeappFile);
if (fileProcessorResult == ProcessResult.ERROR) {
return ProcessResult.ERROR;
}
filesProcessedCount++; filesProcessedCount++;
} }
// Process the logical image as a fs in iLeapp to make sure this is not a logical fs that was added
extractFilesFromImage(dataSource, iLeappPathsToProcess, tempOutputPath);
processILeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
}
IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA,
Bundle.ILeappAnalyzerIngestModule_has_run(), Bundle.ILeappAnalyzerIngestModule_has_run(),
@ -173,6 +172,99 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
return ProcessResult.OK; return ProcessResult.OK;
} }
/**
* Process each tar/zip file that is found in a logical image that contains xLeapp data
* @param dataSource Datasource where the file has been found
* @param currentCase current case
* @param statusHelper Progress bar for messages to show user
* @param filesProcessedCount count that is incremented for progress bar
* @param iLeappFile abstract file that will be processed
*/
private void processILeappFile(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, int filesProcessedCount,
AbstractFile iLeappFile) {
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ILEAPP, currentTime);
try {
Files.createDirectories(moduleOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", moduleOutputPath.toString()), ex);
return;
}
statusHelper.progress(NbBundle.getMessage(this.getClass(), "ILeappAnalyzerIngestModule.processing.file", iLeappFile.getName()), filesProcessedCount);
ProcessBuilder iLeappCommand = buildiLeappCommand(moduleOutputPath, iLeappFile.getLocalAbsPath(), iLeappFile.getNameExtension());
try {
int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
if (result != 0) {
logger.log(Level.WARNING, String.format("Error when trying to execute iLeapp program getting file paths to search for result is %d", result));
return;
}
addILeappReportToReports(moduleOutputPath, currentCase);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program against file %s", iLeappFile.getLocalAbsPath()), ex);
return;
}
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
return;
}
ProcessResult fileProcessorResult = iLeappFileProcessor.processFiles(dataSource, moduleOutputPath, iLeappFile);
if (fileProcessorResult == ProcessResult.ERROR) {
return;
}
}
/**
* Process extracted files from a disk image using xLeapp
* @param dataSource Datasource where the file has been found
* @param currentCase current case
* @param statusHelper Progress bar for messages to show user
* @param directoryToProcess
*/
private void processILeappFs(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, String directoryToProcess) {
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ILEAPP, currentTime);
try {
Files.createDirectories(moduleOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", moduleOutputPath.toString()), ex);
return;
}
statusHelper.progress(NbBundle.getMessage(this.getClass(), "ILeappAnalyzerIngestModule.processing.filesystem"));
ProcessBuilder iLeappCommand = buildiLeappCommand(moduleOutputPath, directoryToProcess, "fs");
try {
int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
if (result != 0) {
logger.log(Level.WARNING, String.format("Error when trying to execute iLeapp program getting file paths to search for result is %d", result));
return;
}
addILeappReportToReports(moduleOutputPath, currentCase);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program against file system"), ex);
return;
}
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
return;
}
ProcessResult fileProcessorResult = iLeappFileProcessor.processFileSystem(dataSource, moduleOutputPath);
if (fileProcessorResult == ProcessResult.ERROR) {
return;
}
}
/** /**
* Find the files that will be processed by the iLeapp program * Find the files that will be processed by the iLeapp program
* *
@ -208,6 +300,13 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
return iLeappFilesToProcess; return iLeappFilesToProcess;
} }
/**
* Build the command to run xLeapp
* @param moduleOutputPath output path for xLeapp
* @param sourceFilePath path where the xLeapp file is
* @param iLeappFileSystem type of file to process tar/zip/fs
* @return process to run
*/
private ProcessBuilder buildiLeappCommand(Path moduleOutputPath, String sourceFilePath, String iLeappFileSystemType) { private ProcessBuilder buildiLeappCommand(Path moduleOutputPath, String sourceFilePath, String iLeappFileSystemType) {
ProcessBuilder processBuilder = buildProcessWithRunAsInvoker( ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
@ -221,10 +320,26 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
return processBuilder; return processBuilder;
} }
/**
* Command to run xLeapp using the path option
* @param moduleOutputPath path where the file paths output will reside
* @return process to run
*/
private ProcessBuilder buildiLeappListCommand(Path moduleOutputPath) {
ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
"\"" + iLeappExecutable + "\"", //NON-NLS
"-p"
);
processBuilder.redirectError(moduleOutputPath.resolve("iLeapp_paths_error.txt").toFile()); //NON-NLS
processBuilder.redirectOutput(moduleOutputPath.resolve("iLeapp_paths.txt").toFile()); //NON-NLS
return processBuilder;
}
static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) { static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) {
ProcessBuilder processBuilder = new ProcessBuilder(commandLine); ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
/* /*
* Add an environment variable to force log2timeline/psort to run with * Add an environment variable to force iLeapp to run with
* the same permissions Autopsy uses. * the same permissions Autopsy uses.
*/ */
processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
@ -254,8 +369,13 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
.filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList()); .filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList());
if (!allIndexFiles.isEmpty()) { if (!allIndexFiles.isEmpty()) {
// Check for existance of directory that holds report data if does not exist then report contains no data
String filePath = FilenameUtils.getFullPathNoEndSeparator(allIndexFiles.get(0));
File dataFilesDir = new File(Paths.get(filePath, "_TSV Exports").toString());
if (dataFilesDir.exists()) {
currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ILeappAnalyzerIngestModule_report_name()); currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ILeappAnalyzerIngestModule_report_name());
} }
}
} catch (IOException | UncheckedIOException | TskCoreException ex) { } catch (IOException | UncheckedIOException | TskCoreException ex) {
// catch the error and continue on as report is not added // catch the error and continue on as report is not added
@ -264,4 +384,129 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
} }
/*
* Reads the iLeapp paths file to get the paths that we want to extract
*
* @param moduleOutputPath path where the file paths output will reside
*/
private List<String> loadIleappPathFile(Path moduleOutputPath) throws FileNotFoundException, IOException {
List<String> iLeappPathsToProcess = new ArrayList<>();
Path filePath = Paths.get(moduleOutputPath.toString(), ILEAPP_PATHS_FILE);
try (BufferedReader reader = new BufferedReader(new FileReader(filePath.toString()))) {
String line = reader.readLine();
while (line != null) {
if (line.contains("path list generation") || line.length() < 2) {
line = reader.readLine();
continue;
}
iLeappPathsToProcess.add(line.trim());
line = reader.readLine();
}
}
return iLeappPathsToProcess;
}
/**
* Extract files from a disk image to process with xLeapp
* @param dataSource Datasource of the image
* @param iLeappPathsToProcess List of paths to extract content from
* @param moduleOutputPath path to write content to
*/
private void extractFilesFromImage(Content dataSource, List<String> iLeappPathsToProcess, Path moduleOutputPath) {
FileManager fileManager = getCurrentCase().getServices().getFileManager();
for (String fullFilePath : iLeappPathsToProcess) {
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
break;
}
String ffp = fullFilePath.replaceAll("\\*", "%");
ffp = FilenameUtils.normalize(ffp, true);
String fileName = FilenameUtils.getName(ffp);
String filePath = FilenameUtils.getPath(ffp);
List<AbstractFile> iLeappFiles = new ArrayList<>();
try {
if (filePath.isEmpty()) {
iLeappFiles = fileManager.findFiles(dataSource, fileName); //NON-NLS
} else {
iLeappFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "No files found to process"); //NON-NLS
return;
}
for (AbstractFile iLeappFile : iLeappFiles) {
Path parentPath = Paths.get(moduleOutputPath.toString(), iLeappFile.getParentPath());
File fileParentPath = new File(parentPath.toString());
extractFileToOutput(dataSource, iLeappFile, fileParentPath, parentPath);
}
}
}
/**
* Create path and file from datasource in temp
* @param dataSource datasource of the image
* @param iLeappFile abstract file to write out
* @param fileParentPath parent file path
* @param parentPath parent file
*/
private void extractFileToOutput(Content dataSource, AbstractFile iLeappFile, File fileParentPath, Path parentPath) {
if (fileParentPath.exists()) {
if (!iLeappFile.isDir()) {
writeiLeappFile(dataSource, iLeappFile, fileParentPath.toString());
} else {
try {
Files.createDirectories(Paths.get(parentPath.toString(), iLeappFile.getName()));
} catch (IOException ex) {
logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
}
}
} else {
try {
Files.createDirectories(parentPath);
} catch (IOException ex) {
logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
}
if (!iLeappFile.isDir()) {
writeiLeappFile(dataSource, iLeappFile, fileParentPath.toString());
} else {
try {
Files.createDirectories(Paths.get(parentPath.toString(), iLeappFile.getName()));
} catch (IOException ex) {
logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
}
}
}
}
/**
* Write out file to output
* @param dataSource datasource of disk image
* @param iLeappFile acstract file to write out
* @param parentPath path to write file to
*/
private void writeiLeappFile(Content dataSource, AbstractFile iLeappFile, String parentPath) {
String fileName = iLeappFile.getName().replace(":", "-");
if (!fileName.matches(".") && !fileName.matches("..") && !fileName.toLowerCase().endsWith("-slack")) {
Path filePath = Paths.get(parentPath, fileName);
File localFile = new File(filePath.toString());
try {
ContentUtils.writeToFile(iLeappFile, localFile, context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStream.ReadContentInputStreamException ex) {
logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d).",
iLeappFile.getName(), iLeappFile.getId()), ex); //NON-NLS
} catch (IOException ex) {
logger.log(Level.WARNING, String.format("Error writing file local file '%s' (id=%d).",
filePath.toString(), iLeappFile.getId()), ex); //NON-NLS
}
}
}
} }

View File

@ -44,6 +44,7 @@ import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
@ -76,12 +77,16 @@ public final class ILeappFileProcessor {
private final Map<String, String> tsvFileArtifactComments; private final Map<String, String> tsvFileArtifactComments;
private final Map<String, List<List<String>>> tsvFileAttributes; private final Map<String, List<List<String>>> tsvFileAttributes;
public ILeappFileProcessor() throws IOException, IngestModuleException { Blackboard blkBoard;
public ILeappFileProcessor() throws IOException, IngestModuleException, NoCurrentCaseException {
this.tsvFiles = new HashMap<>(); this.tsvFiles = new HashMap<>();
this.tsvFileArtifacts = new HashMap<>(); this.tsvFileArtifacts = new HashMap<>();
this.tsvFileArtifactComments = new HashMap<>(); this.tsvFileArtifactComments = new HashMap<>();
this.tsvFileAttributes = new HashMap<>(); this.tsvFileAttributes = new HashMap<>();
blkBoard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
configExtractor(); configExtractor();
loadConfigFile(); loadConfigFile();
@ -110,6 +115,19 @@ public final class ILeappFileProcessor {
return ProcessResult.OK; return ProcessResult.OK;
} }
public ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath) {
try {
List<String> iLeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
processiLeappFiles(iLeappTsvOutputFiles, dataSource);
} catch (IOException | IngestModuleException ex) {
logger.log(Level.SEVERE, String.format("Error trying to process iLeapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS
return ProcessResult.ERROR;
}
return ProcessResult.OK;
}
/** /**
* Find the tsv files in the iLeapp output directory and match them to files * Find the tsv files in the iLeapp output directory and match them to files
* we know we want to process and return the list to process those files. * we know we want to process and return the list to process those files.
@ -124,7 +142,7 @@ public final class ILeappFileProcessor {
.filter(f -> f.toLowerCase().endsWith(".tsv")).collect(Collectors.toList()); .filter(f -> f.toLowerCase().endsWith(".tsv")).collect(Collectors.toList());
for (String tsvFile : allTsvFiles) { for (String tsvFile : allTsvFiles) {
if (tsvFiles.containsKey(FilenameUtils.getName(tsvFile))) { if (tsvFiles.containsKey(FilenameUtils.getName(tsvFile.toLowerCase()))) {
foundTsvFiles.add(tsvFile); foundTsvFiles.add(tsvFile);
} }
} }
@ -160,7 +178,41 @@ public final class ILeappFileProcessor {
processFile(iLeappFile, attrList, fileName, artifactType, bbartifacts, iLeappImageFile); processFile(iLeappFile, attrList, fileName, artifactType, bbartifacts, iLeappImageFile);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
// check this throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex);
}
}
}
if (!bbartifacts.isEmpty()) {
postArtifacts(bbartifacts);
}
}
/**
* Process the iLeapp files that were found that match the xml mapping file
*
* @param iLeappFilesToProcess List of files to process
* @param iLeappImageFile Abstract file to create artifact for
*
* @throws FileNotFoundException
* @throws IOException
*/
private void processiLeappFiles(List<String> iLeappFilesToProcess, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException {
List<BlackboardArtifact> bbartifacts = new ArrayList<>();
for (String iLeappFileName : iLeappFilesToProcess) {
String fileName = FilenameUtils.getName(iLeappFileName);
File iLeappFile = new File(iLeappFileName);
if (tsvFileAttributes.containsKey(fileName)) {
List<List<String>> attrList = tsvFileAttributes.get(fileName);
try {
BlackboardArtifact.Type artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName));
processFile(iLeappFile, attrList, fileName, artifactType, bbartifacts, dataSource);
} catch (TskCoreException ex) {
throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex); throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex);
} }
} }
@ -174,7 +226,8 @@ public final class ILeappFileProcessor {
} }
private void processFile(File iLeappFile, List<List<String>> attrList, String fileName, BlackboardArtifact.Type artifactType, private void processFile(File iLeappFile, List<List<String>> attrList, String fileName, BlackboardArtifact.Type artifactType,
List<BlackboardArtifact> bbartifacts, AbstractFile iLeappImageFile) throws FileNotFoundException, IOException, IngestModuleException { List<BlackboardArtifact> bbartifacts, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException,
TskCoreException {
try (BufferedReader reader = new BufferedReader(new FileReader(iLeappFile))) { try (BufferedReader reader = new BufferedReader(new FileReader(iLeappFile))) {
String line = reader.readLine(); String line = reader.readLine();
// Check first line, if it is null then no heading so nothing to match to, close and go to next file. // Check first line, if it is null then no heading so nothing to match to, close and go to next file.
@ -183,8 +236,8 @@ public final class ILeappFileProcessor {
line = reader.readLine(); line = reader.readLine();
while (line != null) { while (line != null) {
Collection<BlackboardAttribute> bbattributes = processReadLine(line, columnNumberToProcess, fileName); Collection<BlackboardAttribute> bbattributes = processReadLine(line, columnNumberToProcess, fileName);
if (!bbattributes.isEmpty()) { if (!bbattributes.isEmpty() && !blkBoard.artifactExists(dataSource, BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactType.getTypeID()), bbattributes)) {
BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), iLeappImageFile, bbattributes); BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), dataSource, bbattributes);
if (bbartifact != null) { if (bbartifact != null) {
bbartifacts.add(bbartifact); bbartifacts.add(bbartifact);
} }
@ -340,7 +393,7 @@ public final class ILeappFileProcessor {
for (int i = 0; i < nlist.getLength(); i++) { for (int i = 0; i < nlist.getLength(); i++) {
NamedNodeMap nnm = nlist.item(i).getAttributes(); NamedNodeMap nnm = nlist.item(i).getAttributes();
tsvFiles.put(nnm.getNamedItem("filename").getNodeValue(), nnm.getNamedItem("description").getNodeValue()); tsvFiles.put(nnm.getNamedItem("filename").getNodeValue().toLowerCase(), nnm.getNamedItem("description").getNodeValue());
} }
@ -393,16 +446,17 @@ public final class ILeappFileProcessor {
} }
} }
/** /**
* Generic method for creating a blackboard artifact with attributes * Generic method for creating a blackboard artifact with attributes
* *
* @param type is a blackboard.artifact_type enum to determine * @param type is a blackboard.artifact_type enum to determine which
* which type the artifact should be * type the artifact should be
* @param abstractFile is the AbstractFile object that needs to have the * @param abstractFile is the AbstractFile object that needs to have the
* artifact added for it * artifact added for it
* @param bbattributes is the collection of blackboard attributes that * @param bbattributes is the collection of blackboard attributes that need
* need to be added to the artifact after the * to be added to the artifact after the artifact has
* artifact has been created * been created
* *
* @return The newly-created artifact, or null on error * @return The newly-created artifact, or null on error
*/ */
@ -417,6 +471,30 @@ public final class ILeappFileProcessor {
return null; return null;
} }
/**
* Generic method for creating a blackboard artifact with attributes
*
* @param type is a blackboard.artifact_type enum to determine which
* type the artifact should be
* @param datasource is the Content object that needs to have the artifact
* added for it
* @param bbattributes is the collection of blackboard attributes that need
* to be added to the artifact after the artifact has
* been created
*
* @return The newly-created artifact, or null on error
*/
private BlackboardArtifact createArtifactWithAttributes(int type, Content dataSource, Collection<BlackboardAttribute> bbattributes) {
try {
BlackboardArtifact bbart = dataSource.newArtifact(type);
bbart.addAttributes(bbattributes);
return bbart;
} catch (TskException ex) {
logger.log(Level.WARNING, Bundle.ILeappFileProcessor_error_creating_new_artifacts(), ex); //NON-NLS
}
return null;
}
/** /**
* Method to post a list of BlackboardArtifacts to the blackboard. * Method to post a list of BlackboardArtifacts to the blackboard.
* *

View File

@ -47,6 +47,15 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="App Snapshots.tsv" description="App Snapshots (screenshots)">
<ArtifactName artifactname="TSK_SCREEN_SHOTS" comment="null">
<AttributeName attributename="TSK_PROG_NAME" columnName="App Name" required="yes" />
<AttributeName attributename="TSK_PATH" columnName="SOurce Path" required="yes" />
<AttributeName attributename="TSK_DATETIME" columnName="Date Modified" required="yes" />
<AttributeName attributename="null" columnName="Source File Located" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Bluetooth Other.tsv" description="Bluetooth Other"> <FileName filename="Bluetooth Other.tsv" description="Bluetooth Other">
<ArtifactName artifactname="TSK_BLUETOOTH_ADAPTER" comment="Bluetooth Other"> <ArtifactName artifactname="TSK_BLUETOOTH_ADAPTER" comment="Bluetooth Other">
<AttributeName attributename="TSK_NAME" columnName="Name" required="yes" /> <AttributeName attributename="TSK_NAME" columnName="Name" required="yes" />
@ -120,6 +129,13 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="DHCP Received List.tsv" description="DHCP Received List" >
<ArtifactName artifactname="TSK_IP_DHCP" comment="null">
<AttributeName attributename="TSK_NAME" columnName="Key" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC App Activity.tsv" description="KnowledgeC App Activity"> <FileName filename="KnowledgeC App Activity.tsv" description="KnowledgeC App Activity">
<ArtifactName artifactname="TSK_PROG_RUN" comment="KnowledgeC App Activity"> <ArtifactName artifactname="TSK_PROG_RUN" comment="KnowledgeC App Activity">
<AttributeName attributename="TSK_DATETIME" columnName="Entry Creation" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="Entry Creation" required="yes" />
@ -189,6 +205,36 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="KnowledgeC Device is Backlit.tsv" description="KnowledgeC Device is Backlit">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Backlit">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Screen is Backlit" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Start" required="no" />
<AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Battery Level.tsv" description="KnowledgeC Battery Level">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Battery Level">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Battery Level" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Bluetooth Connections.tsv" description="KnowledgeC Bluetooth Connections"> <FileName filename="KnowledgeC Bluetooth Connections.tsv" description="KnowledgeC Bluetooth Connections">
<ArtifactName artifactname="TSK_BLUETOOTH_PAIRING" comment="KnowledgeC Bluetooth Connections"> <ArtifactName artifactname="TSK_BLUETOOTH_PAIRING" comment="KnowledgeC Bluetooth Connections">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
@ -207,15 +253,61 @@
<FileName filename="KnowledgeC Car Play Connections.tsv" description="KnowledgeC Car Play Connections"> <FileName filename="KnowledgeC Car Play Connections.tsv" description="KnowledgeC Car Play Connections">
<ArtifactName artifactname="TSK_DEVICE_INFO" comment="KnowledgeC Car Play Connections"> <ArtifactName artifactname="TSK_DEVICE_INFO" comment="KnowledgeC Car Play Connections">
<AttributeName attributename="TSK_DATETIME" columnName="Start" required="no" /> <AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" />
<AttributeName attributename="null" columnName="End" required="no" /> <AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="null" columnName="Car Play Connected" required="no" /> <AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Car Play Connected" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" /> <AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" /> <AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" /> <AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="TSK_DEVICE_ID" columnName="UUID" required="no" /> <AttributeName attributename="TSK_DEVICE_ID" columnName="UUID" required="yes" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Disk Subsystem Access.tsv" description="KnowledgeC Disk Subsystem Access">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="disk Subsystem">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value String" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Do Not Disturb.tsv" description="KnowledgeC Do Not Disturb">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Do Not Disturb">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Inferred Motion.tsv" description="KnowledgeC Inferred Motion">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Inferred Motion">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" /> <AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -248,6 +340,19 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="KnowledgeC Device Locked.tsv" description="KnowledgeC Device Locked">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Locked">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Is Locked?" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Media Playing.tsv" description="KnowledgeC Media Playing"> <FileName filename="Media Playing.tsv" description="KnowledgeC Media Playing">
<ArtifactName artifactname="TSK_RECENT_OBJ" comment="KnowledgeC Media Playing"> <ArtifactName artifactname="TSK_RECENT_OBJ" comment="KnowledgeC Media Playing">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" />
@ -288,6 +393,36 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="KnowledgeC Screen Orientation.tsv" description="KnowledgeC Screen Orientation">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Screen Orientation">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Orientation" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Plugged In.tsv" description="KnowledgeC Plugged In">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Plugged In">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Is Plugged In?" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Start" required="no" />
<AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Safari Browsing.tsv" description="KnowledgeC Safari Browsing"> <FileName filename="KnowledgeC Safari Browsing.tsv" description="KnowledgeC Safari Browsing">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="KnowledgeC Safari Browsing"> <ArtifactName artifactname="TSK_WEB_HISTORY" comment="KnowledgeC Safari Browsing">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" />
@ -302,6 +437,18 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="KnowledgeC Siri Usage.tsv" description="KnowledgeC Siri Usage">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Siri Usage">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="App Name" required="yes" />
<AttributeName attributename="null" columnName="Weekday" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC App Usage.tsv" description="KnowledgeC App Usage"> <FileName filename="KnowledgeC App Usage.tsv" description="KnowledgeC App Usage">
<ArtifactName artifactname="TSK_PROG_RUN" comment="KnowledgeC App Usage"> <ArtifactName artifactname="TSK_PROG_RUN" comment="KnowledgeC App Usage">
<AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" />
@ -318,6 +465,18 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="KnowledgeC User Waking Events.tsv" description="KnowledgeC User Waking Event">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="User Waking">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Web Usage.tsv" description="KnowledgeC Web Usage"> <FileName filename="KnowledgeC Web Usage.tsv" description="KnowledgeC Web Usage">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="KnowledgeC Web Usage"> <ArtifactName artifactname="TSK_WEB_HISTORY" comment="KnowledgeC Web Usage">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" />
@ -433,6 +592,102 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
--> -->
<FileName filename="Notifications.tsv" description="iOS Notificatons">
<ArtifactName artifactname="TSK_PROG_NOTIFICATIONS" comment="iOS Notificatons">
<AttributeName attributename="TSK_DATETIME" columnName="Creation Time" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName=" Bundle" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName=" Title[Subtitle]" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName=" Message" required="yes" />
<AttributeName attributename="null" columnName=" Other Details" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Agg Bulletins.tsv" description="Powerlog Aggregate Bulletins">
<ArtifactName artifactname="TSK_PROG_NOTIFICATIONS" comment="Powerlog Aggregate Bulletins">
<AttributeName attributename="TSK_DATETIME" columnName="Timestamp" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Bulletin Bundle ID" required="yes" />
<AttributeName attributename="null" columnName="Time Interval in Seconds" required="no" />
<AttributeName attributename="null" columnName="Count" required="no" />
<AttributeName attributename="null" columnName="Post Type" required="no" />
<AttributeName attributename="null" columnName="Aggregate Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Agg Notifications.tsv" description="Powerlog Aggregate Notifications">
<ArtifactName artifactname="TSK_PROG_NOTIFICATIONS" comment="Powerlog Aggregate Notifications">
<AttributeName attributename="TSK_DATETIME" columnName="Timestamp" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Notification Bundle ID" required="yes" />
<AttributeName attributename="null" columnName="Time Interval in Seconds" required="no" />
<AttributeName attributename="null" columnName="Count" required="no" />
<AttributeName attributename="null" columnName="Notification Type" required="no" />
<AttributeName attributename="null" columnName="Aggregate Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Backup Info.tsv" description="Powerlog Backup Info">
<ArtifactName artifactname="TSK_BACKUP_EVENT" comment="null">
<AttributeName attributename="TSK_DATETIME" columnName="Timestamp" required="yes" />
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="null" columnName="State" required="no" />
<AttributeName attributename="null" columnName="Finished" required="no" />
<AttributeName attributename="null" columnName="Has error" required="no" />
<AttributeName attributename="null" columnName="Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Deleted Apps.tsv" description="Powerlog Deleted Apps">
<ArtifactName artifactname="TSK_DELETED_PROG" comment="Powerlog Deleted Apps">
<AttributeName attributename="TSK_DATETIME_DELETED" columnName="App Deleted Date" required="yes" />
<AttributeName attributename="TSK_DATETIME" columnName="Timestamp" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="App Name" required="yes" />
<AttributeName attributename="null" columnName="App Executable Name" required="no" />
<AttributeName attributename="TSK_PATH" columnName="Bundle ID" required="yes" />
<AttributeName attributename="null" columnName="Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Lightning Connector.tsv" description="Powerlog Lightning Connector Status">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Lightning Connector Status">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Accesory Power Mode" required="yes" />
<AttributeName attributename="null" columnName="Original Lightnint Connector Timestamp" required="no" />
<AttributeName attributename="null" columnName="Offset Timestamp" required="no" />
<AttributeName attributename="null" columnName="Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Push Message Received.tsv" description="Powerlog Push Message Received">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Push Message Received">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Connection Type" required="yes" />
<AttributeName attributename="null" columnName="Is Dropped" required="no" />
<AttributeName attributename="null" columnName="Link Quality" required="no" />
<AttributeName attributename="null" columnName="Priority" required="no" />
<AttributeName attributename="null" columnName="Topic" required="no" />
<AttributeName attributename="null" columnName="Server Hostname" required="no" />
<AttributeName attributename="null" columnName="Server IP" required="no" />
<AttributeName attributename="null" columnName="Original Timestamp" required="no" />
<AttributeName attributename="null" columnName="Offset Timestamp" required="no" />
<AttributeName attributename="null" columnName="Time Offset" required="no" />
<AttributeName attributename="null" columnName="Aggregate Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Torch.tsv" description="Powerlog Torch">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Torch">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />
<AttributeName attributename="null" columnName="Bundle ID" required="no" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Status" required="yes" />
<AttributeName attributename="null" columnName="Original Torch Timestamp" required="no" />
<AttributeName attributename="null" columnName="Offset Timestamp" required="no" />
<AttributeName attributename="null" columnName="Time Offset" required="no" />
<AttributeName attributename="null" columnName="Torch ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Wifi Network Connections.tsv" description="Powerlog WiFi Network Connections"> <FileName filename="Powerlog Wifi Network Connections.tsv" description="Powerlog WiFi Network Connections">
<ArtifactName artifactname="TSK_WIFI_NETWORK" comment="Powerlog WiFi Network Connections"> <ArtifactName artifactname="TSK_WIFI_NETWORK" comment="Powerlog WiFi Network Connections">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />

View File

@ -0,0 +1,5 @@
Yara_Module_Description=With the YARA ingest module you use YARA rule files to search files for textual or binary patterns.
Yara_Module_Name=YARA
YaraIngestModule_no_ruleSets=Unable to run YARA ingest, list of YARA rule sets was empty.
YaraIngestModule_windows_error_msg=The YARA ingest module is only available on 64bit Windows.
YaraIngestModule_yarac_not_found=Unable to compile YARA rules files. Unable to find executable at.

View File

@ -0,0 +1,258 @@
/*
* 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.modules.yara;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ExecUtil;
import org.sleuthkit.autopsy.ingest.IngestModule;
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
import org.sleuthkit.autopsy.modules.yara.rules.RuleSet;
import org.sleuthkit.autopsy.modules.yara.rules.RuleSetManager;
import org.sleuthkit.autopsy.yara.YaraJNIWrapper;
import org.sleuthkit.autopsy.yara.YaraWrapperException;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_YARA_HIT;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_RULE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Methods for scanning files for yara rule matches.
*/
final class YaraIngestHelper {
private static final String YARA_DIR = "yara";
private static final String YARA_C_EXE = "yarac64.exe";
private static final String MODULE_NAME = YaraIngestModuleFactory.getModuleName();
private YaraIngestHelper() {
}
/**
* Uses the yarac tool to compile the rules in the given rule sets.
*
* @param ruleSetNames List of names of the selected rule sets.
* @param tempDir Path of the directory to put the compiled rule files.
*
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException
*/
static void compileRules(List<String> ruleSetNames, Path outputDir) throws IngestModuleException {
if (ruleSetNames == null || ruleSetNames.isEmpty()) {
throw new IngestModule.IngestModuleException(Bundle.YaraIngestModule_no_ruleSets());
}
// Find javac
File exeFile = InstalledFileLocator.getDefault().locate(
Paths.get(YARA_DIR, YARA_C_EXE).toString(),
YaraIngestModule.class.getPackage().getName(), false);
if (exeFile == null) {
throw new IngestModuleException(Bundle.YaraIngestModule_yarac_not_found());
}
for (RuleSet set : getRuleSetsForNames(ruleSetNames)) {
compileRuleSet(set, outputDir, exeFile);
}
}
/**
* Scan the given AbstractFile for yara rule matches from the rule sets in
* the given directory creating a blackboard artifact for each matching
* rule.
*
* The baseDirectory should contain a series of directories one for each
* rule set.
*
* @param file The file to scan.
* @param baseRuleSetDirectory Base directory for the compiled rule sets.
*
* @throws TskCoreException
*/
static List<BlackboardArtifact> scanFileForMatches(AbstractFile file, File baseRuleSetDirectory, byte[] fileData, int fileDataSize, int timeout) throws TskCoreException, YaraWrapperException {
List<BlackboardArtifact> artifacts = new ArrayList<>();
File[] ruleSetDirectories = baseRuleSetDirectory.listFiles();
for (File ruleSetDirectory : ruleSetDirectories) {
List<String> ruleMatches = YaraIngestHelper.scanFileForMatches(fileData, fileDataSize, ruleSetDirectory, timeout);
if (!ruleMatches.isEmpty()) {
artifacts.addAll(YaraIngestHelper.createArtifact(file, ruleSetDirectory.getName(), ruleMatches));
}
}
return artifacts;
}
/**
*
* @param file The Abstract File being processed.
* @param baseRuleSetDirectory Base directory of the compiled rule sets.
* @param localFile Local copy of file.
* @param timeout Yara file scan timeout in seconds.
*
* @return
*
* @throws TskCoreException
* @throws YaraWrapperException
*/
static List<BlackboardArtifact> scanFileForMatches(AbstractFile file, File baseRuleSetDirectory, File localFile, int timeout) throws TskCoreException, YaraWrapperException {
List<BlackboardArtifact> artifacts = new ArrayList<>();
File[] ruleSetDirectories = baseRuleSetDirectory.listFiles();
for (File ruleSetDirectory : ruleSetDirectories) {
List<String> ruleMatches = YaraIngestHelper.scanFileForMatch(localFile, ruleSetDirectory, timeout);
if (!ruleMatches.isEmpty()) {
artifacts.addAll(YaraIngestHelper.createArtifact(file, ruleSetDirectory.getName(), ruleMatches));
}
}
return artifacts;
}
/**
* Scan the given file byte array for rule matches using the YaraJNIWrapper
* API.
*
* @param fileBytes
* @param ruleSetDirectory
*
* @return List of rules that match from the given file from the given rule
* set. Empty list is returned if no matches where found.
*
* @throws TskCoreException
*/
private static List<String> scanFileForMatches(byte[] fileBytes, int fileSize, File ruleSetDirectory, int timeout) throws YaraWrapperException {
List<String> matchingRules = new ArrayList<>();
File[] ruleSetCompiledFileList = ruleSetDirectory.listFiles();
for (File ruleFile : ruleSetCompiledFileList) {
matchingRules.addAll(YaraJNIWrapper.findRuleMatch(ruleFile.getAbsolutePath(), fileBytes, fileSize, timeout));
}
return matchingRules;
}
private static List<String> scanFileForMatch(File scanFile, File ruleSetDirectory, int timeout) throws YaraWrapperException {
List<String> matchingRules = new ArrayList<>();
File[] ruleSetCompiledFileList = ruleSetDirectory.listFiles();
for (File ruleFile : ruleSetCompiledFileList) {
matchingRules.addAll(YaraJNIWrapper.findRuleMatchFile(ruleFile.getAbsolutePath(), scanFile.getAbsolutePath(), timeout));
}
return matchingRules;
}
/**
* Create a list of Blackboard Artifacts, one for each matching rule.
*
* @param abstractFile File to add artifact to.
* @param ruleSetName Name rule set with matching rule.
* @param matchingRules Matching rule.
*
* @return List of artifacts or empty list if none were found.
*
* @throws TskCoreException
*/
private static List<BlackboardArtifact> createArtifact(AbstractFile abstractFile, String ruleSetName, List<String> matchingRules) throws TskCoreException {
List<BlackboardArtifact> artifacts = new ArrayList<>();
for (String rule : matchingRules) {
BlackboardArtifact artifact = abstractFile.newArtifact(TSK_YARA_HIT);
List<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_SET_NAME, MODULE_NAME, ruleSetName));
attributes.add(new BlackboardAttribute(TSK_RULE, MODULE_NAME, rule));
artifact.addAttributes(attributes);
artifacts.add(artifact);
}
return artifacts;
}
@NbBundle.Messages({
"YaraIngestModule_yarac_not_found=Unable to compile YARA rules files. Unable to find executable at.",
"YaraIngestModule_no_ruleSets=Unable to run YARA ingest, list of YARA rule sets was empty."
})
/**
* Compiles the rule files in the given rule set.
*
* The compiled rule files are created in outputDir\RuleSetName.
*
* @param set RuleSet for which to compile files.
* @param outputDir Output directory for the compiled rule files.
* @param yarac yarac executeable file.
*
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException
*/
static private void compileRuleSet(RuleSet set, Path outputDir, File yarac) throws IngestModuleException {
File tempFolder = Paths.get(outputDir.toString(), set.getName()).toFile();
if (!tempFolder.exists()) {
tempFolder.mkdir();
}
List<File> fileList = set.getRuleFiles();
for (File file : fileList) {
List<String> commandList = new ArrayList<>();
commandList.add(String.format("\"%s\"", yarac.toString()));
commandList.add(String.format("\"%s\"", file.toString()));
commandList.add(String.format("\"%s\"", Paths.get(tempFolder.getAbsolutePath(), "compiled_" + file.getName())));
ProcessBuilder builder = new ProcessBuilder(commandList);
try {
ExecUtil.execute(builder);
} catch (SecurityException | IOException ex) {
throw new IngestModuleException(String.format("Failed to compile Yara rules file", file.toString()), ex);
}
}
}
/**
* Returns a list of RuleSet objects for the given list of RuleSet names.
*
* @param names List of RuleSet names.
*
* @return List of RuleSet or empty list if none of the names matched
* existing rules.
*/
private static List<RuleSet> getRuleSetsForNames(List<String> names) {
List<RuleSet> ruleSetList = new ArrayList<>();
RuleSetManager manager = new RuleSetManager();
for (RuleSet set : manager.getRuleSetList()) {
if (names.contains(set.getName())) {
ruleSetList.add(set);
}
}
return ruleSetList;
}
}

View File

@ -0,0 +1,106 @@
/*
* 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.modules.yara;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.modules.yara.rules.RuleSet;
/**
* IngestJobSettings for the YARA ingest module.
*/
public final class YaraIngestJobSettings implements IngestModuleIngestJobSettings {
private static final long serialVersionUID = 1L;
private List<String> selectedRuleSetNames;
private boolean onlyExecutableFiles;
// Default constructor.
YaraIngestJobSettings() {
onlyExecutableFiles = true;
selectedRuleSetNames = new ArrayList<>();
}
/**
* Constructor.
*
* @param selected List of selected rules.
* @param onlyExecutableFiles Process only executable files.
*/
public YaraIngestJobSettings(List<RuleSet> selected, boolean onlyExecutableFiles) {
this.selectedRuleSetNames = new ArrayList<>();
for (RuleSet set : selected) {
selectedRuleSetNames.add(set.getName());
}
this.onlyExecutableFiles = onlyExecutableFiles;
}
/**
* Return the list of rule name sets that were selected in the ingest
* settings panel.
*
* @return List of selected RuleSet names.
*/
public List<String> getSelectedRuleSetNames() {
return Collections.unmodifiableList(selectedRuleSetNames);
}
/**
* Set the list of selected rule names.
*
* @param selected List of selected rule Sets.
*/
void setSelectedRuleSetNames(List<RuleSet> selected) {
this.selectedRuleSetNames = new ArrayList<>();
for (RuleSet set : selected) {
selectedRuleSetNames.add(set.getName());
}
}
/**
* Process only executable Files.
*
* @return If true the ingest module should process only executable files,
* if false process all files.
*/
public boolean onlyExecutableFiles() {
return onlyExecutableFiles;
}
/**
* Set whether to process only executable files or all files.
*
* @param onlyExecutableFiles True if the ingest module should only process
* executable files.
*/
void setOnlyExecuteableFile(boolean onlyExecutableFiles) {
this.onlyExecutableFiles = onlyExecutableFiles;
}
@Override
public long getVersionNumber() {
return serialVersionUID;
}
}

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