Merge develop into solr-8-upgrade

This commit is contained in:
Richard Cordovano 2020-08-10 15:51:35 -04:00
commit dca91f0421
579 changed files with 31190 additions and 12809 deletions

View File

@ -37,16 +37,16 @@ to the root 64-bit JRE directory.
2) Get Sleuth Kit Setup 2) Get Sleuth Kit Setup
2a) Download and build a Release version of Sleuth Kit (TSK) 4.0. See 2a) Download and build a Release version of Sleuth Kit (TSK) 4.0. See
win32\BUILDING.txt in the TSK package for more information. You need to win32\BUILDING.txt in the TSK package for more information. You need to
build the tsk_jni project. Select the Release_PostgreSQL Win32 or x64 target, build the tsk_jni project. Select the Release Win32 or x64 target,
depending upon your target build. You can use a released version or download depending upon your target build. You can use a released version or download
the latest from github: the latest from github:
- git://github.com/sleuthkit/sleuthkit.git - git://github.com/sleuthkit/sleuthkit.git
2b) Build the TSK JAR file by typing 'ant dist-PostgreSQL' in 2b) Build the TSK JAR file by typing 'ant dist' in
bindings/java in the bindings/java in the
TSK source code folder from a command line. Note it is case TSK source code folder from a command line. Note it is case
sensitive. You can also add the code to a NetBeans project and build sensitive. You can also add the code to a NetBeans project and build
it from there, selecting the dist-PostgreSQL target. it from there, selecting the dist target.
2c) Set TSK_HOME environment variable to the root directory of TSK 2c) Set TSK_HOME environment variable to the root directory of TSK
@ -54,6 +54,9 @@ to the root 64-bit JRE directory.
from the TSK root directory to install the libraries and such in from the TSK root directory to install the libraries and such in
the needed places (i.e. '/usr/local'). the needed places (i.e. '/usr/local').
2e) Build the TSK CaseUco jar file by running 'ant' in
the case-uco/java folder of the TSK source folder. You can also add the
code to a NetBeans project and build using the regular 'build' action.
3) For Windows builds, GStreamer must be setup. GStreamer is used to view video 3) For Windows builds, GStreamer must be setup. GStreamer is used to view video
files. You can either download it and install it, or you can copy it from the files. You can either download it and install it, or you can copy it from the
@ -100,7 +103,7 @@ the build process.
- The Sleuth Kit Java datamodel JAR file has native JNI libraries - The Sleuth Kit Java datamodel JAR file has native JNI libraries
that are copied into it. These JNI libraries have dependencies on that are copied into it. These JNI libraries have dependencies on
libewf, zlib, libpq, libintl-8, libeay32, and ssleay32 DLL files. On non-Windows libewf, zlib, libintl-8, libeay32, and ssleay32 DLL files. On non-Windows
platforms, the JNI library also has a dependency on libtsk (on Windows, platforms, the JNI library also has a dependency on libtsk (on Windows,
it is compiled into libtsk_jni). it is compiled into libtsk_jni).

View File

@ -54,6 +54,16 @@
<fileset dir="${thirdparty.dir}/7-Zip"/> <fileset dir="${thirdparty.dir}/7-Zip"/>
</copy> </copy>
<!--Copy InterestingFileSetRules to release-->
<copy todir="${basedir}/release/InterestingFileSetRules" >
<fileset dir="${thirdparty.dir}/InterestingFileSetRules"/>
</copy>
<!--Copy OfficialHashSets to release-->
<copy todir="${basedir}/release/OfficialHashSets" >
<fileset dir="${thirdparty.dir}/OfficialHashSets"/>
</copy>
<!-- The 'libgstlibav.dll' file is too big to store on GitHub, so we <!-- The 'libgstlibav.dll' file is too big to store on GitHub, so we
have it stored in a ZIP file. We'll extract it in place and remove have it stored in a ZIP file. We'll extract it in place and remove
the ZIP file afterward. --> the ZIP file afterward. -->
@ -106,6 +116,8 @@
tofile="${ext.dir}/c3p0-0.9.5.jar"/> tofile="${ext.dir}/c3p0-0.9.5.jar"/>
<copy file="${env.TSK_HOME}/bindings/java/lib/SparseBitSet-1.1.jar" <copy file="${env.TSK_HOME}/bindings/java/lib/SparseBitSet-1.1.jar"
tofile="${ext.dir}/SparseBitSet-1.1.jar"/> tofile="${ext.dir}/SparseBitSet-1.1.jar"/>
<copy file="${env.TSK_HOME}/case-uco/java/dist/sleuthkit-caseuco-${TSK_VERSION}.jar"
tofile="${ext.dir}/sleuthkit-caseuco-${TSK_VERSION}.jar"/>
</target> </target>
<target name="download-binlist"> <target name="download-binlist">

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.core/10 OpenIDE-Module: org.sleuthkit.autopsy.core/10
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml
OpenIDE-Module-Implementation-Version: 31 OpenIDE-Module-Implementation-Version: 32
OpenIDE-Module-Requires: org.openide.windows.WindowManager OpenIDE-Module-Requires: org.openide.windows.WindowManager
AutoUpdate-Show-In-Client: true AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: true AutoUpdate-Essential-Module: true

View File

@ -83,6 +83,7 @@ file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar
file.reference.sis-metadata-0.8.jar=release\\modules\\ext\\sis-metadata-0.8.jar file.reference.sis-metadata-0.8.jar=release\\modules\\ext\\sis-metadata-0.8.jar
file.reference.sis-netcdf-0.8.jar=release\\modules\\ext\\sis-netcdf-0.8.jar file.reference.sis-netcdf-0.8.jar=release\\modules\\ext\\sis-netcdf-0.8.jar
file.reference.sis-utility-0.8.jar=release\\modules\\ext\\sis-utility-0.8.jar file.reference.sis-utility-0.8.jar=release\\modules\\ext\\sis-utility-0.8.jar
file.reference.sleuthkit-caseuco-4.10.0.jar=release/modules/ext/sleuthkit-caseuco-4.10.0.jar
file.reference.slf4j-api-1.7.25.jar=release\\modules\\ext\\slf4j-api-1.7.25.jar file.reference.slf4j-api-1.7.25.jar=release\\modules\\ext\\slf4j-api-1.7.25.jar
file.reference.sqlite-jdbc-3.25.2.jar=release/modules/ext/sqlite-jdbc-3.25.2.jar file.reference.sqlite-jdbc-3.25.2.jar=release/modules/ext/sqlite-jdbc-3.25.2.jar
file.reference.StixLib.jar=release/modules/ext/StixLib.jar file.reference.StixLib.jar=release/modules/ext/StixLib.jar
@ -90,7 +91,7 @@ file.reference.javax.ws.rs-api-2.0.1.jar=release/modules/ext/javax.ws.rs-api-2.0
file.reference.cxf-core-3.0.16.jar=release/modules/ext/cxf-core-3.0.16.jar file.reference.cxf-core-3.0.16.jar=release/modules/ext/cxf-core-3.0.16.jar
file.reference.cxf-rt-frontend-jaxrs-3.0.16.jar=release/modules/ext/cxf-rt-frontend-jaxrs-3.0.16.jar file.reference.cxf-rt-frontend-jaxrs-3.0.16.jar=release/modules/ext/cxf-rt-frontend-jaxrs-3.0.16.jar
file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-transports-http-3.0.16.jar file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-transports-http-3.0.16.jar
file.reference.sleuthkit-4.9.0.jar=release/modules/ext/sleuthkit-4.9.0.jar file.reference.sleuthkit-4.10.0.jar=release/modules/ext/sleuthkit-4.10.0.jar
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar
file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar
@ -138,5 +139,5 @@ nbm.homepage=http://www.sleuthkit.org/
nbm.module.author=Brian Carrier nbm.module.author=Brian Carrier
nbm.needs.restart=true nbm.needs.restart=true
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
spec.version.base=10.19 spec.version.base=10.20

View File

@ -472,8 +472,8 @@
<binary-origin>release/modules/ext/commons-pool2-2.4.2.jar</binary-origin> <binary-origin>release/modules/ext/commons-pool2-2.4.2.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/sleuthkit-4.9.0.jar</runtime-relative-path> <runtime-relative-path>ext/sleuthkit-4.10.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-4.9.0.jar</binary-origin> <binary-origin>release/modules/ext/sleuthkit-4.10.0.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/jxmapviewer2-2.4.jar</runtime-relative-path> <runtime-relative-path>ext/jxmapviewer2-2.4.jar</runtime-relative-path>
@ -779,6 +779,10 @@
<runtime-relative-path>ext/curator-client-2.8.0.jar</runtime-relative-path> <runtime-relative-path>ext/curator-client-2.8.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/curator-client-2.8.0.jar</binary-origin> <binary-origin>release/modules/ext/curator-client-2.8.0.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/sleuthkit-caseuco-4.10.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-caseuco-4.10.0.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/fontbox-2.0.13.jar</runtime-relative-path> <runtime-relative-path>ext/fontbox-2.0.13.jar</runtime-relative-path>
<binary-origin>release\modules\ext\fontbox-2.0.13.jar</binary-origin> <binary-origin>release\modules\ext\fontbox-2.0.13.jar</binary-origin>

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2019 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -139,7 +139,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
if (!tagNamesMap.isEmpty()) { if (!tagNamesMap.isEmpty()) {
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) { for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
TagName tagName = entry.getValue(); TagName tagName = entry.getValue();
TagSet tagSet = tagName.getTagSet(); TagSet tagSet = tagsManager.getTagSet(tagName);
// Show custom tags before predefined tags in the menu // Show custom tags before predefined tags in the menu
if (tagSet != null) { if (tagSet != null) {

View File

@ -29,6 +29,9 @@ import org.openide.util.Utilities;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -72,6 +75,12 @@ public class DeleteContentTagAction extends AbstractAction {
new Thread(() -> { new Thread(() -> {
for (ContentTag tag : selectedTags) { for (ContentTag tag : selectedTags) {
try { try {
// Check if there is an image tag before deleting the content tag.
ContentViewerTag<ImageTagRegion> imageTag = ContentViewerTagManager.getTag(tag, ImageTagRegion.class);
if(imageTag != null) {
ContentViewerTagManager.deleteTag(imageTag);
}
Case.getCurrentCaseThrows().getServices().getTagsManager().deleteContentTag(tag); Case.getCurrentCaseThrows().getServices().getTagsManager().deleteContentTag(tag);
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(DeleteContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS Logger.getLogger(DeleteContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS

View File

@ -39,6 +39,9 @@ import org.openide.util.actions.Presenter;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.tags.TagUtils; import org.sleuthkit.autopsy.tags.TagUtils;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
@ -123,6 +126,13 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen
try { try {
logger.log(Level.INFO, "Removing tag {0} from {1}", new Object[]{tagName.getDisplayName(), contentTag.getContent().getName()}); //NON-NLS logger.log(Level.INFO, "Removing tag {0} from {1}", new Object[]{tagName.getDisplayName(), contentTag.getContent().getName()}); //NON-NLS
// Check if there is an image tag before deleting the content tag.
ContentViewerTag<ImageTagRegion> imageTag = ContentViewerTagManager.getTag(contentTag, ImageTagRegion.class);
if(imageTag != null) {
ContentViewerTagManager.deleteTag(imageTag);
}
tagsManager.deleteContentTag(contentTag); tagsManager.deleteContentTag(contentTag);
} catch (TskCoreException tskCoreException) { } catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "Error untagging file", tskCoreException); //NON-NLS logger.log(Level.SEVERE, "Error untagging file", tskCoreException); //NON-NLS

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -23,7 +23,6 @@ import java.awt.Window;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -36,7 +35,6 @@ import javax.swing.JComponent;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -161,7 +159,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
tagNamesMap.entrySet().stream().map((entry) -> entry.getValue()).forEachOrdered((tagName) -> { tagNamesMap.entrySet().stream().map((entry) -> entry.getValue()).forEachOrdered((tagName) -> {
TagSet tagSet = null; TagSet tagSet = null;
try { try {
tagSet = tagName.getTagSet(); tagSet = tagsManager.getTagSet(tagName);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(GetTagNameAndCommentDialog.class Logger.getLogger(GetTagNameAndCommentDialog.class
.getName()).log(Level.SEVERE, "Failed to get tag set", ex); //NON-NLS .getName()).log(Level.SEVERE, "Failed to get tag set", ex); //NON-NLS

View File

@ -29,6 +29,9 @@ import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TagName;
@ -83,9 +86,19 @@ public final class ReplaceContentTagAction extends ReplaceTagAction<ContentTag>
try { try {
logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldTag.getName().getDisplayName(), newTagName.getDisplayName(), oldTag.getContent().getName()}); //NON-NLS logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldTag.getName().getDisplayName(), newTagName.getDisplayName(), oldTag.getContent().getName()}); //NON-NLS
tagsManager.deleteContentTag(oldTag); // Check if there is an image tag before deleting the content tag.
tagsManager.addContentTag(oldTag.getContent(), newTagName, newComment); ContentViewerTag<ImageTagRegion> imageTag = ContentViewerTagManager.getTag(oldTag, ImageTagRegion.class);
if(imageTag != null) {
ContentViewerTagManager.deleteTag(imageTag);
}
tagsManager.deleteContentTag(oldTag);
ContentTag newTag = tagsManager.addContentTag(oldTag.getContent(), newTagName, newComment);
// Resave the image tag if present.
if(imageTag != null) {
ContentViewerTagManager.saveTag(newTag, imageTag.getDetails());
}
} catch (TskCoreException tskCoreException) { } catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS
Platform.runLater(() Platform.runLater(()

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -131,7 +131,7 @@ abstract class ReplaceTagAction<T extends Tag> extends AbstractAction implements
if (!tagNamesMap.isEmpty()) { if (!tagNamesMap.isEmpty()) {
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) { for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
TagName tagName = entry.getValue(); TagName tagName = entry.getValue();
TagSet tagSet = tagName.getTagSet(); TagSet tagSet = tagsManager.getTagSet(tagName);
// Show custom tags before predefined tags in the menu // Show custom tags before predefined tags in the menu
if (tagSet != null) { if (tagSet != null) {

View File

@ -30,6 +30,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imagewriter.ImageWriterService; import org.sleuthkit.autopsy.imagewriter.ImageWriterService;
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
import org.sleuthkit.datamodel.AddDataSourceCallbacks;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.SleuthkitJNI;
@ -42,17 +43,10 @@ import org.sleuthkit.datamodel.TskDataException;
class AddImageTask implements Runnable { class AddImageTask implements Runnable {
private final Logger logger = Logger.getLogger(AddImageTask.class.getName()); private final Logger logger = Logger.getLogger(AddImageTask.class.getName());
private final String deviceId; private final ImageDetails imageDetails;
private final String imagePath;
private final int sectorSize;
private final String timeZone;
private final ImageWriterSettings imageWriterSettings;
private final boolean ignoreFatOrphanFiles;
private final String md5;
private final String sha1;
private final String sha256;
private final DataSourceProcessorProgressMonitor progressMonitor; private final DataSourceProcessorProgressMonitor progressMonitor;
private final DataSourceProcessorCallback callback; private final AddDataSourceCallbacks addDataSourceCallbacks;
private final AddImageTaskCallback addImageTaskCallback;
private boolean criticalErrorOccurred; private boolean criticalErrorOccurred;
/* /*
@ -74,39 +68,17 @@ class AddImageTask implements Runnable {
/** /**
* Constructs a runnable task that adds an image to the case database. * Constructs a runnable task that adds an image to the case database.
* *
* @param deviceId An ASCII-printable identifier for the device * @param imageDetails Holds all data about the image.
* associated with the data source that is
* intended to be unique across multiple cases
* (e.g., a UUID).
* @param imagePath Path to the image file.
* @param sectorSize The sector size (use '0' for autodetect).
* @param timeZone The time zone to use when processing dates
* and times for the image, obtained from
* java.util.TimeZone.getID.
* @param ignoreFatOrphanFiles Whether to parse orphans if the image has a
* FAT filesystem.
* @param md5 The MD5 hash of the image, may be null.
* @param sha1 The SHA-1 hash of the image, may be null.
* @param sha256 The SHA-256 hash of the image, may be null.
* @param imageWriterPath Path that a copy of the image should be
* written to. Use empty string to disable image
* writing
* @param progressMonitor Progress monitor to report progress during * @param progressMonitor Progress monitor to report progress during
* processing. * processing.
* @param callback Callback to call when processing is done. * @param addDataSourceCallbacks Callback for sending data to the ingest pipeline if an ingest stream is being used.
* @param addImageTaskCallback Callback for dealing with add image task completion.
*/ */
AddImageTask(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, ImageWriterSettings imageWriterSettings, AddImageTask(ImageDetails imageDetails, DataSourceProcessorProgressMonitor progressMonitor, AddDataSourceCallbacks addDataSourceCallbacks,
DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { AddImageTaskCallback addImageTaskCallback) {
this.deviceId = deviceId; this.imageDetails = imageDetails;
this.imagePath = imagePath; this.addDataSourceCallbacks = addDataSourceCallbacks;
this.sectorSize = sectorSize; this.addImageTaskCallback = addImageTaskCallback;
this.timeZone = timeZone;
this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
this.md5 = md5;
this.sha1 = sha1;
this.sha256 = sha256;
this.imageWriterSettings = imageWriterSettings;
this.callback = callback;
this.progressMonitor = progressMonitor; this.progressMonitor = progressMonitor;
tskAddImageProcessLock = new Object(); tskAddImageProcessLock = new Object();
} }
@ -120,21 +92,21 @@ class AddImageTask implements Runnable {
try { try {
currentCase = Case.getCurrentCaseThrows(); currentCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, String.format("Failed to add image data source at %s, no current case", imagePath), ex); logger.log(Level.SEVERE, String.format("Failed to start AddImageTask for %s, no current case", imageDetails.getImagePath()), ex);
return; return;
} }
progressMonitor.setIndeterminate(true); progressMonitor.setIndeterminate(true);
progressMonitor.setProgress(0); progressMonitor.setProgress(0);
String imageWriterPath = ""; String imageWriterPath = "";
if (imageWriterSettings != null) { if (imageDetails.imageWriterSettings != null) {
imageWriterPath = imageWriterSettings.getPath(); imageWriterPath = imageDetails.imageWriterSettings.getPath();
} }
List<String> errorMessages = new ArrayList<>(); List<String> errorMessages = new ArrayList<>();
List<Content> newDataSources = new ArrayList<>(); List<Content> newDataSources = new ArrayList<>();
try { try {
synchronized (tskAddImageProcessLock) { synchronized (tskAddImageProcessLock) {
if (!tskAddImageProcessStopped) { if (!tskAddImageProcessStopped) {
tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles, imageWriterPath); tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(imageDetails.timeZone, true, imageDetails.ignoreFatOrphanFiles, imageWriterPath);
} else { } else {
return; return;
} }
@ -143,7 +115,7 @@ class AddImageTask implements Runnable {
progressUpdateThread.start(); progressUpdateThread.start();
runAddImageProcess(errorMessages); runAddImageProcess(errorMessages);
progressUpdateThread.interrupt(); progressUpdateThread.interrupt();
commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources); finishAddImageProcess(errorMessages, newDataSources);
progressMonitor.setProgress(100); progressMonitor.setProgress(100);
} finally { } finally {
DataSourceProcessorCallback.DataSourceProcessorResult result; DataSourceProcessorCallback.DataSourceProcessorResult result;
@ -154,7 +126,7 @@ class AddImageTask implements Runnable {
} else { } else {
result = DataSourceProcessorResult.NO_ERRORS; result = DataSourceProcessorResult.NO_ERRORS;
} }
callback.done(result, errorMessages, newDataSources); addImageTaskCallback.onCompleted(result, errorMessages, newDataSources);
} }
} }
@ -177,7 +149,7 @@ class AddImageTask implements Runnable {
tskAddImageProcess.stop(); tskAddImageProcess.stop();
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error cancelling adding image %s to the case database", imagePath), ex); //NON-NLS logger.log(Level.SEVERE, String.format("Error cancelling adding image %s to the case database", imageDetails.getImagePath()), ex); //NON-NLS
} }
} }
} }
@ -191,23 +163,22 @@ class AddImageTask implements Runnable {
*/ */
private void runAddImageProcess(List<String> errorMessages) { private void runAddImageProcess(List<String> errorMessages) {
try { try {
tskAddImageProcess.run(deviceId, new String[]{imagePath}, sectorSize); tskAddImageProcess.run(imageDetails.deviceId, imageDetails.image, imageDetails.sectorSize, this.addDataSourceCallbacks);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Critical error occurred adding image %s", imagePath), ex); //NON-NLS logger.log(Level.SEVERE, String.format("Critical error occurred adding image %s", imageDetails.getImagePath()), ex); //NON-NLS
criticalErrorOccurred = true; criticalErrorOccurred = true;
errorMessages.add(ex.getMessage()); errorMessages.add(ex.getMessage());
} catch (TskDataException ex) { } catch (TskDataException ex) {
logger.log(Level.WARNING, String.format("Non-critical error occurred adding image %s", imagePath), ex); //NON-NLS logger.log(Level.WARNING, String.format("Non-critical error occurred adding image %s", imageDetails.getImagePath()), ex); //NON-NLS
errorMessages.add(ex.getMessage()); errorMessages.add(ex.getMessage());
} }
} }
/** /**
* Commits or reverts the results of the TSK add image process. If the * Handle the results of the TSK add image process.
* process was stopped before it completed or there was a critical error the * The image will be in the database even if a critical error occurred or
* results are reverted, otherwise they are committed. * the user canceled.
* *
* @param currentCase The current case.
* @param errorMessages Error messages, if any, are added to this list for * @param errorMessages Error messages, if any, are added to this list for
* eventual return via the callback. * eventual return via the callback.
* @param newDataSources If the new image is successfully committed, it is * @param newDataSources If the new image is successfully committed, it is
@ -216,32 +187,26 @@ class AddImageTask implements Runnable {
* *
* @return * @return
*/ */
private void commitOrRevertAddImageProcess(Case currentCase, List<String> errorMessages, List<Content> newDataSources) { private void finishAddImageProcess(List<String> errorMessages, List<Content> newDataSources) {
synchronized (tskAddImageProcessLock) { synchronized (tskAddImageProcessLock) {
if (tskAddImageProcessStopped || criticalErrorOccurred) { Image newImage = imageDetails.image;
try {
tskAddImageProcess.revert();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error reverting after adding image %s to the case database", imagePath), ex); //NON-NLS
errorMessages.add(ex.getMessage());
criticalErrorOccurred = true;
}
} else {
try {
long imageId = tskAddImageProcess.commit();
if (imageId != 0) {
Image newImage = currentCase.getSleuthkitCase().getImageById(imageId);
String verificationError = newImage.verifyImageSize(); String verificationError = newImage.verifyImageSize();
if (!verificationError.isEmpty()) { if (!verificationError.isEmpty()) {
errorMessages.add(verificationError); errorMessages.add(verificationError);
} }
if (imageWriterSettings != null) { if (imageDetails.imageWriterSettings != null) {
ImageWriterService.createImageWriter(imageId, imageWriterSettings); ImageWriterService.createImageWriter(newImage.getId(), imageDetails.imageWriterSettings);
} }
newDataSources.add(newImage); newDataSources.add(newImage);
if (!StringUtils.isBlank(md5)) {
// If the add image process was cancelled don't do any further processing here
if (tskAddImageProcessStopped) {
return;
}
if (!StringUtils.isBlank(imageDetails.md5)) {
try { try {
newImage.setMD5(md5); newImage.setMD5(imageDetails.md5);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to add MD5 hash for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex); logger.log(Level.SEVERE, String.format("Failed to add MD5 hash for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
errorMessages.add(ex.getMessage()); errorMessages.add(ex.getMessage());
@ -254,9 +219,9 @@ class AddImageTask implements Runnable {
*/ */
} }
} }
if (!StringUtils.isBlank(sha1)) { if (!StringUtils.isBlank(imageDetails.sha1)) {
try { try {
newImage.setSha1(sha1); newImage.setSha1(imageDetails.sha1);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to add SHA1 hash for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex); logger.log(Level.SEVERE, String.format("Failed to add SHA1 hash for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
errorMessages.add(ex.getMessage()); errorMessages.add(ex.getMessage());
@ -269,9 +234,9 @@ class AddImageTask implements Runnable {
*/ */
} }
} }
if (!StringUtils.isBlank(sha256)) { if (!StringUtils.isBlank(imageDetails.sha256)) {
try { try {
newImage.setSha256(sha256); newImage.setSha256(imageDetails.sha256);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to add SHA256 for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex); logger.log(Level.SEVERE, String.format("Failed to add SHA256 for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
errorMessages.add(ex.getMessage()); errorMessages.add(ex.getMessage());
@ -284,18 +249,6 @@ class AddImageTask implements Runnable {
*/ */
} }
} }
} else {
String errorMessage = String.format("Error commiting after adding image %s to the case database, no object id returned", imagePath); //NON-NLS
logger.log(Level.SEVERE, errorMessage);
errorMessages.add(errorMessage);
criticalErrorOccurred = true;
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error committing adding image %s to the case database", imagePath), ex); //NON-NLS
errorMessages.add(ex.getMessage());
criticalErrorOccurred = true;
}
}
} }
} }
@ -352,4 +305,37 @@ class AddImageTask implements Runnable {
} }
} }
/**
* Utility class to hold image data.
*/
static class ImageDetails {
String deviceId;
Image image;
int sectorSize;
String timeZone;
boolean ignoreFatOrphanFiles;
String md5;
String sha1;
String sha256;
ImageWriterSettings imageWriterSettings;
ImageDetails(String deviceId, Image image, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, ImageWriterSettings imageWriterSettings) {
this.deviceId = deviceId;
this.image = image;
this.sectorSize = sectorSize;
this.timeZone = timeZone;
this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
this.md5 = md5;
this.sha1 = sha1;
this.sha256 = sha256;
this.imageWriterSettings = imageWriterSettings;
}
String getImagePath() {
if (image.getPaths().length > 0) {
return image.getPaths()[0];
}
return "Unknown data source path";
}
}
} }

View File

@ -0,0 +1,38 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
import java.util.List;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult;
import org.sleuthkit.datamodel.Content;
/**
* Called on completion of the add image task.
*/
interface AddImageTaskCallback {
/**
* Called when the add image task is completed.
*
* @param result The result from the data source processor.
* @param errList The list of errors.
* @param newDataSources The list of new data sources.
*/
void onCompleted(DataSourceProcessorResult result, List<String> errList, List<Content> newDataSources);
}

View File

@ -302,7 +302,9 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
private void startIngest() { private void startIngest() {
if (!newContents.isEmpty() && readyToIngest && !ingested) { if (!newContents.isEmpty() && readyToIngest && !ingested) {
ingested = true; ingested = true;
if (dsProcessor != null && ! dsProcessor.supportsIngestStream()) {
IngestManager.getInstance().queueIngestJob(newContents, ingestJobSettings); IngestManager.getInstance().queueIngestJob(newContents, ingestJobSettings);
}
setStateFinished(); setStateFinished();
} }
} }
@ -361,9 +363,13 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
setStateStarted(); setStateStarted();
// Kick off the DSProcessor // Kick off the DSProcessor
if (dsProcessor.supportsIngestStream()) {
dsProcessor.runWithIngestStream(ingestJobSettings, getDSPProgressMonitorImpl(), cbObj);
} else {
dsProcessor.run(getDSPProgressMonitorImpl(), cbObj); dsProcessor.run(getDSPProgressMonitorImpl(), cbObj);
} }
} }
}
/* /*
* Cancels the data source processing - in case the users presses 'Cancel' * Cancels the data source processing - in case the users presses 'Cancel'

View File

@ -41,6 +41,7 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescript
private final AddImageAction action; private final AddImageAction action;
private int progressPanelIndex; private int progressPanelIndex;
private int dsPanelIndex; private int dsPanelIndex;
private int ingestPanelIndex;
private final static String PROP_LASTPROFILE_NAME = "AIW_LASTPROFILE_NAME"; //NON-NLS private final static String PROP_LASTPROFILE_NAME = "AIW_LASTPROFILE_NAME"; //NON-NLS
AddImageWizardIterator(AddImageAction action) { AddImageWizardIterator(AddImageAction action) {
@ -69,6 +70,7 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescript
panels.add(progressPanel); panels.add(progressPanel);
progressPanelIndex = panels.indexOf(progressPanel); //Doing programatically because number of panels is variable progressPanelIndex = panels.indexOf(progressPanel); //Doing programatically because number of panels is variable
dsPanelIndex = panels.indexOf(dsPanel); dsPanelIndex = panels.indexOf(dsPanel);
ingestPanelIndex = panels.indexOf(ingestConfigPanel);
String[] steps = new String[panels.size()]; String[] steps = new String[panels.size()];
for (int i = 0; i < panels.size(); i++) { for (int i = 0; i < panels.size(); i++) {
Component c = panels.get(i).getComponent(); Component c = panels.get(i).getComponent();
@ -177,7 +179,7 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescript
// Start processing the data source by handing it off to the selected DSP, // Start processing the data source by handing it off to the selected DSP,
// so it gets going in the background while the user is still picking the Ingest modules // so it gets going in the background while the user is still picking the Ingest modules
// This will occur when the next button is clicked on the panel where you have chosen your data to process // This will occur when the next button is clicked on the panel where you have chosen your data to process
if (index == dsPanelIndex) { if (index == ingestPanelIndex) {
((AddImageWizardAddingProgressPanel) panels.get(progressPanelIndex)). ((AddImageWizardAddingProgressPanel) panels.get(progressPanelIndex)).
startDataSourceProcessing(((AddImageWizardDataSourceSettingsPanel) panels.get(dsPanelIndex)).getComponent().getCurrentDSProcessor()); startDataSourceProcessing(((AddImageWizardDataSourceSettingsPanel) panels.get(dsPanelIndex)).getComponent().getCurrentDSProcessor());
} }

View File

@ -107,7 +107,7 @@ import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.events.AutopsyEventException; import org.sleuthkit.autopsy.events.AutopsyEventException;
import org.sleuthkit.autopsy.events.AutopsyEventPublisher; import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
import org.sleuthkit.autopsy.filequery.OpenFileDiscoveryAction; import org.sleuthkit.autopsy.discovery.OpenDiscoveryAction;
import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.IngestServices;
@ -1124,7 +1124,7 @@ public class Case {
CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true); CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true); CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);
CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false); CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
CallableSystemAction.get(OpenFileDiscoveryAction.class).setEnabled(true); CallableSystemAction.get(OpenDiscoveryAction.class).setEnabled(true);
/* /*
* Add the case to the recent cases tracker that supplies a list * Add the case to the recent cases tracker that supplies a list
@ -1179,7 +1179,7 @@ public class Case {
CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false); CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false); CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false); CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false);
CallableSystemAction.get(OpenFileDiscoveryAction.class).setEnabled(false); CallableSystemAction.get(OpenDiscoveryAction.class).setEnabled(false);
/* /*
* Clear the notifications in the notfier component in the lower * Clear the notifications in the notfier component in the lower

View File

@ -0,0 +1,59 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
import java.util.List;
import org.sleuthkit.autopsy.ingest.IngestStream;
import org.sleuthkit.autopsy.ingest.IngestStreamClosedException;
/**
* This is a default ingest stream to use with the data source processors when
* an IngestStream is not supplied. Adding files/data sources are no-ops.
*/
class DefaultIngestStream implements IngestStream {
private boolean isClosed = false;
private boolean isStopped = false;
@Override
public void addFiles(List<Long> fileObjectIds) throws IngestStreamClosedException {
// Do nothing
}
@Override
public synchronized boolean isClosed() {
return isClosed;
}
@Override
public synchronized void close() {
isClosed = true;
}
@Override
public synchronized void stop() {
isClosed = true;
isStopped = true;
}
@Override
public synchronized boolean wasStopped() {
return isStopped;
}
}

View File

@ -24,6 +24,7 @@ import javax.swing.JPanel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import java.util.UUID; import java.util.UUID;
import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileFilter;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -33,7 +34,14 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.DataSourceUtils; import org.sleuthkit.autopsy.coreutils.DataSourceUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestStream;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitJNI;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A image file data source processor that implements the DataSourceProcessor * A image file data source processor that implements the DataSourceProcessor
@ -49,6 +57,7 @@ import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSourceProcessor { public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSourceProcessor {
private final static String DATA_SOURCE_TYPE = NbBundle.getMessage(ImageDSProcessor.class, "ImageDSProcessor.dsType.text"); private final static String DATA_SOURCE_TYPE = NbBundle.getMessage(ImageDSProcessor.class, "ImageDSProcessor.dsType.text");
private final Logger logger = Logger.getLogger(ImageDSProcessor.class.getName());
private static final List<String> allExt = new ArrayList<>(); private static final List<String> allExt = new ArrayList<>();
private static final GeneralFilter rawFilter = new GeneralFilter(GeneralFilter.RAW_IMAGE_EXTS, GeneralFilter.RAW_IMAGE_DESC); private static final GeneralFilter rawFilter = new GeneralFilter(GeneralFilter.RAW_IMAGE_EXTS, GeneralFilter.RAW_IMAGE_DESC);
private static final GeneralFilter encaseFilter = new GeneralFilter(GeneralFilter.ENCASE_IMAGE_EXTS, GeneralFilter.ENCASE_IMAGE_DESC); private static final GeneralFilter encaseFilter = new GeneralFilter(GeneralFilter.ENCASE_IMAGE_EXTS, GeneralFilter.ENCASE_IMAGE_DESC);
@ -58,6 +67,8 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
private static final List<FileFilter> filtersList = new ArrayList<>(); private static final List<FileFilter> filtersList = new ArrayList<>();
private final ImageFilePanel configPanel; private final ImageFilePanel configPanel;
private AddImageTask addImageTask; private AddImageTask addImageTask;
private IngestStream ingestStream = null;
private Image image = null;
/* /*
* TODO: Remove the setDataSourceOptionsCalled flag and the settings fields * TODO: Remove the setDataSourceOptionsCalled flag and the settings fields
* when the deprecated method setDataSourceOptions is removed. * when the deprecated method setDataSourceOptions is removed.
@ -170,6 +181,77 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
*/ */
@Override @Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
ingestStream = new DefaultIngestStream();
readConfigSettings();
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progressMonitor, callback);
}
/**
* Adds a data source to the case database using a background task in a
* separate thread and the settings provided by the selection and
* configuration panel. Files found during ingest will be sent directly to the
* IngestStream provided. Returns as soon as the background task is started.
* The background task uses a callback object to signal task completion and
* return results.
*
* This method should not be called unless isPanelValid returns true, and
* should only be called for DSPs that support ingest streams.
*
* @param settings The ingest job settings.
* @param progress Progress monitor that will be used by the
* background task to report progress.
* @param callBack Callback that will be used by the background task
* to return results.
*/
@Override
public void runWithIngestStream(IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
DataSourceProcessorCallback callBack) {
// Read the settings from the wizard
readConfigSettings();
// Set up the data source before creating the ingest stream
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
// Now initialize the ingest stream
try {
ingestStream = IngestManager.getInstance().openIngestStream(image, settings);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error starting ingest modules", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progress, callBack);
}
/**
* Store the options from the config panel.
*/
private void readConfigSettings() {
if (!setDataSourceOptionsCalled) { if (!setDataSourceOptionsCalled) {
configPanel.storeSettings(); configPanel.storeSettings();
deviceId = UUID.randomUUID().toString(); deviceId = UUID.randomUUID().toString();
@ -190,7 +272,16 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
sha256 = null; sha256 = null;
} }
} }
run(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progressMonitor, callback); }
/**
* Check if this DSP supports ingest streams.
*
* @return True if this DSP supports an ingest stream, false otherwise.
*/
@Override
public boolean supportsIngestStream() {
return true;
} }
/** /**
@ -215,7 +306,19 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
* @param callback Callback to call when processing is done. * @param callback Callback to call when processing is done.
*/ */
public void run(String deviceId, String imagePath, String timeZone, boolean ignoreFatOrphanFiles, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { public void run(String deviceId, String imagePath, String timeZone, boolean ignoreFatOrphanFiles, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
run(deviceId, imagePath, 0, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callback); ingestStream = new DefaultIngestStream();
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
doAddImageProcess(deviceId, imagePath, 0, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callback);
} }
/** /**
@ -225,6 +328,10 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
* is started and uses the callback object to signal task completion and * is started and uses the callback object to signal task completion and
* return results. * return results.
* *
* The image should be loaded in the database and stored in "image"
* before calling this method. Additionally, an ingest stream should be initialized
* and stored in "ingestStream".
*
* @param deviceId An ASCII-printable identifier for the device * @param deviceId An ASCII-printable identifier for the device
* associated with the data source that is * associated with the data source that is
* intended to be unique across multiple cases * intended to be unique across multiple cases
@ -243,8 +350,31 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
* during processing. * during processing.
* @param callback Callback to call when processing is done. * @param callback Callback to call when processing is done.
*/ */
private void run(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { private void doAddImageProcess(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
addImageTask = new AddImageTask(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, progressMonitor, callback);
// If the data source or ingest stream haven't been initialized, stop processing
if (ingestStream == null) {
String message = "Ingest stream was not initialized before running the add image process on " + imagePath;
logger.log(Level.SEVERE, message);
final List<String> errors = new ArrayList<>();
errors.add(message);
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
if (image == null) {
String message = "Image was not added to database before running the add image process on " + imagePath;
logger.log(Level.SEVERE, message);
final List<String> errors = new ArrayList<>();
errors.add(message);
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
AddImageTask.ImageDetails imageDetails = new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null);
addImageTask = new AddImageTask(imageDetails,
progressMonitor,
new StreamingAddDataSourceCallbacks(ingestStream),
new StreamingAddImageTaskCallback(ingestStream, callback));
new Thread(addImageTask).start(); new Thread(addImageTask).start();
} }
@ -260,6 +390,9 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
if (null != addImageTask) { if (null != addImageTask) {
addImageTask.cancelTask(); addImageTask.cancelTask();
} }
if (ingestStream != null) {
ingestStream.stop();
}
} }
/** /**
@ -316,7 +449,20 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
this.timeZone = Calendar.getInstance().getTimeZone().getID(); this.timeZone = Calendar.getInstance().getTimeZone().getID();
this.ignoreFatOrphanFiles = false; this.ignoreFatOrphanFiles = false;
setDataSourceOptionsCalled = true; setDataSourceOptionsCalled = true;
run(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callBack);
ingestStream = new DefaultIngestStream();
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callBack);
} }
/** /**

View File

@ -31,7 +31,9 @@ import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.IngestJobInfo;
import org.sleuthkit.datamodel.IngestModuleInfo; import org.sleuthkit.datamodel.IngestModuleInfo;
@ -47,6 +49,8 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(IngestJobInfoPanel.class.getName()); private static final Logger logger = Logger.getLogger(IngestJobInfoPanel.class.getName());
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED); private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED);
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE);
private List<IngestJobInfo> ingestJobs; private List<IngestJobInfo> ingestJobs;
private final List<IngestJobInfo> ingestJobsForSelectedDataSource = new ArrayList<>(); private final List<IngestJobInfo> ingestJobsForSelectedDataSource = new ArrayList<>();
private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel(); private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel();
@ -79,6 +83,16 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
refresh(); refresh();
} }
}); });
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, (PropertyChangeEvent evt) -> {
if (!(evt instanceof AutopsyEvent) || (((AutopsyEvent) evt).getSourceType() != AutopsyEvent.SourceType.LOCAL)) {
return;
}
if (CURRENT_CASE == Case.Events.valueOf(evt.getPropertyName())) {
refresh();
}
});
} }
/** /**
@ -110,9 +124,15 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
*/ */
private void refresh() { private void refresh() {
try { try {
if (Case.isCaseOpen()) {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
this.ingestJobs = skCase.getIngestJobs(); this.ingestJobs = skCase.getIngestJobs();
setDataSource(selectedDataSource); setDataSource(selectedDataSource);
} else {
this.ingestJobs = new ArrayList<>();
setDataSource(null);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to load ingest jobs.", ex); logger.log(Level.SEVERE, "Failed to load ingest jobs.", ex);
JOptionPane.showMessageDialog(this, Bundle.IngestJobInfoPanel_loadIngestJob_error_text(), Bundle.IngestJobInfoPanel_loadIngestJob_error_title(), JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(this, Bundle.IngestJobInfoPanel_loadIngestJob_error_text(), Bundle.IngestJobInfoPanel_loadIngestJob_error_title(), JOptionPane.ERROR_MESSAGE);

View File

@ -18,15 +18,22 @@
*/ */
package org.sleuthkit.autopsy.casemodule; package org.sleuthkit.autopsy.casemodule;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.logging.Level;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitJNI;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A local drive data source processor that implements the DataSourceProcessor * A local drive data source processor that implements the DataSourceProcessor
@ -37,6 +44,7 @@ import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
@ServiceProvider(service = DataSourceProcessor.class) @ServiceProvider(service = DataSourceProcessor.class)
public class LocalDiskDSProcessor implements DataSourceProcessor { public class LocalDiskDSProcessor implements DataSourceProcessor {
private final Logger logger = Logger.getLogger(LocalDiskDSProcessor.class.getName());
private static final String DATA_SOURCE_TYPE = NbBundle.getMessage(LocalDiskDSProcessor.class, "LocalDiskDSProcessor.dsType.text"); private static final String DATA_SOURCE_TYPE = NbBundle.getMessage(LocalDiskDSProcessor.class, "LocalDiskDSProcessor.dsType.text");
private final LocalDiskPanel configPanel; private final LocalDiskPanel configPanel;
private AddImageTask addDiskTask; private AddImageTask addDiskTask;
@ -139,7 +147,25 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
imageWriterSettings = null; imageWriterSettings = null;
} }
} }
addDiskTask = new AddImageTask(deviceId, drivePath, sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, imageWriterSettings, progressMonitor, callback);
Image image;
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{drivePath}, sectorSize,
timeZone, null, null, null, deviceId);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding local disk with path " + drivePath + " to database", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
addDiskTask = new AddImageTask(
new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, imageWriterSettings),
progressMonitor,
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));
new Thread(addDiskTask).start(); new Thread(addDiskTask).start();
} }
@ -191,7 +217,23 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
* @param callback Callback to call when processing is done. * @param callback Callback to call when processing is done.
*/ */
private void run(String deviceId, String drivePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { private void run(String deviceId, String drivePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
addDiskTask = new AddImageTask(deviceId, drivePath, sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, imageWriterSettings, progressMonitor, callback); Image image;
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{drivePath}, sectorSize,
timeZone, null, null, null, deviceId);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding local disk with path " + drivePath + " to database", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
addDiskTask = new AddImageTask(new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, imageWriterSettings),
progressMonitor,
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));
new Thread(addDiskTask).start(); new Thread(addDiskTask).start();
} }

View File

@ -0,0 +1,66 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
import java.util.List;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestStream;
import org.sleuthkit.autopsy.ingest.IngestStreamClosedException;
import org.sleuthkit.datamodel.AddDataSourceCallbacks;
/**
* A set of callbacks to be called during the process of adding a data source to
* the case database. This implementation of the interface is suitable for
* streaming ingest use cases.
*/
class StreamingAddDataSourceCallbacks implements AddDataSourceCallbacks {
private final Logger logger = Logger.getLogger(StreamingAddDataSourceCallbacks.class.getName());
private final IngestStream ingestStream;
/**
* Constructs a set of callbacks to be called during the process of adding a
* data source to the case database. This implementation of the interface is
* suitable for streaming ingest use cases.
*
* @param stream The IngestStream to send data to
*/
StreamingAddDataSourceCallbacks(IngestStream stream) {
ingestStream = stream;
}
@Override
public void onFilesAdded(List<Long> fileObjectIds) {
if (ingestStream.wasStopped()) {
return;
}
try {
ingestStream.addFiles(fileObjectIds);
} catch (IngestStreamClosedException ex) {
if (!ingestStream.wasStopped()) {
// If the ingest stream is closed but not stopped log the error.
// This state should only happen once the data source is completely
// added which means it's a severe error that files are still being added.
logger.log(Level.SEVERE, "Error adding files to ingest stream - ingest stream is closed");
}
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
import java.util.List;
import org.sleuthkit.autopsy.ingest.IngestStream;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult;
import org.sleuthkit.datamodel.Content;
/**
* A callback to be called on completion of an add image task. This
* implementation of the interface is suitable for streaming ingest use cases.
* It closes the ingest stream and then calls the data source processor done
* callback.
*/
class StreamingAddImageTaskCallback implements AddImageTaskCallback {
private final IngestStream ingestStream;
private final DataSourceProcessorCallback dspCallback;
/**
* Constructs a callback to be called on completion of an add image task.
* This implementation of the interface is suitable for streaming ingest use
* cases. It closes the ingest stream and then calls the data source
* processor done callback.
*
* @param ingestStream The ingest stream that data is being sent to.
* @param dspCallback The callback for non-ingest stream related
* processing.
*/
StreamingAddImageTaskCallback(IngestStream ingestStream, DataSourceProcessorCallback dspCallback) {
this.ingestStream = ingestStream;
this.dspCallback = dspCallback;
}
/**
* Called when the add image task is completed.
*
* @param result The result from the data source processor.
* @param errList The list of errors.
* @param newDataSources The list of new data sources.
*/
@Override
public void onCompleted(DataSourceProcessorResult result, List<String> errList, List<Content> newDataSources) {
ingestStream.close();
dspCallback.done(result, errList, newDataSources);
}
}

View File

@ -1,4 +1,6 @@
CTL_DataSourceSummaryAction=Data Source Summary CTL_DataSourceSummaryAction=Data Source Summary
DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count
DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count

View File

@ -18,7 +18,8 @@
*/ */
package org.sleuthkit.autopsy.casemodule.datasourcesummary; package org.sleuthkit.autopsy.casemodule.datasourcesummary;
import java.util.ArrayList; import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
@ -43,6 +44,297 @@ final class DataSourceInfoUtilities {
private static final Logger logger = Logger.getLogger(DataSourceInfoUtilities.class.getName()); private static final Logger logger = Logger.getLogger(DataSourceInfoUtilities.class.getName());
/**
* Gets a count of files for a particular datasource where it is not a
* virtual directory and has a name.
*
* @param currentDataSource The datasource.
* @param additionalWhere Additional sql where clauses.
* @param onError The message to log on error.
*
* @return The count of files or null on error.
*/
private static Long getCountOfFiles(DataSource currentDataSource, String additionalWhere, String onError) {
if (currentDataSource != null) {
try {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
return skCase.countFilesWhere(
"dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
+ " AND name<>''"
+ " AND data_source_obj_id=" + currentDataSource.getId()
+ " AND " + additionalWhere);
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, onError, ex);
//unable to get count of files for the specified types cell will be displayed as empty
}
}
return null;
}
/**
* Get count of files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*/
static Long getCountOfFiles(DataSource currentDataSource) {
return getCountOfFiles(currentDataSource,
"type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType(),
"Unable to get count of files, providing empty results");
}
/**
* Get count of unallocated files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*/
static Long getCountOfUnallocatedFiles(DataSource currentDataSource) {
return getCountOfFiles(currentDataSource,
"type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
+ " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue(),
"Unable to get counts of unallocated files for datasource, providing empty results");
}
/**
* Get count of directories in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*/
static Long getCountOfDirectories(DataSource currentDataSource) {
return getCountOfFiles(currentDataSource,
"type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
+ " AND meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue(),
"Unable to get count of directories for datasource, providing empty results");
}
/**
* Get count of slack files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*/
static Long getCountOfSlackFiles(DataSource currentDataSource) {
return getCountOfFiles(currentDataSource,
"type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(),
"Unable to get count of slack files for datasources, providing empty results");
}
/**
* An interface for handling a result set and returning a value.
*/
private interface ResultSetHandler<T> {
T process(ResultSet resultset) throws SQLException;
}
/**
* Retrieves a result based on the provided query.
*
* @param query The query.
* @param processor The result set handler.
* @param errorMessage The error message to display if there is an error
* retrieving the resultset.
*
* @return The ResultSetHandler value or null if no ResultSet could be
* obtained.
*/
private static <T> T getBaseQueryResult(String query, ResultSetHandler<T> processor, String errorMessage) {
try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
try {
return processor.process(resultSet);
} catch (SQLException ex) {
logger.log(Level.WARNING, errorMessage, ex);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, errorMessage, ex);
}
return null;
}
/**
* Gets the size of unallocated files in a particular datasource.
*
* @param currentDataSource The data source.
*
* @return The size or null if the query could not be executed.
*/
static Long getSizeOfUnallocatedFiles(DataSource currentDataSource) {
if (currentDataSource == null) {
return null;
}
final String valueParam = "value";
final String countParam = "count";
String query = "SELECT SUM(size) AS " + valueParam + ", COUNT(*) AS " + countParam
+ " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
+ " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue()
+ " AND name<>''"
+ " AND data_source_obj_id=" + currentDataSource.getId();
ResultSetHandler<Long> handler = (resultSet) -> {
if (resultSet.next()) {
// ensure that there is an unallocated count result that is attached to this data source
long resultCount = resultSet.getLong(valueParam);
return (resultCount > 0) ? resultSet.getLong(valueParam) : null;
} else {
return null;
}
};
String errorMessage = "Unable to get size of unallocated files; returning null.";
return getBaseQueryResult(query, handler, errorMessage);
}
/**
* Retrieves counts for each artifact type in a data source.
*
* @param selectedDataSource The data source.
*
* @return A mapping of artifact type name to the counts or null if there
* was an error executing the query.
*/
static Map<String, Long> getCountsOfArtifactsByType(DataSource selectedDataSource) {
if (selectedDataSource == null) {
return Collections.emptyMap();
}
final String nameParam = "name";
final String valueParam = "value";
String query
= "SELECT bbt.display_name AS " + nameParam + ", COUNT(*) AS " + valueParam
+ " FROM blackboard_artifacts bba "
+ " INNER JOIN blackboard_artifact_types bbt ON bba.artifact_type_id = bbt.artifact_type_id"
+ " WHERE bba.data_source_obj_id =" + selectedDataSource.getId()
+ " GROUP BY bbt.display_name";
ResultSetHandler<Map<String, Long>> handler = (resultSet) -> {
Map<String, Long> toRet = new HashMap<>();
while (resultSet.next()) {
try {
toRet.put(resultSet.getString(nameParam), resultSet.getLong(valueParam));
} catch (SQLException ex) {
logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex);
}
}
return toRet;
};
String errorMessage = "Unable to get artifact type counts; returning null.";
return getBaseQueryResult(query, handler, errorMessage);
}
/**
* Generates a string which is a concatenation of the value received from
* the result set.
*
* @param query The query.
* @param valueParam The parameter for the value in the result set.
* @param separator The string separator used in concatenation.
* @param errorMessage The error message if the result set could not
* be received.
* @param singleErrorMessage The error message if a single result could not
* be obtained.
*
* @return The concatenated string or null if the query could not be
* executed.
*/
private static String getConcattedStringsResult(String query, String valueParam, String separator, String errorMessage, String singleErrorMessage) {
ResultSetHandler<String> handler = (resultSet) -> {
String toRet = "";
boolean first = true;
while (resultSet.next()) {
try {
if (first) {
first = false;
} else {
toRet += separator;
}
toRet += resultSet.getString(valueParam);
} catch (SQLException ex) {
logger.log(Level.WARNING, singleErrorMessage, ex);
}
}
return toRet;
};
return getBaseQueryResult(query, handler, errorMessage);
}
/**
* Generates a concatenated string value based on the text value of a
* particular attribute in a particular artifact for a specific data source.
*
* @param dataSourceId The data source id.
* @param artifactTypeId The artifact type id.
* @param attributeTypeId The attribute type id.
*
* @return The concatenated value or null if the query could not be
* executed.
*/
private static String getConcattedAttrValue(long dataSourceId, int artifactTypeId, int attributeTypeId) {
final String valueParam = "concatted_attribute_value";
String query = "SELECT attr.value_text AS " + valueParam
+ " FROM blackboard_artifacts bba "
+ " INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id "
+ " WHERE bba.data_source_obj_id = " + dataSourceId
+ " AND bba.artifact_type_id = " + artifactTypeId
+ " AND attr.attribute_type_id = " + attributeTypeId;
String errorMessage = "Unable to execute query to retrieve concatted attribute values.";
String singleErrorMessage = "There was an error retrieving one of the results. That result will be omitted from concatted value.";
String separator = ", ";
return getConcattedStringsResult(query, valueParam, separator, errorMessage, singleErrorMessage);
}
/**
* Retrieves the concatenation of operating system attributes for a
* particular data source.
*
* @param dataSource The data source.
*
* @return The concatenated value or null if the query could not be
* executed.
*/
static String getOperatingSystems(DataSource dataSource) {
if (dataSource == null) {
return null;
}
return getConcattedAttrValue(dataSource.getId(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID());
}
/**
* Retrieves the concatenation of data source usage for a particular data
* source.
*
* @param dataSource The data source.
*
* @return The concatenated value or null if the query could not be
* executed.
*/
static String getDataSourceType(DataSource dataSource) {
if (dataSource == null) {
return null;
}
return getConcattedAttrValue(dataSource.getId(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID());
}
/** /**
* Get a map containing the TSK_DATA_SOURCE_USAGE description attributes * Get a map containing the TSK_DATA_SOURCE_USAGE description attributes
* associated with each data source in the current case. * associated with each data source in the current case.
@ -147,39 +439,6 @@ final class DataSourceInfoUtilities {
} }
} }
/**
* Get a map containing the names of operating systems joined in a comma
* seperated list to the Data Source they exist on in the current case. No
* item will exist in the map for data sources which do not contain
* TS_OS_INFO artifacts which have a program name.
*
* @return Collection which maps datasource id to a String which is a comma
* seperated list of Operating system names found on the data
* source.
*/
static Map<Long, String> getOperatingSystems() {
Map<Long, String> osDetailMap = new HashMap<>();
try {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
ArrayList<BlackboardArtifact> osInfoArtifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO);
for (BlackboardArtifact osInfo : osInfoArtifacts) {
BlackboardAttribute programName = osInfo.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME));
if (programName != null) {
String currentOsString = osDetailMap.get(osInfo.getDataSource().getId());
if (currentOsString == null || currentOsString.isEmpty()) {
currentOsString = programName.getValueString();
} else {
currentOsString = currentOsString + ", " + programName.getValueString();
}
osDetailMap.put(osInfo.getDataSource().getId(), currentOsString);
}
}
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to load OS info artifacts.", ex);
}
return osDetailMap;
}
/** /**
* Get the number of files in the case database for the current data source * Get the number of files in the case database for the current data source
* which have the specified mimetypes. * which have the specified mimetypes.
@ -212,134 +471,6 @@ final class DataSourceInfoUtilities {
return null; return null;
} }
/**
* Get a map containing the number of unallocated files in each data source
* in the current case.
*
* @return Collection which maps datasource id to a count for the number of
* unallocated files in the datasource, will only contain entries
* for datasources which have at least 1 unallocated file
*/
static Map<Long, Long> getCountsOfUnallocatedFiles() {
try {
final String countUnallocatedFilesQuery = "data_source_obj_id, COUNT(*) AS value"
+ " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
+ " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue()
+ " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS
return getValuesMap(countUnallocatedFilesQuery);
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Unable to get counts of unallocated files for all datasources, providing empty results", ex);
return Collections.emptyMap();
}
}
/**
* Get a map containing the total size of unallocated files in each data
* source in the current case.
*
* @return Collection which maps datasource id to a total size in bytes of
* unallocated files in the datasource, will only contain entries
* for datasources which have at least 1 unallocated file
*/
static Map<Long, Long> getSizeOfUnallocatedFiles() {
try {
final String countUnallocatedFilesQuery = "data_source_obj_id, sum(size) AS value"
+ " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
+ " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue()
+ " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS
return getValuesMap(countUnallocatedFilesQuery);
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Unable to get size of unallocated files for all datasources, providing empty results", ex);
return Collections.emptyMap();
}
}
/**
* Get a map containing the number of directories in each data source in the
* current case.
*
* @return Collection which maps datasource id to a count for the number of
* directories in the datasource, will only contain entries for
* datasources which have at least 1 directory
*/
static Map<Long, Long> getCountsOfDirectories() {
try {
final String countDirectoriesQuery = "data_source_obj_id, COUNT(*) AS value"
+ " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
+ " AND meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()
+ " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS
return getValuesMap(countDirectoriesQuery);
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Unable to get counts of directories for all datasources, providing empty results", ex);
return Collections.emptyMap();
}
}
/**
* Get a map containing the number of slack files in each data source in the
* current case.
*
* @return Collection which maps datasource id to a count for the number of
* slack files in the datasource, will only contain entries for
* datasources which have at least 1 slack file
*/
static Map<Long, Long> getCountsOfSlackFiles() {
try {
final String countSlackFilesQuery = "data_source_obj_id, COUNT(*) AS value"
+ " FROM tsk_files WHERE type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType()
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
+ " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS
return getValuesMap(countSlackFilesQuery);
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Unable to get counts of slack files for all datasources, providing empty results", ex);
return Collections.emptyMap();
}
}
/**
* Get a map containing maps which map artifact type to the number of times
* it exosts in each data source in the current case.
*
* @return Collection which maps datasource id to maps of artifact display
* name to number of occurences in the datasource, will only contain
* entries for artifacts which have at least one occurence in the
* data source.
*/
static Map<Long, Map<String, Long>> getCountsOfArtifactsByType() {
try {
final String countArtifactsQuery = "blackboard_artifacts.data_source_obj_id, blackboard_artifact_types.display_name AS label, COUNT(*) AS value"
+ " FROM blackboard_artifacts, blackboard_artifact_types"
+ " WHERE blackboard_artifacts.artifact_type_id = blackboard_artifact_types.artifact_type_id"
+ " GROUP BY blackboard_artifacts.data_source_obj_id, blackboard_artifact_types.display_name";
return getLabeledValuesMap(countArtifactsQuery);
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Unable to get counts of all artifact types for all datasources, providing empty results", ex);
return Collections.emptyMap();
}
}
/**
* Helper method to execute a select query with a
* DataSourceLabeledValueCallback.
*
* @param query the portion of the query which should follow the SELECT
*
* @return a map of datasource object IDs to maps of String labels to the
* values associated with them.
*
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
private static Map<Long, Map<String, Long>> getLabeledValuesMap(String query) throws TskCoreException, NoCurrentCaseException {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
DataSourceLabeledValueCallback callback = new DataSourceLabeledValueCallback();
skCase.getCaseDbAccessManager().select(query, callback);
return callback.getMapOfLabeledValues();
}
/** /**
* Helper method to execute a select query with a * Helper method to execute a select query with a
* DataSourceSingleValueCallback. * DataSourceSingleValueCallback.

View File

@ -70,18 +70,13 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Component class="javax.swing.JTable" name="fileCountsByMimeTypeTable"> <Component class="javax.swing.JTable" name="fileCountsByMimeTypeTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="filesByMimeTypeTableModel" type="code"/>
</Property>
</Properties>
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JLabel" name="byMimeTypeLabel"> <Component class="javax.swing.JLabel" name="byMimeTypeLabel">
<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/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byMimeTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byMimeTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -93,25 +88,20 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Component class="javax.swing.JTable" name="fileCountsByCategoryTable"> <Component class="javax.swing.JTable" name="fileCountsByCategoryTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="filesByCategoryTableModel" type="code"/>
</Property>
</Properties>
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JLabel" name="byCategoryLabel"> <Component class="javax.swing.JLabel" name="byCategoryLabel">
<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/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byCategoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byCategoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="jLabel1"> <Component class="javax.swing.JLabel" name="jLabel1">
<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/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryCountsPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryCountsPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -123,15 +113,6 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Component class="javax.swing.JTable" name="artifactCountsTable"> <Component class="javax.swing.JTable" name="artifactCountsTable">
<Properties>
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="2" rowCount="0">
<Column editable="false" title="Result Type" type="java.lang.Object"/>
<Column editable="false" title="Count" type="java.lang.Object"/>
</Table>
</Property>
</Properties>
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>

View File

@ -18,15 +18,12 @@
*/ */
package org.sleuthkit.autopsy.casemodule.datasourcesummary; package org.sleuthkit.autopsy.casemodule.datasourcesummary;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils; import org.sleuthkit.autopsy.coreutils.FileTypeUtils;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
@ -34,69 +31,224 @@ import org.sleuthkit.datamodel.DataSource;
* Panel for displaying summary information on the known files present in the * Panel for displaying summary information on the known files present in the
* specified DataSource * specified DataSource
*/ */
@Messages({"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=File Type",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.count.header=Count",
"DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type",
"DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count"
})
class DataSourceSummaryCountsPanel extends javax.swing.JPanel { class DataSourceSummaryCountsPanel extends javax.swing.JPanel {
// Result returned for a data model if no data found.
private static final Object[][] EMPTY_PAIRS = new Object[][]{};
// column headers for mime type table
private static final Object[] MIME_TYPE_COLUMN_HEADERS = new Object[]{
Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_type_header(),
Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_count_header()
};
// column headers for file by category table
private static final Object[] FILE_BY_CATEGORY_COLUMN_HEADERS = new Object[]{
Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header(),
Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header()
};
// column headers for artifact counts table
private static final Object[] ARTIFACT_COUNTS_COLUMN_HEADERS = new Object[]{
Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_type_header(),
Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_count_header()
};
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private FilesByMimeTypeTableModel filesByMimeTypeTableModel = new FilesByMimeTypeTableModel(null);
private FilesByCategoryTableModel filesByCategoryTableModel = new FilesByCategoryTableModel(null);
private static final Logger logger = Logger.getLogger(DataSourceSummaryCountsPanel.class.getName()); private static final Logger logger = Logger.getLogger(DataSourceSummaryCountsPanel.class.getName());
private final Map<Long, Long> allFilesCountsMap;
private final Map<Long, Long> slackFilesCountsMap;
private final Map<Long, Long> directoriesCountsMap;
private final Map<Long, Long> unallocatedFilesCountsMap;
private final Map<Long, Map<String, Long>> artifactsByTypeCountsMap;
private final DefaultTableCellRenderer rightAlignedRenderer = new DefaultTableCellRenderer(); private final DefaultTableCellRenderer rightAlignedRenderer = new DefaultTableCellRenderer();
private DataSource dataSource;
/** /**
* Creates new form DataSourceSummaryCountsPanel * Creates new form DataSourceSummaryCountsPanel
*/ */
DataSourceSummaryCountsPanel(Map<Long, Long> fileCountsMap) { DataSourceSummaryCountsPanel() {
this.allFilesCountsMap = fileCountsMap;
this.slackFilesCountsMap = DataSourceInfoUtilities.getCountsOfSlackFiles();
this.directoriesCountsMap = DataSourceInfoUtilities.getCountsOfDirectories();
this.unallocatedFilesCountsMap = DataSourceInfoUtilities.getCountsOfUnallocatedFiles();
this.artifactsByTypeCountsMap = DataSourceInfoUtilities.getCountsOfArtifactsByType();
rightAlignedRenderer.setHorizontalAlignment(JLabel.RIGHT); rightAlignedRenderer.setHorizontalAlignment(JLabel.RIGHT);
initComponents(); initComponents();
fileCountsByMimeTypeTable.getTableHeader().setReorderingAllowed(false); fileCountsByMimeTypeTable.getTableHeader().setReorderingAllowed(false);
fileCountsByCategoryTable.getTableHeader().setReorderingAllowed(false); fileCountsByCategoryTable.getTableHeader().setReorderingAllowed(false);
artifactCountsTable.getTableHeader().setReorderingAllowed(false);
setDataSource(null);
} }
/** /**
* Specify the DataSource to display file information for * The datasource currently used as the model in this panel.
* *
* @param selectedDataSource the DataSource to display file information for * @return The datasource currently being used as the model in this panel.
*/ */
void updateCountsTableData(DataSource selectedDataSource) { public DataSource getDataSource() {
filesByMimeTypeTableModel = new FilesByMimeTypeTableModel(selectedDataSource); return dataSource;
fileCountsByMimeTypeTable.setModel(filesByMimeTypeTableModel); }
/**
* Sets datasource to visualize in the panel.
*
* @param dataSource The datasource to use in this panel.
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
if (dataSource == null || !Case.isCaseOpen()) {
updateCountsTableData(EMPTY_PAIRS,
EMPTY_PAIRS,
EMPTY_PAIRS);
} else {
updateCountsTableData(getMimeTypeModel(dataSource),
getFileCategoryModel(dataSource),
getArtifactCountsModel(dataSource));
}
}
/**
* Specify the DataSource to display file information for.
*
* @param mimeTypeDataModel The mime type data model.
* @param fileCategoryDataModel The file category data model.
* @param artifactDataModel The artifact type data model.
*/
private void updateCountsTableData(Object[][] mimeTypeDataModel, Object[][] fileCategoryDataModel, Object[][] artifactDataModel) {
fileCountsByMimeTypeTable.setModel(new NonEditableTableModel(mimeTypeDataModel, MIME_TYPE_COLUMN_HEADERS));
fileCountsByMimeTypeTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); fileCountsByMimeTypeTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer);
fileCountsByMimeTypeTable.getColumnModel().getColumn(0).setPreferredWidth(130); fileCountsByMimeTypeTable.getColumnModel().getColumn(0).setPreferredWidth(130);
filesByCategoryTableModel = new FilesByCategoryTableModel(selectedDataSource);
fileCountsByCategoryTable.setModel(filesByCategoryTableModel); fileCountsByCategoryTable.setModel(new NonEditableTableModel(fileCategoryDataModel, FILE_BY_CATEGORY_COLUMN_HEADERS));
fileCountsByCategoryTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); fileCountsByCategoryTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer);
fileCountsByCategoryTable.getColumnModel().getColumn(0).setPreferredWidth(130); fileCountsByCategoryTable.getColumnModel().getColumn(0).setPreferredWidth(130);
updateArtifactCounts(selectedDataSource);
artifactCountsTable.setModel(new NonEditableTableModel(artifactDataModel, ARTIFACT_COUNTS_COLUMN_HEADERS));
artifactCountsTable.getColumnModel().getColumn(0).setPreferredWidth(230);
artifactCountsTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer);
this.repaint(); this.repaint();
} }
/** /**
* Helper method to update the artifact specific counts by clearing the * Determines the JTable data model for datasource mime types.
* table and adding counts for the artifacts which exist in the selected
* data source.
* *
* @param selectedDataSource the data source to display artifact counts for * @param dataSource The DataSource.
*
* @return The model to be used with a JTable.
*/ */
private void updateArtifactCounts(DataSource selectedDataSource) { @Messages({
((DefaultTableModel) artifactCountsTable.getModel()).setRowCount(0); "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images",
if (selectedDataSource != null && artifactsByTypeCountsMap.get(selectedDataSource.getId()) != null) { "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos",
Map<String, Long> artifactCounts = artifactsByTypeCountsMap.get(selectedDataSource.getId()); "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio",
for (String key : artifactCounts.keySet()) { "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents",
((DefaultTableModel) artifactCountsTable.getModel()).addRow(new Object[]{key, artifactCounts.get(key)}); "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables"
})
private static Object[][] getMimeTypeModel(DataSource dataSource) {
return new Object[][]{
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row(),
getCount(dataSource, FileTypeUtils.FileTypeCategory.IMAGE)},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row(),
getCount(dataSource, FileTypeUtils.FileTypeCategory.VIDEO)},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row(),
getCount(dataSource, FileTypeUtils.FileTypeCategory.AUDIO)},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row(),
getCount(dataSource, FileTypeUtils.FileTypeCategory.DOCUMENTS)},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row(),
getCount(dataSource, FileTypeUtils.FileTypeCategory.EXECUTABLE)}
};
}
/**
* Retrieves the counts of files of a particular mime type for a particular
* DataSource.
*
* @param dataSource The DataSource.
* @param category The mime type category.
*
* @return The count.
*/
private static Long getCount(DataSource dataSource, FileTypeUtils.FileTypeCategory category) {
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(dataSource, category.getMediaTypes());
}
/**
* Determines the JTable data model for datasource file categories.
*
* @param dataSource The DataSource.
*
* @return The model to be used with a JTable.
*/
@Messages({
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory"
})
private static Object[][] getFileCategoryModel(DataSource selectedDataSource) {
Long fileCount = zeroIfNull(DataSourceInfoUtilities.getCountOfFiles(selectedDataSource));
Long unallocatedFiles = zeroIfNull(DataSourceInfoUtilities.getCountOfUnallocatedFiles(selectedDataSource));
Long allocatedFiles = zeroIfNull(getAllocatedCount(fileCount, unallocatedFiles));
Long slackFiles = zeroIfNull(DataSourceInfoUtilities.getCountOfSlackFiles(selectedDataSource));
Long directories = zeroIfNull(DataSourceInfoUtilities.getCountOfDirectories(selectedDataSource));
return new Object[][]{
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row(), fileCount},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row(), allocatedFiles},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row(), unallocatedFiles},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row(), slackFiles},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row(), directories}
};
}
/**
* Returns 0 if value is null.
*
* @param origValue The original value.
*
* @return The value or 0 if null.
*/
private static Long zeroIfNull(Long origValue) {
return origValue == null ? 0 : origValue;
}
/**
* Safely gets the allocated files count.
*
* @param allFilesCount The count of all files.
* @param unallocatedFilesCount The count of unallocated files.
*
* @return The count of allocated files.
*/
private static long getAllocatedCount(Long allFilesCount, Long unallocatedFilesCount) {
if (allFilesCount == null) {
return 0;
} else if (unallocatedFilesCount == null) {
return allFilesCount;
} else {
return allFilesCount - unallocatedFilesCount;
} }
} }
artifactCountsTable.getColumnModel().getColumn(0).setPreferredWidth(230);
artifactCountsTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); /**
* The counts of different artifact types found in a DataSource.
*
* @param selectedDataSource The DataSource.
*
* @return The JTable data model of counts of artifact types.
*/
private static Object[][] getArtifactCountsModel(DataSource selectedDataSource) {
Map<String, Long> artifactMapping = DataSourceInfoUtilities.getCountsOfArtifactsByType(selectedDataSource);
if (artifactMapping == null) {
return EMPTY_PAIRS;
}
return artifactMapping.entrySet().stream()
.filter((entrySet) -> entrySet != null && entrySet.getKey() != null)
.sorted((a, b) -> a.getKey().compareTo(b.getKey()))
.map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()})
.toArray(Object[][]::new);
} }
/** /**
@ -118,35 +270,16 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel {
artifactCountsScrollPane = new javax.swing.JScrollPane(); artifactCountsScrollPane = new javax.swing.JScrollPane();
artifactCountsTable = new javax.swing.JTable(); artifactCountsTable = new javax.swing.JTable();
fileCountsByMimeTypeTable.setModel(filesByMimeTypeTableModel);
fileCountsByMimeTypeScrollPane.setViewportView(fileCountsByMimeTypeTable); fileCountsByMimeTypeScrollPane.setViewportView(fileCountsByMimeTypeTable);
org.openide.awt.Mnemonics.setLocalizedText(byMimeTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byMimeTypeLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(byMimeTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byMimeTypeLabel.text")); // NOI18N
fileCountsByCategoryTable.setModel(filesByCategoryTableModel);
fileCountsByCategoryScrollPane.setViewportView(fileCountsByCategoryTable); fileCountsByCategoryScrollPane.setViewportView(fileCountsByCategoryTable);
org.openide.awt.Mnemonics.setLocalizedText(byCategoryLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byCategoryLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(byCategoryLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byCategoryLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.jLabel1.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.jLabel1.text")); // NOI18N
artifactCountsTable.setAutoCreateRowSorter(true);
artifactCountsTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
},
new String [] {
"Result Type", "Count"
}
) {
boolean[] canEdit = new boolean [] {
false, false
};
public boolean isCellEditable(int rowIndex, int columnIndex) {
return canEdit [columnIndex];
}
});
artifactCountsScrollPane.setViewportView(artifactCountsTable); artifactCountsScrollPane.setViewportView(artifactCountsTable);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
@ -206,190 +339,4 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel {
private javax.swing.JTable fileCountsByMimeTypeTable; private javax.swing.JTable fileCountsByMimeTypeTable;
private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel1;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
/**
* Table model for the files table model to display counts of specific file
* types by mime type found in the currently selected data source.
*/
@Messages({"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=File Type",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.count.header=Count"})
private class FilesByMimeTypeTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private final DataSource currentDataSource;
private final List<String> columnHeaders = new ArrayList<>();
private static final int IMAGES_ROW_INDEX = 0;
private static final int VIDEOS_ROW_INDEX = 1;
private static final int AUDIO_ROW_INDEX = 2;
private static final int DOCUMENTS_ROW_INDEX = 3;
private static final int EXECUTABLES_ROW_INDEX = 4;
/**
* Create a FilesByMimeTypeTableModel for the speicified datasource.
*
* @param selectedDataSource the datasource which this
* FilesByMimeTypeTablemodel will represent
*/
FilesByMimeTypeTableModel(DataSource selectedDataSource) {
columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_type_header());
columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_count_header());
currentDataSource = selectedDataSource;
}
@Override
public int getRowCount() {
//should be kept equal to the number of types we are displaying in the tables
return 5;
}
@Override
public int getColumnCount() {
return columnHeaders.size();
}
@Messages({
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables"
})
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
switch (rowIndex) {
case IMAGES_ROW_INDEX:
return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row();
case VIDEOS_ROW_INDEX:
return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row();
case AUDIO_ROW_INDEX:
return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row();
case DOCUMENTS_ROW_INDEX:
return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row();
case EXECUTABLES_ROW_INDEX:
return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row();
default:
break;
}
} else if (columnIndex == 1) {
switch (rowIndex) {
case 0:
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.IMAGE.getMediaTypes());
case 1:
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.VIDEO.getMediaTypes());
case 2:
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.AUDIO.getMediaTypes());
case 3:
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.DOCUMENTS.getMediaTypes());
case 4:
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.EXECUTABLE.getMediaTypes());
default:
break;
}
}
return null;
}
@Override
public String getColumnName(int column) {
return columnHeaders.get(column);
}
}
/**
* Table model for the files table model to display counts of specific file
* types by category found in the currently selected data source.
*/
@Messages({"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count"})
private class FilesByCategoryTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private final DataSource currentDataSource;
private final List<String> columnHeaders = new ArrayList<>();
private static final int ALL_FILES_ROW_INDEX = 0;
private static final int ALLOCATED_FILES_ROW_INDEX = 1;
private static final int UNALLOCATED_FILES_ROW_INDEX = 2;
private static final int SLACK_FILES_ROW_INDEX = 3;
private static final int DIRECTORIES_ROW_INDEX = 4;
/**
* Create a FilesByCategoryTableModel for the speicified datasource.
*
* @param selectedDataSource the datasource which this
* FilesByCategoryTablemodel will represent
*/
FilesByCategoryTableModel(DataSource selectedDataSource) {
columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header());
columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header());
currentDataSource = selectedDataSource;
}
@Override
public int getRowCount() {
//should be kept equal to the number of types we are displaying in the tables
return 5;
}
@Override
public int getColumnCount() {
return columnHeaders.size();
}
@Messages({
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory"
})
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
switch (rowIndex) {
case ALL_FILES_ROW_INDEX:
return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row();
case ALLOCATED_FILES_ROW_INDEX:
return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row();
case UNALLOCATED_FILES_ROW_INDEX:
return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row();
case SLACK_FILES_ROW_INDEX:
return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row();
case DIRECTORIES_ROW_INDEX:
return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row();
default:
break;
}
} else if (columnIndex == 1 && currentDataSource != null) {
switch (rowIndex) {
case 0:
return allFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : allFilesCountsMap.get(currentDataSource.getId());
case 1:
//All files should be either allocated or unallocated as dir_flags only has two values so any file that isn't unallocated is allocated
Long unallocatedFilesCount = unallocatedFilesCountsMap.get(currentDataSource.getId());
Long allFilesCount = allFilesCountsMap.get(currentDataSource.getId());
if (allFilesCount == null) {
return 0;
} else if (unallocatedFilesCount == null) {
return allFilesCount;
} else {
return allFilesCount - unallocatedFilesCount;
}
case 2:
return unallocatedFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : unallocatedFilesCountsMap.get(currentDataSource.getId());
case 3:
return slackFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : slackFilesCountsMap.get(currentDataSource.getId());
case 4:
return directoriesCountsMap.get(currentDataSource.getId()) == null ? 0 : directoriesCountsMap.get(currentDataSource.getId());
default:
break;
}
}
return null;
}
@Override
public String getColumnName(int column) {
return columnHeaders.get(column);
}
}
} }

View File

@ -19,12 +19,12 @@
package org.sleuthkit.autopsy.casemodule.datasourcesummary; package org.sleuthkit.autopsy.casemodule.datasourcesummary;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.Map;
import java.util.HashMap;
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.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -36,23 +36,47 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel {
//Because this panel was made using the gridbaglayout and netbean's Customize Layout tool it will be best to continue to modify it through that //Because this panel was made using the gridbaglayout and netbean's Customize Layout tool it will be best to continue to modify it through that
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Map<Long, String> osDetailMap = new HashMap<>();
private static final Integer SIZE_COVERSION_CONSTANT = 1000; private static final Integer SIZE_COVERSION_CONSTANT = 1000;
private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##"); private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##");
private final Map<Long, Long> unallocatedFilesSizeMap;
private final Map<Long, String> usageMap;
private static final Logger logger = Logger.getLogger(DataSourceSummaryDetailsPanel.class.getName()); private static final Logger logger = Logger.getLogger(DataSourceSummaryDetailsPanel.class.getName());
private DataSource dataSource;
/** /**
* Creates new form DataSourceSummaryDetailsPanel * Creates new form DataSourceSummaryDetailsPanel
*/ */
@Messages({"DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case.", @Messages({"DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case.",
"DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure"}) "DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure"})
DataSourceSummaryDetailsPanel(Map<Long, String> usageMap) { DataSourceSummaryDetailsPanel() {
initComponents(); initComponents();
this.usageMap = usageMap; setDataSource(null);
this.unallocatedFilesSizeMap = DataSourceInfoUtilities.getSizeOfUnallocatedFiles(); }
osDetailMap = DataSourceInfoUtilities.getOperatingSystems();
/**
* The datasource currently used as the model in this panel.
*
* @return The datasource currently being used as the model in this panel.
*/
public DataSource getDataSource() {
return dataSource;
}
/**
* Sets datasource to visualize in the panel.
*
* @param dataSource The datasource to use in this panel.
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
if (dataSource == null || !Case.isCaseOpen()) {
updateDetailsPanelData(null, null, null, null);
} else {
updateDetailsPanelData(dataSource,
DataSourceInfoUtilities.getSizeOfUnallocatedFiles(dataSource),
DataSourceInfoUtilities.getOperatingSystems(dataSource),
DataSourceInfoUtilities.getDataSourceType(dataSource));
}
} }
/** /**
@ -60,77 +84,80 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel {
* *
* @param selectedDataSource the DataSource to display details about. * @param selectedDataSource the DataSource to display details about.
*/ */
void updateDetailsPanelData(DataSource selectedDataSource) { private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize, String osDetails, String usage) {
clearTableValues(); clearTableValues();
if (selectedDataSource != null) { if (selectedDataSource != null) {
String sizeString = ""; unallocatedSizeValue.setText(getSizeString(unallocatedFilesSize));
String sectorSizeString = ""; operatingSystemValue.setText(StringUtils.isBlank(osDetails) ? "" : osDetails);
String md5String = ""; dataSourceUsageValue.setText(StringUtils.isBlank(usage) ? "" : usage);
String sha1String = "";
String sha256String = ""; timeZoneValue.setText(selectedDataSource.getTimeZone());
String acquisitionDetailsString = "";
String imageTypeString = "";
String[] filePaths = new String[0];
String osDetailString = osDetailMap.get(selectedDataSource.getId()) == null ? "" : osDetailMap.get(selectedDataSource.getId());
String dataSourceTypeString = usageMap.get(selectedDataSource.getId()) == null ? "" : usageMap.get(selectedDataSource.getId());
try {
acquisitionDetailsString = selectedDataSource.getAcquisitionDetails();
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get aquisition details for selected data source", ex);
}
if (selectedDataSource instanceof Image) {
imageTypeString = ((Image) selectedDataSource).getType().getName();
filePaths = ((Image) selectedDataSource).getPaths();
sizeString = getSizeString(selectedDataSource.getSize());
sectorSizeString = getSizeString(((Image) selectedDataSource).getSsize());
try {
//older databases may have null as the hash values
md5String = ((Image) selectedDataSource).getMd5();
if (md5String == null) {
md5String = "";
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get MD5 for selected data source", ex);
}
try {
sha1String = ((Image) selectedDataSource).getSha1();
if (sha1String == null) {
sha1String = "";
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get SHA1 for selected data source", ex);
}
try {
sha256String = ((Image) selectedDataSource).getSha256();
if (sha256String == null) {
sha256String = "";
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get SHA256 for selected data source", ex);
}
}
displayNameValue.setText(selectedDataSource.getName()); displayNameValue.setText(selectedDataSource.getName());
originalNameValue.setText(selectedDataSource.getName()); originalNameValue.setText(selectedDataSource.getName());
deviceIdValue.setText(selectedDataSource.getDeviceId()); deviceIdValue.setText(selectedDataSource.getDeviceId());
dataSourceUsageValue.setText(dataSourceTypeString);
operatingSystemValue.setText(osDetailString); try {
timeZoneValue.setText(selectedDataSource.getTimeZone()); acquisitionDetailsTextArea.setText(selectedDataSource.getAcquisitionDetails());
acquisitionDetailsTextArea.setText(acquisitionDetailsString); } catch (TskCoreException ex) {
imageTypeValue.setText(imageTypeString); logger.log(Level.WARNING, "Unable to get aquisition details for selected data source", ex);
sizeValue.setText(sizeString); }
unallocatedSizeValue.setText(getSizeString(unallocatedFilesSizeMap.get(selectedDataSource.getId())));
sectorSizeValue.setText(sectorSizeString); if (selectedDataSource instanceof Image) {
md5HashValue.setText(md5String); setFieldsForImage((Image) selectedDataSource);
sha1HashValue.setText(sha1String);
sha256HashValue.setText(sha256String);
for (String path : filePaths) {
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path});
} }
} }
updateFieldVisibility(); updateFieldVisibility();
this.repaint(); this.repaint();
} }
/**
* Sets text fields for an image. This should be called after
* clearTableValues and before updateFieldVisibility to ensure the proper
* rendering.
*
* @param selectedImage The selected image.
*/
private void setFieldsForImage(Image selectedImage) {
imageTypeValue.setText(selectedImage.getType().getName());
sizeValue.setText(getSizeString(selectedImage.getSize()));
sectorSizeValue.setText(getSizeString(selectedImage.getSsize()));
for (String path : selectedImage.getPaths()) {
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path});
}
try {
//older databases may have null as the hash values
String md5String = selectedImage.getMd5();
if (md5String == null) {
md5String = "";
}
md5HashValue.setText(md5String);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get MD5 for selected data source", ex);
}
try {
String sha1String = selectedImage.getSha1();
if (sha1String == null) {
sha1String = "";
}
sha1HashValue.setText(sha1String);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get SHA1 for selected data source", ex);
}
try {
String sha256String = selectedImage.getSha256();
if (sha256String == null) {
sha256String = "";
}
sha256HashValue.setText(sha256String);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get SHA256 for selected data source", ex);
}
}
/** /**
* Get a long size in bytes as a string formated to be read by users. * Get a long size in bytes as a string formated to be read by users.
* *
@ -147,7 +174,7 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel {
"DataSourceSummaryDetailsPanel.units.terabytes= TB", "DataSourceSummaryDetailsPanel.units.terabytes= TB",
"DataSourceSummaryDetailsPanel.units.petabytes= PB" "DataSourceSummaryDetailsPanel.units.petabytes= PB"
}) })
private String getSizeString(Long size) { private static String getSizeString(Long size) {
if (size == null) { if (size == null) {
return ""; return "";
} }

View File

@ -68,6 +68,11 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Container class="javax.swing.JTabbedPane" name="dataSourceTabbedPane"> <Container class="javax.swing.JTabbedPane" name="dataSourceTabbedPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="dataSourceSummaryTabbedPane"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/> <JSplitPaneConstraints position="right"/>

View File

@ -27,7 +27,6 @@ import java.util.Observer;
import java.util.Set; import java.util.Set;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent;
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent.Reason; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent.Reason;
@ -41,10 +40,8 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED); private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
private final DataSourceSummaryCountsPanel countsPanel;
private final DataSourceSummaryDetailsPanel detailsPanel;
private final DataSourceBrowser dataSourcesPanel; private final DataSourceBrowser dataSourcesPanel;
private final IngestJobInfoPanel ingestHistoryPanel; private final DataSourceSummaryTabbedPane dataSourceSummaryTabbedPane;
/** /**
* Creates new form DataSourceSummaryDialog for displaying a summary of the * Creates new form DataSourceSummaryDialog for displaying a summary of the
@ -61,21 +58,14 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
super(owner, Bundle.DataSourceSummaryDialog_window_title(), true); super(owner, Bundle.DataSourceSummaryDialog_window_title(), true);
Map<Long, String> usageMap = DataSourceInfoUtilities.getDataSourceTypes(); Map<Long, String> usageMap = DataSourceInfoUtilities.getDataSourceTypes();
Map<Long, Long> fileCountsMap = DataSourceInfoUtilities.getCountsOfFiles(); Map<Long, Long> fileCountsMap = DataSourceInfoUtilities.getCountsOfFiles();
countsPanel = new DataSourceSummaryCountsPanel(fileCountsMap);
detailsPanel = new DataSourceSummaryDetailsPanel(usageMap);
dataSourcesPanel = new DataSourceBrowser(usageMap, fileCountsMap); dataSourcesPanel = new DataSourceBrowser(usageMap, fileCountsMap);
ingestHistoryPanel = new IngestJobInfoPanel(); dataSourceSummaryTabbedPane = new DataSourceSummaryTabbedPane();
initComponents(); initComponents();
dataSourceSummarySplitPane.setLeftComponent(dataSourcesPanel); dataSourceSummarySplitPane.setLeftComponent(dataSourcesPanel);
dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel);
dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel);
dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel);
dataSourcesPanel.addListSelectionListener((ListSelectionEvent e) -> { dataSourcesPanel.addListSelectionListener((ListSelectionEvent e) -> {
if (!e.getValueIsAdjusting()) { if (!e.getValueIsAdjusting()) {
DataSource selectedDataSource = dataSourcesPanel.getSelectedDataSource(); DataSource selectedDataSource = dataSourcesPanel.getSelectedDataSource();
countsPanel.updateCountsTableData(selectedDataSource); dataSourceSummaryTabbedPane.setDataSource(selectedDataSource);
detailsPanel.updateDetailsPanelData(selectedDataSource);
ingestHistoryPanel.setDataSource(selectedDataSource);
this.repaint(); this.repaint();
} }
}); });
@ -116,7 +106,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
closeButton = new javax.swing.JButton(); closeButton = new javax.swing.JButton();
dataSourceSummarySplitPane = new javax.swing.JSplitPane(); dataSourceSummarySplitPane = new javax.swing.JSplitPane();
dataSourceTabbedPane = new javax.swing.JTabbedPane(); javax.swing.JTabbedPane dataSourceTabbedPane = dataSourceSummaryTabbedPane;
org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(DataSourceSummaryDialog.class, "DataSourceSummaryDialog.closeButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(DataSourceSummaryDialog.class, "DataSourceSummaryDialog.closeButton.text")); // NOI18N
closeButton.addActionListener(new java.awt.event.ActionListener() { closeButton.addActionListener(new java.awt.event.ActionListener() {
@ -173,6 +163,5 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton closeButton; private javax.swing.JButton closeButton;
private javax.swing.JSplitPane dataSourceSummarySplitPane; private javax.swing.JSplitPane dataSourceSummarySplitPane;
private javax.swing.JTabbedPane dataSourceTabbedPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -0,0 +1,74 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
import javax.swing.JTabbedPane;
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
import org.sleuthkit.datamodel.DataSource;
/**
* A tabbed pane showing the summary of a data source including tabs of:
* DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and
* IngestJobInfoPanel.
*/
public class DataSourceSummaryTabbedPane extends JTabbedPane {
private static final long serialVersionUID = 1L;
private final DataSourceSummaryCountsPanel countsPanel;
private final DataSourceSummaryDetailsPanel detailsPanel;
private final IngestJobInfoPanel ingestHistoryPanel;
private DataSource dataSource = null;
/**
* Constructs a tabbed pane showing the summary of a data source.
*/
public DataSourceSummaryTabbedPane() {
countsPanel = new DataSourceSummaryCountsPanel();
detailsPanel = new DataSourceSummaryDetailsPanel();
ingestHistoryPanel = new IngestJobInfoPanel();
addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel);
addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel);
addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel);
}
/**
* The datasource currently used as the model in this panel.
*
* @return The datasource currently being used as the model in this panel.
*/
public DataSource getDataSource() {
return dataSource;
}
/**
* Sets datasource to visualize in the tabbed panel.
*
* @param dataSource The datasource to use in this panel.
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
detailsPanel.setDataSource(dataSource);
countsPanel.setDataSource(dataSource);
ingestHistoryPanel.setDataSource(dataSource);
}
}

View File

@ -0,0 +1,37 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
import javax.swing.table.DefaultTableModel;
/**
* A Table model where cells are not editable.
*/
class NonEditableTableModel extends DefaultTableModel {
private static final long serialVersionUID = 1L;
NonEditableTableModel(Object[][] data, Object[] columnNames) {
super(data, columnNames);
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
}

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -244,7 +244,7 @@ public class TagsManager implements Closeable {
* @throws IOException * @throws IOException
*/ */
public static void addTagSetDefinition(TagSetDefinition tagSetDef) throws IOException { public static void addTagSetDefinition(TagSetDefinition tagSetDef) throws IOException {
synchronized(lock) { synchronized (lock) {
TagSetDefinition.writeTagSetDefinition(tagSetDef); TagSetDefinition.writeTagSetDefinition(tagSetDef);
} }
} }
@ -267,20 +267,20 @@ public class TagsManager implements Closeable {
caseDb.addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus()); caseDb.addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus());
} }
//Assume new case and add tag sets //Assume new case and add tag sets
for(TagSetDefinition setDef: TagSetDefinition.readTagSetDefinitions()) { for (TagSetDefinition setDef : TagSetDefinition.readTagSetDefinitions()) {
List<TagName> tagNameList = new ArrayList<>(); List<TagName> tagNameList = new ArrayList<>();
for(TagNameDefinition tagNameDef: setDef.getTagNameDefinitions()) { for (TagNameDefinition tagNameDef : setDef.getTagNameDefinitions()) {
tagNameList.add(caseDb.addOrUpdateTagName(tagNameDef.getDisplayName(), tagNameDef.getDescription(), tagNameDef.getColor(), tagNameDef.getKnownStatus())); tagNameList.add(caseDb.addOrUpdateTagName(tagNameDef.getDisplayName(), tagNameDef.getDescription(), tagNameDef.getColor(), tagNameDef.getKnownStatus()));
} }
if(!tagNameList.isEmpty()) { if (!tagNameList.isEmpty()) {
taggingMgr.addTagSet(setDef.getName(), tagNameList); taggingMgr.addTagSet(setDef.getName(), tagNameList);
} }
} }
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error updating standard tag name and tag set definitions", ex); LOGGER.log(Level.SEVERE, "Error updating standard tag name and tag set definitions", ex);
} catch(IOException ex) { } catch (IOException ex) {
LOGGER.log(Level.SEVERE, "Error loading tag set JSON files", ex); LOGGER.log(Level.SEVERE, "Error loading tag set JSON files", ex);
} }
@ -300,6 +300,19 @@ public class TagsManager implements Closeable {
return caseDb.getTaggingManager().getTagSets(); return caseDb.getTaggingManager().getTagSets();
} }
/**
* Gets the tag set a tag name (tag definition) belongs to, if any.
*
* @param tagName The tag name.
*
* @return A TagSet object or null.
*
* @throws TskCoreException If there is an error querying the case database.
*/
public TagSet getTagSet(TagName tagName) throws TskCoreException {
return caseDb.getTaggingManager().getTagSet(tagName);
}
/** /**
* Add a new TagSet to the case database. Tags will be ranked in the order * Add a new TagSet to the case database. Tags will be ranked in the order
* which they are passed to this method. * which they are passed to this method.
@ -501,7 +514,7 @@ public class TagsManager implements Closeable {
* name to the case database. * name to the case database.
*/ */
public TagName addTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TagNameAlreadyExistsException, TskCoreException { public TagName addTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TagNameAlreadyExistsException, TskCoreException {
synchronized(lock) { synchronized (lock) {
try { try {
TagName tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatus); TagName tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatus);
Set<TagNameDefinition> customTypes = TagNameDefinition.getTagNameDefinitions(); Set<TagNameDefinition> customTypes = TagNameDefinition.getTagNameDefinitions();

View File

@ -1,9 +1,9 @@
OpenIDE-Module-Name=Central Repository OpenIDE-Module-Name=Central Repository
OpenIDE-Module-Display-Category=Ingest Module OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Short-Description=Correlation Engine Ingest Module OpenIDE-Module-Short-Description=Central Repository Ingest Module
OpenIDE-Module-Long-Description=\ OpenIDE-Module-Long-Description=\
Correlation Engine ingest module and central database. \n\n\ Central Repository ingest module and central database. \n\n\
The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\ The Central Repository ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\
Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest.
CentralRepoCommentDialog.commentLabel.text=Comment: CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.okButton.text=&OK CentralRepoCommentDialog.okButton.text=&OK

View File

@ -4,10 +4,10 @@ AddEditCentralRepoCommentAction.menuItemText.addEditCentralRepoCommentNoMD5=Add/
CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Repository Comment CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Repository Comment
OpenIDE-Module-Name=Central Repository OpenIDE-Module-Name=Central Repository
OpenIDE-Module-Display-Category=Ingest Module OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Short-Description=Correlation Engine Ingest Module OpenIDE-Module-Short-Description=Central Repository Ingest Module
OpenIDE-Module-Long-Description=\ OpenIDE-Module-Long-Description=\
Correlation Engine ingest module and central database. \n\n\ Central Repository ingest module and central database. \n\n\
The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\ The Central Repository ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\
Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest.
CentralRepoCommentDialog.commentLabel.text=Comment: CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.okButton.text=&OK CentralRepoCommentDialog.okButton.text=&OK

View File

@ -23,8 +23,11 @@ import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang.StringUtils;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.InvalidAccountIDException;
/** /**
* This class abstracts an Account as stored in the CR database. * This class abstracts an Account as stored in the CR database.
@ -40,8 +43,8 @@ public final class CentralRepoAccount {
private final String typeSpecificIdentifier; private final String typeSpecificIdentifier;
/** /**
* Encapsulates a central repo account type and the correlation type * Encapsulates a central repo account type and the correlation type that it
* that it maps to. * maps to.
*/ */
public static final class CentralRepoAccountType { public static final class CentralRepoAccountType {
@ -56,7 +59,6 @@ public final class CentralRepoAccount {
this.accountTypeId = acctTypeID; this.accountTypeId = acctTypeID;
} }
/** /**
* @return the acctType * @return the acctType
*/ */
@ -71,6 +73,37 @@ public final class CentralRepoAccount {
public int getAccountTypeId() { public int getAccountTypeId() {
return this.accountTypeId; return this.accountTypeId;
} }
@Override
public int hashCode() {
int hash = 5;
hash = 29 * hash + this.accountTypeId;
hash = 29 * hash + Objects.hashCode(this.acctType);
hash = 29 * hash + this.correlationTypeId;
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 CentralRepoAccountType other = (CentralRepoAccountType) obj;
if (this.accountTypeId != other.getAccountTypeId()) {
return false;
}
if (this.correlationTypeId != other.getCorrelationTypeId()) {
return false;
}
return Objects.equals(this.acctType, other.getAcctType());
}
} }
public CentralRepoAccount(long accountId, CentralRepoAccountType accountType, String typeSpecificIdentifier) { public CentralRepoAccount(long accountId, CentralRepoAccountType accountType, String typeSpecificIdentifier) {
@ -131,10 +164,10 @@ public final class CentralRepoAccount {
if (this.accountId != other.getId()) { if (this.accountId != other.getId()) {
return false; return false;
} }
if ((this.typeSpecificIdentifier == null) ? (other.getIdentifier() != null) : !this.typeSpecificIdentifier.equals(other.getIdentifier())) { if (!Objects.equals(this.typeSpecificIdentifier, other.getIdentifier())) {
return false; return false;
} }
return !(this.accountType != other.getAccountType() && (this.accountType == null || !this.accountType.equals(other.getAccountType()))); return Objects.equals(this.accountType, other.getAccountType());
} }
/** /**
@ -167,7 +200,6 @@ public final class CentralRepoAccount {
} }
}; };
private static final String ACCOUNTS_QUERY_CLAUSE private static final String ACCOUNTS_QUERY_CLAUSE
= "SELECT accounts.id as account_id, " = "SELECT accounts.id as account_id, "
+ " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier," + " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier,"
@ -176,24 +208,28 @@ public final class CentralRepoAccount {
+ " FROM accounts " + " FROM accounts "
+ " JOIN account_types as account_types on accounts.account_type_id = account_types.id "; + " JOIN account_types as account_types on accounts.account_type_id = account_types.id ";
/** /**
* Get all accounts with account identifier matching the given substring. * Get all accounts with account identifier matching the given substring.
* *
* @param accountIdentifierSubstring Account identifier substring to look for. * @param accountIdentifierSubstring Account identifier substring to look
* for.
* *
* @return Collection of all accounts with identifier matching the given substring, may * @return Collection of all accounts with identifier matching the given
* be empty. * substring, may be empty.
* *
* @throws CentralRepoException If there is an error in getting the accounts. * @throws CentralRepoException If there is an error in getting the
* accounts.
*/ */
public static Collection<CentralRepoAccount> getAccountsWithIdentifierLike(String accountIdentifierSubstring) throws CentralRepoException { public static Collection<CentralRepoAccount> getAccountsWithIdentifierLike(String accountIdentifierSubstring) throws CentralRepoException {
String queryClause = ACCOUNTS_QUERY_CLAUSE String queryClause = ACCOUNTS_QUERY_CLAUSE
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')"; + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER(?)";
List<Object> params = new ArrayList<>();
params.add("%" + accountIdentifierSubstring + "%");
AccountsQueryCallback queryCallback = new AccountsQueryCallback(); AccountsQueryCallback queryCallback = new AccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); CentralRepository.getInstance().executeQuery(queryClause, params, queryCallback);
return queryCallback.getAccountsList(); return queryCallback.getAccountsList();
} }
@ -203,18 +239,23 @@ public final class CentralRepoAccount {
* *
* @param accountIdentifier Account identifier to look for. * @param accountIdentifier Account identifier to look for.
* *
* @return Collection of all accounts with identifier matching the given identifier, may * @return Collection of all accounts with identifier matching the given
* be empty. * identifier, may be empty.
* *
* @throws CentralRepoException If there is an error in getting the accounts. * @throws CentralRepoException If there is an error in getting the
* accounts.
*/ */
public static Collection<CentralRepoAccount> getAccountsWithIdentifier(String accountIdentifier) throws CentralRepoException { public static Collection<CentralRepoAccount> getAccountsWithIdentifier(String accountIdentifier) throws InvalidAccountIDException, CentralRepoException {
String normalizedAccountIdentifier = normalizeAccountIdentifier(accountIdentifier);
String queryClause = ACCOUNTS_QUERY_CLAUSE String queryClause = ACCOUNTS_QUERY_CLAUSE
+ " WHERE LOWER(accounts.account_unique_identifier) = LOWER('" + accountIdentifier + "')"; + " WHERE LOWER(accounts.account_unique_identifier) = LOWER(?)";
List<Object> params = new ArrayList<>();
params.add(normalizedAccountIdentifier);
AccountsQueryCallback queryCallback = new AccountsQueryCallback(); AccountsQueryCallback queryCallback = new AccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); CentralRepository.getInstance().executeQuery(queryClause, params, queryCallback);
return queryCallback.getAccountsList(); return queryCallback.getAccountsList();
} }
@ -222,19 +263,82 @@ public final class CentralRepoAccount {
/** /**
* Get all central repo accounts. * Get all central repo accounts.
* *
* @return Collection of all accounts with identifier matching the given identifier, may * @return Collection of all accounts with identifier matching the given
* be empty. * identifier, may be empty.
* *
* @throws CentralRepoException If there is an error in getting the accounts. * @throws CentralRepoException If there is an error in getting the
* accounts.
*/ */
public static Collection<CentralRepoAccount> getAllAccounts() throws CentralRepoException { public static Collection<CentralRepoAccount> getAllAccounts() throws CentralRepoException {
String queryClause = ACCOUNTS_QUERY_CLAUSE; String queryClause = ACCOUNTS_QUERY_CLAUSE;
List<Object> params = new ArrayList<>(); // empty param list
AccountsQueryCallback queryCallback = new AccountsQueryCallback(); AccountsQueryCallback queryCallback = new AccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); CentralRepository.getInstance().executeQuery(queryClause, params, queryCallback);
return queryCallback.getAccountsList(); return queryCallback.getAccountsList();
} }
/**
* Attempts to normalize an account identifier, after trying to guess the
* account type.
*
* @param accountIdentifier Account identifier to be normalized.
* @return normalized identifier
*
* @throws InvalidAccountIDException If the account identifier is not valid.
*/
private static String normalizeAccountIdentifier(String accountIdentifier) throws InvalidAccountIDException {
if (StringUtils.isEmpty(accountIdentifier)) {
throw new InvalidAccountIDException("Account id is null or empty.");
}
String normalizedAccountIdentifier;
try {
if (CorrelationAttributeNormalizer.isValidPhoneNumber(accountIdentifier)) {
normalizedAccountIdentifier = CorrelationAttributeNormalizer.normalizePhone(accountIdentifier);
} else if (CorrelationAttributeNormalizer.isValidEmailAddress(accountIdentifier)) {
normalizedAccountIdentifier = CorrelationAttributeNormalizer.normalizeEmail(accountIdentifier);
} else {
normalizedAccountIdentifier = accountIdentifier.toLowerCase().trim();
}
} catch (CorrelationAttributeNormalizationException ex) {
throw new InvalidAccountIDException("Failed to normalize the account idenitier " + accountIdentifier, ex);
}
return normalizedAccountIdentifier;
}
/**
* Normalizes an account identifier, based on the given account type.
*
* @param crAccountType Account type.
* @param accountIdentifier Account identifier to be normalized.
* @return Normalized identifier.
*
* @throws InvalidAccountIDException If the account identifier is invalid.
*/
public static String normalizeAccountIdentifier(CentralRepoAccountType crAccountType, String accountIdentifier) throws InvalidAccountIDException {
if (StringUtils.isBlank(accountIdentifier)) {
throw new InvalidAccountIDException("Account identifier is null or empty.");
}
String normalizedAccountIdentifier;
try {
if (crAccountType.getAcctType().equals(Account.Type.PHONE)) {
normalizedAccountIdentifier = CorrelationAttributeNormalizer.normalizePhone(accountIdentifier);
} else if (crAccountType.getAcctType().equals(Account.Type.EMAIL)) {
normalizedAccountIdentifier = CorrelationAttributeNormalizer.normalizeEmail(accountIdentifier);
} else {
// convert to lowercase
normalizedAccountIdentifier = accountIdentifier.toLowerCase();
}
} catch (CorrelationAttributeNormalizationException ex) {
throw new InvalidAccountIDException("Invalid account identifier", ex);
}
return normalizedAccountIdentifier;
}
} }

View File

@ -25,6 +25,9 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.SwingUtilities;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
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 static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo.SOFTWARE_CR_DB_SCHEMA_VERSION; import static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo.SOFTWARE_CR_DB_SCHEMA_VERSION;
@ -259,9 +262,22 @@ public class CentralRepoDbUtil {
* used * used
*/ */
public static void setUseCentralRepo(boolean centralRepoCheckBoxIsSelected) { public static void setUseCentralRepo(boolean centralRepoCheckBoxIsSelected) {
closePersonasTopComponent();
ModuleSettings.setConfigSetting(CENTRAL_REPO_NAME, CENTRAL_REPO_USE_KEY, Boolean.toString(centralRepoCheckBoxIsSelected)); ModuleSettings.setConfigSetting(CENTRAL_REPO_NAME, CENTRAL_REPO_USE_KEY, Boolean.toString(centralRepoCheckBoxIsSelected));
} }
/**
* Closes Personas top component if it exists.
*/
private static void closePersonasTopComponent() {
SwingUtilities.invokeLater(() -> {
TopComponent personasWindow = WindowManager.getDefault().findTopComponent("PersonasTopComponent");
if (personasWindow != null && personasWindow.isOpened()) {
personasWindow.close();
}
});
}
/** /**
* Use the current settings and the validation query to test the connection * Use the current settings and the validation query to test the connection
* to the database. * to the database.

View File

@ -27,6 +27,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.datamodel.HashHitInfo; import org.sleuthkit.datamodel.HashHitInfo;
import org.sleuthkit.datamodel.InvalidAccountIDException;
/** /**
* Main interface for interacting with the database * Main interface for interacting with the database
@ -64,7 +65,8 @@ public interface CentralRepository {
* It will not close active/in-use connections. Thus, it is vital that there * It will not close active/in-use connections. Thus, it is vital that there
* are no in-use connections when you call this method. * are no in-use connections when you call this method.
* *
* @throws CentralRepoException if there is a problem closing the connection pool. * @throws CentralRepoException if there is a problem closing the connection
* pool.
*/ */
void shutdownConnections() throws CentralRepoException; void shutdownConnections() throws CentralRepoException;
@ -163,8 +165,8 @@ public interface CentralRepository {
void updateCase(CorrelationCase eamCase) throws CentralRepoException; void updateCase(CorrelationCase eamCase) throws CentralRepoException;
/** /**
* Queries the examiner table for the given user name. * Queries the examiner table for the given user name. Adds a row if the
* Adds a row if the user is not found in the examiner table. * user is not found in the examiner table.
* *
* @param examinerLoginName user name to look for. * @param examinerLoginName user name to look for.
* @return CentralRepoExaminer for the given user name. * @return CentralRepoExaminer for the given user name.
@ -446,8 +448,7 @@ public interface CentralRepository {
* @param type The type of instance. * @param type The type of instance.
* @param correlationCase The case tied to the instance. * @param correlationCase The case tied to the instance.
* @param correlationDataSource The data source tied to the instance. * @param correlationDataSource The data source tied to the instance.
* @param objectID The object id of the file tied to the * @param objectID The object id of the file tied to the instance.
* instance.
* *
* @return The correlation attribute if it exists; otherwise null. * @return The correlation attribute if it exists; otherwise null.
* *
@ -554,13 +555,13 @@ public interface CentralRepository {
*/ */
public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws CentralRepoException, CorrelationAttributeNormalizationException; public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws CentralRepoException, CorrelationAttributeNormalizationException;
/** /**
* Retrieves the given file HashHitInfo if the given file hash is in this * Retrieves the given file HashHitInfo if the given file hash is in this
* reference set. Only searches the reference_files table. * reference set. Only searches the reference_files table.
* *
* @param hash The hash to find in a search. * @param hash The hash to find in a search.
* @param referenceSetID The referenceSetID within which the file should exist. * @param referenceSetID The referenceSetID within which the file should
* exist.
* *
* @return The HashHitInfo if found or null if not found. * @return The HashHitInfo if found or null if not found.
* *
@ -569,7 +570,6 @@ public interface CentralRepository {
*/ */
HashHitInfo lookupHash(String hash, int referenceSetID) throws CentralRepoException, CorrelationAttributeNormalizationException; HashHitInfo lookupHash(String hash, int referenceSetID) throws CentralRepoException, CorrelationAttributeNormalizationException;
/** /**
* Check if the given value is in a specific reference set * Check if the given value is in a specific reference set
* *
@ -689,8 +689,7 @@ public interface CentralRepository {
* Add a new reference instance * Add a new reference instance
* *
* @param eamGlobalFileInstance The reference instance to add * @param eamGlobalFileInstance The reference instance to add
* @param correlationType Correlation Type that this Reference * @param correlationType Correlation Type that this Reference Instance is
* Instance is
* *
* @throws CentralRepoException * @throws CentralRepoException
*/ */
@ -798,8 +797,8 @@ public interface CentralRepository {
* *
* @return the lock, or null if locking is not supported * @return the lock, or null if locking is not supported
* *
* @throws CentralRepoException if the coordination service is running but we fail * @throws CentralRepoException if the coordination service is running but
* to get the lock * we fail to get the lock
*/ */
public CoordinationService.Lock getExclusiveMultiUserDbLock() throws CentralRepoException; public CoordinationService.Lock getExclusiveMultiUserDbLock() throws CentralRepoException;
@ -834,42 +833,28 @@ public interface CentralRepository {
*/ */
public void processSelectClause(String selectClause, InstanceTableCallback instanceTableCallback) throws CentralRepoException; public void processSelectClause(String selectClause, InstanceTableCallback instanceTableCallback) throws CentralRepoException;
/** /**
* Executes an INSERT sql statement on the central repository database. * Executes an INSERT/UPDATE/DELETE sql as a prepared statement, on the
* @param sql INSERT sql to execute. * central repository database.
*
* @param sql sql to execute.
* @param params List of query params to use, may be empty.
* *
* @throws CentralRepoException If there is an error. * @throws CentralRepoException If there is an error.
*/ */
void executeInsertSQL(String sql) throws CentralRepoException; void executeCommand(String sql, List<Object> params) throws CentralRepoException;
/** /**
* Executes a SELECT sql statement on the central repository database. * Executes a SELECT query sql as a prepared statement, on the central
* repository database.
* *
* @param sql SELECT sql to execute. * @param sql sql to execute.
* @param params List of query params to use, may be empty.
* @param queryCallback Query callback to handle the result of the query. * @param queryCallback Query callback to handle the result of the query.
* *
* @throws CentralRepoException If there is an error. * @throws CentralRepoException If there is an error.
*/ */
void executeSelectSQL(String sql, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException; void executeQuery(String sql, List<Object> params, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException;
/**
* Executes an UPDATE sql statement on the central repository database.
*
* @param sql UPDATE sql to execute.
*
* @throws CentralRepoException If there is an error.
*/
void executeUpdateSQL(String sql) throws CentralRepoException;
/**
* Executes a DELETE sql statement on the central repository database.
*
* @param sql DELETE sql to execute.
*
* @throws CentralRepoException If there is an error.
*/
void executeDeleteSQL(String sql) throws CentralRepoException;
/** /**
* Get account type by type name. * Get account type by type name.
@ -897,8 +882,23 @@ public interface CentralRepository {
* @param accountUniqueID type specific unique account id * @param accountUniqueID type specific unique account id
* @return CR account * @return CR account
* *
* @throws CentralRepoException * @throws CentralRepoException If there is an error accessing Central Repository.
* @throws InvalidAccountIDException If the account identifier is not valid.
*/ */
CentralRepoAccount getOrCreateAccount(CentralRepoAccount.CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException; CentralRepoAccount getOrCreateAccount(CentralRepoAccount.CentralRepoAccountType crAccountType, String accountUniqueID) throws InvalidAccountIDException, CentralRepoException;
/**
* Gets an account from the accounts table matching the given type/ID, if
* one exists.
*
* @param crAccountType CR account type to look for or create
* @param accountUniqueID type specific unique account id
*
* @return CR account, if found, null otherwise.
*
* @throws CentralRepoException If there is an error accessing Central Repository.
* @throws InvalidAccountIDException If the account identifier is not valid.
*/
CentralRepoAccount getAccount(CentralRepoAccount.CentralRepoAccountType crAccountType, String accountUniqueID) throws InvalidAccountIDException, CentralRepoException;
} }

View File

@ -19,12 +19,14 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.validator.routines.DomainValidator; import org.apache.commons.validator.routines.DomainValidator;
import org.apache.commons.validator.routines.EmailValidator; import org.apache.commons.validator.routines.EmailValidator;
import org.sleuthkit.datamodel.CommunicationsUtils;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Provides functions for normalizing data by attribute type before insertion or * Provides functions for normalizing data by attribute type before insertion or
@ -155,25 +157,43 @@ final public class CorrelationAttributeNormalizer {
/** /**
* Verify and normalize email address. * Verify and normalize email address.
*
* @param emailAddress Address to normalize.
* @return Normalized email address.
* @throws CorrelationAttributeNormalizationExceptions If the input is not a
* valid email address.
*
*/ */
private static String normalizeEmail(String data) throws CorrelationAttributeNormalizationException { static String normalizeEmail(String emailAddress) throws CorrelationAttributeNormalizationException {
try { if (isValidEmailAddress(emailAddress)) {
return CommunicationsUtils.normalizeEmailAddress(data); return emailAddress.toLowerCase().trim();
} } else {
catch(TskCoreException ex) { throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid email address: %s", emailAddress));
throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid email address: %s", data), ex);
} }
} }
/** /**
* Verify and normalize phone number. * Verify and normalize phone number.
*
* @param phoneNumber Phone number to normalize.
* @return Normalized phone number.
* @throws CorrelationAttributeNormalizationExceptions If the input is not a
* valid phone number.
*
*/ */
private static String normalizePhone(String data) throws CorrelationAttributeNormalizationException { static String normalizePhone(String phoneNumber) throws CorrelationAttributeNormalizationException {
try { if (isValidPhoneNumber(phoneNumber)) {
return CommunicationsUtils.normalizePhoneNum(data); String normalizedNumber = phoneNumber.replaceAll("\\s+", ""); // remove spaces.
normalizedNumber = normalizedNumber.replaceAll("[\\-()]", ""); // remove parens & dashes.
// ensure a min length
if (normalizedNumber.length() < MIN_PHONENUMBER_LEN) {
throw new CorrelationAttributeNormalizationException(String.format("Phone number string %s is too short ", phoneNumber));
} }
catch(TskCoreException ex) { return normalizedNumber;
throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid phone number: %s", data));
} else {
throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid phone number: %s", phoneNumber));
} }
} }
@ -318,6 +338,58 @@ final public class CorrelationAttributeNormalizer {
} }
} }
// These symbols are allowed in written form of phone numbers.
// A '+' is allowed only as a leading digit and hence not inlcuded here.
// While a dialed sequence may have additonal special characters, such as #, * or ',',
// CR attributes represent accounts and hence those chatracter are not allowed.
private static final Set<String> PHONENUMBER_CHARS = new HashSet<>(Arrays.asList(
"-", "(", ")"
));
private static final int MIN_PHONENUMBER_LEN = 5;
/**
* Checks if the given string is a valid phone number.
*
* @param phoneNumber String to check.
*
* @return True if the given string is a valid phone number, false
* otherwise.
*/
static boolean isValidPhoneNumber(String phoneNumber) {
// A phone number may have a leading '+', special telephony chars, or digits.
// Anything else implies an invalid phone number.
for (int i = 0; i < phoneNumber.length(); i++) {
if ( !((i == 0 && phoneNumber.charAt(i) == '+')
|| Character.isSpaceChar(phoneNumber.charAt(i))
|| Character.isDigit(phoneNumber.charAt(i))
|| PHONENUMBER_CHARS.contains(String.valueOf(phoneNumber.charAt(i))))) {
return false;
}
}
// ensure a min length
return phoneNumber.length() >= MIN_PHONENUMBER_LEN;
}
/**
* Checks if the given string is a valid email address.
*
* @param emailAddress String to check.
*
* @return True if the given string is a valid email address, false
* otherwise.
*/
static boolean isValidEmailAddress(String emailAddress) {
if (!StringUtils.isEmpty(emailAddress)) {
EmailValidator validator = EmailValidator.getInstance(true, true);
return validator.isValid(emailAddress);
}
return false;
}
/** /**
* This is a utility class - no need for constructing or subclassing, etc... * This is a utility class - no need for constructing or subclassing, etc...
*/ */

View File

@ -34,8 +34,8 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.CommunicationsUtils;
import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.HashUtility;
import org.sleuthkit.datamodel.InvalidAccountIDException;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -184,7 +184,15 @@ public class CorrelationAttributeUtil {
makeCorrAttrsFromCommunicationArtifacts(correlationAttrs, sourceArtifact); makeCorrAttrsFromCommunicationArtifacts(correlationAttrs, sourceArtifact);
} }
} }
} catch (CentralRepoException ex) { } catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, String.format("Error normalizing correlation attribute (%s)", artifact), ex); // NON-NLS
return correlationAttrs;
}
catch (InvalidAccountIDException ex) {
logger.log(Level.WARNING, String.format("Invalid account identifier (artifactID: %d)", artifact.getId())); // NON-NLS
return correlationAttrs;
}
catch (CentralRepoException ex) {
logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", artifact), ex); // NON-NLS logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", artifact), ex); // NON-NLS
return correlationAttrs; return correlationAttrs;
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
@ -198,18 +206,19 @@ public class CorrelationAttributeUtil {
} }
/** /**
* Makes a correlation attribute instance from a phone number attribute of an * Makes a correlation attribute instance from a phone number attribute of
* artifact. * an artifact.
* *
* @param corrAttrInstances Correlation attributes will be added to this. * @param corrAttrInstances Correlation attributes will be added to this.
* @param artifact An artifact with a phone number attribute. * @param artifact An artifact with a phone number attribute.
* *
* @throws TskCoreException If there is an error querying the case * @throws TskCoreException If there is an error querying the case database.
* database.
* @throws CentralRepoException If there is an error querying the central * @throws CentralRepoException If there is an error querying the central
* repository. * repository.
* @throws CorrelationAttributeNormalizationException If there is an error
* in normalizing the attribute.
*/ */
private static void makeCorrAttrsFromCommunicationArtifacts(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact artifact) throws TskCoreException, CentralRepoException { private static void makeCorrAttrsFromCommunicationArtifacts(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact artifact) throws TskCoreException, CentralRepoException, CorrelationAttributeNormalizationException {
CorrelationAttributeInstance corrAttr = null; CorrelationAttributeInstance corrAttr = null;
/* /*
@ -227,16 +236,16 @@ public class CorrelationAttributeUtil {
/* /*
* Normalize the phone number. * Normalize the phone number.
*/ */
if (value != null) { if (value != null
if(CommunicationsUtils.isValidPhoneNumber(value)) { && CorrelationAttributeNormalizer.isValidPhoneNumber(value)) {
value = CommunicationsUtils.normalizePhoneNum(value);
value = CorrelationAttributeNormalizer.normalizePhone(value);
corrAttr = makeCorrAttr(artifact, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), value); corrAttr = makeCorrAttr(artifact, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), value);
if(corrAttr != null) { if (corrAttr != null) {
corrAttrInstances.add(corrAttr); corrAttrInstances.add(corrAttr);
} }
} }
} }
}
/** /**
* Gets the associated artifact of a "meta-artifact" such as an interesting * Gets the associated artifact of a "meta-artifact" such as an interesting
@ -277,7 +286,7 @@ public class CorrelationAttributeUtil {
* *
* @return The correlation attribute instance. * @return The correlation attribute instance.
*/ */
private static void makeCorrAttrFromAcctArtifact(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact acctArtifact) throws TskCoreException, CentralRepoException { private static void makeCorrAttrFromAcctArtifact(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact acctArtifact) throws InvalidAccountIDException, TskCoreException, CentralRepoException {
// Get the account type from the artifact // Get the account type from the artifact
BlackboardAttribute accountTypeAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE)); BlackboardAttribute accountTypeAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE));

View File

@ -28,7 +28,7 @@ import org.sleuthkit.datamodel.TskDataException;
/** /**
* *
* Stores information about a Data Source in the correlation engine * Stores information about a Data Source in the Central Repository
* *
*/ */
public class CorrelationDataSource implements Serializable { public class CorrelationDataSource implements Serializable {

View File

@ -24,11 +24,11 @@ import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.SleuthkitCase;
/** /**
* This class abstracts a persona. * This class abstracts a persona.
@ -42,11 +42,9 @@ public class Persona {
* Defines level of confidence in assigning a persona to an account. * Defines level of confidence in assigning a persona to an account.
*/ */
public enum Confidence { public enum Confidence {
UNKNOWN(1, "Unknown"), LOW(1, "Low confidence"),
LOW(2, "Low confidence"), MODERATE(2, "Moderate confidence"),
MEDIUM(3, "Medium confidence"), HIGH(3, "High confidence");
HIGH(4, "High confidence"),
DERIVED(5, "Derived directly");
private final String name; private final String name;
private final int level_id; private final int level_id;
@ -72,7 +70,7 @@ public class Persona {
return confidence; return confidence;
} }
} }
return Confidence.UNKNOWN; return Confidence.LOW;
} }
} }
@ -212,6 +210,7 @@ public class Persona {
* account. * account.
* *
* @return Persona Persona created. * @return Persona Persona created.
*
* @throws CentralRepoException If there is an error creating the Persona. * @throws CentralRepoException If there is an error creating the Persona.
*/ */
public static Persona createPersonaForAccount(String personaName, String comment, PersonaStatus status, CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException { public static Persona createPersonaForAccount(String personaName, String comment, PersonaStatus status, CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException {
@ -236,24 +235,45 @@ public class Persona {
private static Persona createPersona(String name, String comment, PersonaStatus status) throws CentralRepoException { private static Persona createPersona(String name, String comment, PersonaStatus status) throws CentralRepoException {
// generate a UUID for the persona // generate a UUID for the persona
String uuidStr = UUID.randomUUID().toString(); String uuidStr = UUID.randomUUID().toString();
CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name")); CentralRepoExaminer examiner = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now(); Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli(); Long timeStampMillis = instant.toEpochMilli();
String insertClause = " INTO personas (uuid, comment, name, created_date, modified_date, status_id, examiner_id ) "
+ "VALUES ( '" + uuidStr + "', "
+ "'" + ((StringUtils.isBlank(comment) ? "" : SleuthkitCase.escapeSingleQuotes(comment))) + "',"
+ "'" + ((StringUtils.isBlank(name) ? getDefaultName() : SleuthkitCase.escapeSingleQuotes(name))) + "',"
+ timeStampMillis.toString() + ","
+ timeStampMillis.toString() + ","
+ status.getStatusId() + ","
+ examiner.getId()
+ ")";
CentralRepository.getInstance().executeInsertSQL(insertClause); String insertPersonaSQL = "INSERT INTO personas (uuid, comment, name, created_date, modified_date, status_id, examiner_id ) " //NON-NLS
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
List<Object> params = new ArrayList<>();
params.add(uuidStr);
params.add(StringUtils.isBlank(comment) ? "" : comment);
params.add(StringUtils.isBlank(name) ? getDefaultName() : name);
params.add(timeStampMillis);
params.add(timeStampMillis);
params.add(status.getStatusId());
params.add(examiner.getId());
getCRInstance().executeCommand(insertPersonaSQL, params);
return getPersonaByUUID(uuidStr); return getPersonaByUUID(uuidStr);
} }
/**
* Sets the comment of this persona.
*
* @param comment The new comment.
*
* @throws CentralRepoException If there is an error.
*/
public void setComment(String comment) throws CentralRepoException {
String updateSQL = "UPDATE personas SET comment = ? WHERE id = ?";
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
List<Object> params = new ArrayList<>();
params.add(StringUtils.isBlank(comment) ? "" : comment);
params.add(id);
getCRInstance().executeCommand(updateSQL, params);
}
}
/** /**
* Sets the name of this persona * Sets the name of this persona
* *
@ -262,8 +282,15 @@ public class Persona {
* @throws CentralRepoException If there is an error. * @throws CentralRepoException If there is an error.
*/ */
public void setName(String name) throws CentralRepoException { public void setName(String name) throws CentralRepoException {
String updateClause = "UPDATE personas SET name = \"" + name + "\" WHERE id = " + id; String updateSQL = "UPDATE personas SET name = ? WHERE id = ?";
CentralRepository.getInstance().executeUpdateSQL(updateClause); CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
List<Object> params = new ArrayList<>();
params.add(StringUtils.isBlank(name) ? getDefaultName() : name);
params.add(id);
getCRInstance().executeCommand(updateSQL, params);
}
} }
/** /**
@ -276,6 +303,7 @@ public class Persona {
* @param confidence Confidence level. * @param confidence Confidence level.
* *
* @return PersonaAccount * @return PersonaAccount
*
* @throws CentralRepoException If there is an error. * @throws CentralRepoException If there is an error.
*/ */
public PersonaAccount addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException { public PersonaAccount addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException {
@ -294,12 +322,33 @@ public class Persona {
PersonaAccount.removePersonaAccount(account.getId()); PersonaAccount.removePersonaAccount(account.getId());
} }
/**
* Modifies the confidence / justification of the given PersonaAccount
*
* @param account Account to modify.
* @param confidence Level of confidence.
* @param justification Justification.
*
* @throws CentralRepoException If there is an error in querying the
* Personas table.
*/
public void modifyAccount(PersonaAccount account, Confidence confidence, String justification) throws CentralRepoException {
PersonaAccount.modifyPersonaAccount(account.getId(), confidence, justification);
}
/** /**
* Marks this persona as deleted * Marks this persona as deleted
*/ */
public void delete() throws CentralRepoException { public void delete() throws CentralRepoException {
String deleteSQL = "UPDATE personas SET status_id = " + PersonaStatus.DELETED.status_id + " WHERE id = " + this.id; String deleteSQL = "UPDATE personas SET status_id = ? WHERE id = ?";
CentralRepository.getInstance().executeUpdateSQL(deleteSQL); CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
List<Object> params = new ArrayList<>();
params.add(PersonaStatus.DELETED.getStatusId());
params.add(id);
getCRInstance().executeCommand(deleteSQL, params);
}
} }
/** /**
@ -340,17 +389,17 @@ public class Persona {
// Partial query string to select from personas table, // Partial query string to select from personas table,
// just supply the where clause. // just supply the where clause.
private static final String PERSONA_QUERY = private static final String PERSONA_QUERY
"SELECT p.id, p.uuid, p.name, p.comment, p.created_date, p.modified_date, p.status_id, p.examiner_id, e.login_name, e.display_name " = "SELECT p.id, p.uuid, p.name, p.comment, p.created_date, p.modified_date, p.status_id, p.examiner_id, e.login_name, e.display_name "
+ "FROM personas as p " + "FROM personas as p "
+ "INNER JOIN examiners as e ON e.id = p.examiner_id "; + "INNER JOIN examiners as e ON e.id = p.examiner_id ";
/** /**
* Gets the row from the Personas table with the given UUID, creates and * Gets the row from the Personas table with the given UUID, creates and
* returns the Persona from that data. * returns the Persona from that data.
* *
* @param uuid Persona UUID to match. * @param uuid Persona UUID to match.
*
* @return Persona matching the given UUID, may be null if no match is * @return Persona matching the given UUID, may be null if no match is
* found. * found.
* *
@ -359,12 +408,15 @@ public class Persona {
*/ */
private static Persona getPersonaByUUID(String uuid) throws CentralRepoException { private static Persona getPersonaByUUID(String uuid) throws CentralRepoException {
String queryClause = String queryClause
PERSONA_QUERY = PERSONA_QUERY
+ "WHERE p.uuid = '" + uuid + "'"; + "WHERE p.uuid = ?";
List<Object> params = new ArrayList<>();
params.add(uuid);
PersonaQueryCallback queryCallback = new PersonaQueryCallback(); PersonaQueryCallback queryCallback = new PersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(queryClause, params, queryCallback);
Collection<Persona> personas = queryCallback.getPersonas(); Collection<Persona> personas = queryCallback.getPersonas();
@ -372,10 +424,11 @@ public class Persona {
} }
/** /**
* Gets the rows from the Personas table with matching name. * Gets the rows from the Personas table with matching name. Persona marked
* Persona marked as DELETED are not returned. * as DELETED are not returned.
* *
* @param partialName Name substring to match. * @param partialName Name substring to match.
*
* @return Collection of personas matching the given name substring, may be * @return Collection of personas matching the given name substring, may be
* empty if no match is found. * empty if no match is found.
* *
@ -385,15 +438,76 @@ public class Persona {
public static Collection<Persona> getPersonaByName(String partialName) throws CentralRepoException { public static Collection<Persona> getPersonaByName(String partialName) throws CentralRepoException {
String queryClause = PERSONA_QUERY String queryClause = PERSONA_QUERY
+ "WHERE p.status_id != " + PersonaStatus.DELETED.status_id + + "WHERE p.status_id != ? "
" AND LOWER(p.name) LIKE " + "LOWER('%" + partialName + "%')" ; + " AND LOWER(p.name) LIKE LOWER(?) ESCAPE '!'";
List<Object> params = new ArrayList<>();
params.add(PersonaStatus.DELETED.getStatusId());
params.add("%" + getLikeEscaped(partialName) + "%"); // partial substring search
PersonaQueryCallback queryCallback = new PersonaQueryCallback(); PersonaQueryCallback queryCallback = new PersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(queryClause, params, queryCallback);
return queryCallback.getPersonas(); return queryCallback.getPersonas();
} }
/**
* Escapes string for use with like statements removing '%', '_', '\'. This
* uses '!' as the escape character and the sql should reflect this
* accordingly. See
* https://stackoverflow.com/questions/8247970/using-like-wildcard-in-prepared-statement,
* https://www.postgresql.org/docs/8.3/functions-matching.html and
* https://www.sqlite.org/lang_expr.html for more information.
*
* @param initial The initial string.
*
* @return The resulting string.
*/
private static String getLikeEscaped(String initial) {
if (initial == null) {
return null;
}
return initial
.replace("!", "!!")
.replace("%", "!%")
.replace("_", "!_");
}
/**
* Gets the rows from the Personas table where persona accounts' names are
* similar to the given one. Persona marked as DELETED are not returned.
*
* @param partialName Name substring to match.
*
* @return Collection of personas matching the given name substring, may be
* empty if no match is found.
*
* @throws CentralRepoException If there is an error in querying the
* Personas table.
*/
public static Collection<Persona> getPersonaByAccountIdentifierLike(String partialName) throws CentralRepoException {
String queryClause = "SELECT p.id, p.uuid, p.name, p.comment, p.created_date, p.modified_date, p.status_id, p.examiner_id, e.login_name\n"
+ "FROM personas p\n"
+ "LEFT JOIN examiners e ON e.id = p.examiner_id\n"
+ "WHERE p.status_id <> ?\n"
+ "AND p.id IN (\n"
+ " SELECT pa.persona_id\n"
+ " FROM persona_accounts pa\n"
+ " INNER JOIN accounts a ON a.id = pa.account_id\n"
+ " WHERE LOWER(a.account_unique_identifier) LIKE LOWER(?) ESCAPE '!'\n"
+ ")";
PersonaQueryCallback queryCallback = new PersonaQueryCallback();
List<Object> params = new ArrayList<>();
params.add(PersonaStatus.DELETED.getStatusId());
params.add("%" + getLikeEscaped(partialName) + "%"); // partial substring search
getCRInstance().executeQuery(queryClause, params, queryCallback);
return queryCallback.getPersonas();
}
/** /**
* Creates an alias for the Persona. * Creates an alias for the Persona.
* *
@ -402,6 +516,7 @@ public class Persona {
* @param confidence Confidence level. * @param confidence Confidence level.
* *
* @return PersonaAlias * @return PersonaAlias
*
* @throws CentralRepoException If there is an error in creating the alias. * @throws CentralRepoException If there is an error in creating the alias.
*/ */
public PersonaAlias addAlias(String alias, String justification, Persona.Confidence confidence) throws CentralRepoException { public PersonaAlias addAlias(String alias, String justification, Persona.Confidence confidence) throws CentralRepoException {
@ -409,7 +524,7 @@ public class Persona {
} }
/** /**
* Removes the given alias * Removes the given alias.
* *
* @param alias alias to remove * @param alias alias to remove
* *
@ -420,6 +535,20 @@ public class Persona {
PersonaAlias.removePersonaAlias(alias); PersonaAlias.removePersonaAlias(alias);
} }
/**
* Modifies the given alias.
*
* @param key Key for the alias to modify.
* @param confidence Level of confidence.
* @param justification Justification.
*
* @throws CentralRepoException If there is an error in querying the
* Personas table.
*/
public void modifyAlias(PersonaAlias key, Confidence confidence, String justification) throws CentralRepoException {
PersonaAlias.modifyPersonaAlias(key, confidence, justification);
}
/** /**
* Gets all aliases for the persona. * Gets all aliases for the persona.
* *
@ -440,6 +569,7 @@ public class Persona {
* @param confidence Confidence level. * @param confidence Confidence level.
* *
* @return PersonaMetadata * @return PersonaMetadata
*
* @throws CentralRepoException If there is an error in adding metadata. * @throws CentralRepoException If there is an error in adding metadata.
*/ */
public PersonaMetadata addMetadata(String name, String value, String justification, Persona.Confidence confidence) throws CentralRepoException { public PersonaMetadata addMetadata(String name, String value, String justification, Persona.Confidence confidence) throws CentralRepoException {
@ -447,7 +577,7 @@ public class Persona {
} }
/** /**
* Removes the given metadata from this persona * Removes the given metadata from this persona.
* *
* @param metadata metadata to remove * @param metadata metadata to remove
* *
@ -458,6 +588,20 @@ public class Persona {
PersonaMetadata.removePersonaMetadata(metadata); PersonaMetadata.removePersonaMetadata(metadata);
} }
/**
* Modifies the given metadata.
*
* @param key Key for the metadata to modify.
* @param confidence Level of confidence.
* @param justification Justification.
*
* @throws CentralRepoException If there is an error in querying the
* Personas table.
*/
public void modifyMetadata(PersonaMetadata key, Confidence confidence, String justification) throws CentralRepoException {
PersonaMetadata.modifyPersonaMetadata(key, confidence, justification);
}
/** /**
* Gets all metadata for the persona. * Gets all metadata for the persona.
* *
@ -494,7 +638,7 @@ public class Persona {
while (resultSet.next()) { while (resultSet.next()) {
// get Case for case_id // get Case for case_id
CorrelationCase correlationCase = CentralRepository.getInstance().getCaseById(resultSet.getInt("case_id")); CorrelationCase correlationCase = getCRInstance().getCaseById(resultSet.getInt("case_id"));
correlationCases.add(correlationCase); correlationCases.add(correlationCase);
} }
} }
@ -508,6 +652,7 @@ public class Persona {
* Gets a list of cases that the persona appears in. * Gets a list of cases that the persona appears in.
* *
* @return Collection of cases that the persona appears in, may be empty. * @return Collection of cases that the persona appears in, may be empty.
*
* @throws CentralRepoException If there is an error in getting the cases * @throws CentralRepoException If there is an error in getting the cases
* from the database. * from the database.
*/ */
@ -519,14 +664,17 @@ public class Persona {
Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId()); Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId());
for (CentralRepoAccount account : accounts) { for (CentralRepoAccount account : accounts) {
int corrTypeId = account.getAccountType().getCorrelationTypeId(); int corrTypeId = account.getAccountType().getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId); CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType); String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
String querySql = "SELECT DISTINCT case_id FROM " + tableName String querySql = "SELECT DISTINCT case_id FROM " + tableName
+ " WHERE account_id = " + account.getId(); + " WHERE account_id = ?"; // param 1
List<Object> params = new ArrayList<>();
params.add(account.getId());
CaseForAccountInstanceQueryCallback queryCallback = new CaseForAccountInstanceQueryCallback(); CaseForAccountInstanceQueryCallback queryCallback = new CaseForAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback); getCRInstance().executeQuery(querySql, params, queryCallback);
// Add any cases that aren't already on the list. // Add any cases that aren't already on the list.
for (CorrelationCase corrCase : queryCallback.getCases()) { for (CorrelationCase corrCase : queryCallback.getCases()) {
@ -553,8 +701,8 @@ public class Persona {
while (resultSet.next()) { while (resultSet.next()) {
// get Case for case_id // get Case for case_id
CorrelationCase correlationCase = CentralRepository.getInstance().getCaseById(resultSet.getInt("case_id")); CorrelationCase correlationCase = getCRInstance().getCaseById(resultSet.getInt("case_id"));
CorrelationDataSource correlationDatasource = CentralRepository.getInstance().getDataSourceById(correlationCase, resultSet.getInt("data_source_id")); CorrelationDataSource correlationDatasource = getCRInstance().getDataSourceById(correlationCase, resultSet.getInt("data_source_id"));
// Add data source to list if not already on it. // Add data source to list if not already on it.
if (!correlationDataSources.stream().anyMatch(p -> Objects.equals(p.getDataSourceObjectID(), correlationDatasource.getDataSourceObjectID()))) { if (!correlationDataSources.stream().anyMatch(p -> Objects.equals(p.getDataSourceObjectID(), correlationDatasource.getDataSourceObjectID()))) {
@ -582,14 +730,17 @@ public class Persona {
Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId()); Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId());
for (CentralRepoAccount account : accounts) { for (CentralRepoAccount account : accounts) {
int corrTypeId = account.getAccountType().getCorrelationTypeId(); int corrTypeId = account.getAccountType().getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId); CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType); String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
String querySql = "SELECT case_id, data_source_id FROM " + tableName String querySql = "SELECT case_id, data_source_id FROM " + tableName
+ " WHERE account_id = " + account.getId(); + " WHERE account_id = ?"; // param 1
List<Object> params = new ArrayList<>();
params.add(account.getId());
DatasourceForAccountInstanceQueryCallback queryCallback = new DatasourceForAccountInstanceQueryCallback(); DatasourceForAccountInstanceQueryCallback queryCallback = new DatasourceForAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback); getCRInstance().executeQuery(querySql, params, queryCallback);
// Add any data sources that aren't already on the list. // Add any data sources that aren't already on the list.
for (CorrelationDataSource correlationDatasource : queryCallback.getDataSources()) { for (CorrelationDataSource correlationDatasource : queryCallback.getDataSources()) {
@ -646,13 +797,15 @@ public class Persona {
* the X_instance table for the given account type. * the X_instance table for the given account type.
* *
* @param crAccountType Account type to generate the query string for. * @param crAccountType Account type to generate the query string for.
*
* @return Query substring. * @return Query substring.
*
* @throws CentralRepoException * @throws CentralRepoException
*/ */
private static String getPersonaFromInstanceTableQueryTemplate(CentralRepoAccount.CentralRepoAccountType crAccountType) throws CentralRepoException { private static String getPersonaFromInstanceTableQueryTemplate(CentralRepoAccount.CentralRepoAccountType crAccountType) throws CentralRepoException {
int corrTypeId = crAccountType.getCorrelationTypeId(); int corrTypeId = crAccountType.getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId); CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String instanceTableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType); String instanceTableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
return "SELECT " + instanceTableName + ".account_id, case_id, data_source_id, " return "SELECT " + instanceTableName + ".account_id, case_id, data_source_id, "
@ -671,20 +824,25 @@ public class Persona {
* @param correlationCase Case to look the persona in. * @param correlationCase Case to look the persona in.
* *
* @return Collection of personas, may be empty. * @return Collection of personas, may be empty.
*
* @throws CentralRepoException * @throws CentralRepoException
*/ */
public static Collection<Persona> getPersonasForCase(CorrelationCase correlationCase) throws CentralRepoException { public static Collection<Persona> getPersonasForCase(CorrelationCase correlationCase) throws CentralRepoException {
Collection<Persona> personaList = new ArrayList<>(); Collection<Persona> personaList = new ArrayList<>();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = CentralRepository.getInstance().getAllAccountTypes(); Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = getCRInstance().getAllAccountTypes();
for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) { for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) {
String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType) String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType)
+ " WHERE case_id = " + correlationCase.getID() + " WHERE case_id = ?" // param 1
+ "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); + " AND personas.status_id != ?"; // param 2
List<Object> params = new ArrayList<>();
params.add(correlationCase.getID());
params.add(Persona.PersonaStatus.DELETED.getStatusId());
PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback(); PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback); getCRInstance().executeQuery(querySql, params, queryCallback);
// Add persona that aren't already on the list. // Add persona that aren't already on the list.
for (Persona persona : queryCallback.getPersonasList()) { for (Persona persona : queryCallback.getPersonasList()) {
@ -703,20 +861,25 @@ public class Persona {
* @param dataSource Data source to look the persona in. * @param dataSource Data source to look the persona in.
* *
* @return Collection of personas, may be empty. * @return Collection of personas, may be empty.
*
* @throws CentralRepoException * @throws CentralRepoException
*/ */
public static Collection<Persona> getPersonasForDataSource(CorrelationDataSource dataSource) throws CentralRepoException { public static Collection<Persona> getPersonasForDataSource(CorrelationDataSource dataSource) throws CentralRepoException {
Collection<Persona> personaList = new ArrayList<>(); Collection<Persona> personaList = new ArrayList<>();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = CentralRepository.getInstance().getAllAccountTypes(); Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = getCRInstance().getAllAccountTypes();
for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) { for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) {
String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType) String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType)
+ " WHERE data_source_id = " + dataSource.getID() + " WHERE data_source_id = ?"
+ "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); + " AND personas.status_id != ?";
List<Object> params = new ArrayList<>();
params.add(dataSource.getID());
params.add(Persona.PersonaStatus.DELETED.getStatusId());
PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback(); PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback); getCRInstance().executeQuery(querySql, params, queryCallback);
// Add persona that aren't already on the list. // Add persona that aren't already on the list.
for (Persona persona : queryCallback.getPersonasList()) { for (Persona persona : queryCallback.getPersonasList()) {
@ -728,4 +891,22 @@ public class Persona {
} }
return personaList; return personaList;
} }
/**
* Wraps the call to CentralRepository.getInstance() throwing an exception
* if instance is null;
*
* @return Instance of CentralRepository
*
* @throws CentralRepoException
*/
private static CentralRepository getCRInstance() throws CentralRepoException {
CentralRepository instance = CentralRepository.getInstance();
if (instance == null) {
throw new CentralRepoException("Failed to get instance of CentralRespository, CR was null");
}
return instance;
}
} }

View File

@ -24,9 +24,9 @@ import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
/** /**
@ -127,38 +127,41 @@ public class PersonaAccount {
* @param confidence Confidence level. * @param confidence Confidence level.
* *
* @return PersonaAccount * @return PersonaAccount
*
* @throws CentralRepoException If there is an error in creating the * @throws CentralRepoException If there is an error in creating the
* account. * account.
*/ */
static PersonaAccount addPersonaAccount(Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException { static PersonaAccount addPersonaAccount(Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance(); CentralRepoExaminer currentExaminer = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
if(cr == null) {
throw new CentralRepoException("Failed to add Persona, Central Repository is not enable");
}
CentralRepoExaminer currentExaminer = cr.getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now(); Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli(); Long timeStampMillis = instant.toEpochMilli();
String insertClause = " INTO persona_accounts (persona_id, account_id, justification, confidence_id, date_added, examiner_id ) "
+ "VALUES ( "
+ persona.getId() + ", "
+ account.getId() + ", "
+ "'" + ((StringUtils.isBlank(justification) ? "" : SleuthkitCase.escapeSingleQuotes(justification))) + "', "
+ confidence.getLevelId() + ", "
+ timeStampMillis.toString() + ", "
+ currentExaminer.getId()
+ ")";
CentralRepository.getInstance().executeInsertSQL(insertClause); String insertSQL = "INSERT INTO persona_accounts (persona_id, account_id, justification, confidence_id, date_added, examiner_id ) "
+ " VALUES ( ?, ?, ?, ?, ?, ?)";
List<Object> params = new ArrayList<>();
params.add(persona.getId());
params.add(account.getId());
params.add(StringUtils.isBlank(justification) ? "" : justification);
params.add(confidence.getLevelId());
params.add(timeStampMillis);
params.add(currentExaminer.getId());
getCRInstance().executeCommand(insertSQL, params);
String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ "WHERE persona_id = ? "
+ " AND account_type_id = ?"
+ " AND account_unique_identifier = ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add(persona.getId());
queryParams.add(account.getAccountType().getAccountTypeId());
queryParams.add(account.getIdentifier());
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ "WHERE persona_id = " + persona.getId()
+ " AND account_type_id = " + account.getAccountType().getAccountTypeId()
+ " AND account_unique_identifier = \"" + account.getIdentifier() + "\"";
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(querySQL, queryParams, queryCallback);
Collection<PersonaAccount> accounts = queryCallback.getPersonaAccountsList(); Collection<PersonaAccount> accounts = queryCallback.getPersonaAccountsList();
if (accounts.size() != 1) { if (accounts.size() != 1) {
@ -203,7 +206,7 @@ public class PersonaAccount {
); );
// create account // create account
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(rs.getString("type_name")); CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
CentralRepoAccount account = new CentralRepoAccount( CentralRepoAccount account = new CentralRepoAccount(
rs.getInt("account_id"), rs.getInt("account_id"),
crAccountType, crAccountType,
@ -226,7 +229,7 @@ public class PersonaAccount {
}; };
// Query clause to select from persona_accounts table to create PersonaAccount(s) // Query clause to select from persona_accounts table to create PersonaAccount(s)
private static final String PERSONA_ACCOUNTS_QUERY_CALUSE = "SELECT persona_accounts.id as persona_accounts_id, justification, confidence_id, date_added, persona_accounts.examiner_id as pa_examiner_id, pa_examiner.login_name as pa_examiner_login_name, pa_examiner.display_name as pa_examiner_display_name," private static final String PERSONA_ACCOUNTS_QUERY_CLAUSE = "SELECT persona_accounts.id as persona_accounts_id, justification, confidence_id, date_added, persona_accounts.examiner_id as pa_examiner_id, pa_examiner.login_name as pa_examiner_login_name, pa_examiner.display_name as pa_examiner_display_name,"
+ " personas.id as persona_id, personas.uuid, personas.name, personas.comment, personas.created_date, personas.modified_date, personas.status_id, " + " personas.id as persona_id, personas.uuid, personas.name, personas.comment, personas.created_date, personas.modified_date, personas.status_id, "
+ " personas.examiner_id as persona_examiner_id, persona_examiner.login_name as persona_examiner_login_name, persona_examiner.display_name as persona_examiner_display_name, " + " personas.examiner_id as persona_examiner_id, persona_examiner.login_name as persona_examiner_login_name, persona_examiner.display_name as persona_examiner_display_name, "
+ " accounts.id as account_id, account_type_id, account_unique_identifier," + " accounts.id as account_id, account_type_id, account_unique_identifier,"
@ -249,21 +252,18 @@ public class PersonaAccount {
* persona_account. * persona_account.
*/ */
static Collection<PersonaAccount> getPersonaAccountsForPersona(long personaId) throws CentralRepoException { static Collection<PersonaAccount> getPersonaAccountsForPersona(long personaId) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance(); String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " WHERE persona_accounts.persona_id = ?";
if (cr != null) { List<Object> queryParams = new ArrayList<>();
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE queryParams.add(personaId);
+ " WHERE persona_accounts.persona_id = " + personaId;
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(querySQL, queryParams, queryCallback);
return queryCallback.getPersonaAccountsList(); return queryCallback.getPersonaAccountsList();
} }
return new ArrayList<>();
}
/** /**
* Gets all the Persona for the specified Account. * Gets all the Persona for the specified Account.
* *
@ -275,22 +275,19 @@ public class PersonaAccount {
* persona_account. * persona_account.
*/ */
public static Collection<PersonaAccount> getPersonaAccountsForAccount(long accountId) throws CentralRepoException { public static Collection<PersonaAccount> getPersonaAccountsForAccount(long accountId) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " WHERE persona_accounts.account_id = " + accountId + " WHERE persona_accounts.account_id = ?"
+ "AND p.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); + " AND personas.status_id != ?";
CentralRepository cr = CentralRepository.getInstance(); List<Object> queryParams = new ArrayList<>();
queryParams.add(accountId);
queryParams.add(Persona.PersonaStatus.DELETED.getStatusId());
if (cr != null) {
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(querySQL, queryParams, queryCallback);
return queryCallback.getPersonaAccountsList(); return queryCallback.getPersonaAccountsList();
} }
return new ArrayList<>();
}
/** /**
* Gets all the Persona associated with all the accounts matching the given * Gets all the Persona associated with all the accounts matching the given
* account identifier substring. * account identifier substring.
@ -304,21 +301,19 @@ public class PersonaAccount {
* persona_account. * persona_account.
*/ */
public static Collection<PersonaAccount> getPersonaAccountsForIdentifierLike(String accountIdentifierSubstring) throws CentralRepoException { public static Collection<PersonaAccount> getPersonaAccountsForIdentifierLike(String accountIdentifierSubstring) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')" + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER(?)"
+ "AND p.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); + " AND personas.status_id != ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add("%" + accountIdentifierSubstring + "%"); // substring match
queryParams.add(Persona.PersonaStatus.DELETED.getStatusId());
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(querySQL, queryParams, queryCallback);
return queryCallback.getPersonaAccountsList(); return queryCallback.getPersonaAccountsList();
} }
return new ArrayList<>();
}
/** /**
* Gets all the Persona associated with the given account. * Gets all the Persona associated with the given account.
* *
@ -330,19 +325,21 @@ public class PersonaAccount {
* @throws CentralRepoException * @throws CentralRepoException
*/ */
public static Collection<PersonaAccount> getPersonaAccountsForAccount(Account account) throws CentralRepoException { public static Collection<PersonaAccount> getPersonaAccountsForAccount(Account account) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + account.getTypeSpecificID() + "%') AND type_name = '" + account.getAccountType().getTypeName() + "' "; + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER(?)"
+ " AND type_name = ?"
+ " AND personas.status_id != ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add("%" + account.getTypeSpecificID() + "%"); // substring match
queryParams.add(account.getAccountType().getTypeName());
queryParams.add(Persona.PersonaStatus.DELETED.getStatusId());
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(querySQL, queryParams, queryCallback);
return queryCallback.getPersonaAccountsList(); return queryCallback.getPersonaAccountsList();
} }
return new ArrayList<>();
}
/** /**
* Removes the PersonaAccount row by the given id * Removes the PersonaAccount row by the given id
* *
@ -352,14 +349,30 @@ public class PersonaAccount {
* account. * account.
*/ */
static void removePersonaAccount(long id) throws CentralRepoException { static void removePersonaAccount(long id) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance(); String deleteSQL = " DELETE FROM persona_accounts WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(id);
if(cr == null) { getCRInstance().executeCommand(deleteSQL, params);
throw new CentralRepoException("Failed to remove persona account, Central Repo is not enabled");
} }
String deleteClause = " DELETE FROM persona_accounts WHERE id = " + id; /**
cr.executeDeleteSQL(deleteClause); * Modifies the PersonaAccount row by the given id
*
* @param id row id for the account to be removed
*
* @throws CentralRepoException If there is an error in removing the
* account.
*/
static void modifyPersonaAccount(long id, Persona.Confidence confidence, String justification) throws CentralRepoException {
String updateSQL = "UPDATE persona_accounts SET confidence_id = ?, justification = ? WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(confidence.getLevelId());
params.add(StringUtils.isBlank(justification) ? "" : justification);
params.add(id);
getCRInstance().executeCommand(updateSQL, params);
} }
/** /**
@ -376,7 +389,7 @@ public class PersonaAccount {
while (rs.next()) { while (rs.next()) {
// create account // create account
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(rs.getString("type_name")); CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
CentralRepoAccount account = new CentralRepoAccount( CentralRepoAccount account = new CentralRepoAccount(
rs.getInt("account_id"), rs.getInt("account_id"),
crAccountType, crAccountType,
@ -398,27 +411,43 @@ public class PersonaAccount {
* *
* @return Collection of all accounts associated with the given persona, may * @return Collection of all accounts associated with the given persona, may
* be empty. * be empty.
*
* @throws CentralRepoException If there is an error in getting the * @throws CentralRepoException If there is an error in getting the
* accounts. * accounts.
*/ */
static Collection<CentralRepoAccount> getAccountsForPersona(long personaId) throws CentralRepoException { static Collection<CentralRepoAccount> getAccountsForPersona(long personaId) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
String queryClause = "SELECT account_id, " String queryClause = "SELECT account_id, "
+ " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier," + " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier,"
+ " account_types.type_name as type_name " + " account_types.type_name as type_name "
+ " FROM persona_accounts " + " FROM persona_accounts "
+ " JOIN accounts as accounts on persona_accounts.account_id = accounts.id " + " JOIN accounts as accounts on persona_accounts.account_id = accounts.id "
+ " JOIN account_types as account_types on accounts.account_type_id = account_types.id " + " JOIN account_types as account_types on accounts.account_type_id = account_types.id "
+ " WHERE persona_accounts.persona_id = " + personaId; + " WHERE persona_accounts.persona_id = ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add(personaId);
AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback(); AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(queryClause, queryParams, queryCallback);
return queryCallback.getAccountsList(); return queryCallback.getAccountsList();
} }
return new ArrayList<>(); /**
* Wraps the call to CentralRepository.getInstance() throwing an exception
* if instance is null;
*
* @return Instance of CentralRepository
*
* @throws CentralRepoException
*/
private static CentralRepository getCRInstance() throws CentralRepoException {
CentralRepository instance = CentralRepository.getInstance();
if (instance == null) {
throw new CentralRepoException("Failed to get instance of CentralRespository, CR was null");
}
return instance;
} }
} }

View File

@ -24,18 +24,18 @@ import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.datamodel.SleuthkitCase;
/** /**
* This class abstracts an alias assigned to a Persona. * This class abstracts an alias assigned to a Persona. A Persona may have
* A Persona may have multiple aliases. * multiple aliases.
* *
*/ */
public class PersonaAlias { public class PersonaAlias {
private static final String SELECT_QUERY_BASE = private static final String SELECT_QUERY_BASE
"SELECT pa.id, pa.persona_id, pa.alias, pa.justification, pa.confidence_id, pa.date_added, pa.examiner_id, e.login_name, e.display_name " = "SELECT pa.id, pa.persona_id, pa.alias, pa.justification, pa.confidence_id, pa.date_added, pa.examiner_id, e.login_name, e.display_name "
+ "FROM persona_alias as pa " + "FROM persona_alias as pa "
+ "INNER JOIN examiners as e ON e.id = pa.examiner_id "; + "INNER JOIN examiners as e ON e.id = pa.examiner_id ";
@ -98,31 +98,38 @@ public class PersonaAlias {
*/ */
static PersonaAlias addPersonaAlias(Persona persona, String alias, String justification, Persona.Confidence confidence) throws CentralRepoException { static PersonaAlias addPersonaAlias(Persona persona, String alias, String justification, Persona.Confidence confidence) throws CentralRepoException {
CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name")); CentralRepoExaminer examiner = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now(); Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli(); Long timeStampMillis = instant.toEpochMilli();
String insertClause = " INTO persona_alias (persona_id, alias, justification, confidence_id, date_added, examiner_id ) " String insertSQL = "INSERT INTO persona_alias (persona_id, alias, justification, confidence_id, date_added, examiner_id ) "
+ "VALUES ( " + " VALUES ( ?, ?, ?, ?, ?, ?)";
+ persona.getId() + ", "
+ "'" + alias + "', "
+ "'" + ((StringUtils.isBlank(justification) ? "" : SleuthkitCase.escapeSingleQuotes(justification))) + "', "
+ confidence.getLevelId() + ", "
+ timeStampMillis.toString() + ", "
+ examiner.getId()
+ ")";
CentralRepository.getInstance().executeInsertSQL(insertClause); List<Object> params = new ArrayList<>();
params.add(persona.getId());
params.add(alias);
params.add(StringUtils.isBlank(justification) ? "" : justification);
params.add(confidence.getLevelId());
params.add(timeStampMillis);
params.add(examiner.getId());
getCRInstance().executeCommand(insertSQL, params);
String queryClause = SELECT_QUERY_BASE String queryClause = SELECT_QUERY_BASE
+ "WHERE pa.persona_id = " + persona.getId() + "WHERE pa.persona_id = ?"
+ " AND pa.alias = \"" + alias + "\"" + " AND pa.alias = ?"
+ " AND pa.date_added = " + timeStampMillis + " AND pa.date_added = ?"
+ " AND pa.examiner_id = " + examiner.getId(); + " AND pa.examiner_id = ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add(persona.getId());
queryParams.add(alias);
queryParams.add(timeStampMillis);
queryParams.add(examiner.getId());
PersonaAliasesQueryCallback queryCallback = new PersonaAliasesQueryCallback(); PersonaAliasesQueryCallback queryCallback = new PersonaAliasesQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(queryClause, queryParams, queryCallback);
Collection<PersonaAlias> aliases = queryCallback.getAliases(); Collection<PersonaAlias> aliases = queryCallback.getAliases();
if (aliases.size() != 1) { if (aliases.size() != 1) {
@ -140,8 +147,36 @@ public class PersonaAlias {
* @throws CentralRepoException If there is an error in removing the alias. * @throws CentralRepoException If there is an error in removing the alias.
*/ */
static void removePersonaAlias(PersonaAlias alias) throws CentralRepoException { static void removePersonaAlias(PersonaAlias alias) throws CentralRepoException {
String deleteClause = " DELETE FROM persona_alias WHERE id = " + alias.getId(); String deleteSQL = " DELETE FROM persona_alias WHERE id = ?";
CentralRepository.getInstance().executeDeleteSQL(deleteClause);
List<Object> params = new ArrayList<>();
params.add(alias.getId());
getCRInstance().executeCommand(deleteSQL, params);
}
/**
* Modifies a PesronaAlias.
*
* @param alias Alias to modify.
*
* @throws CentralRepoException If there is an error in modifying the alias.
*/
static void modifyPersonaAlias(PersonaAlias alias, Persona.Confidence confidence, String justification) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance();
if (cr == null) {
throw new CentralRepoException("Failed to modify persona alias, Central Repo is not enabled");
}
String updateClause = "UPDATE persona_alias SET confidence_id = ?, justification = ? WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(confidence.getLevelId());
params.add(StringUtils.isBlank(justification) ? "" : justification);
params.add(alias.getId());
cr.executeCommand(updateClause, params);
} }
/** /**
@ -186,12 +221,33 @@ public class PersonaAlias {
* @throws CentralRepoException If there is an error in retrieving aliases. * @throws CentralRepoException If there is an error in retrieving aliases.
*/ */
public static Collection<PersonaAlias> getPersonaAliases(long personaId) throws CentralRepoException { public static Collection<PersonaAlias> getPersonaAliases(long personaId) throws CentralRepoException {
String queryClause = SELECT_QUERY_BASE + "WHERE pa.persona_id = " + personaId; String queryClause = SELECT_QUERY_BASE
+ "WHERE pa.persona_id = ?";
List<Object> params = new ArrayList<>();
params.add(personaId);
PersonaAliasesQueryCallback queryCallback = new PersonaAliasesQueryCallback(); PersonaAliasesQueryCallback queryCallback = new PersonaAliasesQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(queryClause, params, queryCallback);
return queryCallback.getAliases(); return queryCallback.getAliases();
} }
/**
* Wraps the call to CentralRepository.getInstance() throwing an exception
* if instance is null;
*
* @return Instance of CentralRepository
*
* @throws CentralRepoException
*/
private static CentralRepository getCRInstance() throws CentralRepoException {
CentralRepository instance = CentralRepository.getInstance();
if (instance == null) {
throw new CentralRepoException("Failed to get instance of CentralRespository, CR was null");
}
return instance;
}
} }

View File

@ -24,20 +24,20 @@ import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.datamodel.SleuthkitCase;
/** /**
* This class abstracts metadata associated with a Persona. * This class abstracts metadata associated with a Persona. Metadata is in the
* Metadata is in the form of a name/value pair. * form of a name/value pair.
* *
* A Persona may have zero or more metadata. * A Persona may have zero or more metadata.
* *
*/ */
public class PersonaMetadata { public class PersonaMetadata {
private static final String SELECT_QUERY_BASE = private static final String SELECT_QUERY_BASE
"SELECT pmd.id, pmd.persona_id, pmd.name, pmd.value, pmd.justification, pmd.confidence_id, pmd.date_added, pmd.examiner_id, e.login_name, e.display_name " = "SELECT pmd.id, pmd.persona_id, pmd.name, pmd.value, pmd.justification, pmd.confidence_id, pmd.date_added, pmd.examiner_id, e.login_name, e.display_name "
+ "FROM persona_metadata as pmd " + "FROM persona_metadata as pmd "
+ "INNER JOIN examiners as e ON e.id = pmd.examiner_id "; + "INNER JOIN examiners as e ON e.id = pmd.examiner_id ";
@ -107,33 +107,41 @@ public class PersonaMetadata {
*/ */
static PersonaMetadata addPersonaMetadata(long personaId, String name, String value, String justification, Persona.Confidence confidence) throws CentralRepoException { static PersonaMetadata addPersonaMetadata(long personaId, String name, String value, String justification, Persona.Confidence confidence) throws CentralRepoException {
CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name")); CentralRepoExaminer examiner = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now(); Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli(); Long timeStampMillis = instant.toEpochMilli();
String insertClause = " INTO persona_metadata (persona_id, name, value, justification, confidence_id, date_added, examiner_id ) " String insertSQL = "INSERT INTO persona_metadata (persona_id, name, value, justification, confidence_id, date_added, examiner_id ) "
+ "VALUES ( " + "VALUES ( ?, ?, ?, ?, ?, ?, ?)";
+ personaId + ", "
+ "'" + name + "', "
+ "'" + value + "', "
+ "'" + ((StringUtils.isBlank(justification) ? "" : SleuthkitCase.escapeSingleQuotes(justification))) + "', "
+ confidence.getLevelId() + ", "
+ timeStampMillis.toString() + ", "
+ examiner.getId()
+ ")";
CentralRepository.getInstance().executeInsertSQL(insertClause); List<Object> params = new ArrayList<>();
params.add(personaId);
params.add(name);
params.add(value);
params.add(StringUtils.isBlank(justification) ? "" : justification);
params.add(confidence.getLevelId());
params.add(timeStampMillis);
params.add(examiner.getId());
getCRInstance().executeCommand(insertSQL, params);
String queryClause = SELECT_QUERY_BASE String queryClause = SELECT_QUERY_BASE
+ "WHERE pmd.persona_id = " + personaId + "WHERE pmd.persona_id = ?"
+ " AND pmd.name = \"" + name + "\"" + " AND pmd.name = ?"
+ " AND pmd.value = \"" + value + "\"" + " AND pmd.value = ?"
+ " AND pmd.date_added = " + timeStampMillis + " AND pmd.date_added = ?"
+ " AND pmd.examiner_id = " + examiner.getId(); + " AND pmd.examiner_id = ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add(personaId);
queryParams.add(name);
queryParams.add(value);
queryParams.add(timeStampMillis);
queryParams.add(examiner.getId());
PersonaMetadataQueryCallback queryCallback = new PersonaMetadataQueryCallback(); PersonaMetadataQueryCallback queryCallback = new PersonaMetadataQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(queryClause, queryParams, queryCallback);
Collection<PersonaMetadata> metadata = queryCallback.getMetadataList(); Collection<PersonaMetadata> metadata = queryCallback.getMetadataList();
if (metadata.size() != 1) { if (metadata.size() != 1) {
@ -148,11 +156,41 @@ public class PersonaMetadata {
* *
* @param metadata Metadata to remove. * @param metadata Metadata to remove.
* *
* @throws CentralRepoException If there is an error in removing the metadata. * @throws CentralRepoException If there is an error in removing the
* metadata.
*/ */
static void removePersonaMetadata(PersonaMetadata metadata) throws CentralRepoException { static void removePersonaMetadata(PersonaMetadata metadata) throws CentralRepoException {
String deleteClause = " DELETE FROM persona_metadata WHERE id = " + metadata.getId(); String deleteSql = " DELETE FROM persona_metadata WHERE id = ?";
CentralRepository.getInstance().executeDeleteSQL(deleteClause);
List<Object> params = new ArrayList<>();
params.add(metadata.getId());
getCRInstance().executeCommand(deleteSql, params);
}
/**
* Modifies the given PersonaMetadata
*
* @param metadata Metadata to modify.
*
* @throws CentralRepoException If there is an error in modifying the
* metadata.
*/
static void modifyPersonaMetadata(PersonaMetadata metadata, Persona.Confidence confidence, String justification) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance();
if (cr == null) {
throw new CentralRepoException("Failed to modify persona metadata, Central Repo is not enabled");
}
String updateSql = "UPDATE persona_metadata SET confidence_id = ?, justification = ? WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(confidence.getLevelId());
params.add(StringUtils.isBlank(justification) ? "" : justification);
params.add(metadata.id);
getCRInstance().executeCommand(updateSql, params);
} }
/** /**
@ -198,13 +236,34 @@ public class PersonaMetadata {
* @throws CentralRepoException If there is an error in retrieving aliases. * @throws CentralRepoException If there is an error in retrieving aliases.
*/ */
static Collection<PersonaMetadata> getPersonaMetadata(long personaId) throws CentralRepoException { static Collection<PersonaMetadata> getPersonaMetadata(long personaId) throws CentralRepoException {
String queryClause = SELECT_QUERY_BASE + "WHERE pmd.persona_id = " + personaId; String queryClause = SELECT_QUERY_BASE
+ "WHERE pmd.persona_id = ?";
List<Object> params = new ArrayList<>();
params.add(personaId);
PersonaMetadataQueryCallback queryCallback = new PersonaMetadataQueryCallback(); PersonaMetadataQueryCallback queryCallback = new PersonaMetadataQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeQuery(queryClause, params, queryCallback);
return queryCallback.getMetadataList(); return queryCallback.getMetadataList();
} }
/**
* Wraps the call to CentralRepository.getInstance() throwing an exception
* if instance is null;
*
* @return Instance of CentralRepository
*
* @throws CentralRepoException
*/
private static CentralRepository getCRInstance() throws CentralRepoException {
CentralRepository instance = CentralRepository.getInstance();
if (instance == null) {
throw new CentralRepoException("Failed to get instance of CentralRespository, CR was null");
}
return instance;
}
} }

View File

@ -41,7 +41,6 @@ import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.lang.StringUtils;
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.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -53,6 +52,7 @@ import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
import org.sleuthkit.datamodel.HashHitInfo; import org.sleuthkit.datamodel.HashHitInfo;
import org.sleuthkit.datamodel.InvalidAccountIDException;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -1081,31 +1081,39 @@ abstract class RdbmsCentralRepo implements CentralRepository {
* within TSK core * within TSK core
*/ */
@Override @Override
public CentralRepoAccount getOrCreateAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException { public CentralRepoAccount getOrCreateAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws InvalidAccountIDException, CentralRepoException {
// Get the account fom the accounts table // Get the account fom the accounts table
CentralRepoAccount account = getAccount(crAccountType, accountUniqueID); String normalizedAccountID = CentralRepoAccount.normalizeAccountIdentifier(crAccountType, accountUniqueID);
// account not found in the table, create it // insert the account. If there is a conflict, ignore it.
if (null == account) { String insertSQL;
switch (CentralRepoDbManager.getSavedDbChoice().getDbPlatform()) {
case POSTGRESQL:
insertSQL = "INSERT INTO accounts (account_type_id, account_unique_identifier) VALUES (?, ?) " + getConflictClause(); //NON-NLS
break;
case SQLITE:
insertSQL = "INSERT OR IGNORE INTO accounts (account_type_id, account_unique_identifier) VALUES (?, ?) "; //NON-NLS
break;
default:
throw new CentralRepoException(String.format("Cannot add account to currently selected CR database platform %s", CentralRepoDbManager.getSavedDbChoice().getDbPlatform())); //NON-NLS
}
String query = "INSERT INTO accounts (account_type_id, account_unique_identifier) "
+ "VALUES ( " + crAccountType.getAccountTypeId() + ", '"
+ accountUniqueID + "' )";
try (Connection connection = connect(); try (Connection connection = connect();
Statement s = connection.createStatement();) { PreparedStatement preparedStatement = connection.prepareStatement(insertSQL);) {
preparedStatement.setInt(1, crAccountType.getAccountTypeId());
preparedStatement.setString(2, normalizedAccountID);
preparedStatement.executeUpdate();
s.execute(query);
// get the account from the db - should exist now. // get the account from the db - should exist now.
account = getAccount(crAccountType, accountUniqueID); return getAccount(crAccountType, normalizedAccountID);
} catch (SQLException ex) { } catch (SQLException ex) {
throw new CentralRepoException("Error adding an account to CR database.", ex); throw new CentralRepoException("Error adding an account to CR database.", ex);
} }
} }
return account;
}
@Override @Override
public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException { public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
try { try {
@ -1183,15 +1191,17 @@ abstract class RdbmsCentralRepo implements CentralRepository {
* @return CentralRepoAccount for the give type/id. May return null if not * @return CentralRepoAccount for the give type/id. May return null if not
* found. * found.
* *
* @throws CentralRepoException * @throws CentralRepoException If there is an error accessing Central Repository.
* @throws InvalidAccountIDException If the account identifier is not valid.
*/ */
private CentralRepoAccount getAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException { @Override
public CentralRepoAccount getAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws InvalidAccountIDException, CentralRepoException {
CentralRepoAccount crAccount = accountsCache.getIfPresent(Pair.of(crAccountType, accountUniqueID)); String normalizedAccountID = CentralRepoAccount.normalizeAccountIdentifier(crAccountType, accountUniqueID);
CentralRepoAccount crAccount = accountsCache.getIfPresent(Pair.of(crAccountType, normalizedAccountID));
if (crAccount == null) { if (crAccount == null) {
crAccount = getCRAccountFromDb(crAccountType, accountUniqueID); crAccount = getCRAccountFromDb(crAccountType, normalizedAccountID);
if (crAccount != null) { if (crAccount != null) {
accountsCache.put(Pair.of(crAccountType, accountUniqueID), crAccount); accountsCache.put(Pair.of(crAccountType, normalizedAccountID), crAccount);
} }
} }
@ -1679,7 +1689,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
bulkArtifacts.get(tableName).clear(); bulkArtifacts.get(tableName).clear();
} }
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Bulk insert"); TimingMetric timingMetric = HealthMonitor.getTimingMetric("Central Repository: Bulk insert");
HealthMonitor.submitTimingMetric(timingMetric); HealthMonitor.submitTimingMetric(timingMetric);
// Reset state // Reset state
@ -2543,88 +2553,51 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
@Override @Override
public void executeInsertSQL(String insertClause) throws CentralRepoException { public void executeCommand(String sql, List<Object> params) throws CentralRepoException {
if (insertClause == null) { try (Connection conn = connect();) {
throw new CentralRepoException("Insert SQL is null");
PreparedStatement preparedStatement = conn.prepareStatement(sql);
// Fill in the params
if (params != null) {
int paramIndex = 1;
for (Object param : params) {
preparedStatement.setObject(paramIndex, param);
paramIndex += 1;
} }
}
String sql = getPlatformSpecificInsertSQL(insertClause); // execute the prepared statement
try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
preparedStatement.executeUpdate(); preparedStatement.executeUpdate();
} catch (SQLException ex) { } catch (SQLException ex) {
throw new CentralRepoException(String.format("Error running SQL %s, exception = %s", sql, ex.getMessage()), ex); throw new CentralRepoException(String.format("Error executing prepared statement for SQL %s", sql), ex);
} }
} }
@Override @Override
public void executeSelectSQL(String selectSQL, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException { public void executeQuery(String sql, List<Object> params, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException {
if (queryCallback == null) { if (queryCallback == null) {
throw new CentralRepoException("Query callback is null"); throw new CentralRepoException("Query callback is null");
} }
if (selectSQL == null) {
throw new CentralRepoException("Select SQL is null"); try ( Connection conn = connect();) {
PreparedStatement preparedStatement = conn.prepareStatement(sql);
// fill in the params
if (params != null) {
int paramIndex = 1;
for (Object param : params) {
preparedStatement.setObject(paramIndex, param);
paramIndex += 1;
} }
StringBuilder sqlSb = new StringBuilder(QUERY_STR_MAX_LEN);
if (selectSQL.trim().toUpperCase().startsWith("SELECT") == false) {
sqlSb.append("SELECT ");
} }
// execute query, and the callback to process result
sqlSb.append(selectSQL); try (ResultSet resultSet = preparedStatement.executeQuery();) {
try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sqlSb.toString());
ResultSet resultSet = preparedStatement.executeQuery();) {
queryCallback.process(resultSet); queryCallback.process(resultSet);
}
} catch (SQLException ex) { } catch (SQLException ex) {
throw new CentralRepoException(String.format("Error running SQL %s, exception = %s", selectSQL, ex.getMessage()), ex); throw new CentralRepoException(String.format("Error executing prepared statement for SQL query %s", sql), ex);
}
}
@Override
public void executeUpdateSQL(String updateSQL) throws CentralRepoException {
if (updateSQL == null) {
throw new CentralRepoException("Update SQL is null");
}
StringBuilder sqlSb = new StringBuilder(QUERY_STR_MAX_LEN);
if (updateSQL.trim().toUpperCase().startsWith("UPDATE") == false) {
sqlSb.append("UPDATE ");
}
sqlSb.append(updateSQL);
try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sqlSb.toString());) {
preparedStatement.executeUpdate();
} catch (SQLException ex) {
throw new CentralRepoException(String.format("Error running SQL %s, exception = %s", updateSQL, ex.getMessage()), ex);
}
}
@Override
public void executeDeleteSQL(String deleteSQL) throws CentralRepoException {
if (deleteSQL == null) {
throw new CentralRepoException("Delete SQL is null");
}
StringBuilder sqlSb = new StringBuilder(QUERY_STR_MAX_LEN);
if (deleteSQL.trim().toUpperCase().startsWith("DELETE") == false) {
sqlSb.append("DELETE ");
}
sqlSb.append(deleteSQL);
try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sqlSb.toString());) {
preparedStatement.executeUpdate();
} catch (SQLException ex) {
throw new CentralRepoException(String.format("Error running SQL %s, exception = %s", deleteSQL, ex.getMessage()), ex);
} }
} }

View File

@ -834,46 +834,25 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
} }
@Override @Override
public void executeInsertSQL(String insertSQL) throws CentralRepoException { public void executeCommand(String sql, List<Object> params) throws CentralRepoException {
try { try {
acquireSharedLock(); acquireExclusiveLock();
super.executeInsertSQL(insertSQL); super.executeCommand(sql, params);
} finally { } finally {
releaseSharedLock(); releaseExclusiveLock();
} }
} }
@Override @Override
public void executeSelectSQL(String selectSQL, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException { public void executeQuery(String sql, List<Object> params, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException {
try { try {
acquireSharedLock(); acquireSharedLock();
super.executeSelectSQL(selectSQL, queryCallback); super.executeQuery(sql, params, queryCallback);
} finally { } finally {
releaseSharedLock(); releaseSharedLock();
} }
} }
@Override
public void executeUpdateSQL(String updateSQL) throws CentralRepoException {
try {
acquireSharedLock();
super.executeUpdateSQL(updateSQL);
} finally {
releaseSharedLock();
}
}
@Override
public void executeDeleteSQL(String deleteSQL) throws CentralRepoException {
try {
acquireSharedLock();
super.executeDeleteSQL(deleteSQL);
} finally {
releaseSharedLock();
}
}
/** /**
* Check whether a reference set with the given name/version is in the * Check whether a reference set with the given name/version is in the
* central repo. Used to check for name collisions when creating reference * central repo. Used to check for name collisions when creating reference

View File

@ -1,5 +1,5 @@
caseeventlistener.evidencetag=Evidence caseeventlistener.evidencetag=Evidence
IngestEventsListener.ingestmodule.name=Correlation Engine IngestEventsListener.ingestmodule.name=Central Repository
IngestEventsListener.prevCaseComment.text=Previous Case: IngestEventsListener.prevCaseComment.text=Previous Case:
# {0} - typeName # {0} - typeName
# {1} - count # {1} - count

View File

@ -73,7 +73,7 @@ import org.sleuthkit.datamodel.CommunicationsUtils;
* Listen for ingest events and update entries in the Central Repository * Listen for ingest events and update entries in the Central Repository
* database accordingly * database accordingly
*/ */
@NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Correlation Engine"}) @NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Central Repository"})
public class IngestEventsListener { public class IngestEventsListener {
private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName()); private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName());
@ -116,24 +116,24 @@ public class IngestEventsListener {
/** /**
* Increase the number of IngestEventsListeners adding contents to the * Increase the number of IngestEventsListeners adding contents to the
* Correlation Engine. * Central Repository.
*/ */
public synchronized static void incrementCorrelationEngineModuleCount() { public synchronized static void incrementCorrelationEngineModuleCount() {
correlationModuleInstanceCount++; //Should be called once in the Correlation Engine module's startup method. correlationModuleInstanceCount++; //Should be called once in the Central Repository module's startup method.
} }
/** /**
* Decrease the number of IngestEventsListeners adding contents to the * Decrease the number of IngestEventsListeners adding contents to the
* Correlation Engine. * Central Repository.
*/ */
public synchronized static void decrementCorrelationEngineModuleCount() { public synchronized static void decrementCorrelationEngineModuleCount() {
if (getCeModuleInstanceCount() > 0) { //prevent it ingestJobCounter from going negative if (getCeModuleInstanceCount() > 0) { //prevent it ingestJobCounter from going negative
correlationModuleInstanceCount--; //Should be called once in the Correlation Engine module's shutdown method. correlationModuleInstanceCount--; //Should be called once in the Central Repository module's shutdown method.
} }
} }
/** /**
* Reset the counter which keeps track of if the Correlation Engine Module * Reset the counter which keeps track of if the Central Repository Module
* is being run during injest to 0. * is being run during injest to 0.
*/ */
synchronized static void resetCeModuleInstanceCount() { synchronized static void resetCeModuleInstanceCount() {
@ -141,10 +141,10 @@ public class IngestEventsListener {
} }
/** /**
* Whether or not the Correlation Engine Module is enabled for any of the * Whether or not the Central Repository Module is enabled for any of the
* currently running ingest jobs. * currently running ingest jobs.
* *
* @return boolean True for Correlation Engine enabled, False for disabled * @return boolean True for Central Repository enabled, False for disabled
*/ */
public synchronized static int getCeModuleInstanceCount() { public synchronized static int getCeModuleInstanceCount() {
return correlationModuleInstanceCount; return correlationModuleInstanceCount;
@ -282,7 +282,7 @@ public class IngestEventsListener {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
//if ingest is running we want there to check if there is a Correlation Engine module running //if ingest is running we want there to check if there is a Central Repository module running
//sometimes artifacts are generated by DSPs or other sources while ingest is not running //sometimes artifacts are generated by DSPs or other sources while ingest is not running
//in these cases we still want to create correlation attributesForNewArtifact for those artifacts when appropriate //in these cases we still want to create correlation attributesForNewArtifact for those artifacts when appropriate
if (!IngestManager.getInstance().isIngestRunning() || getCeModuleInstanceCount() > 0) { if (!IngestManager.getInstance().isIngestRunning() || getCeModuleInstanceCount() > 0) {
@ -349,7 +349,7 @@ public class IngestEventsListener {
if (getCeModuleInstanceCount() == 0) { if (getCeModuleInstanceCount() == 0) {
recentlyAddedCeArtifacts.clear(); recentlyAddedCeArtifacts.clear();
} }
//else another instance of the Correlation Engine Module is still being run. //else another instance of the Central Repository Module is still being run.
/* /*
* Ensure the data source in the Central Repository has hash values * Ensure the data source in the Central Repository has hash values

View File

@ -1,6 +1,6 @@
CentralRepoIngestModel_name_header=Name:<br> CentralRepoIngestModel_name_header=Name:<br>
CentralRepoIngestModel_previous_case_header=<br>Previous Cases:<br> CentralRepoIngestModel_previous_case_header=<br>Previous Cases:<br>
CentralRepoIngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Correlation Engine ingest module. CentralRepoIngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Central Repository ingest module.
CentralRepoIngestModule.notfyBubble.title=Central Repository Not Initialized CentralRepoIngestModule.notfyBubble.title=Central Repository Not Initialized
CentralRepoIngestModule.prevCaseComment.text=Previous Case: CentralRepoIngestModule.prevCaseComment.text=Previous Case:
CentralRepoIngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository) CentralRepoIngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)
@ -8,7 +8,7 @@ CentralRepoIngestModule_notable_message_header=<html>A file in this data source
# {0} - Name of file that is Notable # {0} - Name of file that is Notable
CentralRepoIngestModule_postToBB_knownBadMsg=Notable: {0} CentralRepoIngestModule_postToBB_knownBadMsg=Notable: {0}
CentralRepoIngestModuleFactory.ingestmodule.desc=Saves properties to the central repository for later correlation CentralRepoIngestModuleFactory.ingestmodule.desc=Saves properties to the central repository for later correlation
CentralRepoIngestModuleFactory.ingestmodule.name=Correlation Engine CentralRepoIngestModuleFactory.ingestmodule.name=Central Repository
IngestSettingsPanel.ingestSettingsLabel.text=Ingest Settings IngestSettingsPanel.ingestSettingsLabel.text=Ingest Settings
IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable
IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text=Flag devices previously seen in other cases IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text=Flag devices previously seen in other cases

View File

@ -85,7 +85,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
private final boolean createCorrelationProperties; private final boolean createCorrelationProperties;
/** /**
* Instantiate the Correlation Engine ingest module. * Instantiate the Central Repository ingest module.
* *
* @param settings The ingest settings for the module instance. * @param settings The ingest settings for the module instance.
*/ */
@ -147,7 +147,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
*/ */
if (abstractFile.getKnown() != TskData.FileKnown.KNOWN && flagTaggedNotableItems) { if (abstractFile.getKnown() != TskData.FileKnown.KNOWN && flagTaggedNotableItems) {
try { try {
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Notable artifact query"); TimingMetric timingMetric = HealthMonitor.getTimingMetric("Central Repository: Notable artifact query");
List<String> caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5); List<String> caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5);
HealthMonitor.submitTimingMetric(timingMetric); HealthMonitor.submitTimingMetric(timingMetric);
if (!caseDisplayNamesList.isEmpty()) { if (!caseDisplayNamesList.isEmpty()) {
@ -220,7 +220,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
// see ArtifactManagerTimeTester for details // see ArtifactManagerTimeTester for details
@Messages({ @Messages({
"CentralRepoIngestModule.notfyBubble.title=Central Repository Not Initialized", "CentralRepoIngestModule.notfyBubble.title=Central Repository Not Initialized",
"CentralRepoIngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Correlation Engine ingest module." "CentralRepoIngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Central Repository ingest module."
}) })
@Override @Override
public void startUp(IngestJobContext context) throws IngestModuleException { public void startUp(IngestJobContext context) throws IngestModuleException {
@ -235,7 +235,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
* posited. * posited.
* *
* Note: Flagging cannot be disabled if any other instances of the * Note: Flagging cannot be disabled if any other instances of the
* Correlation Engine module are running. This restriction is to prevent * Central Repository module are running. This restriction is to prevent
* missing results in the case where the first module is flagging * missing results in the case where the first module is flagging
* notable items, and the proceeding module (with flagging disabled) * notable items, and the proceeding module (with flagging disabled)
* causes the first to stop flagging. * causes the first to stop flagging.
@ -276,7 +276,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
// Don't allow sqlite central repo databases to be used for multi user cases // Don't allow sqlite central repo databases to be used for multi user cases
if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
&& (CentralRepoDbManager.getSavedDbChoice().getDbPlatform() == CentralRepoPlatforms.SQLITE)) { && (CentralRepoDbManager.getSavedDbChoice().getDbPlatform() == CentralRepoPlatforms.SQLITE)) {
logger.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository."); logger.log(Level.SEVERE, "Cannot run Central Repository ingest module on a multi-user case with a SQLite central repository.");
throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS
} }
jobId = context.getJobId(); jobId = context.getJobId();

View File

@ -33,7 +33,7 @@ import org.sleuthkit.autopsy.ingest.NoIngestModuleIngestJobSettings;
* Factory for Central Repository ingest modules * Factory for Central Repository ingest modules
*/ */
@ServiceProvider(service = org.sleuthkit.autopsy.ingest.IngestModuleFactory.class) @ServiceProvider(service = org.sleuthkit.autopsy.ingest.IngestModuleFactory.class)
@NbBundle.Messages({"CentralRepoIngestModuleFactory.ingestmodule.name=Correlation Engine", @NbBundle.Messages({"CentralRepoIngestModuleFactory.ingestmodule.name=Central Repository",
"CentralRepoIngestModuleFactory.ingestmodule.desc=Saves properties to the central repository for later correlation"}) "CentralRepoIngestModuleFactory.ingestmodule.desc=Saves properties to the central repository for later correlation"})
public class CentralRepoIngestModuleFactory extends IngestModuleFactoryAdapter { public class CentralRepoIngestModuleFactory extends IngestModuleFactoryAdapter {

View File

@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.ingestmodule;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
/** /**
* Ingest job settings for the Correlation Engine module. * Ingest job settings for the Central Repository module.
*/ */
final class IngestSettings implements IngestModuleIngestJobSettings { final class IngestSettings implements IngestModuleIngestJobSettings {

View File

@ -22,7 +22,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/** /**
* Ingest job settings panel for the Correlation Engine module. * Ingest job settings panel for the Central Repository module.
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {

View File

@ -43,6 +43,7 @@ import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileFilter;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
@ -660,6 +661,8 @@ public class EamDbSettingsDialog extends JDialog {
* found. * found.
*/ */
private static boolean testStatusAndCreate(Component parent, CentralRepoDbManager manager, EamDbSettingsDialog dialog) { private static boolean testStatusAndCreate(Component parent, CentralRepoDbManager manager, EamDbSettingsDialog dialog) {
closePersonasTopComponent();
parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
manager.testStatus(); manager.testStatus();
@ -691,6 +694,21 @@ public class EamDbSettingsDialog extends JDialog {
return true; return true;
} }
/**
* Closes Personas top component if it exists.
*/
private static void closePersonasTopComponent() {
SwingUtilities.invokeLater(() -> {
TopComponent personasWindow = WindowManager.getDefault().findTopComponent("PersonasTopComponent");
if (personasWindow != null && personasWindow.isOpened()) {
personasWindow.close();
}
});
}
/** /**
* 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

@ -1,21 +1,8 @@
CTL_OpenPersonaManager=Persona Manager CTL_OpenPersonas=Personas
CTL_PersonaManagerTopComponentAction=Persona Manager CTL_PersonasTopComponentAction=Personas
CTL_PersonaDetailsTopComponent=Persona Details CTL_PersonaDetailsTopComponent=Persona Details
PersonaManagerTopComponent.createBtn.text=New Persona
PersonaManagerTopComponent.searchBtn.text=Search
PersonaManagerTopComponent.resultsTable.columnModel.title1=Name
PersonaManagerTopComponent.resultsTable.columnModel.title0=ID
PersonaManagerTopComponent.resultsTable.toolTipText=
PersonaManagerTopComponent.searchAccountRadio.text=Account
PersonaManagerTopComponent.searchNameRadio.text=Name
PersonaManagerTopComponent.searchField.text=
AddAccountDialog.cancelBtn.text=Cancel
AddAccountDialog.okBtn.text=OK
PersonaManagerTopComponent.editBtn.text=Edit Persona
PersonaDetailsDialog.cancelBtn.text=Cancel PersonaDetailsDialog.cancelBtn.text=Cancel
PersonaDetailsDialog.okBtn.text=OK PersonaDetailsDialog.okBtn.text=OK
PersonaDetailsPanel.deleteCaseBtn.text=Delete
PersonaDetailsPanel.addCaseBtn.text=Add
PersonaDetailsPanel.casesLbl.text=Cases found in: PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add PersonaDetailsPanel.addAliasBtn.text=Add
@ -31,26 +18,66 @@ PersonaDetailsPanel.nameLbl.text=Name:
AddAliasDialog.accountsLbl.text=Account: AddAliasDialog.accountsLbl.text=Account:
AddAliasDialog.okBtn.text=OK AddAliasDialog.okBtn.text=OK
AddAliasDialog.cancelBtn.text=Cancel AddAliasDialog.cancelBtn.text=Cancel
AddMetadataDialog.cancelBtn.text=Cancel PersonaDetailsPanel.casesLbl.text=Cases found in:
AddMetadataDialog.okBtn.text=OK PersonaDetailsPanel.deleteAliasBtn.text=Delete
AddMetadataDialog.nameLbl.text=Name: PersonaDetailsPanel.addAliasBtn.text=Add
AddMetadataDialog.nameTextField.text= PersonaDetailsPanel.aliasesLabel.text=Aliases:
AddMetadataDialog.valueLbl.text=Value: PersonaDetailsPanel.deleteMetadataBtn.text=Delete
AddMetadataDialog.valueTextField.text= PersonaDetailsPanel.addMetadataBtn.text=Add
AddMetadataDialog.justificationLbl.text=Justification: PersonaDetailsPanel.metadataLabel.text=Metadata:
AddMetadataDialog.justificationTextField.text= PersonaDetailsPanel.deleteAccountBtn.text=Delete
AddMetadataDialog.confidenceLbl.text=Confidence: PersonaDetailsPanel.addAccountBtn.text=Add
AddAliasDialog.justificationLbl.text=Justification: PersonaDetailsPanel.accountsLbl.text=Accounts:
AddAliasDialog.okBtn.text_1=OK PersonaDetailsPanel.commentField.text=
AddAliasDialog.cancelBtn.text_1=Cancel PersonaDetailsPanel.commentLbl.text=Comment:
AddAliasDialog.confidenceLbl.text=Confidence: PersonaDetailsPanel.nameField.text=
AddAliasDialog.justificationTextField.text= PersonaDetailsPanel.nameLbl.text=Name:
AddAliasDialog.aliasLbl.text=Alias: PersonaDetailsPanel.examinerLbl.text=Created by:
AddAliasDialog.aliasTextField.text= PersonaDetailsPanel.examinerField.text=
AddAccountDialog.justificationTextField.text= PersonaDetailsPanel.creationDateLbl.text=Created on:
AddAccountDialog.justificationLbl.text=Justification: PersonaDetailsPanel.creationDateField.text=
AddAccountDialog.confidenceLbl.text=Confidence: PersonaAccountDialog.confidenceLbl.text=Confidence:
AddAccountDialog.typeLbl.text=Type: PersonaAccountDialog.justificationTextField.text=
AddAccountDialog.identiferLbl.text=Identifier: PersonaAccountDialog.justificationLbl.text=Justification:
AddAccountDialog.identifierTextField.text= PersonaAccountDialog.typeLbl.text=Type:
PersonaManagerTopComponent.deleteBtn.text=Delete Persona PersonaAccountDialog.identifierTextField.text=
PersonaAccountDialog.identiferLbl.text=Identifier:
PersonaAccountDialog.okBtn.text=OK
PersonaAccountDialog.cancelBtn.text=Cancel
PersonaAliasDialog.cancelBtn.text_1=Cancel
PersonaAliasDialog.confidenceLbl.text=Confidence:
PersonaAliasDialog.justificationTextField.text=
PersonaAliasDialog.justificationLbl.text=Justification:
PersonaAliasDialog.aliasTextField.text=
PersonaAliasDialog.aliasLbl.text=Alias:
PersonaAliasDialog.okBtn.text_1=OK
PersonaMetadataDialog.confidenceLbl.text=Confidence:
PersonaMetadataDialog.justificationTextField.text=
PersonaMetadataDialog.justificationLbl.text=Justification:
PersonaMetadataDialog.valueTextField.text=
PersonaMetadataDialog.valueLbl.text=Value:
PersonaMetadataDialog.nameTextField.text=
PersonaMetadataDialog.nameLbl.text=Name:
PersonaMetadataDialog.okBtn.text=OK
PersonaMetadataDialog.cancelBtn.text=Cancel
PersonaDetailsPanel.editAccountBtn.text=Edit
PersonaDetailsPanel.editMetadataBtn.text=Edit
PersonaDetailsPanel.editAliasBtn.text=Edit
PersonasTopComponent.searchAccountRadio.text=Account
PersonasTopComponent.searchNameRadio.text=Name
PersonasTopComponent.searchField.text=
PersonasTopComponent.deleteBtn.text=Delete Persona
PersonasTopComponent.editBtn.text=Edit Persona
PersonasTopComponent.createBtn.text=New Persona
PersonasTopComponent.createAccountBtn.text=Create Account
PersonasTopComponent.searchBtn.text=Show
PersonasTopComponent.resultsTable.columnModel.title1=Name
PersonasTopComponent.resultsTable.columnModel.title0=ID
PersonasTopComponent.resultsTable.toolTipText=
CreatePersonaAccountDialog.cancelBtn.text=Cancel
CreatePersonaAccountDialog.typeLbl.text=Type:
CreatePersonaAccountDialog.identifierTextField.text=
CreatePersonaAccountDialog.identiferLbl.text=Identifier:
CreatePersonaAccountDialog.okBtn.text=OK
PersonasTopComponent.introText.text=Personas represent an online identity. They span cases and are stored in the Central Repository based on accounts that were found in artifacts. You can create, edit, and delete personas here.
PersonasTopComponent.cbFilterByKeyword.text=Filter personas by keyword

View File

@ -1,52 +1,42 @@
AddAccountDialog.title.text=Add Account
AddAccountDialog_dup_msg=This account is already added to the persona
AddAccountDialog_dup_Title=Account add failure
AddAccountDialog_empty_msg=The identifier field cannot be empty
AddAccountDialog_empty_Title=Empty identifier
AddAccountDialog_get_types_exception_msg=Failed to access central repository
AddAccountDialog_get_types_exception_Title=Central Repository failure
AddAccountDialog_search_empty_msg=Account not found for given identifier and type
AddAccountDialog_search_empty_Title=Account not found
AddAccountDialog_search_failure_msg=Central Repository account search failed
AddAccountDialog_search_failure_Title=Account add failure
AddAliasDialog.title.text=Add Alias
AddAliasDialog_dup_msg=This alias has already been added to this persona
AddAliasDialog_dup_Title=Alias add failure
AddMetadataDialog.title.text=Add Metadata AddMetadataDialog.title.text=Add Metadata
AddMetadataDialog_dup_msg=A metadata entry with this name has already been added to this persona AddMetadataDialog_dup_msg=A metadata entry with this name has already been added to this persona.
AddMetadataDialog_dup_Title=Metadata add failure AddMetadataDialog_dup_Title=Metadata add failure
CTL_OpenPersonaManager=Persona Manager AddMetadataDialog_empty_name_msg=A metadata entry cannot have an empty name or value.
CTL_PersonaManagerTopComponentAction=Persona Manager AddMetadataDialog_empty_name_Title=Missing field(s)
CreatePersonaAccountDialog.title.text=Create Account
CreatePersonaAccountDialog_error_msg=Failed to create account.
CreatePersonaAccountDialog_error_title=Account failure
CreatePersonaAccountDialog_invalid_account_msg=Account identifier is not valid.
CreatePersonaAccountDialog_invalid_account_Title=Invalid account identifier
CreatePersonaAccountDialog_success_msg=Account added.
CreatePersonaAccountDialog_success_title=Account added
CTL_OpenPersonas=Personas
CTL_PersonasTopComponentAction=Personas
CTL_PersonaDetailsTopComponent=Persona Details CTL_PersonaDetailsTopComponent=Persona Details
OpenPersonasAction.displayName=Persona Manager OpenPersonasAction.displayName=Personas
PersonaDetailsDialogCreateTitle=Create Persona PersonaAccountDialog.title.text=Add Account
PersonaDetailsDialogEditTitle=Edit Persona PersonaAccountDialog_dup_msg=This account is already added to the persona.
PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository PersonaAccountDialog_dup_Title=Account add failure
PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure PersonaAccountDialog_get_types_exception_msg=Failed to access central repository.
PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty PersonaAccountDialog_get_types_exception_Title=Central Repository failure
PersonaDetailsPanel_EmptyName_Title=Empty persona name PersonaAccountDialog_identifier_empty_msg=The identifier field cannot be empty.
PersonaDetailsPanel_load_exception_msg=Failed to load persona PersonaAccountDialog_identifier_empty_Title=Empty identifier
PersonaDetailsPanel_load_exception_Title=Initialization failure PersonaAccountDialog_invalid_account_msg=Account identifier is not valid.
PersonaDetailsPanel_NameCreate=Create Persona PersonaAccountDialog_invalid_account_Title=Invalid account identifier
PersonaDetailsPanel_NameEdit=Edit Persona PersonaAccountDialog_search_empty_msg=Account not found for given identifier and type.
PersonaDetailsPanel_NameView=View Persona PersonaAccountDialog_search_empty_Title=Account not found
PersonaDetailsPanel_NotEnoughAccounts_msg=Two or more accounts are necessary to create a persona PersonaAccountDialog_search_failure_msg=Central Repository account search failed.
PersonaDetailsPanel_NotEnoughAccounts_Title=Not enough accounts PersonaAccountDialog_search_failure_Title=Account add failure
PersonaManagerTopComponent.createBtn.text=New Persona PersonaAliasDialog.title.text=Add Alias
PersonaManagerTopComponent.searchBtn.text=Search PersonaAliasDialog_dup_msg=This alias has already been added to this persona.
PersonaManagerTopComponent.resultsTable.columnModel.title1=Name PersonaAliasDialog_dup_Title=Alias add failure
PersonaManagerTopComponent.resultsTable.columnModel.title0=ID PersonaAliasDialog_empty_msg=An alias cannot be empty.
PersonaManagerTopComponent.resultsTable.toolTipText= PersonaAliasDialog_empty_Title=Empty alias
PersonaManagerTopComponent.searchAccountRadio.text=Account
PersonaManagerTopComponent.searchNameRadio.text=Name
PersonaManagerTopComponent.searchField.text=
AddAccountDialog.cancelBtn.text=Cancel
AddAccountDialog.okBtn.text=OK
PersonaManagerTopComponent.editBtn.text=Edit Persona
PersonaDetailsDialog.cancelBtn.text=Cancel PersonaDetailsDialog.cancelBtn.text=Cancel
PersonaDetailsDialog.okBtn.text=OK PersonaDetailsDialog.okBtn.text=OK
PersonaDetailsPanel.deleteCaseBtn.text=Delete PersonaDetailsDialogCreateTitle=Create Persona
PersonaDetailsPanel.addCaseBtn.text=Add PersonaDetailsDialogEditTitle=Edit Persona
PersonaDetailsDialogViewTitle=View Persona
PersonaDetailsPanel.casesLbl.text=Cases found in: PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add PersonaDetailsPanel.addAliasBtn.text=Add
@ -62,28 +52,86 @@ PersonaDetailsPanel.nameLbl.text=Name:
AddAliasDialog.accountsLbl.text=Account: AddAliasDialog.accountsLbl.text=Account:
AddAliasDialog.okBtn.text=OK AddAliasDialog.okBtn.text=OK
AddAliasDialog.cancelBtn.text=Cancel AddAliasDialog.cancelBtn.text=Cancel
AddMetadataDialog.cancelBtn.text=Cancel PersonaDetailsPanel.casesLbl.text=Cases found in:
AddMetadataDialog.okBtn.text=OK PersonaDetailsPanel.deleteAliasBtn.text=Delete
AddMetadataDialog.nameLbl.text=Name: PersonaDetailsPanel.addAliasBtn.text=Add
AddMetadataDialog.nameTextField.text= PersonaDetailsPanel.aliasesLabel.text=Aliases:
AddMetadataDialog.valueLbl.text=Value: PersonaDetailsPanel.deleteMetadataBtn.text=Delete
AddMetadataDialog.valueTextField.text= PersonaDetailsPanel.addMetadataBtn.text=Add
AddMetadataDialog.justificationLbl.text=Justification: PersonaDetailsPanel.metadataLabel.text=Metadata:
AddMetadataDialog.justificationTextField.text= PersonaDetailsPanel.deleteAccountBtn.text=Delete
AddMetadataDialog.confidenceLbl.text=Confidence: PersonaDetailsPanel.addAccountBtn.text=Add
AddAliasDialog.justificationLbl.text=Justification: PersonaDetailsPanel.accountsLbl.text=Accounts:
AddAliasDialog.okBtn.text_1=OK PersonaDetailsPanel.commentField.text=
AddAliasDialog.cancelBtn.text_1=Cancel PersonaDetailsPanel.commentLbl.text=Comment:
AddAliasDialog.confidenceLbl.text=Confidence: PersonaDetailsPanel.nameField.text=
AddAliasDialog.justificationTextField.text= PersonaDetailsPanel.nameLbl.text=Name:
AddAliasDialog.aliasLbl.text=Alias: PersonaDetailsPanel.examinerLbl.text=Created by:
AddAliasDialog.aliasTextField.text= PersonaDetailsPanel.examinerField.text=
AddAccountDialog.justificationTextField.text= PersonaDetailsPanel.creationDateLbl.text=Created on:
AddAccountDialog.justificationLbl.text=Justification: PersonaDetailsPanel.creationDateField.text=
AddAccountDialog.confidenceLbl.text=Confidence: PersonaAccountDialog.confidenceLbl.text=Confidence:
AddAccountDialog.typeLbl.text=Type: PersonaAccountDialog.justificationTextField.text=
AddAccountDialog.identiferLbl.text=Identifier: PersonaAccountDialog.justificationLbl.text=Justification:
AddAccountDialog.identifierTextField.text= PersonaAccountDialog.typeLbl.text=Type:
PMTopComponent_Name=Persona Manager PersonaAccountDialog.identifierTextField.text=
PMTopComponent_search_exception_msg=Failed to search personas PersonaAccountDialog.identiferLbl.text=Identifier:
PMTopComponent_search_exception_Title=Search failure PersonaAccountDialog.okBtn.text=OK
PersonaAccountDialog.cancelBtn.text=Cancel
PersonaAliasDialog.cancelBtn.text_1=Cancel
PersonaAliasDialog.confidenceLbl.text=Confidence:
PersonaAliasDialog.justificationTextField.text=
PersonaAliasDialog.justificationLbl.text=Justification:
PersonaAliasDialog.aliasTextField.text=
PersonaAliasDialog.aliasLbl.text=Alias:
PersonaAliasDialog.okBtn.text_1=OK
PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository.
PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure
PersonaDetailsPanel_empty_justification_msg=The justification field cannot be empty
PersonaDetailsPanel_empty_justification_Title=Empty justification
PersonaDetailsPanel_EmptyComment_msg=Persona comment cannot be empty.
PersonaDetailsPanel_EmptyComment_Title=Empty persona comment
PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty.
PersonaDetailsPanel_EmptyName_Title=Empty persona name
PersonaDetailsPanel_load_exception_msg=Failed to load persona.
PersonaDetailsPanel_load_exception_Title=Initialization failure
PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs at least one account.
PersonaDetailsPanel_NotEnoughAccounts_Title=Missing account
PersonaMetadataDialog.confidenceLbl.text=Confidence:
PersonaMetadataDialog.justificationTextField.text=
PersonaMetadataDialog.justificationLbl.text=Justification:
PersonaMetadataDialog.valueTextField.text=
PersonaMetadataDialog.valueLbl.text=Value:
PersonaMetadataDialog.nameTextField.text=
PersonaMetadataDialog.nameLbl.text=Name:
PersonaMetadataDialog.okBtn.text=OK
PersonaMetadataDialog.cancelBtn.text=Cancel
PersonaDetailsPanel.editAccountBtn.text=Edit
PersonaDetailsPanel.editMetadataBtn.text=Edit
PersonaDetailsPanel.editAliasBtn.text=Edit
PersonasTopComponent.searchAccountRadio.text=Account
PersonasTopComponent.searchNameRadio.text=Name
PersonasTopComponent.searchField.text=
PersonasTopComponent.deleteBtn.text=Delete Persona
PersonasTopComponent.editBtn.text=Edit Persona
PersonasTopComponent.createBtn.text=New Persona
PersonasTopComponent.createAccountBtn.text=Create Account
PersonasTopComponent.searchBtn.text=Show
PersonasTopComponent.resultsTable.columnModel.title1=Name
PersonasTopComponent.resultsTable.columnModel.title0=ID
PersonasTopComponent.resultsTable.toolTipText=
CreatePersonaAccountDialog.cancelBtn.text=Cancel
CreatePersonaAccountDialog.typeLbl.text=Type:
CreatePersonaAccountDialog.identifierTextField.text=
CreatePersonaAccountDialog.identiferLbl.text=Identifier:
CreatePersonaAccountDialog.okBtn.text=OK
PersonasTopComponent.introText.text=Personas represent an online identity. They span cases and are stored in the Central Repository based on accounts that were found in artifacts. You can create, edit, and delete personas here.
PersonasTopComponent.cbFilterByKeyword.text=Filter personas by keyword
PersonasTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?
PersonasTopComponent_delete_confirmation_Title=Are you sure?
PersonasTopComponent_delete_exception_msg=Failed to delete persona.
PersonasTopComponent_delete_exception_Title=Delete failure
PersonasTopComponent_Name=Personas
PersonasTopComponent_noCR_msg=Central Repository is not enabled.
PersonasTopComponent_search_exception_msg=Failed to search personas.
PersonasTopComponent_search_exception_Title=There was a failure during the search. Try opening a case to fully initialize the central repository database.

View File

@ -1,10 +1,12 @@
AddAccountDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
AddAccountDialog.okBtn.text=OK
PersonaDetailsDialog.okBtn.text=OK PersonaDetailsDialog.okBtn.text=OK
PersonaDetailsDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057 PersonaDetailsDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
AddAliasDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057 AddAliasDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
AddAliasDialog.okBtn.text=OK AddAliasDialog.okBtn.text=OK
AddMetadataDialog.okBtn.text=OK PersonaAccountDialog.okBtn.text=OK
AddMetadataDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057 PersonaAccountDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
AddAliasDialog.cancelBtn.text_1=\u53d6\u308a\u6d88\u3057 PersonaAliasDialog.cancelBtn.text_1=\u53d6\u308a\u6d88\u3057
AddAliasDialog.okBtn.text_1=OK PersonaAliasDialog.okBtn.text_1=OK
PersonaMetadataDialog.okBtn.text=OK
PersonaMetadataDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
CreatePersonaAccountDialog.okBtn.text=OK
CreatePersonaAccountDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
<Property name="resizable" type="boolean" value="false"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace pref="194" max="32767" attributes="0"/>
<Component id="okBtn" linkSize="2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelBtn" linkSize="2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Component id="settingsPanel" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="settingsPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="okBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="settingsPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="typeLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="typeComboBox" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="identiferLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="identifierTextField" pref="281" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="identiferLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="identifierTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="typeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="typeLbl" alignment="3" min="-2" pref="9" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="identiferLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="CreatePersonaAccountDialog.identiferLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="identifierTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="CreatePersonaAccountDialog.identifierTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="identifierTextFieldActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="typeLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="CreatePersonaAccountDialog.typeLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JComboBox" name="typeComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new javax.swing.DefaultComboBoxModel&lt;&gt;(getAllAccountTypes())" type="code"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="cancelBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="CreatePersonaAccountDialog.cancelBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[79, 23]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[79, 23]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[79, 23]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelBtnActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="okBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="CreatePersonaAccountDialog.okBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okBtnActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,290 @@
/*
* 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.persona;
import java.awt.Component;
import java.io.Serializable;
import java.util.Collection;
import java.util.logging.Level;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.InvalidAccountIDException;
/**
* Configuration dialog for creating an account.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class CreatePersonaAccountDialog extends JDialog {
private static final Logger logger = Logger.getLogger(CreatePersonaAccountDialog.class.getName());
private static final long serialVersionUID = 1L;
private final TypeChoiceRenderer TYPE_CHOICE_RENDERER = new TypeChoiceRenderer();
/**
* Creates new create account dialog.
*/
@Messages({"CreatePersonaAccountDialog.title.text=Create Account",})
public CreatePersonaAccountDialog(PersonaDetailsPanel pdp) {
super(SwingUtilities.windowForComponent(pdp),
Bundle.PersonaAccountDialog_title_text(),
ModalityType.APPLICATION_MODAL);
initComponents();
typeComboBox.setRenderer(TYPE_CHOICE_RENDERER);
display();
}
/**
* This class handles displaying and rendering drop down menu for account
* choices.
*/
private class TypeChoiceRenderer extends JLabel implements ListCellRenderer<CentralRepoAccountType>, Serializable {
private static final long serialVersionUID = 1L;
@Override
public Component getListCellRendererComponent(
JList<? extends CentralRepoAccountType> list, CentralRepoAccountType value,
int index, boolean isSelected, boolean cellHasFocus) {
setText(value.getAcctType().getDisplayName());
return this;
}
}
private CentralRepoAccountType[] getAllAccountTypes() {
Collection<CentralRepoAccountType> allAccountTypes;
try {
allAccountTypes = CentralRepository.getInstance().getAllAccountTypes();
} catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to access central repository", e);
JOptionPane.showMessageDialog(this,
Bundle.PersonaAccountDialog_get_types_exception_Title(),
Bundle.PersonaAccountDialog_get_types_exception_msg(),
JOptionPane.ERROR_MESSAGE);
return new CentralRepoAccountType[0];
}
return allAccountTypes.toArray(new CentralRepoAccountType[0]);
}
/**
* 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() {
settingsPanel = new javax.swing.JPanel();
identiferLbl = new javax.swing.JLabel();
identifierTextField = new javax.swing.JTextField();
typeLbl = new javax.swing.JLabel();
typeComboBox = new javax.swing.JComboBox<>();
cancelBtn = new javax.swing.JButton();
okBtn = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setResizable(false);
settingsPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
org.openide.awt.Mnemonics.setLocalizedText(identiferLbl, org.openide.util.NbBundle.getMessage(CreatePersonaAccountDialog.class, "CreatePersonaAccountDialog.identiferLbl.text")); // NOI18N
identifierTextField.setText(org.openide.util.NbBundle.getMessage(CreatePersonaAccountDialog.class, "CreatePersonaAccountDialog.identifierTextField.text")); // NOI18N
identifierTextField.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
identifierTextFieldActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(typeLbl, org.openide.util.NbBundle.getMessage(CreatePersonaAccountDialog.class, "CreatePersonaAccountDialog.typeLbl.text")); // NOI18N
typeComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(getAllAccountTypes()));
javax.swing.GroupLayout settingsPanelLayout = new javax.swing.GroupLayout(settingsPanel);
settingsPanel.setLayout(settingsPanelLayout);
settingsPanelLayout.setHorizontalGroup(
settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(settingsPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(settingsPanelLayout.createSequentialGroup()
.addComponent(typeLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(typeComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(settingsPanelLayout.createSequentialGroup()
.addComponent(identiferLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(identifierTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 281, Short.MAX_VALUE)))
.addContainerGap())
);
settingsPanelLayout.setVerticalGroup(
settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(settingsPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(identiferLbl)
.addComponent(identifierTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(typeLbl, javax.swing.GroupLayout.PREFERRED_SIZE, 9, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
org.openide.awt.Mnemonics.setLocalizedText(cancelBtn, org.openide.util.NbBundle.getMessage(CreatePersonaAccountDialog.class, "CreatePersonaAccountDialog.cancelBtn.text")); // NOI18N
cancelBtn.setMaximumSize(new java.awt.Dimension(79, 23));
cancelBtn.setMinimumSize(new java.awt.Dimension(79, 23));
cancelBtn.setPreferredSize(new java.awt.Dimension(79, 23));
cancelBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelBtnActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(okBtn, org.openide.util.NbBundle.getMessage(CreatePersonaAccountDialog.class, "CreatePersonaAccountDialog.okBtn.text")); // NOI18N
okBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
okBtnActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap(194, Short.MAX_VALUE)
.addComponent(okBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelBtn, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
.addComponent(settingsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelBtn, okBtn});
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(settingsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(okBtn)
.addComponent(cancelBtn, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void display() {
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setVisible(true);
}
@Messages({
"CreatePersonaAccountDialog_error_title=Account failure",
"CreatePersonaAccountDialog_error_msg=Failed to create account.",
"CreatePersonaAccountDialog_invalid_account_Title=Invalid account identifier",
"CreatePersonaAccountDialog_invalid_account_msg=Account identifier is not valid.",})
private CentralRepoAccount createAccount(CentralRepoAccount.CentralRepoAccountType type, String identifier) {
CentralRepoAccount ret = null;
try {
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
ret = cr.getOrCreateAccount(type, identifier);
}
} catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to create account", e);
JOptionPane.showMessageDialog(this,
Bundle.CreatePersonaAccountDialog_error_msg(),
Bundle.CreatePersonaAccountDialog_error_title(),
JOptionPane.ERROR_MESSAGE);
} catch (InvalidAccountIDException e) {
logger.log(Level.WARNING, "Invalid account identifier", e);
JOptionPane.showMessageDialog(this,
Bundle.CreatePersonaAccountDialog_invalid_account_msg(),
Bundle.CreatePersonaAccountDialog_invalid_account_Title(),
JOptionPane.ERROR_MESSAGE);
}
return ret;
}
@Messages({
"CreatePersonaAccountDialog_success_title=Account added",
"CreatePersonaAccountDialog_success_msg=Account added.",
})
private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed
if (StringUtils.isBlank(identifierTextField.getText())) {
JOptionPane.showMessageDialog(this,
Bundle.PersonaAccountDialog_identifier_empty_msg(),
Bundle.PersonaAccountDialog_identifier_empty_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
CentralRepoAccount.CentralRepoAccountType type =
(CentralRepoAccount.CentralRepoAccountType) typeComboBox.getSelectedItem();
String identifier = identifierTextField.getText();
if (createAccount(type, identifier) != null) {
// show account created message
JOptionPane.showMessageDialog(this,
Bundle.CreatePersonaAccountDialog_success_msg(),
Bundle.CreatePersonaAccountDialog_success_title(),
JOptionPane.INFORMATION_MESSAGE);
dispose();
}
}//GEN-LAST:event_okBtnActionPerformed
private void cancelBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelBtnActionPerformed
dispose();
}//GEN-LAST:event_cancelBtnActionPerformed
private void identifierTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_identifierTextFieldActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_identifierTextFieldActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton cancelBtn;
private javax.swing.JLabel identiferLbl;
private javax.swing.JTextField identifierTextField;
private javax.swing.JButton okBtn;
private javax.swing.JPanel settingsPanel;
private javax.swing.JComboBox<org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType> typeComboBox;
private javax.swing.JLabel typeLbl;
// End of variables declaration//GEN-END:variables
}

View File

@ -35,18 +35,18 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
* An Action that opens the Persona Search window. * An Action that opens the Persona Search window.
*/ */
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.centralrepository.persona.OpenPersonaManagerAction") @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.centralrepository.persona.OpenPersonasAction")
@ActionRegistration(displayName = "#CTL_OpenPersonaManager", lazy = false) @ActionRegistration(displayName = "#CTL_OpenPersonas", lazy = false)
@ActionReferences(value = { @ActionReferences(value = {
@ActionReference(path = "Menu/Tools", position = 105) @ActionReference(path = "Menu/Tools", position = 105)
}) })
public final class OpenPersonaManagerAction extends CallableSystemAction { public final class OpenPersonasAction extends CallableSystemAction {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final JMenuItem menuItem; private final JMenuItem menuItem;
public OpenPersonaManagerAction() { public OpenPersonasAction() {
menuItem = super.getMenuPresenter(); menuItem = super.getMenuPresenter();
this.setEnabled(CentralRepository.isEnabled()); this.setEnabled(CentralRepository.isEnabled());
} }
@ -54,7 +54,7 @@ public final class OpenPersonaManagerAction extends CallableSystemAction {
@Override @Override
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void performAction() { public void performAction() {
final TopComponent topComponent = WindowManager.getDefault().findTopComponent("PersonaManagerTopComponent"); final TopComponent topComponent = WindowManager.getDefault().findTopComponent("PersonasTopComponent");
if (topComponent != null) { if (topComponent != null) {
if (topComponent.isOpened() == false) { if (topComponent.isOpened() == false) {
topComponent.open(); topComponent.open();
@ -65,7 +65,7 @@ public final class OpenPersonaManagerAction extends CallableSystemAction {
} }
@Override @Override
@NbBundle.Messages("OpenPersonasAction.displayName=Persona Manager") @NbBundle.Messages("OpenPersonasAction.displayName=Personas")
public String getName() { public String getName() {
return Bundle.OpenPersonasAction_displayName(); return Bundle.OpenPersonasAction_displayName();
} }

View File

@ -25,24 +25,21 @@
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <EmptySpace pref="194" max="32767" attributes="0"/>
<Component id="settingsPanel" alignment="0" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace pref="202" max="32767" attributes="0"/>
<Component id="okBtn" linkSize="2" min="-2" max="-2" attributes="0"/> <Component id="okBtn" linkSize="2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="cancelBtn" linkSize="2" min="-2" max="-2" attributes="0"/> <Component id="cancelBtn" linkSize="2" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
<Component id="settingsPanel" alignment="0" max="32767" attributes="0"/>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="settingsPanel" min="-2" max="-2" attributes="0"/> <Component id="settingsPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="okBtn" alignment="1" min="-2" max="-2" attributes="0"/> <Component id="okBtn" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="cancelBtn" alignment="1" min="-2" max="-2" attributes="0"/> <Component id="cancelBtn" alignment="1" min="-2" max="-2" attributes="0"/>
@ -81,12 +78,12 @@
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="confidenceLbl" min="-2" max="-2" attributes="0"/> <Component id="confidenceLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="confidenceComboBox" pref="269" max="32767" attributes="0"/> <Component id="confidenceComboBox" max="32767" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="justificationLbl" min="-2" max="-2" attributes="0"/> <Component id="justificationLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="justificationTextField" pref="264" max="32767" attributes="0"/> <Component id="justificationTextField" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
@ -101,21 +98,21 @@
<Component id="identiferLbl" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="identiferLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="identifierTextField" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="identifierTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="typeComboBox" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="typeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="typeLbl" alignment="3" min="-2" pref="9" max="-2" attributes="0"/> <Component id="typeLbl" alignment="3" min="-2" pref="9" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="justificationLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="justificationTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="confidenceLbl" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="confidenceLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="confidenceComboBox" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="confidenceComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="justificationLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="justificationTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -125,14 +122,14 @@
<Component class="javax.swing.JLabel" name="identiferLbl"> <Component class="javax.swing.JLabel" name="identiferLbl">
<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/centralrepository/persona/Bundle.properties" key="AddAccountDialog.identiferLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAccountDialog.identiferLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JTextField" name="identifierTextField"> <Component class="javax.swing.JTextField" name="identifierTextField">
<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/centralrepository/persona/Bundle.properties" key="AddAccountDialog.identifierTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAccountDialog.identifierTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events> <Events>
@ -142,7 +139,7 @@
<Component class="javax.swing.JLabel" name="typeLbl"> <Component class="javax.swing.JLabel" name="typeLbl">
<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/centralrepository/persona/Bundle.properties" key="AddAccountDialog.typeLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAccountDialog.typeLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -156,24 +153,10 @@
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType&gt;"/> <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType&gt;"/>
</AuxValues> </AuxValues>
</Component> </Component>
<Component class="javax.swing.JLabel" name="justificationLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="AddAccountDialog.justificationLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="justificationTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="AddAccountDialog.justificationTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="confidenceLbl"> <Component class="javax.swing.JLabel" name="confidenceLbl">
<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/centralrepository/persona/Bundle.properties" key="AddAccountDialog.confidenceLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAccountDialog.confidenceLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -187,12 +170,26 @@
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence&gt;"/> <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence&gt;"/>
</AuxValues> </AuxValues>
</Component> </Component>
<Component class="javax.swing.JLabel" name="justificationLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAccountDialog.justificationLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="justificationTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAccountDialog.justificationTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JButton" name="cancelBtn"> <Component class="javax.swing.JButton" name="cancelBtn">
<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/centralrepository/persona/Bundle.properties" key="AddAccountDialog.cancelBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAccountDialog.cancelBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<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="[79, 23]"/> <Dimension value="[79, 23]"/>
@ -211,7 +208,7 @@
<Component class="javax.swing.JButton" name="okBtn"> <Component class="javax.swing.JButton" name="okBtn">
<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/centralrepository/persona/Bundle.properties" key="AddAccountDialog.okBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAccountDialog.okBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events> <Events>

View File

@ -23,11 +23,12 @@ import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
@ -36,28 +37,31 @@ 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.centralrepository.datamodel.Persona; import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.InvalidAccountIDException;
/** /**
* Configuration dialog for adding an account to a persona. * Configuration dialog for adding an account to a persona.
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class AddAccountDialog extends JDialog { public class PersonaAccountDialog extends JDialog {
private static final Logger logger = Logger.getLogger(AddAccountDialog.class.getName()); private static final Logger logger = Logger.getLogger(PersonaAccountDialog.class.getName());
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final TypeChoiceRenderer TYPE_CHOICE_RENDERER = new TypeChoiceRenderer(); private final TypeChoiceRenderer TYPE_CHOICE_RENDERER = new TypeChoiceRenderer();
private final PersonaDetailsPanel pdp; private final PersonaDetailsPanel pdp;
private PersonaDetailsPanel.PAccount currentAccount = null;
/** /**
* Creates new add account dialog * Creates new add account dialog
*/ */
@Messages({"AddAccountDialog.title.text=Add Account",}) @Messages({"PersonaAccountDialog.title.text=Add Account",})
public AddAccountDialog(PersonaDetailsPanel pdp) { public PersonaAccountDialog(PersonaDetailsPanel pdp) {
super((JFrame) WindowManager.getDefault().getMainWindow(), super(SwingUtilities.windowForComponent(pdp),
Bundle.AddAccountDialog_title_text(), Bundle.PersonaAccountDialog_title_text(),
true); ModalityType.APPLICATION_MODAL);
this.pdp = pdp; this.pdp = pdp;
initComponents(); initComponents();
@ -65,8 +69,29 @@ public class AddAccountDialog extends JDialog {
display(); display();
} }
PersonaAccountDialog(PersonaDetailsPanel pdp, PersonaDetailsPanel.PAccount acc) {
super(SwingUtilities.windowForComponent(pdp),
Bundle.PersonaAccountDialog_title_text(),
ModalityType.APPLICATION_MODAL);
this.pdp = pdp;
initComponents();
currentAccount = acc;
confidenceComboBox.setSelectedItem(acc.confidence);
justificationTextField.setText(acc.justification);
typeComboBox.setSelectedItem(acc.account.getAccountType());
identifierTextField.setText(acc.account.getIdentifier());
typeComboBox.setEnabled(false);
identifierTextField.setEnabled(false);
typeComboBox.setRenderer(TYPE_CHOICE_RENDERER);
display();
}
/** /**
* This class handles displaying and rendering drop down menu for account choices * This class handles displaying and rendering drop down menu for account
* choices
*/ */
private class TypeChoiceRenderer extends JLabel implements ListCellRenderer<CentralRepoAccountType>, Serializable { private class TypeChoiceRenderer extends JLabel implements ListCellRenderer<CentralRepoAccountType>, Serializable {
@ -82,9 +107,8 @@ public class AddAccountDialog extends JDialog {
} }
@Messages({ @Messages({
"AddAccountDialog_get_types_exception_Title=Central Repository failure", "PersonaAccountDialog_get_types_exception_Title=Central Repository failure",
"AddAccountDialog_get_types_exception_msg=Failed to access central repository", "PersonaAccountDialog_get_types_exception_msg=Failed to access central repository.",})
})
private CentralRepoAccountType[] getAllAccountTypes() { private CentralRepoAccountType[] getAllAccountTypes() {
Collection<CentralRepoAccountType> allAccountTypes; Collection<CentralRepoAccountType> allAccountTypes;
try { try {
@ -92,8 +116,8 @@ public class AddAccountDialog extends JDialog {
} catch (CentralRepoException e) { } catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to access central repository", e); logger.log(Level.SEVERE, "Failed to access central repository", e);
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_get_types_exception_Title(), Bundle.PersonaAccountDialog_get_types_exception_Title(),
Bundle.AddAccountDialog_get_types_exception_msg(), Bundle.PersonaAccountDialog_get_types_exception_msg(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return new CentralRepoAccountType[0]; return new CentralRepoAccountType[0];
} }
@ -114,10 +138,10 @@ public class AddAccountDialog extends JDialog {
identifierTextField = new javax.swing.JTextField(); identifierTextField = new javax.swing.JTextField();
typeLbl = new javax.swing.JLabel(); typeLbl = new javax.swing.JLabel();
typeComboBox = new javax.swing.JComboBox<>(); typeComboBox = new javax.swing.JComboBox<>();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
confidenceLbl = new javax.swing.JLabel(); confidenceLbl = new javax.swing.JLabel();
confidenceComboBox = new javax.swing.JComboBox<>(); confidenceComboBox = new javax.swing.JComboBox<>();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
cancelBtn = new javax.swing.JButton(); cancelBtn = new javax.swing.JButton();
okBtn = new javax.swing.JButton(); okBtn = new javax.swing.JButton();
@ -126,27 +150,27 @@ public class AddAccountDialog extends JDialog {
settingsPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); settingsPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
org.openide.awt.Mnemonics.setLocalizedText(identiferLbl, org.openide.util.NbBundle.getMessage(AddAccountDialog.class, "AddAccountDialog.identiferLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(identiferLbl, org.openide.util.NbBundle.getMessage(PersonaAccountDialog.class, "PersonaAccountDialog.identiferLbl.text")); // NOI18N
identifierTextField.setText(org.openide.util.NbBundle.getMessage(AddAccountDialog.class, "AddAccountDialog.identifierTextField.text")); // NOI18N identifierTextField.setText(org.openide.util.NbBundle.getMessage(PersonaAccountDialog.class, "PersonaAccountDialog.identifierTextField.text")); // NOI18N
identifierTextField.addActionListener(new java.awt.event.ActionListener() { identifierTextField.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
identifierTextFieldActionPerformed(evt); identifierTextFieldActionPerformed(evt);
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(typeLbl, org.openide.util.NbBundle.getMessage(AddAccountDialog.class, "AddAccountDialog.typeLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(typeLbl, org.openide.util.NbBundle.getMessage(PersonaAccountDialog.class, "PersonaAccountDialog.typeLbl.text")); // NOI18N
typeComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(getAllAccountTypes())); typeComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(getAllAccountTypes()));
org.openide.awt.Mnemonics.setLocalizedText(justificationLbl, org.openide.util.NbBundle.getMessage(AddAccountDialog.class, "AddAccountDialog.justificationLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(confidenceLbl, org.openide.util.NbBundle.getMessage(PersonaAccountDialog.class, "PersonaAccountDialog.confidenceLbl.text")); // NOI18N
justificationTextField.setText(org.openide.util.NbBundle.getMessage(AddAccountDialog.class, "AddAccountDialog.justificationTextField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(confidenceLbl, org.openide.util.NbBundle.getMessage(AddAccountDialog.class, "AddAccountDialog.confidenceLbl.text")); // NOI18N
confidenceComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence.values())); confidenceComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence.values()));
org.openide.awt.Mnemonics.setLocalizedText(justificationLbl, org.openide.util.NbBundle.getMessage(PersonaAccountDialog.class, "PersonaAccountDialog.justificationLbl.text")); // NOI18N
justificationTextField.setText(org.openide.util.NbBundle.getMessage(PersonaAccountDialog.class, "PersonaAccountDialog.justificationTextField.text")); // NOI18N
javax.swing.GroupLayout settingsPanelLayout = new javax.swing.GroupLayout(settingsPanel); javax.swing.GroupLayout settingsPanelLayout = new javax.swing.GroupLayout(settingsPanel);
settingsPanel.setLayout(settingsPanelLayout); settingsPanel.setLayout(settingsPanelLayout);
settingsPanelLayout.setHorizontalGroup( settingsPanelLayout.setHorizontalGroup(
@ -165,11 +189,11 @@ public class AddAccountDialog extends JDialog {
.addGroup(settingsPanelLayout.createSequentialGroup() .addGroup(settingsPanelLayout.createSequentialGroup()
.addComponent(confidenceLbl) .addComponent(confidenceLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(confidenceComboBox, 0, 269, Short.MAX_VALUE)) .addComponent(confidenceComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(settingsPanelLayout.createSequentialGroup() .addGroup(settingsPanelLayout.createSequentialGroup()
.addComponent(justificationLbl) .addComponent(justificationLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(justificationTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 264, Short.MAX_VALUE))) .addComponent(justificationTextField)))
.addContainerGap()) .addContainerGap())
); );
settingsPanelLayout.setVerticalGroup( settingsPanelLayout.setVerticalGroup(
@ -179,22 +203,22 @@ public class AddAccountDialog extends JDialog {
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(identiferLbl) .addComponent(identiferLbl)
.addComponent(identifierTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(identifierTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(typeLbl, javax.swing.GroupLayout.PREFERRED_SIZE, 9, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(typeLbl, javax.swing.GroupLayout.PREFERRED_SIZE, 9, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(justificationLbl)
.addComponent(justificationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(confidenceLbl) .addComponent(confidenceLbl)
.addComponent(confidenceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(confidenceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(justificationLbl)
.addComponent(justificationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
); );
org.openide.awt.Mnemonics.setLocalizedText(cancelBtn, org.openide.util.NbBundle.getMessage(AddAccountDialog.class, "AddAccountDialog.cancelBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(cancelBtn, org.openide.util.NbBundle.getMessage(PersonaAccountDialog.class, "PersonaAccountDialog.cancelBtn.text")); // NOI18N
cancelBtn.setMaximumSize(new java.awt.Dimension(79, 23)); cancelBtn.setMaximumSize(new java.awt.Dimension(79, 23));
cancelBtn.setMinimumSize(new java.awt.Dimension(79, 23)); cancelBtn.setMinimumSize(new java.awt.Dimension(79, 23));
cancelBtn.setPreferredSize(new java.awt.Dimension(79, 23)); cancelBtn.setPreferredSize(new java.awt.Dimension(79, 23));
@ -204,7 +228,7 @@ public class AddAccountDialog extends JDialog {
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(okBtn, org.openide.util.NbBundle.getMessage(AddAccountDialog.class, "AddAccountDialog.okBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(okBtn, org.openide.util.NbBundle.getMessage(PersonaAccountDialog.class, "PersonaAccountDialog.okBtn.text")); // NOI18N
okBtn.addActionListener(new java.awt.event.ActionListener() { okBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
okBtnActionPerformed(evt); okBtnActionPerformed(evt);
@ -216,14 +240,12 @@ public class AddAccountDialog extends JDialog {
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addContainerGap(194, Short.MAX_VALUE)
.addComponent(settingsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap(202, Short.MAX_VALUE)
.addComponent(okBtn) .addComponent(okBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelBtn, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addComponent(cancelBtn, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap()) .addContainerGap())
.addComponent(settingsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
); );
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelBtn, okBtn}); layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelBtn, okBtn});
@ -231,8 +253,9 @@ public class AddAccountDialog extends JDialog {
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(settingsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(settingsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(okBtn, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(okBtn, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(cancelBtn, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(cancelBtn, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
@ -248,19 +271,29 @@ public class AddAccountDialog extends JDialog {
} }
@Messages({ @Messages({
"AddAccountDialog_dup_Title=Account add failure", "PersonaAccountDialog_dup_Title=Account add failure",
"AddAccountDialog_dup_msg=This account is already added to the persona", "PersonaAccountDialog_dup_msg=This account is already added to the persona.",
"AddAccountDialog_empty_Title=Empty identifier", "PersonaAccountDialog_identifier_empty_Title=Empty identifier",
"AddAccountDialog_empty_msg=The identifier field cannot be empty", "PersonaAccountDialog_identifier_empty_msg=The identifier field cannot be empty.",
"AddAccountDialog_search_failure_Title=Account add failure", "PersonaAccountDialog_search_failure_Title=Account add failure",
"AddAccountDialog_search_failure_msg=Central Repository account search failed", "PersonaAccountDialog_search_failure_msg=Central Repository account search failed.",
"AddAccountDialog_search_empty_Title=Account not found", "PersonaAccountDialog_search_empty_Title=Account not found",
"AddAccountDialog_search_empty_msg=Account not found for given identifier and type",}) "PersonaAccountDialog_search_empty_msg=Account not found for given identifier and type.",
"PersonaAccountDialog_invalid_account_Title=Invalid account identifier",
"PersonaAccountDialog_invalid_account_msg=Account identifier is not valid.",
})
private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed
if (identifierTextField.getText().isEmpty()) { if (StringUtils.isBlank(identifierTextField.getText())) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_empty_msg(), Bundle.PersonaAccountDialog_identifier_empty_msg(),
Bundle.AddAccountDialog_empty_Title(), Bundle.PersonaAccountDialog_identifier_empty_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
if (StringUtils.isBlank(justificationTextField.getText())) {
JOptionPane.showMessageDialog(this,
Bundle.PersonaDetailsPanel_empty_justification_msg(),
Bundle.PersonaDetailsPanel_empty_justification_Title(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
@ -270,15 +303,23 @@ public class AddAccountDialog extends JDialog {
} catch (CentralRepoException e) { } catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to access central repository", e); logger.log(Level.SEVERE, "Failed to access central repository", e);
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_search_failure_msg(), Bundle.PersonaAccountDialog_search_failure_msg(),
Bundle.AddAccountDialog_search_failure_Title(), Bundle.PersonaAccountDialog_search_failure_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
catch (InvalidAccountIDException e) {
logger.log(Level.SEVERE, "Invalid account identifier", e);
JOptionPane.showMessageDialog(this,
Bundle.PersonaAccountDialog_invalid_account_msg(),
Bundle.PersonaAccountDialog_invalid_account_Title(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
if (candidates.isEmpty()) { if (candidates.isEmpty()) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_search_empty_msg(), Bundle.PersonaAccountDialog_search_empty_msg(),
Bundle.AddAccountDialog_search_empty_Title(), Bundle.PersonaAccountDialog_search_empty_Title(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
@ -292,23 +333,29 @@ public class AddAccountDialog extends JDialog {
} }
if (result == null) { if (result == null) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_search_empty_msg(), Bundle.PersonaAccountDialog_search_empty_msg(),
Bundle.AddAccountDialog_search_empty_Title(), Bundle.PersonaAccountDialog_search_empty_Title(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
if (pdp.addAccount( Persona.Confidence confidence = (Persona.Confidence) confidenceComboBox.getSelectedItem();
result, String justification = justificationTextField.getText();
justificationTextField.getText(),
(Persona.Confidence) confidenceComboBox.getSelectedItem())) { if (currentAccount != null) {
currentAccount.confidence = confidence;
currentAccount.justification = justification;
dispose();
} else {
if (pdp.addAccount(result, justification, confidence)) {
dispose(); dispose();
} else { } else {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_dup_msg(), Bundle.PersonaAccountDialog_dup_msg(),
Bundle.AddAccountDialog_dup_Title(), Bundle.PersonaAccountDialog_dup_Title(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
} }
}
}//GEN-LAST:event_okBtnActionPerformed }//GEN-LAST:event_okBtnActionPerformed
private void cancelBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelBtnActionPerformed private void cancelBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelBtnActionPerformed

View File

@ -38,7 +38,7 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="settingsPanel" min="-2" max="-2" attributes="0"/> <Component id="settingsPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="okBtn" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="okBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelBtn" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="cancelBtn" alignment="3" min="-2" max="-2" attributes="0"/>
@ -92,17 +92,17 @@
<Component id="aliasLbl" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="aliasLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="aliasTextField" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="aliasTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="justificationTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="justificationLbl" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="confidenceComboBox" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="confidenceComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="confidenceLbl" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="confidenceLbl" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="justificationTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="justificationLbl" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -111,35 +111,21 @@
<Component class="javax.swing.JLabel" name="aliasLbl"> <Component class="javax.swing.JLabel" name="aliasLbl">
<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/centralrepository/persona/Bundle.properties" key="AddAliasDialog.aliasLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAliasDialog.aliasLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JTextField" name="aliasTextField"> <Component class="javax.swing.JTextField" name="aliasTextField">
<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/centralrepository/persona/Bundle.properties" key="AddAliasDialog.aliasTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAliasDialog.aliasTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="justificationLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="AddAliasDialog.justificationLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="justificationTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="AddAliasDialog.justificationTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="confidenceLbl"> <Component class="javax.swing.JLabel" name="confidenceLbl">
<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/centralrepository/persona/Bundle.properties" key="AddAliasDialog.confidenceLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAliasDialog.confidenceLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -153,12 +139,26 @@
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence&gt;"/> <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence&gt;"/>
</AuxValues> </AuxValues>
</Component> </Component>
<Component class="javax.swing.JLabel" name="justificationLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAliasDialog.justificationLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="justificationTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAliasDialog.justificationTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JButton" name="cancelBtn"> <Component class="javax.swing.JButton" name="cancelBtn">
<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/centralrepository/persona/Bundle.properties" key="AddAliasDialog.cancelBtn.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAliasDialog.cancelBtn.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<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="[79, 23]"/> <Dimension value="[79, 23]"/>
@ -177,7 +177,7 @@
<Component class="javax.swing.JButton" name="okBtn"> <Component class="javax.swing.JButton" name="okBtn">
<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/centralrepository/persona/Bundle.properties" key="AddAliasDialog.okBtn.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaAliasDialog.okBtn.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events> <Events>

View File

@ -19,8 +19,9 @@
package org.sleuthkit.autopsy.centralrepository.persona; package org.sleuthkit.autopsy.centralrepository.persona;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
@ -29,26 +30,45 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
* Configuration dialog for adding aliases to a persona. * Configuration dialog for adding aliases to a persona.
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class AddAliasDialog extends JDialog { public class PersonaAliasDialog extends JDialog {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final PersonaDetailsPanel pdp; private final PersonaDetailsPanel pdp;
private PersonaDetailsPanel.PAlias currentAlias = null;
/** /**
* Creates new add alias dialog * Creates new add alias dialog
*/ */
@Messages({"AddAliasDialog.title.text=Add Alias",}) @Messages({"PersonaAliasDialog.title.text=Add Alias",})
public AddAliasDialog(PersonaDetailsPanel pdp) { public PersonaAliasDialog(PersonaDetailsPanel pdp) {
super((JFrame) WindowManager.getDefault().getMainWindow(), super(SwingUtilities.windowForComponent(pdp),
Bundle.AddAliasDialog_title_text(), Bundle.PersonaAliasDialog_title_text(),
true); ModalityType.APPLICATION_MODAL);
this.pdp = pdp; this.pdp = pdp;
initComponents(); initComponents();
display(); display();
} }
PersonaAliasDialog(PersonaDetailsPanel pdp, PersonaDetailsPanel.PAlias pa) {
super(SwingUtilities.windowForComponent(pdp),
Bundle.PersonaAliasDialog_title_text(),
ModalityType.APPLICATION_MODAL);
this.pdp = pdp;
initComponents();
currentAlias = pa;
confidenceComboBox.setSelectedItem(pa.confidence);
justificationTextField.setText(pa.justification);
aliasTextField.setText(pa.alias);
aliasTextField.setEnabled(false);
display();
}
/** /**
* This method is called from within the constructor to initialize the form. * 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 * WARNING: Do NOT modify this code. The content of this method is always
@ -61,10 +81,10 @@ public class AddAliasDialog extends JDialog {
settingsPanel = new javax.swing.JPanel(); settingsPanel = new javax.swing.JPanel();
aliasLbl = new javax.swing.JLabel(); aliasLbl = new javax.swing.JLabel();
aliasTextField = new javax.swing.JTextField(); aliasTextField = new javax.swing.JTextField();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
confidenceLbl = new javax.swing.JLabel(); confidenceLbl = new javax.swing.JLabel();
confidenceComboBox = new javax.swing.JComboBox<>(); confidenceComboBox = new javax.swing.JComboBox<>();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
cancelBtn = new javax.swing.JButton(); cancelBtn = new javax.swing.JButton();
okBtn = new javax.swing.JButton(); okBtn = new javax.swing.JButton();
@ -73,18 +93,18 @@ public class AddAliasDialog extends JDialog {
settingsPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); settingsPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
org.openide.awt.Mnemonics.setLocalizedText(aliasLbl, org.openide.util.NbBundle.getMessage(AddAliasDialog.class, "AddAliasDialog.aliasLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(aliasLbl, org.openide.util.NbBundle.getMessage(PersonaAliasDialog.class, "PersonaAliasDialog.aliasLbl.text")); // NOI18N
aliasTextField.setText(org.openide.util.NbBundle.getMessage(AddAliasDialog.class, "AddAliasDialog.aliasTextField.text")); // NOI18N aliasTextField.setText(org.openide.util.NbBundle.getMessage(PersonaAliasDialog.class, "PersonaAliasDialog.aliasTextField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(justificationLbl, org.openide.util.NbBundle.getMessage(AddAliasDialog.class, "AddAliasDialog.justificationLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(confidenceLbl, org.openide.util.NbBundle.getMessage(PersonaAliasDialog.class, "PersonaAliasDialog.confidenceLbl.text")); // NOI18N
justificationTextField.setText(org.openide.util.NbBundle.getMessage(AddAliasDialog.class, "AddAliasDialog.justificationTextField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(confidenceLbl, org.openide.util.NbBundle.getMessage(AddAliasDialog.class, "AddAliasDialog.confidenceLbl.text")); // NOI18N
confidenceComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence.values())); confidenceComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence.values()));
org.openide.awt.Mnemonics.setLocalizedText(justificationLbl, org.openide.util.NbBundle.getMessage(PersonaAliasDialog.class, "PersonaAliasDialog.justificationLbl.text")); // NOI18N
justificationTextField.setText(org.openide.util.NbBundle.getMessage(PersonaAliasDialog.class, "PersonaAliasDialog.justificationTextField.text")); // NOI18N
javax.swing.GroupLayout settingsPanelLayout = new javax.swing.GroupLayout(settingsPanel); javax.swing.GroupLayout settingsPanelLayout = new javax.swing.GroupLayout(settingsPanel);
settingsPanel.setLayout(settingsPanelLayout); settingsPanel.setLayout(settingsPanelLayout);
settingsPanelLayout.setHorizontalGroup( settingsPanelLayout.setHorizontalGroup(
@ -114,17 +134,17 @@ public class AddAliasDialog extends JDialog {
.addComponent(aliasLbl) .addComponent(aliasLbl)
.addComponent(aliasTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(aliasTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(justificationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(justificationLbl))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(confidenceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(confidenceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(confidenceLbl)) .addComponent(confidenceLbl))
.addContainerGap()) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(justificationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(justificationLbl))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
); );
org.openide.awt.Mnemonics.setLocalizedText(cancelBtn, org.openide.util.NbBundle.getMessage(AddAliasDialog.class, "AddAliasDialog.cancelBtn.text_1")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(cancelBtn, org.openide.util.NbBundle.getMessage(PersonaAliasDialog.class, "PersonaAliasDialog.cancelBtn.text_1")); // NOI18N
cancelBtn.setMaximumSize(new java.awt.Dimension(79, 23)); cancelBtn.setMaximumSize(new java.awt.Dimension(79, 23));
cancelBtn.setMinimumSize(new java.awt.Dimension(79, 23)); cancelBtn.setMinimumSize(new java.awt.Dimension(79, 23));
cancelBtn.setPreferredSize(new java.awt.Dimension(79, 23)); cancelBtn.setPreferredSize(new java.awt.Dimension(79, 23));
@ -134,7 +154,7 @@ public class AddAliasDialog extends JDialog {
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(okBtn, org.openide.util.NbBundle.getMessage(AddAliasDialog.class, "AddAliasDialog.okBtn.text_1")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(okBtn, org.openide.util.NbBundle.getMessage(PersonaAliasDialog.class, "PersonaAliasDialog.okBtn.text_1")); // NOI18N
okBtn.addActionListener(new java.awt.event.ActionListener() { okBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
okBtnActionPerformed(evt); okBtnActionPerformed(evt);
@ -176,20 +196,43 @@ public class AddAliasDialog extends JDialog {
} }
@Messages({ @Messages({
"AddAliasDialog_dup_Title=Alias add failure", "PersonaAliasDialog_empty_Title=Empty alias",
"AddAliasDialog_dup_msg=This alias has already been added to this persona",}) "PersonaAliasDialog_empty_msg=An alias cannot be empty.",
"PersonaAliasDialog_dup_Title=Alias add failure",
"PersonaAliasDialog_dup_msg=This alias has already been added to this persona.",})
private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed
if (pdp.addAlias( if (StringUtils.isBlank(aliasTextField.getText())) {
aliasTextField.getText(), JOptionPane.showMessageDialog(this,
justificationTextField.getText(), Bundle.PersonaAliasDialog_empty_msg(),
(Persona.Confidence) confidenceComboBox.getSelectedItem())) { Bundle.PersonaAliasDialog_empty_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
if (StringUtils.isBlank(justificationTextField.getText())) {
JOptionPane.showMessageDialog(this,
Bundle.PersonaDetailsPanel_empty_justification_msg(),
Bundle.PersonaDetailsPanel_empty_justification_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
Persona.Confidence confidence = (Persona.Confidence) confidenceComboBox.getSelectedItem();
String justification = justificationTextField.getText();
if (currentAlias != null) {
currentAlias.confidence = confidence;
currentAlias.justification = justification;
dispose();
} else {
if (pdp.addAlias(aliasTextField.getText(), justification, confidence)) {
dispose(); dispose();
} else { } else {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.AddAliasDialog_dup_msg(), Bundle.PersonaAliasDialog_dup_msg(),
Bundle.AddAliasDialog_dup_Title(), Bundle.PersonaAliasDialog_dup_Title(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
} }
}
}//GEN-LAST:event_okBtnActionPerformed }//GEN-LAST:event_okBtnActionPerformed
private void cancelBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelBtnActionPerformed private void cancelBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelBtnActionPerformed

View File

@ -44,6 +44,10 @@ public class PersonaDetailsDialog extends JDialog {
"PersonaDetailsDialogEditTitle=Edit Persona", "PersonaDetailsDialogEditTitle=Edit Persona",
"PersonaDetailsDialogViewTitle=View Persona",}) "PersonaDetailsDialogViewTitle=View Persona",})
public PersonaDetailsDialog(Component parent, PersonaDetailsMode mode, Persona persona, PersonaDetailsDialogCallback callback) { public PersonaDetailsDialog(Component parent, PersonaDetailsMode mode, Persona persona, PersonaDetailsDialogCallback callback) {
// by default, display the dialog right away
this(parent, mode, persona, callback, true);
}
public PersonaDetailsDialog(Component parent, PersonaDetailsMode mode, Persona persona, PersonaDetailsDialogCallback callback, boolean displayDialog) {
super((JFrame) WindowManager.getDefault().getMainWindow(), super((JFrame) WindowManager.getDefault().getMainWindow(),
getTitle(mode), getTitle(mode),
true); true);
@ -53,8 +57,10 @@ public class PersonaDetailsDialog extends JDialog {
pdp.setMode(parent, mode, persona); pdp.setMode(parent, mode, persona);
if (displayDialog) {
display(); display();
} }
}
private static String getTitle(PersonaDetailsMode mode) { private static String getTitle(PersonaDetailsMode mode) {
switch (mode) { switch (mode) {
@ -136,7 +142,7 @@ public class PersonaDetailsDialog extends JDialog {
pack(); pack();
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void display() { public final void display() {
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setVisible(true); setVisible(true);
} }
@ -153,6 +159,10 @@ public class PersonaDetailsDialog extends JDialog {
dispose(); dispose();
}//GEN-LAST:event_cancelBtnActionPerformed }//GEN-LAST:event_cancelBtnActionPerformed
public PersonaDetailsPanel getDetailsPanel() {
return this.pdp;
}
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton cancelBtn; private javax.swing.JButton cancelBtn;
private javax.swing.JButton okBtn; private javax.swing.JButton okBtn;

View File

@ -37,17 +37,20 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="561" max="32767" attributes="0"/> <Component id="detailsPanel" alignment="1" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0"> <Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="detailsPanel" alignment="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="617" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="559" max="32767" attributes="0"/> <EmptySpace min="0" pref="521" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0"> <Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="detailsPanel" alignment="0" max="32767" attributes="0"/> <Group type="102" alignment="0" attributes="0">
<Component id="detailsPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -61,12 +64,7 @@
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="accountsTablePane" alignment="1" pref="549" max="32767" attributes="0"/> <Component id="accountsTablePane" alignment="1" pref="605" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="nameLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="nameField" max="32767" attributes="0"/>
</Group>
<Component id="accountsLbl" alignment="0" max="32767" attributes="0"/> <Component id="accountsLbl" alignment="0" max="32767" attributes="0"/>
<Component id="metadataLabel" alignment="0" max="32767" attributes="0"/> <Component id="metadataLabel" alignment="0" max="32767" attributes="0"/>
<Component id="metadataTablePane" alignment="1" max="32767" attributes="0"/> <Component id="metadataTablePane" alignment="1" max="32767" attributes="0"/>
@ -74,31 +72,51 @@
<Component id="aliasesTablePane" alignment="0" max="32767" attributes="0"/> <Component id="aliasesTablePane" alignment="0" max="32767" attributes="0"/>
<Component id="casesLbl" alignment="0" max="32767" attributes="0"/> <Component id="casesLbl" alignment="0" max="32767" attributes="0"/>
<Component id="casesTablePane" max="32767" attributes="0"/> <Component id="casesTablePane" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="commentLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="commentField" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="nameLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="nameField" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="addCaseBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="deleteCaseBtn" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="addAccountBtn" min="-2" max="-2" attributes="0"/> <Component id="addAccountBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/> <EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="editAccountBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="deleteAccountBtn" min="-2" max="-2" attributes="0"/> <Component id="deleteAccountBtn" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="addMetadataBtn" min="-2" max="-2" attributes="0"/> <Component id="addMetadataBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/> <EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="editMetadataBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="deleteMetadataBtn" min="-2" max="-2" attributes="0"/> <Component id="deleteMetadataBtn" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="addAliasBtn" min="-2" max="-2" attributes="0"/> <Component id="addAliasBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/> <EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="editAliasBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="deleteAliasBtn" min="-2" max="-2" attributes="0"/> <Component id="deleteAliasBtn" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0">
<Component id="examinerLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="examinerField" min="-2" pref="161" max="-2" attributes="0"/>
<EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
<Component id="creationDateLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="creationDateField" max="32767" attributes="0"/>
</Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -107,7 +125,19 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="examinerLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="examinerField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="creationDateLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="creationDateField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="commentField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="commentLbl" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="nameLbl" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="nameLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="nameField" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="nameField" alignment="3" min="-2" max="-2" attributes="0"/>
@ -120,40 +150,83 @@
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="addAccountBtn" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="addAccountBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="deleteAccountBtn" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="deleteAccountBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="editAccountBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="metadataLabel" min="-2" max="-2" attributes="0"/> <Component id="metadataLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="metadataTablePane" min="-2" pref="66" max="-2" attributes="0"/> <Component id="metadataTablePane" min="-2" pref="66" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="deleteMetadataBtn" min="-2" max="-2" attributes="0"/> <Component id="addMetadataBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="addMetadataBtn" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="deleteMetadataBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="editMetadataBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="aliasesLabel" min="-2" max="-2" attributes="0"/> <Component id="aliasesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="aliasesTablePane" min="-2" pref="74" max="-2" attributes="0"/> <Component id="aliasesTablePane" min="-2" pref="74" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="deleteAliasBtn" min="-2" max="-2" attributes="0"/> <Component id="addAliasBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="addAliasBtn" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="deleteAliasBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="editAliasBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="casesLbl" min="-2" max="-2" attributes="0"/> <Component id="casesLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="casesTablePane" min="-2" pref="63" max="-2" attributes="0"/> <Component id="casesTablePane" min="-2" pref="63" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="addCaseBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="deleteCaseBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Component class="javax.swing.JLabel" name="examinerLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.examinerLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="examinerField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.examinerField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="creationDateLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.creationDateLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="creationDateField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.creationDateField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="commentLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.commentLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="commentField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.commentField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="nameLbl"> <Component class="javax.swing.JLabel" name="nameLbl">
<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">
@ -168,9 +241,6 @@
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.nameField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.nameField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="nameFieldActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="accountsLbl"> <Component class="javax.swing.JLabel" name="accountsLbl">
<Properties> <Properties>
@ -203,6 +273,14 @@
<Property name="enabled" type="boolean" value="false"/> <Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JButton" name="editAccountBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.editAccountBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="deleteAccountBtn"> <Component class="javax.swing.JButton" name="deleteAccountBtn">
<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">
@ -242,6 +320,14 @@
<Property name="enabled" type="boolean" value="false"/> <Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JButton" name="editMetadataBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.editMetadataBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="deleteMetadataBtn"> <Component class="javax.swing.JButton" name="deleteMetadataBtn">
<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">
@ -281,6 +367,14 @@
<Property name="enabled" type="boolean" value="false"/> <Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JButton" name="editAliasBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.editAliasBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="deleteAliasBtn"> <Component class="javax.swing.JButton" name="deleteAliasBtn">
<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">
@ -312,22 +406,6 @@
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JButton" name="addCaseBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.addCaseBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="deleteCaseBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaDetailsPanel.deleteCaseBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>

View File

@ -20,9 +20,13 @@ package org.sleuthkit.autopsy.centralrepository.persona;
import java.awt.Component; import java.awt.Component;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@ -30,11 +34,14 @@ import javax.swing.JTable;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.table.DefaultTableModel; import javax.swing.table.DefaultTableModel;
import org.apache.commons.lang.StringUtils;
import org.openide.windows.TopComponent; import org.openide.windows.TopComponent;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.RetainLocation; import org.openide.windows.RetainLocation;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoExaminer;
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.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
@ -65,6 +72,10 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
private final List<PersonaMetadata> metadataToRemove = new ArrayList<>(); private final List<PersonaMetadata> metadataToRemove = new ArrayList<>();
private final List<PersonaAlias> aliasesToRemove = new ArrayList<>(); private final List<PersonaAlias> aliasesToRemove = new ArrayList<>();
private final Map<PersonaAccount, PAccount> accountsToEdit = new HashMap<>();
private final Map<PersonaMetadata, PMetadata> metadataToEdit = new HashMap<>();
private final Map<PersonaAlias, PAlias> aliasesToEdit = new HashMap<>();
private Persona currentPersona; private Persona currentPersona;
private List<PersonaAccount> currentAccounts = new ArrayList<>(); private List<PersonaAccount> currentAccounts = new ArrayList<>();
private List<PersonaMetadata> currentMetadata = new ArrayList<>(); private List<PersonaMetadata> currentMetadata = new ArrayList<>();
@ -76,13 +87,29 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
private PersonaDetailsTableModel aliasesModel; private PersonaDetailsTableModel aliasesModel;
private PersonaDetailsTableModel casesModel; private PersonaDetailsTableModel casesModel;
@Messages({
"PersonaDetailsPanel_empty_justification_Title=Empty justification",
"PersonaDetailsPanel_empty_justification_msg=The justification field cannot be empty",})
public PersonaDetailsPanel() { public PersonaDetailsPanel() {
initComponents(); initComponents();
clear(); clear();
// Accounts // Accounts
addAccountBtn.addActionListener((ActionEvent e) -> { addAccountBtn.addActionListener((ActionEvent e) -> {
new AddAccountDialog(this); new PersonaAccountDialog(this);
});
editAccountBtn.addActionListener((ActionEvent e) -> {
int selectedRow = accountsTable.getSelectedRow();
if (selectedRow != -1) {
if (selectedRow >= currentAccounts.size()) {
PAccount acc = accountsToAdd.get(selectedRow - currentAccounts.size());
new PersonaAccountDialog(this, acc);
} else {
PersonaAccount personaAccount = currentAccounts.get(selectedRow);
accountsToEdit.putIfAbsent(personaAccount, new PAccount(personaAccount.getAccount(), personaAccount.getJustification(), personaAccount.getConfidence()));
new PersonaAccountDialog(this, accountsToEdit.get(personaAccount));
}
}
}); });
deleteAccountBtn.addActionListener((ActionEvent e) -> { deleteAccountBtn.addActionListener((ActionEvent e) -> {
int selectedRow = accountsTable.getSelectedRow(); int selectedRow = accountsTable.getSelectedRow();
@ -91,20 +118,35 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
if (selectedRow >= currentAccounts.size()) { if (selectedRow >= currentAccounts.size()) {
accountsToAdd.remove(selectedRow - currentAccounts.size()); accountsToAdd.remove(selectedRow - currentAccounts.size());
} else { } else {
accountsToRemove.add(currentAccounts.get(selectedRow)); PersonaAccount toRemove = currentAccounts.get(selectedRow);
currentAccounts.remove(selectedRow); accountsToEdit.remove(toRemove);
accountsToRemove.add(toRemove);
currentAccounts.remove(toRemove);
} }
updateAccountsTable(); updateAccountsTable();
} }
}); });
accountsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); accountsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
accountsTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> { accountsTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
handleSelectionChange(e, deleteAccountBtn, accountsTable); handleSelectionChange(e, editAccountBtn, deleteAccountBtn, accountsTable);
}); });
// Metadata // Metadata
addMetadataBtn.addActionListener((ActionEvent e) -> { addMetadataBtn.addActionListener((ActionEvent e) -> {
new AddMetadataDialog(this); new PersonaMetadataDialog(this);
});
editMetadataBtn.addActionListener((ActionEvent e) -> {
int selectedRow = metadataTable.getSelectedRow();
if (selectedRow != -1) {
if (selectedRow >= currentMetadata.size()) {
PMetadata md = metadataToAdd.get(selectedRow - currentMetadata.size());
new PersonaMetadataDialog(this, md);
} else {
PersonaMetadata personaMetadata = currentMetadata.get(selectedRow);
metadataToEdit.putIfAbsent(personaMetadata, new PMetadata(personaMetadata.getName(), personaMetadata.getValue(), personaMetadata.getJustification(), personaMetadata.getConfidence()));
new PersonaMetadataDialog(this, metadataToEdit.get(personaMetadata));
}
}
}); });
deleteMetadataBtn.addActionListener((ActionEvent e) -> { deleteMetadataBtn.addActionListener((ActionEvent e) -> {
int selectedRow = metadataTable.getSelectedRow(); int selectedRow = metadataTable.getSelectedRow();
@ -113,20 +155,35 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
if (selectedRow >= currentMetadata.size()) { if (selectedRow >= currentMetadata.size()) {
metadataToAdd.remove(selectedRow - currentMetadata.size()); metadataToAdd.remove(selectedRow - currentMetadata.size());
} else { } else {
metadataToRemove.add(currentMetadata.get(selectedRow)); PersonaMetadata toRemove = currentMetadata.get(selectedRow);
currentMetadata.remove(selectedRow); metadataToEdit.remove(toRemove);
metadataToRemove.add(toRemove);
currentMetadata.remove(toRemove);
} }
updateMetadataTable(); updateMetadataTable();
} }
}); });
metadataTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); metadataTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
metadataTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> { metadataTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
handleSelectionChange(e, deleteMetadataBtn, metadataTable); handleSelectionChange(e, editMetadataBtn, deleteMetadataBtn, metadataTable);
}); });
// Aliases // Aliases
addAliasBtn.addActionListener((ActionEvent e) -> { addAliasBtn.addActionListener((ActionEvent e) -> {
new AddAliasDialog(this); new PersonaAliasDialog(this);
});
editAliasBtn.addActionListener((ActionEvent e) -> {
int selectedRow = aliasesTable.getSelectedRow();
if (selectedRow != -1) {
if (selectedRow >= currentAliases.size()) {
PAlias pa = aliasesToAdd.get(selectedRow - currentAliases.size());
new PersonaAliasDialog(this, pa);
} else {
PersonaAlias personaAlias = currentAliases.get(selectedRow);
aliasesToEdit.putIfAbsent(personaAlias, new PAlias(personaAlias.getAlias(), personaAlias.getJustification(), personaAlias.getConfidence()));
new PersonaAliasDialog(this, aliasesToEdit.get(personaAlias));
}
}
}); });
deleteAliasBtn.addActionListener((ActionEvent e) -> { deleteAliasBtn.addActionListener((ActionEvent e) -> {
int selectedRow = aliasesTable.getSelectedRow(); int selectedRow = aliasesTable.getSelectedRow();
@ -135,33 +192,48 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
if (selectedRow >= currentAliases.size()) { if (selectedRow >= currentAliases.size()) {
aliasesToAdd.remove(selectedRow - currentAliases.size()); aliasesToAdd.remove(selectedRow - currentAliases.size());
} else { } else {
aliasesToRemove.add(currentAliases.get(selectedRow)); PersonaAlias toRemove = currentAliases.get(selectedRow);
currentAliases.remove(selectedRow); aliasesToEdit.remove(toRemove);
aliasesToRemove.add(toRemove);
currentAliases.remove(toRemove);
} }
updateAliasesTable(); updateAliasesTable();
} }
}); });
aliasesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); aliasesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
aliasesTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> { aliasesTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
handleSelectionChange(e, deleteAliasBtn, aliasesTable); handleSelectionChange(e, editAliasBtn, deleteAliasBtn, aliasesTable);
}); });
} }
private void handleSelectionChange(ListSelectionEvent e, JButton btn, JTable table) { private void handleSelectionChange(ListSelectionEvent e, JButton editBtn, JButton deleteBtn, JTable table) {
if (e.getValueIsAdjusting()) { if (e.getValueIsAdjusting()) {
return; return;
} }
btn.setEnabled(mode != PersonaDetailsMode.VIEW && table.getSelectedRow() != -1); editBtn.setEnabled(mode != PersonaDetailsMode.VIEW && table.getSelectedRow() != -1);
deleteBtn.setEnabled(mode != PersonaDetailsMode.VIEW && table.getSelectedRow() != -1);
}
void addEditExistingAccount(PersonaAccount account, String justification, Persona.Confidence confidence) {
accountsToEdit.put(account, new PAccount(account.getAccount(), justification, confidence));
}
void addEditExistingMetadata(PersonaMetadata metadata, String justification, Persona.Confidence confidence) {
metadataToEdit.put(metadata, new PMetadata(metadata.getName(), metadata.getValue(), justification, confidence));
}
void addEditExistingAlias(PersonaAlias alias, String justification, Persona.Confidence confidence) {
aliasesToEdit.put(alias, new PAlias(alias.getAlias(), justification, confidence));
} }
/** /**
* A data bucket class for yet-to-be-created PersonaAccount * A data bucket class for yet-to-be-created PersonaAccount
*/ */
private class PAccount { class PAccount {
private final CentralRepoAccount account; CentralRepoAccount account;
private final String justification; String justification;
private final Persona.Confidence confidence; Persona.Confidence confidence;
PAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) { PAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) {
this.account = account; this.account = account;
@ -184,7 +256,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
return false; return false;
} }
boolean addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) { public boolean addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) {
if (!accountExists(account)) { if (!accountExists(account)) {
accountsToAdd.add(new PAccount(account, justification, confidence)); accountsToAdd.add(new PAccount(account, justification, confidence));
updateAccountsTable(); updateAccountsTable();
@ -196,12 +268,12 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
/** /**
* A data bucket class for yet-to-be-created PersonaMetadata * A data bucket class for yet-to-be-created PersonaMetadata
*/ */
private class PMetadata { class PMetadata {
private final String name; String name;
private final String value; String value;
private final String justification; String justification;
private final Persona.Confidence confidence; Persona.Confidence confidence;
PMetadata(String name, String value, String justification, Persona.Confidence confidence) { PMetadata(String name, String value, String justification, Persona.Confidence confidence) {
this.name = name; this.name = name;
@ -237,11 +309,11 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
/** /**
* A data bucket class for yet-to-be-created PersonaAlias * A data bucket class for yet-to-be-created PersonaAlias
*/ */
private class PAlias { class PAlias {
private final String alias; String alias;
private final String justification; String justification;
private final Persona.Confidence confidence; Persona.Confidence confidence;
PAlias(String alias, String justification, Persona.Confidence confidence) { PAlias(String alias, String justification, Persona.Confidence confidence) {
this.alias = alias; this.alias = alias;
@ -282,38 +354,55 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
private void initComponents() { private void initComponents() {
detailsPanel = new javax.swing.JPanel(); detailsPanel = new javax.swing.JPanel();
examinerLbl = new javax.swing.JLabel();
examinerField = new javax.swing.JTextField();
creationDateLbl = new javax.swing.JLabel();
creationDateField = new javax.swing.JTextField();
commentLbl = new javax.swing.JLabel();
commentField = new javax.swing.JTextField();
nameLbl = new javax.swing.JLabel(); nameLbl = new javax.swing.JLabel();
nameField = new javax.swing.JTextField(); nameField = new javax.swing.JTextField();
accountsLbl = new javax.swing.JLabel(); accountsLbl = new javax.swing.JLabel();
accountsTablePane = new javax.swing.JScrollPane(); accountsTablePane = new javax.swing.JScrollPane();
accountsTable = new javax.swing.JTable(); accountsTable = new javax.swing.JTable();
addAccountBtn = new javax.swing.JButton(); addAccountBtn = new javax.swing.JButton();
editAccountBtn = new javax.swing.JButton();
deleteAccountBtn = new javax.swing.JButton(); deleteAccountBtn = new javax.swing.JButton();
metadataLabel = new javax.swing.JLabel(); metadataLabel = new javax.swing.JLabel();
metadataTablePane = new javax.swing.JScrollPane(); metadataTablePane = new javax.swing.JScrollPane();
metadataTable = new javax.swing.JTable(); metadataTable = new javax.swing.JTable();
addMetadataBtn = new javax.swing.JButton(); addMetadataBtn = new javax.swing.JButton();
editMetadataBtn = new javax.swing.JButton();
deleteMetadataBtn = new javax.swing.JButton(); deleteMetadataBtn = new javax.swing.JButton();
aliasesLabel = new javax.swing.JLabel(); aliasesLabel = new javax.swing.JLabel();
aliasesTablePane = new javax.swing.JScrollPane(); aliasesTablePane = new javax.swing.JScrollPane();
aliasesTable = new javax.swing.JTable(); aliasesTable = new javax.swing.JTable();
addAliasBtn = new javax.swing.JButton(); addAliasBtn = new javax.swing.JButton();
editAliasBtn = new javax.swing.JButton();
deleteAliasBtn = new javax.swing.JButton(); deleteAliasBtn = new javax.swing.JButton();
casesLbl = new javax.swing.JLabel(); casesLbl = new javax.swing.JLabel();
casesTablePane = new javax.swing.JScrollPane(); casesTablePane = new javax.swing.JScrollPane();
casesTable = new javax.swing.JTable(); casesTable = new javax.swing.JTable();
addCaseBtn = new javax.swing.JButton();
deleteCaseBtn = new javax.swing.JButton(); org.openide.awt.Mnemonics.setLocalizedText(examinerLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.examinerLbl.text")); // NOI18N
examinerField.setEditable(false);
examinerField.setText(org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.examinerField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(creationDateLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.creationDateLbl.text")); // NOI18N
creationDateField.setEditable(false);
creationDateField.setText(org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.creationDateField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(commentLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.commentLbl.text")); // NOI18N
commentField.setEditable(false);
commentField.setText(org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.commentField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(nameLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.nameLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(nameLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.nameLbl.text")); // NOI18N
nameField.setEditable(false); nameField.setEditable(false);
nameField.setText(org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.nameField.text")); // NOI18N nameField.setText(org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.nameField.text")); // NOI18N
nameField.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
nameFieldActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(accountsLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.accountsLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(accountsLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.accountsLbl.text")); // NOI18N
@ -333,6 +422,9 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
org.openide.awt.Mnemonics.setLocalizedText(addAccountBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.addAccountBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(addAccountBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.addAccountBtn.text")); // NOI18N
addAccountBtn.setEnabled(false); addAccountBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(editAccountBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.editAccountBtn.text")); // NOI18N
editAccountBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(deleteAccountBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.deleteAccountBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(deleteAccountBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.deleteAccountBtn.text")); // NOI18N
deleteAccountBtn.setEnabled(false); deleteAccountBtn.setEnabled(false);
@ -354,6 +446,9 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
org.openide.awt.Mnemonics.setLocalizedText(addMetadataBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.addMetadataBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(addMetadataBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.addMetadataBtn.text")); // NOI18N
addMetadataBtn.setEnabled(false); addMetadataBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(editMetadataBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.editMetadataBtn.text")); // NOI18N
editMetadataBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(deleteMetadataBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.deleteMetadataBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(deleteMetadataBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.deleteMetadataBtn.text")); // NOI18N
deleteMetadataBtn.setEnabled(false); deleteMetadataBtn.setEnabled(false);
@ -375,6 +470,9 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
org.openide.awt.Mnemonics.setLocalizedText(addAliasBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.addAliasBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(addAliasBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.addAliasBtn.text")); // NOI18N
addAliasBtn.setEnabled(false); addAliasBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(editAliasBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.editAliasBtn.text")); // NOI18N
editAliasBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(deleteAliasBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.deleteAliasBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(deleteAliasBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.deleteAliasBtn.text")); // NOI18N
deleteAliasBtn.setEnabled(false); deleteAliasBtn.setEnabled(false);
@ -393,12 +491,6 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
)); ));
casesTablePane.setViewportView(casesTable); casesTablePane.setViewportView(casesTable);
org.openide.awt.Mnemonics.setLocalizedText(addCaseBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.addCaseBtn.text")); // NOI18N
addCaseBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(deleteCaseBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.deleteCaseBtn.text")); // NOI18N
deleteCaseBtn.setEnabled(false);
javax.swing.GroupLayout detailsPanelLayout = new javax.swing.GroupLayout(detailsPanel); javax.swing.GroupLayout detailsPanelLayout = new javax.swing.GroupLayout(detailsPanel);
detailsPanel.setLayout(detailsPanelLayout); detailsPanel.setLayout(detailsPanelLayout);
detailsPanelLayout.setHorizontalGroup( detailsPanelLayout.setHorizontalGroup(
@ -406,11 +498,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
.addGroup(detailsPanelLayout.createSequentialGroup() .addGroup(detailsPanelLayout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(accountsTablePane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 549, Short.MAX_VALUE) .addComponent(accountsTablePane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 605, Short.MAX_VALUE)
.addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(nameLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(nameField))
.addComponent(accountsLbl, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(accountsLbl, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(metadataLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(metadataLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(metadataTablePane, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(metadataTablePane, javax.swing.GroupLayout.Alignment.TRAILING)
@ -418,31 +506,59 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
.addComponent(aliasesTablePane) .addComponent(aliasesTablePane)
.addComponent(casesLbl, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(casesLbl, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(casesTablePane) .addComponent(casesTablePane)
.addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(commentLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(commentField))
.addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(nameLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(nameField))
.addGroup(detailsPanelLayout.createSequentialGroup() .addGroup(detailsPanelLayout.createSequentialGroup()
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(addCaseBtn)
.addGap(18, 18, 18)
.addComponent(deleteCaseBtn))
.addGroup(detailsPanelLayout.createSequentialGroup() .addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(addAccountBtn) .addComponent(addAccountBtn)
.addGap(18, 18, 18) .addGap(18, 18, 18)
.addComponent(editAccountBtn)
.addGap(18, 18, 18)
.addComponent(deleteAccountBtn)) .addComponent(deleteAccountBtn))
.addGroup(detailsPanelLayout.createSequentialGroup() .addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(addMetadataBtn) .addComponent(addMetadataBtn)
.addGap(18, 18, 18) .addGap(18, 18, 18)
.addComponent(editMetadataBtn)
.addGap(18, 18, 18)
.addComponent(deleteMetadataBtn)) .addComponent(deleteMetadataBtn))
.addGroup(detailsPanelLayout.createSequentialGroup() .addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(addAliasBtn) .addComponent(addAliasBtn)
.addGap(18, 18, 18) .addGap(18, 18, 18)
.addComponent(editAliasBtn)
.addGap(18, 18, 18)
.addComponent(deleteAliasBtn))) .addComponent(deleteAliasBtn)))
.addGap(0, 0, Short.MAX_VALUE))) .addGap(0, 0, Short.MAX_VALUE))
.addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(examinerLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(examinerField, javax.swing.GroupLayout.PREFERRED_SIZE, 161, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(creationDateLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(creationDateField)))
.addContainerGap()) .addContainerGap())
); );
detailsPanelLayout.setVerticalGroup( detailsPanelLayout.setVerticalGroup(
detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(detailsPanelLayout.createSequentialGroup() .addGroup(detailsPanelLayout.createSequentialGroup()
.addContainerGap() .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(examinerLbl)
.addComponent(examinerField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(creationDateLbl)
.addComponent(creationDateField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(commentField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(commentLbl))
.addGap(20, 20, 20)
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(nameLbl) .addComponent(nameLbl)
.addComponent(nameField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(nameField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
@ -453,61 +569,57 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(addAccountBtn) .addComponent(addAccountBtn)
.addComponent(deleteAccountBtn)) .addComponent(deleteAccountBtn)
.addComponent(editAccountBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(metadataLabel) .addComponent(metadataLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(metadataTablePane, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(metadataTablePane, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(addMetadataBtn)
.addComponent(deleteMetadataBtn) .addComponent(deleteMetadataBtn)
.addComponent(addMetadataBtn)) .addComponent(editMetadataBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(aliasesLabel) .addComponent(aliasesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(aliasesTablePane, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(aliasesTablePane, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(addAliasBtn)
.addComponent(deleteAliasBtn) .addComponent(deleteAliasBtn)
.addComponent(addAliasBtn)) .addComponent(editAliasBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(casesLbl) .addComponent(casesLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(casesTablePane, javax.swing.GroupLayout.PREFERRED_SIZE, 63, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(casesTablePane, javax.swing.GroupLayout.PREFERRED_SIZE, 63, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addContainerGap())
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(addCaseBtn)
.addComponent(deleteCaseBtn))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
); );
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 561, Short.MAX_VALUE) .addComponent(detailsPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(detailsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGap(0, 617, Short.MAX_VALUE))
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 559, Short.MAX_VALUE) .addGap(0, 521, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(detailsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup()
.addComponent(detailsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE)))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void nameFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nameFieldActionPerformed
}//GEN-LAST:event_nameFieldActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel accountsLbl; private javax.swing.JLabel accountsLbl;
private javax.swing.JTable accountsTable; private javax.swing.JTable accountsTable;
private javax.swing.JScrollPane accountsTablePane; private javax.swing.JScrollPane accountsTablePane;
private javax.swing.JButton addAccountBtn; private javax.swing.JButton addAccountBtn;
private javax.swing.JButton addAliasBtn; private javax.swing.JButton addAliasBtn;
private javax.swing.JButton addCaseBtn;
private javax.swing.JButton addMetadataBtn; private javax.swing.JButton addMetadataBtn;
private javax.swing.JLabel aliasesLabel; private javax.swing.JLabel aliasesLabel;
private javax.swing.JTable aliasesTable; private javax.swing.JTable aliasesTable;
@ -515,11 +627,19 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
private javax.swing.JLabel casesLbl; private javax.swing.JLabel casesLbl;
private javax.swing.JTable casesTable; private javax.swing.JTable casesTable;
private javax.swing.JScrollPane casesTablePane; private javax.swing.JScrollPane casesTablePane;
private javax.swing.JTextField commentField;
private javax.swing.JLabel commentLbl;
private javax.swing.JTextField creationDateField;
private javax.swing.JLabel creationDateLbl;
private javax.swing.JButton deleteAccountBtn; private javax.swing.JButton deleteAccountBtn;
private javax.swing.JButton deleteAliasBtn; private javax.swing.JButton deleteAliasBtn;
private javax.swing.JButton deleteCaseBtn;
private javax.swing.JButton deleteMetadataBtn; private javax.swing.JButton deleteMetadataBtn;
private javax.swing.JPanel detailsPanel; private javax.swing.JPanel detailsPanel;
private javax.swing.JButton editAccountBtn;
private javax.swing.JButton editAliasBtn;
private javax.swing.JButton editMetadataBtn;
private javax.swing.JTextField examinerField;
private javax.swing.JLabel examinerLbl;
private javax.swing.JLabel metadataLabel; private javax.swing.JLabel metadataLabel;
private javax.swing.JTable metadataTable; private javax.swing.JTable metadataTable;
private javax.swing.JScrollPane metadataTablePane; private javax.swing.JScrollPane metadataTablePane;
@ -529,14 +649,24 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
@Messages({ @Messages({
"PersonaDetailsPanel_load_exception_Title=Initialization failure", "PersonaDetailsPanel_load_exception_Title=Initialization failure",
"PersonaDetailsPanel_load_exception_msg=Failed to load persona",}) "PersonaDetailsPanel_load_exception_msg=Failed to load persona.",})
private void loadPersona(Component parent, Persona persona) { private void loadPersona(Component parent, Persona persona) {
String examiner;
String creationDate;
String comment;
String name; String name;
Collection<PersonaAccount> accounts; Collection<PersonaAccount> accounts;
Collection<PersonaMetadata> metadata; Collection<PersonaMetadata> metadata;
Collection<PersonaAlias> aliases; Collection<PersonaAlias> aliases;
Collection<CorrelationCase> cases; Collection<CorrelationCase> cases;
try { try {
examiner = persona.getExaminer().getLoginName();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date cDate = new Date(persona.getCreatedDate());
creationDate = dateFormat.format(cDate);
comment = persona.getComment();
name = persona.getName(); name = persona.getName();
accounts = persona.getPersonaAccounts(); accounts = persona.getPersonaAccounts();
metadata = persona.getMetadata(); metadata = persona.getMetadata();
@ -551,6 +681,9 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
return; return;
} }
this.currentPersona = persona; this.currentPersona = persona;
this.examinerField.setText(examiner);
this.creationDateField.setText(creationDate);
this.commentField.setText(comment);
this.nameField.setText(name); this.nameField.setText(name);
this.currentAccounts.addAll(accounts); this.currentAccounts.addAll(accounts);
this.currentMetadata.addAll(metadata); this.currentMetadata.addAll(metadata);
@ -560,6 +693,9 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
void clear() { void clear() {
currentPersona = null; currentPersona = null;
examinerField.setText("");
creationDateField.setText("");
commentField.setText("");
nameField.setText(mode == PersonaDetailsMode.CREATE ? Persona.getDefaultName() : ""); nameField.setText(mode == PersonaDetailsMode.CREATE ? Persona.getDefaultName() : "");
currentAccounts = new ArrayList<>(); currentAccounts = new ArrayList<>();
currentMetadata = new ArrayList<>(); currentMetadata = new ArrayList<>();
@ -569,17 +705,19 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
metadataToAdd.clear(); metadataToAdd.clear();
aliasesToAdd.clear(); aliasesToAdd.clear();
nameField.setEditable(false); nameField.setEditable(false);
commentField.setEditable(false);
initializeFields(); initializeFields();
addAccountBtn.setEnabled(false); addAccountBtn.setEnabled(false);
addMetadataBtn.setEnabled(false); addMetadataBtn.setEnabled(false);
addAliasBtn.setEnabled(false); addAliasBtn.setEnabled(false);
addCaseBtn.setEnabled(false);
deleteAccountBtn.setEnabled(false); deleteAccountBtn.setEnabled(false);
deleteMetadataBtn.setEnabled(false); deleteMetadataBtn.setEnabled(false);
deleteAliasBtn.setEnabled(false); deleteAliasBtn.setEnabled(false);
deleteCaseBtn.setEnabled(false); editAccountBtn.setEnabled(false);
editMetadataBtn.setEnabled(false);
editAliasBtn.setEnabled(false);
} }
/** /**
@ -677,15 +815,37 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
casesTable.setModel(casesModel); casesTable.setModel(casesModel);
} }
void enableEditUIComponents() { void configureEditComponents(boolean enabled) {
nameField.setEditable(true); commentField.setEditable(enabled);
addAccountBtn.setEnabled(true); nameField.setEditable(enabled);
addMetadataBtn.setEnabled(true); addAccountBtn.setEnabled(enabled);
addAliasBtn.setEnabled(true); addMetadataBtn.setEnabled(enabled);
//addCaseBtn.setEnabled(true); //todo addAliasBtn.setEnabled(enabled);
addAccountBtn.setVisible(enabled);
editAccountBtn.setVisible(enabled);
deleteAccountBtn.setVisible(enabled);
addMetadataBtn.setVisible(enabled);
editMetadataBtn.setVisible(enabled);
deleteMetadataBtn.setVisible(enabled);
addAliasBtn.setVisible(enabled);
editAliasBtn.setVisible(enabled);
deleteAliasBtn.setVisible(enabled);
} }
void initializeFields() { void initializeFields() {
if (mode == PersonaDetailsMode.CREATE) {
try {
CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name"));
examinerField.setText(examiner.getLoginName());
} catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to access central repository", e);
JOptionPane.showMessageDialog(this,
Bundle.PersonaDetailsPanel_CentralRepoErr_msg(),
Bundle.PersonaDetailsPanel_CentralRepoErr_Title(),
JOptionPane.ERROR_MESSAGE);
}
}
updateAccountsTable(); updateAccountsTable();
updateMetadataTable(); updateMetadataTable();
updateAliasesTable(); updateAliasesTable();
@ -697,14 +857,15 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
this.mode = mode; this.mode = mode;
switch (mode) { switch (mode) {
case CREATE: case CREATE:
enableEditUIComponents(); configureEditComponents(true);
break; break;
case EDIT: case EDIT:
loadPersona(parent, persona); loadPersona(parent, persona);
enableEditUIComponents(); configureEditComponents(true);
break; break;
case VIEW: case VIEW:
loadPersona(parent, persona); loadPersona(parent, persona);
configureEditComponents(false);
break; break;
default: default:
logger.log(Level.WARNING, "Unsupported mode: {0}", mode); logger.log(Level.WARNING, "Unsupported mode: {0}", mode);
@ -714,12 +875,14 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
} }
@Messages({ @Messages({
"PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs at least one account", "PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs at least one account.",
"PersonaDetailsPanel_NotEnoughAccounts_Title=Missing account", "PersonaDetailsPanel_NotEnoughAccounts_Title=Missing account",
"PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository", "PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository.",
"PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure", "PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure",
"PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty", "PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty.",
"PersonaDetailsPanel_EmptyName_Title=Empty persona name",}) "PersonaDetailsPanel_EmptyName_Title=Empty persona name",
"PersonaDetailsPanel_EmptyComment_msg=Persona comment cannot be empty.",
"PersonaDetailsPanel_EmptyComment_Title=Empty persona comment",})
Persona okHandler() { Persona okHandler() {
if (accountsToAdd.size() + currentAccounts.size() < 1) { if (accountsToAdd.size() + currentAccounts.size() < 1) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
@ -729,7 +892,14 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
return null; return null;
} }
if (nameField.getText().isEmpty()) { if (StringUtils.isBlank(commentField.getText())) {
JOptionPane.showMessageDialog(this,
Bundle.PersonaDetailsPanel_EmptyComment_msg(),
Bundle.PersonaDetailsPanel_EmptyComment_Title(),
JOptionPane.ERROR_MESSAGE);
return null;
}
if (StringUtils.isBlank(nameField.getText())) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.PersonaDetailsPanel_EmptyName_msg(), Bundle.PersonaDetailsPanel_EmptyName_msg(),
Bundle.PersonaDetailsPanel_EmptyName_Title(), Bundle.PersonaDetailsPanel_EmptyName_Title(),
@ -743,7 +913,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
try { try {
PAccount firstAccount = accountsToAdd.get(0); PAccount firstAccount = accountsToAdd.get(0);
ret = Persona.createPersonaForAccount(nameField.getText(), ret = Persona.createPersonaForAccount(nameField.getText(),
"", Persona.PersonaStatus.ACTIVE, firstAccount.account, commentField.getText(), Persona.PersonaStatus.ACTIVE, firstAccount.account,
firstAccount.justification, firstAccount.confidence); firstAccount.justification, firstAccount.confidence);
for (int i = 1; i < accountsToAdd.size(); i++) { for (int i = 1; i < accountsToAdd.size(); i++) {
ret.addAccount(accountsToAdd.get(i).account, ret.addAccount(accountsToAdd.get(i).account,
@ -768,6 +938,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
case EDIT: case EDIT:
try { try {
ret = currentPersona; ret = currentPersona;
currentPersona.setComment(commentField.getText());
currentPersona.setName(nameField.getText()); currentPersona.setName(nameField.getText());
for (PAccount acc : accountsToAdd) { for (PAccount acc : accountsToAdd) {
ret.addAccount(acc.account, acc.justification, acc.confidence); ret.addAccount(acc.account, acc.justification, acc.confidence);
@ -775,18 +946,27 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
for (PersonaAccount acc : accountsToRemove) { for (PersonaAccount acc : accountsToRemove) {
ret.removeAccount(acc); ret.removeAccount(acc);
} }
for (HashMap.Entry<PersonaAccount, PAccount> entry : accountsToEdit.entrySet()) {
ret.modifyAccount(entry.getKey(), entry.getValue().confidence, entry.getValue().justification);
}
for (PMetadata md : metadataToAdd) { for (PMetadata md : metadataToAdd) {
ret.addMetadata(md.name, md.value, md.justification, md.confidence); ret.addMetadata(md.name, md.value, md.justification, md.confidence);
} }
for (PersonaMetadata md : metadataToRemove) { for (PersonaMetadata md : metadataToRemove) {
ret.removeMetadata(md); ret.removeMetadata(md);
} }
for (HashMap.Entry<PersonaMetadata, PMetadata> entry : metadataToEdit.entrySet()) {
ret.modifyMetadata(entry.getKey(), entry.getValue().confidence, entry.getValue().justification);
}
for (PAlias pa : aliasesToAdd) { for (PAlias pa : aliasesToAdd) {
ret.addAlias(pa.alias, pa.justification, pa.confidence); ret.addAlias(pa.alias, pa.justification, pa.confidence);
} }
for (PersonaAlias pa : aliasesToRemove) { for (PersonaAlias pa : aliasesToRemove) {
ret.removeAlias(pa); ret.removeAlias(pa);
} }
for (HashMap.Entry<PersonaAlias, PAlias> entry : aliasesToEdit.entrySet()) {
ret.modifyAlias(entry.getKey(), entry.getValue().confidence, entry.getValue().justification);
}
} catch (CentralRepoException e) { } catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to access central repository", e); logger.log(Level.SEVERE, "Failed to access central repository", e);
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
@ -797,10 +977,21 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
} }
break; break;
case VIEW: case VIEW:
ret = currentPersona;
break; break;
default: default:
logger.log(Level.SEVERE, "Unsupported mode: {0}", mode); logger.log(Level.SEVERE, "Unsupported mode: {0}", mode);
} }
return ret; return ret;
} }
/**
* Sets the persona name field.
*
* @param name Persona name.
*/
public void setPersonaName(String name) {
nameField.setText(name);
}
} }

View File

@ -103,16 +103,16 @@
<Component id="valueTextField" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="valueTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="justificationTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="justificationLbl" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="confidenceComboBox" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="confidenceComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="confidenceLbl" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="confidenceLbl" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="justificationTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="justificationLbl" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -121,49 +121,35 @@
<Component class="javax.swing.JLabel" name="nameLbl"> <Component class="javax.swing.JLabel" name="nameLbl">
<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/centralrepository/persona/Bundle.properties" key="AddMetadataDialog.nameLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaMetadataDialog.nameLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JTextField" name="nameTextField"> <Component class="javax.swing.JTextField" name="nameTextField">
<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/centralrepository/persona/Bundle.properties" key="AddMetadataDialog.nameTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaMetadataDialog.nameTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="valueLbl"> <Component class="javax.swing.JLabel" name="valueLbl">
<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/centralrepository/persona/Bundle.properties" key="AddMetadataDialog.valueLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaMetadataDialog.valueLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JTextField" name="valueTextField"> <Component class="javax.swing.JTextField" name="valueTextField">
<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/centralrepository/persona/Bundle.properties" key="AddMetadataDialog.valueTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaMetadataDialog.valueTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="justificationLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="AddMetadataDialog.justificationLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="justificationTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="AddMetadataDialog.justificationTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="confidenceLbl"> <Component class="javax.swing.JLabel" name="confidenceLbl">
<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/centralrepository/persona/Bundle.properties" key="AddMetadataDialog.confidenceLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaMetadataDialog.confidenceLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -177,12 +163,26 @@
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence&gt;"/> <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence&gt;"/>
</AuxValues> </AuxValues>
</Component> </Component>
<Component class="javax.swing.JLabel" name="justificationLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaMetadataDialog.justificationLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="justificationTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaMetadataDialog.justificationTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JButton" name="cancelBtn"> <Component class="javax.swing.JButton" name="cancelBtn">
<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/centralrepository/persona/Bundle.properties" key="AddMetadataDialog.cancelBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaMetadataDialog.cancelBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<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="[79, 23]"/> <Dimension value="[79, 23]"/>
@ -201,7 +201,7 @@
<Component class="javax.swing.JButton" name="okBtn"> <Component class="javax.swing.JButton" name="okBtn">
<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/centralrepository/persona/Bundle.properties" key="AddMetadataDialog.okBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaMetadataDialog.okBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events> <Events>

View File

@ -19,8 +19,9 @@
package org.sleuthkit.autopsy.centralrepository.persona; package org.sleuthkit.autopsy.centralrepository.persona;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
@ -29,26 +30,47 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
* Configuration dialog for adding metadata to a persona. * Configuration dialog for adding metadata to a persona.
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class AddMetadataDialog extends JDialog { public class PersonaMetadataDialog extends JDialog {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final PersonaDetailsPanel pdp; private final PersonaDetailsPanel pdp;
private PersonaDetailsPanel.PMetadata currentMetadata;
/** /**
* Creates new add metadata dialog * Creates new add metadata dialog
*/ */
@Messages({"AddMetadataDialog.title.text=Add Metadata",}) @Messages({"AddMetadataDialog.title.text=Add Metadata",})
public AddMetadataDialog(PersonaDetailsPanel pdp) { public PersonaMetadataDialog(PersonaDetailsPanel pdp) {
super((JFrame) WindowManager.getDefault().getMainWindow(), super(SwingUtilities.windowForComponent(pdp),
Bundle.AddMetadataDialog_title_text(), Bundle.AddMetadataDialog_title_text(),
true); ModalityType.APPLICATION_MODAL);
this.pdp = pdp; this.pdp = pdp;
initComponents(); initComponents();
display(); display();
} }
PersonaMetadataDialog(PersonaDetailsPanel pdp, PersonaDetailsPanel.PMetadata md) {
super(SwingUtilities.windowForComponent(pdp),
Bundle.AddMetadataDialog_title_text(),
ModalityType.APPLICATION_MODAL);
this.pdp = pdp;
initComponents();
currentMetadata = md;
confidenceComboBox.setSelectedItem(md.confidence);
justificationTextField.setText(md.justification);
nameTextField.setText(md.name);
valueTextField.setText(md.value);
nameTextField.setEnabled(false);
valueTextField.setEnabled(false);
display();
}
/** /**
* This method is called from within the constructor to initialize the form. * 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 * WARNING: Do NOT modify this code. The content of this method is always
@ -63,10 +85,10 @@ public class AddMetadataDialog extends JDialog {
nameTextField = new javax.swing.JTextField(); nameTextField = new javax.swing.JTextField();
valueLbl = new javax.swing.JLabel(); valueLbl = new javax.swing.JLabel();
valueTextField = new javax.swing.JTextField(); valueTextField = new javax.swing.JTextField();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
confidenceLbl = new javax.swing.JLabel(); confidenceLbl = new javax.swing.JLabel();
confidenceComboBox = new javax.swing.JComboBox<>(); confidenceComboBox = new javax.swing.JComboBox<>();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
cancelBtn = new javax.swing.JButton(); cancelBtn = new javax.swing.JButton();
okBtn = new javax.swing.JButton(); okBtn = new javax.swing.JButton();
@ -75,22 +97,22 @@ public class AddMetadataDialog extends JDialog {
settingsPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); settingsPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
org.openide.awt.Mnemonics.setLocalizedText(nameLbl, org.openide.util.NbBundle.getMessage(AddMetadataDialog.class, "AddMetadataDialog.nameLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(nameLbl, org.openide.util.NbBundle.getMessage(PersonaMetadataDialog.class, "PersonaMetadataDialog.nameLbl.text")); // NOI18N
nameTextField.setText(org.openide.util.NbBundle.getMessage(AddMetadataDialog.class, "AddMetadataDialog.nameTextField.text")); // NOI18N nameTextField.setText(org.openide.util.NbBundle.getMessage(PersonaMetadataDialog.class, "PersonaMetadataDialog.nameTextField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(valueLbl, org.openide.util.NbBundle.getMessage(AddMetadataDialog.class, "AddMetadataDialog.valueLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(valueLbl, org.openide.util.NbBundle.getMessage(PersonaMetadataDialog.class, "PersonaMetadataDialog.valueLbl.text")); // NOI18N
valueTextField.setText(org.openide.util.NbBundle.getMessage(AddMetadataDialog.class, "AddMetadataDialog.valueTextField.text")); // NOI18N valueTextField.setText(org.openide.util.NbBundle.getMessage(PersonaMetadataDialog.class, "PersonaMetadataDialog.valueTextField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(justificationLbl, org.openide.util.NbBundle.getMessage(AddMetadataDialog.class, "AddMetadataDialog.justificationLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(confidenceLbl, org.openide.util.NbBundle.getMessage(PersonaMetadataDialog.class, "PersonaMetadataDialog.confidenceLbl.text")); // NOI18N
justificationTextField.setText(org.openide.util.NbBundle.getMessage(AddMetadataDialog.class, "AddMetadataDialog.justificationTextField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(confidenceLbl, org.openide.util.NbBundle.getMessage(AddMetadataDialog.class, "AddMetadataDialog.confidenceLbl.text")); // NOI18N
confidenceComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence.values())); confidenceComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence.values()));
org.openide.awt.Mnemonics.setLocalizedText(justificationLbl, org.openide.util.NbBundle.getMessage(PersonaMetadataDialog.class, "PersonaMetadataDialog.justificationLbl.text")); // NOI18N
justificationTextField.setText(org.openide.util.NbBundle.getMessage(PersonaMetadataDialog.class, "PersonaMetadataDialog.justificationTextField.text")); // NOI18N
javax.swing.GroupLayout settingsPanelLayout = new javax.swing.GroupLayout(settingsPanel); javax.swing.GroupLayout settingsPanelLayout = new javax.swing.GroupLayout(settingsPanel);
settingsPanel.setLayout(settingsPanelLayout); settingsPanel.setLayout(settingsPanelLayout);
settingsPanelLayout.setHorizontalGroup( settingsPanelLayout.setHorizontalGroup(
@ -128,17 +150,17 @@ public class AddMetadataDialog extends JDialog {
.addComponent(valueLbl) .addComponent(valueLbl)
.addComponent(valueTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(valueTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(justificationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(justificationLbl))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(confidenceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(confidenceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(confidenceLbl)) .addComponent(confidenceLbl))
.addContainerGap()) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(justificationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(justificationLbl))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
); );
org.openide.awt.Mnemonics.setLocalizedText(cancelBtn, org.openide.util.NbBundle.getMessage(AddMetadataDialog.class, "AddMetadataDialog.cancelBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(cancelBtn, org.openide.util.NbBundle.getMessage(PersonaMetadataDialog.class, "PersonaMetadataDialog.cancelBtn.text")); // NOI18N
cancelBtn.setMaximumSize(new java.awt.Dimension(79, 23)); cancelBtn.setMaximumSize(new java.awt.Dimension(79, 23));
cancelBtn.setMinimumSize(new java.awt.Dimension(79, 23)); cancelBtn.setMinimumSize(new java.awt.Dimension(79, 23));
cancelBtn.setPreferredSize(new java.awt.Dimension(79, 23)); cancelBtn.setPreferredSize(new java.awt.Dimension(79, 23));
@ -148,7 +170,7 @@ public class AddMetadataDialog extends JDialog {
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(okBtn, org.openide.util.NbBundle.getMessage(AddMetadataDialog.class, "AddMetadataDialog.okBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(okBtn, org.openide.util.NbBundle.getMessage(PersonaMetadataDialog.class, "PersonaMetadataDialog.okBtn.text")); // NOI18N
okBtn.addActionListener(new java.awt.event.ActionListener() { okBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
okBtnActionPerformed(evt); okBtnActionPerformed(evt);
@ -191,13 +213,34 @@ public class AddMetadataDialog extends JDialog {
@Messages({ @Messages({
"AddMetadataDialog_dup_Title=Metadata add failure", "AddMetadataDialog_dup_Title=Metadata add failure",
"AddMetadataDialog_dup_msg=A metadata entry with this name has already been added to this persona",}) "AddMetadataDialog_dup_msg=A metadata entry with this name has already been added to this persona.",
"AddMetadataDialog_empty_name_Title=Missing field(s)",
"AddMetadataDialog_empty_name_msg=A metadata entry cannot have an empty name or value.",})
private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed
if (pdp.addMetadata( if (StringUtils.isBlank(nameTextField.getText()) || StringUtils.isBlank(valueTextField.getText())) {
nameTextField.getText(), JOptionPane.showMessageDialog(this,
valueTextField.getText(), Bundle.AddMetadataDialog_empty_name_msg(),
justificationTextField.getText(), Bundle.AddMetadataDialog_empty_name_Title(),
(Persona.Confidence) confidenceComboBox.getSelectedItem())) { JOptionPane.ERROR_MESSAGE);
return;
}
if (StringUtils.isBlank(justificationTextField.getText())) {
JOptionPane.showMessageDialog(this,
Bundle.PersonaDetailsPanel_empty_justification_msg(),
Bundle.PersonaDetailsPanel_empty_justification_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
Persona.Confidence confidence = (Persona.Confidence) confidenceComboBox.getSelectedItem();
String justification = justificationTextField.getText();
if (currentMetadata != null) {
currentMetadata.confidence = confidence;
currentMetadata.justification = justification;
dispose();
} else {
if (pdp.addMetadata(nameTextField.getText(), valueTextField.getText(), justification, confidence)) {
dispose(); dispose();
} else { } else {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
@ -205,6 +248,7 @@ public class AddMetadataDialog extends JDialog {
Bundle.AddMetadataDialog_dup_Title(), Bundle.AddMetadataDialog_dup_Title(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
} }
}
}//GEN-LAST:event_okBtnActionPerformed }//GEN-LAST:event_okBtnActionPerformed
private void cancelBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelBtnActionPerformed private void cancelBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelBtnActionPerformed

View File

@ -1,35 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents> <NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="searchButtonGroup"> <Component class="javax.swing.ButtonGroup" name="searchButtonGroup">
</Component> </Component>
</NonVisualComponents> </NonVisualComponents>
<Properties> <Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="name" type="java.lang.String" value="" noResource="true"/>
<Dimension value="[400, 400]"/>
</Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
@ -46,17 +23,52 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jSplitPane1" alignment="0" pref="692" max="32767" attributes="0"/> <Component id="introTextScrollPane" alignment="0" max="32767" attributes="0"/>
<Component id="mainSplitPane" alignment="0" pref="724" max="32767" attributes="0"/>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jSplitPane1" alignment="0" max="32767" attributes="0"/> <Group type="102" alignment="1" attributes="0">
<Component id="introTextScrollPane" min="-2" pref="49" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="mainSplitPane" pref="489" max="32767" attributes="0"/>
</Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Container class="javax.swing.JSplitPane" name="jSplitPane1"> <Container class="javax.swing.JScrollPane" name="introTextScrollPane">
<Properties>
<Property name="verticalScrollBarPolicy" type="int" value="21"/>
</Properties>
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="introText">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection component="Form" name="getBackground" type="method"/>
</Property>
<Property name="columns" type="int" value="20"/>
<Property name="lineWrap" type="boolean" value="true"/>
<Property name="rows" type="int" value="5"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.introText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="wrapStyleWord" type="boolean" value="true"/>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
<Properties>
<Property name="dividerLocation" type="int" value="400"/>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents> <SubComponents>
@ -73,13 +85,7 @@
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Component id="createButtonSeparator" max="32767" attributes="0"/>
<Component id="createBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="editBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="deleteBtn" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="resultsPane" pref="0" max="32767" attributes="0"/> <Component id="resultsPane" pref="0" max="32767" attributes="0"/>
<Component id="searchField" max="32767" attributes="0"/> <Component id="searchField" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
@ -89,6 +95,20 @@
<EmptySpace max="32767" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
<Component id="searchBtn" min="-2" max="-2" attributes="0"/> <Component id="searchBtn" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="createAccountBtn" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="createBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="editBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="deleteBtn" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cbFilterByKeyword" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="50" max="32767" attributes="0"/>
</Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -97,7 +117,8 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/> <Component id="cbFilterByKeyword" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
<Component id="searchField" min="-2" max="-2" attributes="0"/> <Component id="searchField" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
@ -106,7 +127,7 @@
<Component id="searchBtn" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="searchBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="resultsPane" pref="528" max="32767" attributes="0"/> <Component id="resultsPane" min="-2" pref="302" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="editBtn" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="editBtn" alignment="3" min="-2" max="-2" attributes="0"/>
@ -114,6 +135,10 @@
<Component id="deleteBtn" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="deleteBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="createButtonSeparator" min="-2" pref="4" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="createAccountBtn" min="-2" pref="32" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -122,7 +147,7 @@
<Component class="javax.swing.JTextField" name="searchField"> <Component class="javax.swing.JTextField" name="searchField">
<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/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.searchField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.searchField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -133,7 +158,7 @@
</Property> </Property>
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<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/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.searchNameRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.searchNameRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -143,9 +168,15 @@
<ComponentRef name="searchButtonGroup"/> <ComponentRef name="searchButtonGroup"/>
</Property> </Property>
<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/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.searchAccountRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.searchAccountRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="searchBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.searchBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>
</Component> </Component>
<Container class="javax.swing.JScrollPane" name="resultsPane"> <Container class="javax.swing.JScrollPane" name="resultsPane">
@ -158,20 +189,20 @@
<Component class="javax.swing.JTable" name="resultsTable"> <Component class="javax.swing.JTable" name="resultsTable">
<Properties> <Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.resultsTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.resultsTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor"> <Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0"> <TableColumnModel selectionModel="0">
<Column maxWidth="25" minWidth="-1" prefWidth="-1" resizable="true"> <Column maxWidth="25" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.resultsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.resultsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title> </Title>
<Editor/> <Editor/>
<Renderer/> <Renderer/>
</Column> </Column>
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.resultsTable.columnModel.title1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.resultsTable.columnModel.title1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title> </Title>
<Editor/> <Editor/>
<Renderer/> <Renderer/>
@ -185,49 +216,70 @@
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JButton" name="searchBtn"> <Component class="javax.swing.JButton" name="createAccountBtn">
<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/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.searchBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.createAccountBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="editBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.editBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="createBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.createBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/> <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
</AuxValues> </AuxValues>
</Component> </Component>
<Component class="javax.swing.JButton" name="deleteBtn"> <Component class="javax.swing.JButton" name="editBtn">
<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/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.deleteBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.editBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="enabled" type="boolean" value="false"/> <Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JButton" name="deleteBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.deleteBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JSeparator" name="createButtonSeparator">
</Component>
<Component class="javax.swing.JButton" name="createBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.createBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
</AuxValues>
</Component>
<Component class="javax.swing.JCheckBox" name="cbFilterByKeyword">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.cbFilterByKeyword.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbFilterByKeywordActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel"> <Container class="javax.swing.JScrollPane" name="detailsScrollPane">
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/> <JSplitPaneConstraints position="right"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel">
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>
</Container>
</SubComponents>
</Form> </Form>

View File

@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.centralrepository.persona;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -27,14 +29,19 @@ import java.util.logging.Level;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
import javax.swing.event.AncestorListener;
import javax.swing.event.AncestorEvent;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel; import javax.swing.table.DefaultTableModel;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.RetainLocation; import org.openide.windows.RetainLocation;
import org.openide.windows.TopComponent; import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
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.Persona; import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -42,26 +49,28 @@ import org.sleuthkit.autopsy.coreutils.Logger;
* Top component for the Personas tool * Top component for the Personas tool
* *
*/ */
@TopComponent.Description(preferredID = "PersonaManagerTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER) @TopComponent.Description(preferredID = "PersonasTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "personamanager", openAtStartup = false) @TopComponent.Registration(mode = "personas", openAtStartup = false)
@RetainLocation("personamanager") @RetainLocation("personas")
@SuppressWarnings("PMD.SingularField") @SuppressWarnings("PMD.SingularField")
public final class PersonaManagerTopComponent extends TopComponent { public final class PersonasTopComponent extends TopComponent {
private static final Logger logger = Logger.getLogger(PersonaManagerTopComponent.class.getName()); private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(PersonasTopComponent.class.getName());
private List<Persona> currentResults = null; private List<Persona> currentResults = null;
private Persona selectedPersona = null; private Persona selectedPersona = null;
@Messages({ @Messages({
"PMTopComponent_Name=Persona Manager", "PersonasTopComponent_Name=Personas",
"PMTopComponent_delete_exception_Title=Delete failure", "PersonasTopComponent_delete_exception_Title=Delete failure",
"PMTopComponent_delete_exception_msg=Failed to delete persona", "PersonasTopComponent_delete_exception_msg=Failed to delete persona.",
}) "PersonasTopComponent_delete_confirmation_Title=Are you sure?",
public PersonaManagerTopComponent() { "PersonasTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?",})
public PersonasTopComponent() {
initComponents(); initComponents();
setName(Bundle.PMTopComponent_Name()); setName(Bundle.PersonasTopComponent_Name());
executeSearch();
searchBtn.addActionListener(new ActionListener() { searchBtn.addActionListener(new ActionListener() {
@Override @Override
@ -73,7 +82,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
editBtn.addActionListener(new ActionListener() { editBtn.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
new PersonaDetailsDialog(PersonaManagerTopComponent.this, new PersonaDetailsDialog(PersonasTopComponent.this,
PersonaDetailsMode.EDIT, selectedPersona, new CreateEditCallbackImpl()); PersonaDetailsMode.EDIT, selectedPersona, new CreateEditCallbackImpl());
} }
}); });
@ -81,7 +90,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
createBtn.addActionListener(new ActionListener() { createBtn.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
new PersonaDetailsDialog(PersonaManagerTopComponent.this, new PersonaDetailsDialog(PersonasTopComponent.this,
PersonaDetailsMode.CREATE, selectedPersona, new CreateEditCallbackImpl()); PersonaDetailsMode.CREATE, selectedPersona, new CreateEditCallbackImpl());
} }
}); });
@ -89,20 +98,27 @@ public final class PersonaManagerTopComponent extends TopComponent {
deleteBtn.addActionListener(new ActionListener() { deleteBtn.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
NotifyDescriptor confirm = new NotifyDescriptor.Confirmation(
Bundle.PersonasTopComponent_delete_confirmation_msg(),
Bundle.PersonasTopComponent_delete_confirmation_Title(),
NotifyDescriptor.YES_NO_OPTION);
DialogDisplayer.getDefault().notify(confirm);
if (confirm.getValue().equals(NotifyDescriptor.YES_OPTION)) {
try { try {
if (selectedPersona != null) { if (selectedPersona != null) {
selectedPersona.delete(); selectedPersona.delete();
} }
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to delete persona: " + selectedPersona.getName(), ex); logger.log(Level.SEVERE, "Failed to delete persona: " + selectedPersona.getName(), ex);
JOptionPane.showMessageDialog(PersonaManagerTopComponent.this, JOptionPane.showMessageDialog(PersonasTopComponent.this,
Bundle.PMTopComponent_delete_exception_msg(), Bundle.PersonasTopComponent_delete_exception_msg(),
Bundle.PMTopComponent_delete_exception_Title(), Bundle.PersonasTopComponent_delete_exception_Title(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
executeSearch(); executeSearch();
} }
}
}); });
// Results table // Results table
@ -113,6 +129,33 @@ public final class PersonaManagerTopComponent extends TopComponent {
handleSelectionChange(e); handleSelectionChange(e);
} }
}); });
searchNameRadio.addActionListener((ActionEvent e) -> {
searchField.setText("");
});
searchAccountRadio.addActionListener((ActionEvent e) -> {
searchField.setText("");
});
createAccountBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new CreatePersonaAccountDialog(detailsPanel);
}
});
/**
* Listens for when this component will be rendered and executes a
* search to update gui when it is displayed.
*/
addComponentListener(new ComponentAdapter() {
@Override
public void componentShown(ComponentEvent e) {
resetSearchControls();
setKeywordSearchEnabled(false, true);
}
});
} }
/** /**
@ -133,6 +176,34 @@ public final class PersonaManagerTopComponent extends TopComponent {
} }
} }
/**
* Resets search controls to default state.
*/
private void resetSearchControls() {
searchField.setText("");
searchNameRadio.setSelected(true);
searchAccountRadio.setSelected(false);
}
/**
* Sets up the GUI for appropriate state for keyword search enabled state.
*
* @param selected Whether or not keyword search is enabled.
* @param setFilterCb Whether or not the filter checkbox should be
* manipulated as a part of this change.
*/
private void setKeywordSearchEnabled(boolean selected, boolean setFilterCb) {
if (setFilterCb && cbFilterByKeyword.isSelected() != selected) {
cbFilterByKeyword.setSelected(selected);
}
searchField.setEnabled(selected);
searchNameRadio.setEnabled(selected);
searchAccountRadio.setEnabled(selected);
executeSearch();
}
void setPersona(int index) { void setPersona(int index) {
Persona persona = currentResults.get(index); Persona persona = currentResults.get(index);
selectedPersona = persona; selectedPersona = persona;
@ -195,17 +266,36 @@ public final class PersonaManagerTopComponent extends TopComponent {
} }
@Messages({ @Messages({
"PMTopComponent_search_exception_Title=Search failure", "PersonasTopComponent_search_exception_Title=There was a failure during the search. Try opening a case to fully initialize the central repository database.",
"PMTopComponent_search_exception_msg=Failed to search personas",}) "PersonasTopComponent_search_exception_msg=Failed to search personas.",
"PersonasTopComponent_noCR_msg=Central Repository is not enabled.",})
private void executeSearch() { private void executeSearch() {
// To prevent downstream failures, only execute search if central repository is enabled
if (!CentralRepository.isEnabled()) {
logger.log(Level.SEVERE, "Central Repository is not enabled, but execute search was called.");
JOptionPane.showMessageDialog(this,
Bundle.PersonasTopComponent_search_exception_Title(),
Bundle.PersonasTopComponent_noCR_msg(),
JOptionPane.ERROR_MESSAGE);
return;
}
Collection<Persona> results; Collection<Persona> results;
try { try {
if (cbFilterByKeyword.isSelected()) {
if (searchNameRadio.isSelected()) {
results = Persona.getPersonaByName(searchField.getText()); results = Persona.getPersonaByName(searchField.getText());
} else {
results = Persona.getPersonaByAccountIdentifierLike(searchField.getText());
}
} else {
results = Persona.getPersonaByName("");
}
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to search personas", ex); logger.log(Level.SEVERE, "Failed to search personas", ex);
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.PMTopComponent_search_exception_Title(), Bundle.PersonasTopComponent_search_exception_Title(),
Bundle.PMTopComponent_search_exception_msg(), Bundle.PersonasTopComponent_search_exception_msg(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
@ -231,50 +321,77 @@ public final class PersonaManagerTopComponent extends TopComponent {
private void initComponents() { private void initComponents() {
searchButtonGroup = new javax.swing.ButtonGroup(); searchButtonGroup = new javax.swing.ButtonGroup();
jSplitPane1 = new javax.swing.JSplitPane(); introTextScrollPane = new javax.swing.JScrollPane();
introText = new javax.swing.JTextArea();
mainSplitPane = new javax.swing.JSplitPane();
searchPanel = new javax.swing.JPanel(); searchPanel = new javax.swing.JPanel();
searchField = new javax.swing.JTextField(); searchField = new javax.swing.JTextField();
searchNameRadio = new javax.swing.JRadioButton(); searchNameRadio = new javax.swing.JRadioButton();
searchAccountRadio = new javax.swing.JRadioButton(); searchAccountRadio = new javax.swing.JRadioButton();
searchBtn = new javax.swing.JButton();
resultsPane = new javax.swing.JScrollPane(); resultsPane = new javax.swing.JScrollPane();
resultsTable = new javax.swing.JTable(); resultsTable = new javax.swing.JTable();
searchBtn = new javax.swing.JButton(); createAccountBtn = new javax.swing.JButton();
editBtn = new javax.swing.JButton(); editBtn = new javax.swing.JButton();
createBtn = new javax.swing.JButton();
deleteBtn = new javax.swing.JButton(); deleteBtn = new javax.swing.JButton();
createButtonSeparator = new javax.swing.JSeparator();
createBtn = new javax.swing.JButton();
cbFilterByKeyword = new javax.swing.JCheckBox();
detailsScrollPane = new javax.swing.JScrollPane();
detailsPanel = new org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel(); detailsPanel = new org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel();
setMinimumSize(new java.awt.Dimension(400, 400)); setName(""); // NOI18N
searchField.setText(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchField.text")); // NOI18N introTextScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
introText.setBackground(getBackground());
introText.setColumns(20);
introText.setLineWrap(true);
introText.setRows(5);
introText.setText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.introText.text")); // NOI18N
introText.setWrapStyleWord(true);
introText.setFocusable(false);
introTextScrollPane.setViewportView(introText);
mainSplitPane.setDividerLocation(400);
searchField.setText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchField.text")); // NOI18N
searchButtonGroup.add(searchNameRadio); searchButtonGroup.add(searchNameRadio);
searchNameRadio.setSelected(true); searchNameRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(searchNameRadio, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchNameRadio.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(searchNameRadio, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchNameRadio.text")); // NOI18N
searchButtonGroup.add(searchAccountRadio); searchButtonGroup.add(searchAccountRadio);
org.openide.awt.Mnemonics.setLocalizedText(searchAccountRadio, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchAccountRadio.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(searchAccountRadio, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchAccountRadio.text")); // NOI18N
searchAccountRadio.setEnabled(false);
resultsTable.setToolTipText(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.resultsTable.toolTipText")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(searchBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchBtn.text")); // NOI18N
resultsTable.setToolTipText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultsTable.toolTipText")); // NOI18N
resultsTable.getTableHeader().setReorderingAllowed(false); resultsTable.getTableHeader().setReorderingAllowed(false);
resultsPane.setViewportView(resultsTable); resultsPane.setViewportView(resultsTable);
if (resultsTable.getColumnModel().getColumnCount() > 0) { if (resultsTable.getColumnModel().getColumnCount() > 0) {
resultsTable.getColumnModel().getColumn(0).setMaxWidth(25); resultsTable.getColumnModel().getColumn(0).setMaxWidth(25);
resultsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.resultsTable.columnModel.title0")); // NOI18N resultsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultsTable.columnModel.title0")); // NOI18N
resultsTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.resultsTable.columnModel.title1")); // NOI18N resultsTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultsTable.columnModel.title1")); // NOI18N
} }
org.openide.awt.Mnemonics.setLocalizedText(searchBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(createAccountBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.createAccountBtn.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(editBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.editBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(editBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.editBtn.text")); // NOI18N
editBtn.setEnabled(false); editBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(createBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.createBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(deleteBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.deleteBtn.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deleteBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.deleteBtn.text")); // NOI18N
deleteBtn.setEnabled(false); deleteBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(createBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.createBtn.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(cbFilterByKeyword, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.cbFilterByKeyword.text")); // NOI18N
cbFilterByKeyword.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cbFilterByKeywordActionPerformed(evt);
}
});
javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel); javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel);
searchPanel.setLayout(searchPanelLayout); searchPanel.setLayout(searchPanelLayout);
searchPanelLayout.setHorizontalGroup( searchPanelLayout.setHorizontalGroup(
@ -282,12 +399,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addGroup(searchPanelLayout.createSequentialGroup() .addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup() .addComponent(createButtonSeparator)
.addComponent(createBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(deleteBtn))
.addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) .addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(searchField) .addComponent(searchField)
.addGroup(searchPanelLayout.createSequentialGroup() .addGroup(searchPanelLayout.createSequentialGroup()
@ -295,13 +407,25 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchAccountRadio) .addComponent(searchAccountRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(searchBtn))) .addComponent(searchBtn))
.addGroup(searchPanelLayout.createSequentialGroup()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(createAccountBtn)
.addGroup(searchPanelLayout.createSequentialGroup()
.addComponent(createBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(deleteBtn))
.addComponent(cbFilterByKeyword))
.addGap(0, 50, Short.MAX_VALUE)))
.addContainerGap()) .addContainerGap())
); );
searchPanelLayout.setVerticalGroup( searchPanelLayout.setVerticalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup() .addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap() .addComponent(cbFilterByKeyword)
.addGap(1, 1, 1)
.addComponent(searchField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(searchField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
@ -309,36 +433,57 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addComponent(searchAccountRadio) .addComponent(searchAccountRadio)
.addComponent(searchBtn)) .addComponent(searchBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(resultsPane, javax.swing.GroupLayout.DEFAULT_SIZE, 528, Short.MAX_VALUE) .addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 302, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(editBtn) .addComponent(editBtn)
.addComponent(createBtn) .addComponent(createBtn)
.addComponent(deleteBtn)) .addComponent(deleteBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(createButtonSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 4, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(createAccountBtn, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap()) .addContainerGap())
); );
jSplitPane1.setLeftComponent(searchPanel); mainSplitPane.setLeftComponent(searchPanel);
jSplitPane1.setRightComponent(detailsPanel);
detailsScrollPane.setViewportView(detailsPanel);
mainSplitPane.setRightComponent(detailsScrollPane);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 692, Short.MAX_VALUE) .addComponent(introTextScrollPane)
.addComponent(mainSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 724, Short.MAX_VALUE)
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(introTextScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(mainSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 489, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void cbFilterByKeywordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbFilterByKeywordActionPerformed
setKeywordSearchEnabled(cbFilterByKeyword.isSelected(), false);
}//GEN-LAST:event_cbFilterByKeywordActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox cbFilterByKeyword;
private javax.swing.JButton createAccountBtn;
private javax.swing.JButton createBtn; private javax.swing.JButton createBtn;
private javax.swing.JSeparator createButtonSeparator;
private javax.swing.JButton deleteBtn; private javax.swing.JButton deleteBtn;
private org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel detailsPanel; private org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel detailsPanel;
private javax.swing.JScrollPane detailsScrollPane;
private javax.swing.JButton editBtn; private javax.swing.JButton editBtn;
private javax.swing.JSplitPane jSplitPane1; private javax.swing.JTextArea introText;
private javax.swing.JScrollPane introTextScrollPane;
private javax.swing.JSplitPane mainSplitPane;
private javax.swing.JScrollPane resultsPane; private javax.swing.JScrollPane resultsPane;
private javax.swing.JTable resultsTable; private javax.swing.JTable resultsTable;
private javax.swing.JRadioButton searchAccountRadio; private javax.swing.JRadioButton searchAccountRadio;

View File

@ -862,7 +862,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
/** /**
* If the settings reflect that a inter-case search is being performed, * If the settings reflect that a inter-case search is being performed,
* checks that the data sources in the current case have been processed with * checks that the data sources in the current case have been processed with
* Correlation Engine enabled and exist in the central repository. Prompting * Central Repository enabled and exist in the central repository. Prompting
* the user as to whether they still want to perform the search in the case * the user as to whether they still want to perform the search in the case
* any data sources are unprocessed. If the settings reflect that a * any data sources are unprocessed. If the settings reflect that a
* intra-case search is being performed, it just performs the search. * intra-case search is being performed, it just performs the search.
@ -870,7 +870,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
* Notes: - Does not check that the data sources were processed into the * Notes: - Does not check that the data sources were processed into the
* current central repository instead of another. - Does not check that the * current central repository instead of another. - Does not check that the
* appropriate modules to make all correlation types available were run. - * appropriate modules to make all correlation types available were run. -
* Does not check if the correlation engine was run with any of the * Does not check if the Central Repository was run with any of the
* correlation properties properties disabled. * correlation properties properties disabled.
*/ */
@Messages({"CommonAttributePanel.incompleteResults.introText=Results may be incomplete. Not all data sources in the current case were ingested into the current Central Repository. The following data sources have not been processed:", @Messages({"CommonAttributePanel.incompleteResults.introText=Results may be incomplete. Not all data sources in the current case were ingested into the current Central Repository. The following data sources have not been processed:",
@ -902,14 +902,14 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
//if the datasource was previously processed we do not need to perform this check //if the datasource was previously processed we do not need to perform this check
for (CorrelationDataSource correlatedDataSource : correlatedDataSources) { for (CorrelationDataSource correlatedDataSource : correlatedDataSources) {
if (deviceID.equals(correlatedDataSource.getDeviceID())) { if (deviceID.equals(correlatedDataSource.getDeviceID())) {
//if the datasource exists in the central repository it may of been processed with the correlation engine //if the datasource exists in the central repository it may of been processed with the Central Repository
dataSourceCorrelationMap.put(dataSource, CorrelatedStatus.IN_CENTRAL_REPO); dataSourceCorrelationMap.put(dataSource, CorrelatedStatus.IN_CENTRAL_REPO);
break; break;
} }
} }
} }
if (dataSourceCorrelationMap.get(dataSource) == CorrelatedStatus.IN_CENTRAL_REPO) { if (dataSourceCorrelationMap.get(dataSource) == CorrelatedStatus.IN_CENTRAL_REPO) {
//if the data source was in the central repository check if any of the modules run on it were the correlation engine //if the data source was in the central repository check if any of the modules run on it were the Central Repository
for (IngestModuleInfo ingestModuleInfo : jobInfo.getIngestModuleInfo()) { for (IngestModuleInfo ingestModuleInfo : jobInfo.getIngestModuleInfo()) {
if (correlationEngineModuleName.equals(ingestModuleInfo.getDisplayName())) { if (correlationEngineModuleName.equals(ingestModuleInfo.getDisplayName())) {
dataSourceCorrelationMap.put(dataSource, CorrelatedStatus.CORRELATED); dataSourceCorrelationMap.put(dataSource, CorrelatedStatus.CORRELATED);

View File

@ -14,7 +14,7 @@ FiltersPanel.endCheckBox.text=End:
FiltersPanel.refreshButton.text=Refresh FiltersPanel.refreshButton.text=Refresh
FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.deviceRequiredLabel.text=Select at least one.
FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one.
FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. FiltersPanel.needsRefreshLabel.text=Displayed data may be out of date. Press Refresh to update.
VisualizationPanel.jButton1.text=Fast Organic VisualizationPanel.jButton1.text=Fast Organic
CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize
CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse

View File

@ -1,3 +1,8 @@
# {0} - PersonaAccount count
AccountInstanceNode_Tooltip_suffix=(1 of {0})
# {0} - Contact Name
# {1} - Persona Name
AccountInstanceNode_Tooltip_Template=Contact: {0} - Persona: {1}
AccountNode.accountName=Account AccountNode.accountName=Account
AccountNode.accountType=Type AccountNode.accountType=Type
AccountNode.device=Device AccountNode.device=Device
@ -21,7 +26,7 @@ FiltersPanel.endCheckBox.text=End:
FiltersPanel.refreshButton.text=Refresh FiltersPanel.refreshButton.text=Refresh
FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.deviceRequiredLabel.text=Select at least one.
FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one.
FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. FiltersPanel.needsRefreshLabel.text=Displayed data may be out of date. Press Refresh to update.
OpenCVTAction.displayName=Communications OpenCVTAction.displayName=Communications
PinAccountsAction.pluralText=Add Selected Accounts to Visualization PinAccountsAction.pluralText=Add Selected Accounts to Visualization
PinAccountsAction.singularText=Add Selected Account to Visualization PinAccountsAction.singularText=Add Selected Account to Visualization

View File

@ -0,0 +1,163 @@
/*
* 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.communications;
import java.beans.PropertyChangeEvent;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Refreshes the CVTFilterPanel.
*/
abstract class CVTFilterRefresher implements RefreshThrottler.Refresher {
private static final Logger logger = Logger.getLogger(CVTFilterRefresher.class.getName());
/**
* contains all of the gui control specific update code. Refresh will call
* this method with an involkLater so that the updating of the swing
* controls can happen on the EDT.
*
* @param data
*/
abstract void updateFilterPanel(FilterPanelData data);
@Override
public void refresh() {
try {
Integer startTime;
Integer endTime;
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
// Fetch Min/Max start times
try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery("SELECT MAX(date_time) as end, MIN(date_time) as start from account_relationships")) {
// ResultSet is closed by CasDBQuery
ResultSet rs = dbQuery.getResultSet();
rs.next();
startTime = rs.getInt("start"); // NON-NLS
endTime = rs.getInt("end"); // NON-NLS
}
// Get the devices with CVT artifacts
List<Integer> deviceObjIds = new ArrayList<>();
try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery("SELECT DISTINCT data_source_obj_id FROM account_relationships")) {
// ResultSet is closed by CasDBQuery
ResultSet rs = queryResult.getResultSet();
while (rs.next()) {
deviceObjIds.add(rs.getInt(1));
}
}
// The map key is the Content name instead of the data source name
// to match how the CVT filters work.
Map<String, DataSource> dataSourceMap = new HashMap<>();
for (DataSource dataSource : skCase.getDataSources()) {
if (deviceObjIds.contains((int) dataSource.getId())) {
String dsName = skCase.getContentById(dataSource.getId()).getName();
dataSourceMap.put(dsName, dataSource);
}
}
List<Account.Type> accountTypesInUse = skCase.getCommunicationsManager().getAccountTypesInUse();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateFilterPanel(new FilterPanelData(dataSourceMap, accountTypesInUse, startTime, endTime));
}
});
} catch (SQLException | TskCoreException ex) {
logger.log(Level.WARNING, "Unable to update CVT filter panel.", ex);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(DATA_ADDED.toString())) {
// Indicate that a refresh may be needed, unless the data added is Keyword or Hashset hits
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
return (null != eventData
&& (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()));
}
return false;
}
/**
* Class to hold the data for setting up the filter panel gui controls.
*/
class FilterPanelData {
private final Map<String, DataSource> dataSourceMap;
private final Integer startTime;
private final Integer endTime;
private final List<Account.Type> accountTypesInUse;
FilterPanelData(Map<String, DataSource> dataSourceMap, List<Account.Type> accountTypesInUse, Integer startTime, Integer endTime) {
this.dataSourceMap = dataSourceMap;
this.startTime = startTime;
this.endTime = endTime;
this.accountTypesInUse = accountTypesInUse;
}
Map<String, DataSource> getDataSourceMap() {
return dataSourceMap;
}
Integer getStartTime() {
return startTime;
}
Integer getEndTime() {
return endTime;
}
List<Account.Type> getAccountTypesInUse() {
return accountTypesInUse;
}
}
}

View File

@ -189,7 +189,7 @@ public final class CVTTopComponent extends TopComponent {
* *
* Re-applying the filters means we will lose the selection... * Re-applying the filters means we will lose the selection...
*/ */
filtersPane.updateAndApplyFilters(true); filtersPane.initalizeFilters();
} }
@Override @Override

View File

@ -18,11 +18,11 @@
<SubComponents> <SubComponents>
<Container class="javax.swing.JScrollPane" name="scrollPane"> <Container class="javax.swing.JScrollPane" name="scrollPane">
<Properties> <Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="autoscrolls" type="boolean" value="true"/>
<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="null"/> <Border info="null"/>
</Property> </Property>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="autoscrolls" type="boolean" value="true"/>
</Properties> </Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
@ -222,9 +222,14 @@
</SubComponents> </SubComponents>
</Container> </Container>
<Container class="javax.swing.JPanel" name="devicesPane"> <Container class="javax.swing.JPanel" name="devicesPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 300]"/>
</Property>
</Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="100" insetsTop="15" insetsLeft="0" insetsBottom="0" insetsRight="25" anchor="18" weightX="1.0" weightY="0.0"/> <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="0" insetsBottom="0" insetsRight="25" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
@ -277,7 +282,6 @@
</Component> </Component>
<Container class="javax.swing.JScrollPane" name="devicesScrollPane"> <Container class="javax.swing.JScrollPane" name="devicesScrollPane">
<Properties> <Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<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="[32767, 30]"/> <Dimension value="[32767, 30]"/>
</Property> </Property>

View File

@ -18,25 +18,24 @@
*/ */
package org.sleuthkit.autopsy.communications; package org.sleuthkit.autopsy.communications;
import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.awt.event.ItemListener; import java.awt.event.ItemListener;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.Box; import javax.swing.Box;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
@ -45,11 +44,9 @@ import javax.swing.ImageIcon;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.SwingWorker;
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.Events.CURRENT_CASE; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
@ -59,7 +56,6 @@ import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_
import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.CaseDbAccessManager.CaseDbAccessQueryCallback;
import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter;
import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter;
@ -69,8 +65,6 @@ import org.sleuthkit.datamodel.DataSource;
import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG; import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG;
import static org.sleuthkit.datamodel.Relationship.Type.CONTACT; import static org.sleuthkit.datamodel.Relationship.Type.CONTACT;
import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE; import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Panel that holds the Filter control widgets and triggers queries against the * Panel that holds the Filter control widgets and triggers queries against the
@ -114,6 +108,8 @@ final public class FiltersPanel extends JPanel {
*/ */
private final ItemListener validationListener; private final ItemListener validationListener;
private final RefreshThrottler refreshThrottler;
/** /**
* Is the device account type filter enabled or not. It should be enabled * Is the device account type filter enabled or not. It should be enabled
* when the Table/Brows mode is active and disabled when the visualization * when the Table/Brows mode is active and disabled when the visualization
@ -129,6 +125,7 @@ final public class FiltersPanel extends JPanel {
initComponents(); initComponents();
initalizeDeviceAccountType(); initalizeDeviceAccountType();
setDateTimeFiltersToDefault();
deviceRequiredLabel.setVisible(false); deviceRequiredLabel.setVisible(false);
accountTypeRequiredLabel.setVisible(false); accountTypeRequiredLabel.setVisible(false);
@ -160,25 +157,27 @@ final public class FiltersPanel extends JPanel {
if (eventType.equals(DATA_ADDED.toString())) { if (eventType.equals(DATA_ADDED.toString())) {
// Indicate that a refresh may be needed, unless the data added is Keyword or Hashset hits // Indicate that a refresh may be needed, unless the data added is Keyword or Hashset hits
ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue();
if (null != eventData if (!needsRefresh
&& null != eventData
&& (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID() || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())) { || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())) {
updateFilters(true);
needsRefresh = true; needsRefresh = true;
validateFilters(); validateFilters();
} }
} }
}; };
refreshThrottler = new RefreshThrottler(new FilterPanelRefresher(false, false));
this.ingestJobListener = pce -> { this.ingestJobListener = pce -> {
String eventType = pce.getPropertyName(); String eventType = pce.getPropertyName();
if (eventType.equals(COMPLETED.toString()) if (eventType.equals(COMPLETED.toString()) && !needsRefresh) {
&& updateFilters(true)) {
needsRefresh = true; needsRefresh = true;
validateFilters(); validateFilters();
} }
}; };
@ -220,39 +219,24 @@ final public class FiltersPanel extends JPanel {
} }
} }
/** void initalizeFilters() {
* Update the filter widgets, and apply them. Runnable runnable = new Runnable() {
*/ @Override
void updateAndApplyFilters(boolean initialState) { public void run() {
updateFilters(initialState); new FilterPanelRefresher(true, true).refresh();
applyFilters(); }
initalizeDateTimeFilters(); };
runnable.run();
} }
private void updateTimeZone() { private void updateTimeZone() {
dateRangeLabel.setText("Date Range (" + Utils.getUserPreferredZoneId().toString() + "):"); dateRangeLabel.setText("Date Range (" + Utils.getUserPreferredZoneId().toString() + "):");
} }
/**
* Updates the filter widgets to reflect he data sources/types in the case.
*/
private boolean updateFilters(boolean initialState) {
final SleuthkitCase sleuthkitCase;
try {
sleuthkitCase = Case.getCurrentCaseThrows().getSleuthkitCase();
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Unable to perform filter update, update has been cancelled. Case is closed.", ex);
return false;
}
boolean newAccountType = updateAccountTypeFilter(initialState, sleuthkitCase);
boolean newDeviceFilter = updateDeviceFilter(initialState, sleuthkitCase);
// both or either are true, return true;
return newAccountType || newDeviceFilter;
}
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, ingestListener); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, ingestListener);
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestJobListener); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestJobListener);
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
@ -270,6 +254,7 @@ final public class FiltersPanel extends JPanel {
@Override @Override
public void removeNotify() { public void removeNotify() {
super.removeNotify(); super.removeNotify();
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); IngestManager.getInstance().removeIngestModuleEventListener(ingestListener);
IngestManager.getInstance().removeIngestJobEventListener(ingestJobListener); IngestManager.getInstance().removeIngestJobEventListener(ingestJobListener);
} }
@ -283,21 +268,17 @@ final public class FiltersPanel extends JPanel {
/** /**
* Populate the Account Types filter widgets. * Populate the Account Types filter widgets.
* *
* @param selected The initial value for the account type checkbox. * @param accountTypesInUse List of accountTypes currently in use
* @param sleuthkitCase The sleuthkit case for containing the account * @param checkNewOnes
* information.
* *
* @return True, if a new accountType was found * @return True, if a new accountType was found
*/ */
private boolean updateAccountTypeFilter(boolean selected, SleuthkitCase sleuthkitCase) { private boolean updateAccountTypeFilter(List<Account.Type> accountTypesInUse, boolean checkNewOnes) {
boolean newOneFound = false; boolean newOneFound = false;
try {
List<Account.Type> accountTypesInUse = sleuthkitCase.getCommunicationsManager().getAccountTypesInUse();
for (Account.Type type : accountTypesInUse) { for (Account.Type type : accountTypesInUse) {
if (!accountTypeMap.containsKey(type) && !type.equals(Account.Type.CREDIT_CARD)) { if (!accountTypeMap.containsKey(type) && !type.equals(Account.Type.CREDIT_CARD)) {
CheckBoxIconPanel panel = createAccoutTypeCheckBoxPanel(type, selected); CheckBoxIconPanel panel = createAccoutTypeCheckBoxPanel(type, checkNewOnes);
accountTypeMap.put(type, panel.getCheckBox()); accountTypeMap.put(type, panel.getCheckBox());
accountTypeListPane.add(panel); accountTypeListPane.add(panel);
@ -305,11 +286,8 @@ final public class FiltersPanel extends JPanel {
} }
} }
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to update to update Account Types Filter", ex);
}
if (newOneFound) { if (newOneFound) {
accountTypeListPane.revalidate(); accountTypeListPane.validate();
} }
return newOneFound; return newOneFound;
@ -337,38 +315,48 @@ final public class FiltersPanel extends JPanel {
/** /**
* Populate the devices filter widgets. * Populate the devices filter widgets.
* *
* @param selected Sets the initial state of device check box. * @param dataSourceMap
* @param sleuthkitCase The sleuthkit case for containing the data source * @param checkNewOnes
* information.
* *
* @return true if a new device was found * @return true if a new device was found
*/ */
private boolean updateDeviceFilter(boolean selected, SleuthkitCase sleuthkitCase) { private void updateDeviceFilterPanel(Map<String, DataSource> dataSourceMap, boolean checkNewOnes) {
boolean newOneFound = false; boolean newOneFound = false;
try { for (Entry<String, DataSource> entry : dataSourceMap.entrySet()) {
for (DataSource dataSource : sleuthkitCase.getDataSources()) { if (devicesMap.containsKey(entry.getValue().getDeviceId())) {
String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName();
if (devicesMap.containsKey(dataSource.getDeviceId())) {
continue; continue;
} }
final JCheckBox jCheckBox = new JCheckBox(dsName, selected); final JCheckBox jCheckBox = new JCheckBox(entry.getKey(), checkNewOnes);
jCheckBox.addItemListener(validationListener); jCheckBox.addItemListener(validationListener);
jCheckBox.setToolTipText(entry.getKey());
devicesListPane.add(jCheckBox); devicesListPane.add(jCheckBox);
devicesMap.put(dataSource.getDeviceId(), jCheckBox); devicesMap.put(entry.getValue().getDeviceId(), jCheckBox);
newOneFound = true; newOneFound = true;
}
} catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException);
} }
if (newOneFound) { if (newOneFound) {
devicesListPane.revalidate(); devicesListPane.removeAll();
List<JCheckBox> checkList = new ArrayList<>(devicesMap.values());
checkList.sort(new DeviceCheckBoxComparator());
for (JCheckBox cb : checkList) {
devicesListPane.add(cb);
} }
return newOneFound; devicesListPane.revalidate();
}
}
private void updateDateTimePicker(Integer start, Integer end) {
if (start != null && start != 0) {
startDatePicker.setDate(LocalDateTime.ofInstant(Instant.ofEpochSecond(start), Utils.getUserPreferredZoneId()).toLocalDate());
}
if (end != null && end != 0) {
endDatePicker.setDate(LocalDateTime.ofInstant(Instant.ofEpochSecond(end), Utils.getUserPreferredZoneId()).toLocalDate());
}
} }
/** /**
@ -477,9 +465,9 @@ final public class FiltersPanel extends JPanel {
setLayout(new java.awt.GridBagLayout()); setLayout(new java.awt.GridBagLayout());
scrollPane.setBorder(null);
scrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setAutoscrolls(true); scrollPane.setAutoscrolls(true);
scrollPane.setBorder(null);
mainPanel.setLayout(new java.awt.GridBagLayout()); mainPanel.setLayout(new java.awt.GridBagLayout());
@ -613,6 +601,7 @@ final public class FiltersPanel extends JPanel {
gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 25); gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 25);
mainPanel.add(dateRangePane, gridBagConstraints); mainPanel.add(dateRangePane, gridBagConstraints);
devicesPane.setPreferredSize(new java.awt.Dimension(300, 300));
devicesPane.setLayout(new java.awt.GridBagLayout()); devicesPane.setLayout(new java.awt.GridBagLayout());
unCheckAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllDevicesButton.text")); // NOI18N unCheckAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllDevicesButton.text")); // NOI18N
@ -652,7 +641,6 @@ final public class FiltersPanel extends JPanel {
gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0);
devicesPane.add(checkAllDevicesButton, gridBagConstraints); devicesPane.add(checkAllDevicesButton, gridBagConstraints);
devicesScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
devicesScrollPane.setMaximumSize(new java.awt.Dimension(32767, 30)); devicesScrollPane.setMaximumSize(new java.awt.Dimension(32767, 30));
devicesScrollPane.setMinimumSize(new java.awt.Dimension(27, 30)); devicesScrollPane.setMinimumSize(new java.awt.Dimension(27, 30));
devicesScrollPane.setPreferredSize(new java.awt.Dimension(3, 30)); devicesScrollPane.setPreferredSize(new java.awt.Dimension(3, 30));
@ -686,10 +674,10 @@ final public class FiltersPanel extends JPanel {
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0; gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2; gridBagConstraints.gridy = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.ipady = 100;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0; gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 25); gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 25);
mainPanel.add(devicesPane, gridBagConstraints); mainPanel.add(devicesPane, gridBagConstraints);
@ -836,10 +824,11 @@ final public class FiltersPanel extends JPanel {
/** /**
* Post an event with the new filters. * Post an event with the new filters.
*/ */
private void applyFilters() { void applyFilters() {
CVTEvents.getCVTEventBus().post(new CVTEvents.FilterChangeEvent(getFilter(), getStartControlState(), getEndControlState()));
needsRefresh = false; needsRefresh = false;
validateFilters(); validateFilters();
CVTEvents.getCVTEventBus().post(new CVTEvents.FilterChangeEvent(getFilter(), getStartControlState(), getEndControlState()));
} }
/** /**
@ -958,31 +947,6 @@ final public class FiltersPanel extends JPanel {
map.values().forEach(box -> box.setSelected(selected)); map.values().forEach(box -> box.setSelected(selected));
} }
/**
* initalize the DateTimePickers by grabbing the earliest and latest time
* from the autopsy db.
*/
private void initalizeDateTimeFilters() {
Case currentCase = null;
try {
currentCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
logger.log(Level.INFO, "Tried to intialize communication filters date range filters without an open case, using default values");
}
if (currentCase == null) {
setDateTimeFiltersToDefault();
openCase = null;
return;
}
if (!currentCase.equals(openCase)) {
setDateTimeFiltersToDefault();
openCase = currentCase;
(new DatePickerWorker()).execute();
}
}
private void setDateTimeFiltersToDefault() { private void setDateTimeFiltersToDefault() {
startDatePicker.setDate(LocalDate.now().minusWeeks(3)); startDatePicker.setDate(LocalDate.now().minusWeeks(3));
endDatePicker.setDate(LocalDate.now()); endDatePicker.setDate(LocalDate.now());
@ -1159,69 +1123,51 @@ final public class FiltersPanel extends JPanel {
} }
/** /**
* A simple class that implements CaseDbAccessQueryCallback. Can be used as * Extends the CVTFilterRefresher abstract class to add the calls to update
* an anonymous innerclass with the CaseDbAccessManager select function. * the ui controls with the data found. Note that updateFilterPanel is run
* in the EDT.
*/ */
class FilterPanelQueryCallback implements CaseDbAccessQueryCallback { final class FilterPanelRefresher extends CVTFilterRefresher {
@Override private final boolean selectNewOption;
public void process(ResultSet rs) { private final boolean refreshAfterUpdate;
// Subclasses can implement their own process function.
}
}
final class DatePickerWorker extends SwingWorker<Map<String, Integer>, Void> { FilterPanelRefresher(boolean selectNewOptions, boolean refreshAfterUpdate) {
this.selectNewOption = selectNewOptions;
@Override this.refreshAfterUpdate = refreshAfterUpdate;
protected Map<String, Integer> doInBackground() throws Exception {
if (openCase == null) {
return null;
}
Map<String, Integer> resultMap = new HashMap<>();
String queryString = "max(date_time) as end, min(date_time) as start from account_relationships"; // NON-NLS
openCase.getSleuthkitCase().getCaseDbAccessManager().select(queryString, new FilterPanelQueryCallback() {
@Override
public void process(ResultSet rs) {
try {
if (rs.next()) {
int startDate = rs.getInt("start"); // NON-NLS
int endDate = rs.getInt("end"); // NON-NLS
resultMap.put("start", startDate); // NON-NLS
resultMap.put("end", endDate); // NON-NLS
}
} catch (SQLException ex) {
// Not the end of the world if this fails.
logger.log(Level.WARNING, String.format("SQL Exception thrown from Query: %s", queryString), ex);
}
}
});
return resultMap;
} }
@Override @Override
protected void done() { void updateFilterPanel(CVTFilterRefresher.FilterPanelData data) {
try { updateDateTimePicker(data.getStartTime(), data.getEndTime());
Map<String, Integer> resultMap = get(); updateDeviceFilterPanel(data.getDataSourceMap(), selectNewOption);
if (resultMap != null) { updateAccountTypeFilter(data.getAccountTypesInUse(), selectNewOption);
Integer start = resultMap.get("start");
Integer end = resultMap.get("end");
if (start != null && start != 0) { FiltersPanel.this.repaint();
startDatePicker.setDate(LocalDateTime.ofInstant(Instant.ofEpochSecond(start), Utils.getUserPreferredZoneId()).toLocalDate());
if (refreshAfterUpdate) {
applyFilters();
} }
if (end != null && end != 0) { if (!isEnabled()) {
endDatePicker.setDate(LocalDateTime.ofInstant(Instant.ofEpochSecond(end), Utils.getUserPreferredZoneId()).toLocalDate()); setEnabled(true);
}
}
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.WARNING, "Exception occured after date time sql query", ex);
} }
validateFilters();
repaint();
} }
} }
/**
* Sorts a list of JCheckBoxes in alphabetical order of the text field
* value.
*/
class DeviceCheckBoxComparator implements Comparator<JCheckBox> {
@Override
public int compare(JCheckBox e1, JCheckBox e2) {
return e1.getText().toLowerCase().compareTo(e2.getText().toLowerCase());
}
}
} }

View File

@ -18,9 +18,12 @@
*/ */
package org.sleuthkit.autopsy.communications; package org.sleuthkit.autopsy.communications;
import java.awt.Component;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.util.TimeZone; import java.util.TimeZone;
import javax.swing.table.TableCellRenderer;
import org.netbeans.swing.outline.Outline;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts; import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
@ -48,4 +51,28 @@ public final class Utils {
return Accounts.getIconFilePath(type); return Accounts.getIconFilePath(type);
} }
static public void setColumnWidths(Outline outline) {
int margin = 4;
int padding = 8;
final int rows = Math.min(100, outline.getRowCount());
for (int column = 0; column < outline.getColumnCount(); column++) {
int columnWidthLimit = 500;
int columnWidth = 0;
// find the maximum width needed to fit the values for the first 100 rows, at most
for (int row = 0; row < rows; row++) {
TableCellRenderer renderer = outline.getCellRenderer(row, column);
Component comp = outline.prepareRenderer(renderer, row, column);
columnWidth = Math.max(comp.getPreferredSize().width, columnWidth);
}
columnWidth += 2 * margin + padding; // add margin and regular padding
columnWidth = Math.min(columnWidth, columnWidthLimit);
outline.getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
}
}
} }

View File

@ -33,6 +33,7 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.FileAttachment; import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.FileAttachment;
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments; import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
import org.sleuthkit.datamodel.CommunicationsUtils; import org.sleuthkit.datamodel.CommunicationsUtils;
import org.sleuthkit.datamodel.InvalidAccountIDException;
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil; import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
/** /**
@ -113,7 +114,7 @@ class AccountSummary {
isReference = true; isReference = true;
break; break;
} }
} catch (TskCoreException ex) { } catch (InvalidAccountIDException ex) {
logger.log(Level.WARNING, String.format("Exception thrown " logger.log(Level.WARNING, String.format("Exception thrown "
+ "in trying to normalize attribute value: %s", + "in trying to normalize attribute value: %s",
attributeValue), ex); //NON-NLS attributeValue), ex); //NON-NLS

View File

@ -9,7 +9,6 @@ SummaryViewer.callLogsLabel.text=Call Logs:
ThreadRootMessagePanel.showAllCheckBox.text=Show All Messages ThreadRootMessagePanel.showAllCheckBox.text=Show All Messages
ThreadPane.backButton.text=<--- ThreadPane.backButton.text=<---
SummaryViewer.caseReferencesPanel.border.title=Other Occurrences SummaryViewer.caseReferencesPanel.border.title=Other Occurrences
SummaryViewer.fileReferencesPanel.border.title=File References in Current Case
MessageViewer.threadsLabel.text=Select a Thread to View MessageViewer.threadsLabel.text=Select a Thread to View
MessageViewer.threadNameLabel.text=<threadName> MessageViewer.threadNameLabel.text=<threadName>
MessageViewer.showingMessagesLabel.text=Showing Messages for Thread: MessageViewer.showingMessagesLabel.text=Showing Messages for Thread:
@ -27,3 +26,5 @@ SummaryViewer.referencesLabel.text=Communication References:
SummaryViewer.referencesDataLabel.text=<reference count> SummaryViewer.referencesDataLabel.text=<reference count>
SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.contactsLabel.text=Book Entries:
SummaryViewer.accountCountry.text=<account country> SummaryViewer.accountCountry.text=<account country>
SummaryViewer.fileRefPane.border.title=File References in Current Case
SummaryViewer.selectAccountFileRefLabel.text=<Select a single account to see File References>

View File

@ -17,7 +17,7 @@ ContactsViewer_columnHeader_Phone=Phone
ContactsViewer_noContacts_message=<No contacts found for selected account> ContactsViewer_noContacts_message=<No contacts found for selected account>
ContactsViewer_tabTitle=Contacts ContactsViewer_tabTitle=Contacts
MediaViewer_Name=Media Attachments MediaViewer_Name=Media Attachments
MessageNode_Node_Property_Attms=Attachments MessageNode_Node_Property_Attms=Attachment Count
MessageNode_Node_Property_Date=Date MessageNode_Node_Property_Date=Date
MessageNode_Node_Property_From=From MessageNode_Node_Property_From=From
MessageNode_Node_Property_Subject=Subject MessageNode_Node_Property_Subject=Subject
@ -49,13 +49,13 @@ SummaryViewer_CentralRepository_Message=<Enable Central Respository to see Other
SummaryViewer_Country_Code=Country: SummaryViewer_Country_Code=Country:
SummaryViewer_Creation_Date_Title=Creation Date SummaryViewer_Creation_Date_Title=Creation Date
SummaryViewer_Device_Account_Description=This account was referenced by a device in the case. SummaryViewer_Device_Account_Description=This account was referenced by a device in the case.
SummaryViewer_Fetching_References=<Fetching File References>
SummaryViewer_FileRef_Message=<Select a single account to see File References> SummaryViewer_FileRef_Message=<Select a single account to see File References>
SummaryViewer_FileRefNameColumn_Title=Path SummaryViewer_FileRefNameColumn_Title=Path
SummaryViewer_TabTitle=Summary SummaryViewer_TabTitle=Summary
ThreadRootMessagePanel.showAllCheckBox.text=Show All Messages ThreadRootMessagePanel.showAllCheckBox.text=Show All Messages
ThreadPane.backButton.text=<--- ThreadPane.backButton.text=<---
SummaryViewer.caseReferencesPanel.border.title=Other Occurrences SummaryViewer.caseReferencesPanel.border.title=Other Occurrences
SummaryViewer.fileReferencesPanel.border.title=File References in Current Case
MessageViewer.threadsLabel.text=Select a Thread to View MessageViewer.threadsLabel.text=Select a Thread to View
MessageViewer.threadNameLabel.text=<threadName> MessageViewer.threadNameLabel.text=<threadName>
MessageViewer.showingMessagesLabel.text=Showing Messages for Thread: MessageViewer.showingMessagesLabel.text=Showing Messages for Thread:
@ -73,3 +73,5 @@ SummaryViewer.referencesLabel.text=Communication References:
SummaryViewer.referencesDataLabel.text=<reference count> SummaryViewer.referencesDataLabel.text=<reference count>
SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.contactsLabel.text=Book Entries:
SummaryViewer.accountCountry.text=<account country> SummaryViewer.accountCountry.text=<account country>
SummaryViewer.fileRefPane.border.title=File References in Current Case
SummaryViewer.selectAccountFileRefLabel.text=<Select a single account to see File References>

View File

@ -49,7 +49,6 @@ SummeryViewer_FileRef_Message=<\u30a2\u30ab\u30a6\u30f3\u30c8\u30921\u3064\u9078
ThreadRootMessagePanel.showAllCheckBox.text=\u3059\u3079\u3066\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u8868\u793a ThreadRootMessagePanel.showAllCheckBox.text=\u3059\u3079\u3066\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u8868\u793a
ThreadPane.backButton.text=<--- ThreadPane.backButton.text=<---
SummaryViewer.caseReferencesPanel.border.title=\u305d\u306e\u4ed6\u306e\u767a\u751f SummaryViewer.caseReferencesPanel.border.title=\u305d\u306e\u4ed6\u306e\u767a\u751f
SummaryViewer.fileReferencesPanel.border.title=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u30ec\u30d5\u30a1\u30ec\u30f3\u30b9
MessageViewer.threadsLabel.text=\u30b9\u30ec\u30c3\u30c9\u3092\u9078\u629e\u3057\u3066\u8868\u793a MessageViewer.threadsLabel.text=\u30b9\u30ec\u30c3\u30c9\u3092\u9078\u629e\u3057\u3066\u8868\u793a
MessageViewer.threadNameLabel.text=<threadName> MessageViewer.threadNameLabel.text=<threadName>
MessageViewer.showingMessagesLabel.text=\u6b21\u306e\u30b9\u30ec\u30c3\u30c9\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u8868\u793a\u4e2d\u3067\u3059: MessageViewer.showingMessagesLabel.text=\u6b21\u306e\u30b9\u30ec\u30c3\u30c9\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u8868\u793a\u4e2d\u3067\u3059:

View File

@ -0,0 +1,58 @@
/*
* 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.communications.relationships;
import java.beans.PropertyChangeEvent;
import org.openide.explorer.ExplorerManager;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.CallLogArtifactViewer;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
*
* Wrapper around CallLogArtifactViewer to add Node support and an
* ExplorerManager.
*/
public class CallLogDataViewer extends CallLogArtifactViewer implements DataContent, ExplorerManager.Provider {
private static final long serialVersionUID = 1L;
final private ExplorerManager explorerManager = new ExplorerManager();
@Override
public void setNode(Node selectedNode) {
BlackboardArtifact artifact = null;
if (selectedNode != null) {
artifact = selectedNode.getLookup().lookup(BlackboardArtifact.class);
}
this.setArtifact(artifact);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public ExplorerManager getExplorerManager() {
return explorerManager;
}
}

View File

@ -16,12 +16,35 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents> <SubComponents>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel"> <Container class="javax.swing.JSplitPane" name="splitPane">
<Properties>
<Property name="orientation" type="int" value="0"/>
</Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="15" insetsBottom="15" insetsRight="15" anchor="12" weightX="1.0" weightY="1.0"/> <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component> </Component>
<Container class="javax.swing.JScrollPane" name="bottomScrollPane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
</Container>
</SubComponents>
</Container>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -18,15 +18,24 @@
*/ */
package org.sleuthkit.autopsy.communications.relationships; package org.sleuthkit.autopsy.communications.relationships;
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JPanel; import javax.swing.JPanel;
import static javax.swing.SwingUtilities.isDescendingFrom;
import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline; import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager;
import static org.openide.explorer.ExplorerUtils.createLookup;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.NodeAdapter; import org.openide.nodes.NodeAdapter;
import org.openide.nodes.NodeMemberEvent; import org.openide.nodes.NodeMemberEvent;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER;
@ -39,8 +48,14 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIR
*/ */
final class CallLogViewer extends javax.swing.JPanel implements RelationshipsViewer { final class CallLogViewer extends javax.swing.JPanel implements RelationshipsViewer {
private static final long serialVersionUID = 1L;
private final CallLogsChildNodeFactory nodeFactory; private final CallLogsChildNodeFactory nodeFactory;
private final CallLogDataViewer callLogDataViewer;
private final ModifiableProxyLookup proxyLookup;
private PropertyChangeListener focusPropertyListener;
@Messages({ @Messages({
"CallLogViewer_title=Call Logs", "CallLogViewer_title=Call Logs",
"CallLogViewer_noCallLogs=<No call logs found for selected account>", "CallLogViewer_noCallLogs=<No call logs found for selected account>",
@ -52,10 +67,19 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
/** /**
* Creates new form CallLogViewer * Creates new form CallLogViewer
*/ */
public CallLogViewer() { CallLogViewer() {
initComponents(); initComponents();
callLogDataViewer = new CallLogDataViewer();
bottomScrollPane.setViewportView(callLogDataViewer);
splitPane.setResizeWeight(0.5);
splitPane.setDividerLocation(0.5);
nodeFactory = new CallLogsChildNodeFactory(null); nodeFactory = new CallLogsChildNodeFactory(null);
proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
outlineViewPanel.hideOutlineView(Bundle.CallLogViewer_noCallLogs()); outlineViewPanel.hideOutlineView(Bundle.CallLogViewer_noCallLogs());
outlineViewPanel.getOutlineView().setPropertyColumns( outlineViewPanel.getOutlineView().setPropertyColumns(
@ -69,12 +93,19 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
outline.setRootVisible(false); outline.setRootVisible(false);
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.CallLogViewer_device_label()); ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.CallLogViewer_device_label());
outlineViewPanel.getExplorerManager().addPropertyChangeListener((PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
final Node[] nodes = outlineViewPanel.getExplorerManager().getSelectedNodes();
callLogDataViewer.setNode(nodes != null && nodes.length > 0 ? nodes[0] : null);
}
});
outlineViewPanel.getExplorerManager().setRootContext( outlineViewPanel.getExplorerManager().setRootContext(
new TableFilterNode( new TableFilterNode(
new DataResultFilterNode( new DataResultFilterNode(
new AbstractNode(Children.create(nodeFactory, true)), outlineViewPanel.getExplorerManager()), true)); new AbstractNode(Children.create(nodeFactory, true)), outlineViewPanel.getExplorerManager()), true));
outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter(){ outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter() {
@Override @Override
public void childrenAdded(NodeMemberEvent nme) { public void childrenAdded(NodeMemberEvent nme) {
updateOutlineViewPanel(); updateOutlineViewPanel();
@ -98,16 +129,21 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
private void initComponents() { private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints; java.awt.GridBagConstraints gridBagConstraints;
splitPane = new javax.swing.JSplitPane();
outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
bottomScrollPane = new javax.swing.JScrollPane();
setLayout(new java.awt.GridBagLayout()); setLayout(new java.awt.GridBagLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setLeftComponent(outlineViewPanel);
splitPane.setRightComponent(bottomScrollPane);
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
gridBagConstraints.weightx = 1.0; gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0; gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(15, 15, 15, 15); add(splitPane, gridBagConstraints);
add(outlineViewPanel, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@Override @Override
@ -130,9 +166,57 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
return outlineViewPanel.getLookup(); return outlineViewPanel.getLookup();
} }
@Override
public void addNotify() {
super.addNotify();
if (focusPropertyListener == null) {
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
handleFocusChange((Component) focusEvent.getNewValue());
}
};
}
//add listener that maintains correct selection in the Global Actions Context
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("focusOwner", focusPropertyListener); //NON-NLS
}
/**
* Handle the switching of the proxyLookup due to focus change.
*
* @param newFocusOwner
*/
private void handleFocusChange(Component newFocusOwner) {
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, callLogDataViewer)) {
//if the focus owner is within the MessageContentViewer (the attachments table)
proxyLookup.setNewLookups(createLookup(callLogDataViewer.getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, this)) {
//... or if it is within the Results table.
proxyLookup.setNewLookups(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
}
}
@Override
public void removeNotify() {
super.removeNotify();
if (focusPropertyListener != null) {
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", focusPropertyListener); //NON-NLS
}
}
private void updateOutlineViewPanel() { private void updateOutlineViewPanel() {
int nodeCount = outlineViewPanel.getExplorerManager().getRootContext().getChildren().getNodesCount(); int nodeCount = outlineViewPanel.getExplorerManager().getRootContext().getChildren().getNodesCount();
if(nodeCount == 0) { if (nodeCount == 0) {
outlineViewPanel.hideOutlineView(Bundle.ContactsViewer_noContacts_message()); outlineViewPanel.hideOutlineView(Bundle.ContactsViewer_noContacts_message());
} else { } else {
outlineViewPanel.showOutlineView(); outlineViewPanel.showOutlineView();
@ -141,6 +225,8 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane bottomScrollPane;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel; private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;
private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -0,0 +1,58 @@
/*
* 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.communications.relationships;
import java.beans.PropertyChangeEvent;
import org.openide.explorer.ExplorerManager;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.ContactArtifactViewer;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
*
* Wrapper around ContactArtifactViewer to add Node support and an
* ExplorerManager.
*/
public class ContactDataViewer extends ContactArtifactViewer implements DataContent, ExplorerManager.Provider {
private static final long serialVersionUID = 1L;
final private ExplorerManager explorerManager = new ExplorerManager();
@Override
public void setNode(Node selectedNode) {
BlackboardArtifact artifact = null;
if (selectedNode != null) {
artifact = selectedNode.getLookup().lookup(BlackboardArtifact.class);
}
this.setArtifact(artifact);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public ExplorerManager getExplorerManager() {
return explorerManager;
}
}

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="nameLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font component="nameLabel" property="font" relativeSize="true" size="13"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="ContactDetailsPane.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="16" insetsLeft="15" insetsBottom="15" insetsRight="15" anchor="23" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.openide.explorer.propertysheet.PropertySheet" name="propertySheet">
<Properties>
<Property name="descriptionAreaVisible" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="2" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="15" insetsBottom="16" insetsRight="15" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -1,163 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 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.communications.relationships;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import org.openide.explorer.ExplorerManager;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Displays the propertied of a ContactNode in a PropertySheet.
*/
public final class ContactDetailsPane extends javax.swing.JPanel implements ExplorerManager.Provider {
private static final Logger logger = Logger.getLogger(ContactDetailsPane.class.getName());
private final static String DEFAULT_IMAGE_PATH = "/org/sleuthkit/autopsy/communications/images/defaultContact.png";
private final ExplorerManager explorerManager = new ExplorerManager();
private final ImageIcon defaultImage;
/**
* Displays the propertied of a ContactNode in a PropertySheet.
*/
public ContactDetailsPane() {
initComponents();
nameLabel.setText("");
defaultImage = new ImageIcon(ContactDetailsPane.class.getResource(DEFAULT_IMAGE_PATH));
}
/**
* Sets the list of nodes for the property sheet.
*
* @param nodes List of nodes to set
*/
public void setNode(Node[] nodes) {
if (nodes != null && nodes.length == 1) {
nameLabel.setText(nodes[0].getDisplayName());
nameLabel.setIcon(null);
propertySheet.setNodes(nodes);
BlackboardArtifact n = nodes[0].getLookup().lookup(BlackboardArtifact.class);
if(n != null) {
nameLabel.setIcon(getImageFromArtifact(n));
}
} else {
nameLabel.setText("");
nameLabel.setIcon(null);
propertySheet.setNodes(null);
}
}
@Override
public ExplorerManager getExplorerManager() {
return explorerManager;
}
public ImageIcon getImageFromArtifact(BlackboardArtifact artifact){
ImageIcon imageIcon = defaultImage;
if(artifact == null) {
return imageIcon;
}
BlackboardArtifact.ARTIFACT_TYPE artifactType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
if(artifactType != BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) {
return imageIcon;
}
try {
for(Content content: artifact.getChildren()) {
if(content instanceof AbstractFile) {
AbstractFile file = (AbstractFile)content;
try {
BufferedImage image = ImageIO.read(new File(file.getLocalAbsPath()));
imageIcon = new ImageIcon(image);
break;
} catch (IOException ex) {
// ImageIO.read will through an IOException if file is not an image
// therefore we don't need to report this exception just try
// the next file.
}
}
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, String.format("Unable to load image for contact: %d", artifact.getId()), ex);
}
return imageIcon;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
nameLabel = new javax.swing.JLabel();
propertySheet = new org.openide.explorer.propertysheet.PropertySheet();
setLayout(new java.awt.GridBagLayout());
nameLabel.setFont(nameLabel.getFont().deriveFont(nameLabel.getFont().getSize()+13f));
org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(ContactDetailsPane.class, "ContactDetailsPane.nameLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(16, 15, 15, 15);
add(nameLabel, gridBagConstraints);
propertySheet.setDescriptionAreaVisible(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(9, 15, 16, 15);
add(propertySheet, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel nameLabel;
private org.openide.explorer.propertysheet.PropertySheet propertySheet;
// End of variables declaration//GEN-END:variables
}

View File

@ -28,13 +28,6 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Component class="org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane" name="contactPane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel"> <Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel">
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">

View File

@ -45,14 +45,15 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
* Visualization for contact nodes. * Visualization for contact nodes.
* *
*/ */
@ServiceProvider(service = RelationshipsViewer.class) final class ContactsViewer extends JPanel implements RelationshipsViewer {
public final class ContactsViewer extends JPanel implements RelationshipsViewer{
private static final long serialVersionUID = 1L;
// private final ExplorerManager tableEM;
private final Outline outline; private final Outline outline;
private final ModifiableProxyLookup proxyLookup; private final ModifiableProxyLookup proxyLookup;
private final PropertyChangeListener focusPropertyListener; private PropertyChangeListener focusPropertyListener;
private final ContactsChildNodeFactory nodeFactory; private final ContactsChildNodeFactory nodeFactory;
private final ContactDataViewer contactPane;
@NbBundle.Messages({ @NbBundle.Messages({
"ContactsViewer_tabTitle=Contacts", "ContactsViewer_tabTitle=Contacts",
@ -65,34 +66,17 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
/** /**
* Visualization for contact nodes. * Visualization for contact nodes.
*/ */
public ContactsViewer() { ContactsViewer() {
initComponents(); initComponents();
contactPane = new ContactDataViewer();
splitPane.setRightComponent(contactPane);
outlineViewPanel.hideOutlineView(Bundle.ContactsViewer_noContacts_message()); outlineViewPanel.hideOutlineView(Bundle.ContactsViewer_noContacts_message());
proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap())); proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
nodeFactory = new ContactsChildNodeFactory(null); nodeFactory = new ContactsChildNodeFactory(null);
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
final Component newFocusOwner = (Component) focusEvent.getNewValue();
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, contactPane)) {
//if the focus owner is within the MessageContentViewer (the attachments table)
proxyLookup.setNewLookups(createLookup(contactPane.getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, ContactsViewer.this)) {
//... or if it is within the Results table.
proxyLookup.setNewLookups(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
}
}
};
outline = outlineViewPanel.getOutlineView().getOutline(); outline = outlineViewPanel.getOutlineView().getOutline();
outlineViewPanel.getOutlineView().setPropertyColumns( outlineViewPanel.getOutlineView().setPropertyColumns(
"TSK_EMAIL", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getDisplayName(), "TSK_EMAIL", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getDisplayName(),
@ -106,7 +90,7 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
outlineViewPanel.getExplorerManager().addPropertyChangeListener((PropertyChangeEvent evt) -> { outlineViewPanel.getExplorerManager().addPropertyChangeListener((PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
final Node[] nodes = outlineViewPanel.getExplorerManager().getSelectedNodes(); final Node[] nodes = outlineViewPanel.getExplorerManager().getSelectedNodes();
contactPane.setNode(nodes); contactPane.setNode(nodes != null && nodes.length > 0 ? nodes[0] : null);
} }
}); });
@ -114,7 +98,7 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
// When a new set of nodes are added to the OutlineView the childrenAdded // When a new set of nodes are added to the OutlineView the childrenAdded
// seems to be fired before the childrenRemoved. // seems to be fired before the childrenRemoved.
outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter(){ outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter() {
@Override @Override
public void childrenAdded(NodeMemberEvent nme) { public void childrenAdded(NodeMemberEvent nme) {
updateOutlineViewPanel(); updateOutlineViewPanel();
@ -153,21 +137,53 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
if (focusPropertyListener == null) {
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
handleFocusChange((Component) focusEvent.getNewValue());
}
};
}
//add listener that maintains correct selection in the Global Actions Context //add listener that maintains correct selection in the Global Actions Context
KeyboardFocusManager.getCurrentKeyboardFocusManager() KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("focusOwner", focusPropertyListener); //NON-NLS .addPropertyChangeListener("focusOwner", focusPropertyListener); //NON-NLS
} }
/**
* Handle the switching of the proxyLookup due to focus change.
*
* @param newFocusOwner
*/
private void handleFocusChange(Component newFocusOwner) {
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, contactPane)) {
//if the focus owner is within the MessageContentViewer (the attachments table)
proxyLookup.setNewLookups(createLookup(contactPane.getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, this)) {
//... or if it is within the Results table.
proxyLookup.setNewLookups(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
}
}
@Override @Override
public void removeNotify() { public void removeNotify() {
super.removeNotify(); super.removeNotify();
if (focusPropertyListener != null) {
KeyboardFocusManager.getCurrentKeyboardFocusManager() KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", focusPropertyListener); //NON-NLS .removePropertyChangeListener("focusOwner", focusPropertyListener); //NON-NLS
} }
}
private void updateOutlineViewPanel() { private void updateOutlineViewPanel() {
int nodeCount = outlineViewPanel.getExplorerManager().getRootContext().getChildren().getNodesCount(); int nodeCount = outlineViewPanel.getExplorerManager().getRootContext().getChildren().getNodesCount();
if(nodeCount == 0) { if (nodeCount == 0) {
outlineViewPanel.hideOutlineView(Bundle.ContactsViewer_noContacts_message()); outlineViewPanel.hideOutlineView(Bundle.ContactsViewer_noContacts_message());
} else { } else {
outlineViewPanel.showOutlineView(); outlineViewPanel.showOutlineView();
@ -185,13 +201,11 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
java.awt.GridBagConstraints gridBagConstraints; java.awt.GridBagConstraints gridBagConstraints;
splitPane = new javax.swing.JSplitPane(); splitPane = new javax.swing.JSplitPane();
contactPane = new org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane();
outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
setLayout(new java.awt.GridBagLayout()); setLayout(new java.awt.GridBagLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setRightComponent(contactPane);
splitPane.setLeftComponent(outlineViewPanel); splitPane.setLeftComponent(outlineViewPanel);
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
@ -206,7 +220,6 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane contactPane;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel; private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;
private javax.swing.JSplitPane splitPane; private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View File

@ -46,21 +46,6 @@
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component> </Component>
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="contentViewer">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[450, 400]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>

View File

@ -33,8 +33,6 @@ import org.openide.nodes.AbstractNode;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -42,7 +40,6 @@ import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import org.sleuthkit.datamodel.AbstractContent; import org.sleuthkit.datamodel.AbstractContent;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -52,46 +49,33 @@ import org.sleuthkit.datamodel.TskCoreException;
final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerManager.Provider, Lookup.Provider { final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerManager.Provider, Lookup.Provider {
private static final Logger logger = Logger.getLogger(MediaViewer.class.getName()); private static final Logger logger = Logger.getLogger(MediaViewer.class.getName());
private static final long serialVersionUID = 1L;
private final ExplorerManager tableEM = new ExplorerManager(); private final ExplorerManager tableEM = new ExplorerManager();
private final PropertyChangeListener focusPropertyListener; private PropertyChangeListener focusPropertyListener;
private final ModifiableProxyLookup proxyLookup; private final ModifiableProxyLookup proxyLookup;
private final MessageDataContent contentViewer;
@Messages({ @Messages({
"MediaViewer_Name=Media Attachments" "MediaViewer_Name=Media Attachments"
}) })
/** /**
* Creates new form ThumbnailViewer * Creates new form ThumbnailViewer
*/ */
public MediaViewer() { MediaViewer() {
initComponents(); initComponents();
splitPane.setResizeWeight(0.5); splitPane.setResizeWeight(0.5);
splitPane.setDividerLocation(0.5); splitPane.setDividerLocation(0.5);
contentViewer = new MessageDataContent();
contentViewer.setPreferredSize(new java.awt.Dimension(450, 400));
splitPane.setRightComponent(contentViewer);
proxyLookup = new ModifiableProxyLookup(createLookup(tableEM, getActionMap())); proxyLookup = new ModifiableProxyLookup(createLookup(tableEM, getActionMap()));
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
final Component newFocusOwner = (Component) focusEvent.getNewValue();
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, contentViewer)) {
//if the focus owner is within the MessageContentViewer (the attachments table)
proxyLookup.setNewLookups(createLookup(((MessageDataContent) contentViewer).getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, MediaViewer.this)) {
//... or if it is within the Results table.
proxyLookup.setNewLookups(createLookup(tableEM, getActionMap()));
}
}
};
tableEM.addPropertyChangeListener((PropertyChangeEvent evt) -> { tableEM.addPropertyChangeListener((PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
handleNodeSelectionChange(); handleNodeSelectionChange();
@ -124,7 +108,7 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
}); });
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to update selection." , ex); logger.log(Level.WARNING, "Unable to update selection.", ex);
} }
thumbnailViewer.resetComponent(); thumbnailViewer.resetComponent();
@ -145,17 +129,50 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
if (focusPropertyListener == null) {
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
handleFocusChange((Component) focusEvent.getNewValue());
}
};
}
//add listener that maintains correct selection in the Global Actions Context //add listener that maintains correct selection in the Global Actions Context
KeyboardFocusManager.getCurrentKeyboardFocusManager() KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("focusOwner", focusPropertyListener); .addPropertyChangeListener("focusOwner", focusPropertyListener);
} }
/**
* Handle the switching of the proxyLookup due to focus change.
*
* @param newFocusOwner
*/
private void handleFocusChange(Component newFocusOwner) {
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, contentViewer)) {
//if the focus owner is within the MessageContentViewer (the attachments table)
proxyLookup.setNewLookups(createLookup(contentViewer.getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, this)) {
//... or if it is within the Results table.
proxyLookup.setNewLookups(createLookup(tableEM, getActionMap()));
}
}
@Override @Override
public void removeNotify() { public void removeNotify() {
super.removeNotify(); super.removeNotify();
if (focusPropertyListener != null) {
KeyboardFocusManager.getCurrentKeyboardFocusManager() KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", focusPropertyListener); .removePropertyChangeListener("focusOwner", focusPropertyListener);
} }
}
/** /**
* Handle the change in thumbnail node selection. * Handle the change in thumbnail node selection.
@ -192,7 +209,6 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
splitPane = new javax.swing.JSplitPane(); splitPane = new javax.swing.JSplitPane();
thumbnailViewer = new org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail(tableEM); thumbnailViewer = new org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail(tableEM);
contentViewer = new MessageDataContent();
setLayout(new java.awt.GridBagLayout()); setLayout(new java.awt.GridBagLayout());
@ -202,9 +218,6 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
thumbnailViewer.setPreferredSize(new java.awt.Dimension(450, 400)); thumbnailViewer.setPreferredSize(new java.awt.Dimension(450, 400));
splitPane.setLeftComponent(thumbnailViewer); splitPane.setLeftComponent(thumbnailViewer);
contentViewer.setPreferredSize(new java.awt.Dimension(450, 400));
splitPane.setRightComponent(contentViewer);
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0; gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
@ -217,7 +230,6 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer contentViewer;
private javax.swing.JSplitPane splitPane; private javax.swing.JSplitPane splitPane;
private org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail thumbnailViewer; private org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail thumbnailViewer;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View File

@ -19,9 +19,21 @@
package org.sleuthkit.autopsy.communications.relationships; package org.sleuthkit.autopsy.communications.relationships;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.util.List;
import java.util.logging.Level;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import org.sleuthkit.autopsy.contentviewers.MessageContentViewer; import org.openide.nodes.Node;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.MessageArtifactViewer;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Extends MessageContentViewer so that it implements DataContent and can be set * Extends MessageContentViewer so that it implements DataContent and can be set
@ -32,7 +44,9 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
* solution to a very similar problem. * solution to a very similar problem.
* *
*/ */
final class MessageDataContent extends MessageContentViewer implements DataContent, ExplorerManager.Provider { final class MessageDataContent extends MessageArtifactViewer implements DataContent, ExplorerManager.Provider {
private static final Logger LOGGER = Logger.getLogger(MessageDataContent.class.getName());
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
final private ExplorerManager explorerManager = new ExplorerManager(); final private ExplorerManager explorerManager = new ExplorerManager();
@ -46,4 +60,53 @@ final class MessageDataContent extends MessageContentViewer implements DataConte
public ExplorerManager getExplorerManager() { public ExplorerManager getExplorerManager() {
return explorerManager; return explorerManager;
} }
@Override
public void setNode(Node node) {
BlackboardArtifact artifact = null;
if (node != null) {
artifact = getNodeArtifact(node);
}
setArtifact(artifact);
}
/**
* Returns the artifact represented by node.
*
* If the node lookup has an artifact, that artifact is returned. However,
* if the node lookup is a file, then we look for a TSK_ASSOCIATED_OBJECT
* artifact on the file, and if a message artifact is found associated with
* the file, that artifact is returned.
*
* @param node Node to check.
*
* @return Blackboard artifact for the node, null if there isn't any.
*/
private BlackboardArtifact getNodeArtifact(Node node) {
BlackboardArtifact nodeArtifact = node.getLookup().lookup(BlackboardArtifact.class);
if (nodeArtifact == null) {
try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
if (file != null) {
List<BlackboardArtifact> artifactsList = tskCase.getBlackboardArtifacts(TSK_ASSOCIATED_OBJECT, file.getId());
for (BlackboardArtifact fileArtifact : artifactsList) {
BlackboardAttribute associatedArtifactAttribute = fileArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
if (associatedArtifactAttribute != null) {
BlackboardArtifact associatedArtifact = fileArtifact.getSleuthkitCase().getBlackboardArtifact(associatedArtifactAttribute.getValueLong());
if (isMessageArtifact(associatedArtifact)) {
nodeArtifact = associatedArtifact;
}
}
}
}
} catch (NoCurrentCaseException | TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Failed to get file for selected node.", ex); //NON-NLS
}
}
return nodeArtifact;
}
} }

View File

@ -18,7 +18,9 @@
*/ */
package org.sleuthkit.autopsy.communications.relationships; package org.sleuthkit.autopsy.communications.relationships;
import java.awt.event.ActionEvent;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.Action; import javax.swing.Action;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
@ -55,6 +57,8 @@ class MessageNode extends BlackboardArtifactNode {
private final Action preferredAction; private final Action preferredAction;
private final Action defaultNoopAction = new DefaultMessageAction();
MessageNode(BlackboardArtifact artifact, String threadID, Action preferredAction) { MessageNode(BlackboardArtifact artifact, String threadID, Action preferredAction) {
super(artifact); super(artifact);
@ -73,12 +77,12 @@ class MessageNode extends BlackboardArtifactNode {
"MessageNode_Node_Property_To=To", "MessageNode_Node_Property_To=To",
"MessageNode_Node_Property_Date=Date", "MessageNode_Node_Property_Date=Date",
"MessageNode_Node_Property_Subject=Subject", "MessageNode_Node_Property_Subject=Subject",
"MessageNode_Node_Property_Attms=Attachments" "MessageNode_Node_Property_Attms=Attachment Count"
}) })
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet sheet = super.createSheet(); Sheet sheet = Sheet.createDefault();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) { if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet(); sheetSet = Sheet.createPropertiesSet();
@ -90,13 +94,14 @@ class MessageNode extends BlackboardArtifactNode {
final BlackboardArtifact artifact = getArtifact(); final BlackboardArtifact artifact = getArtifact();
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
if(fromID == null || if (fromID == null
(fromID != TSK_EMAIL_MSG && || (fromID != TSK_EMAIL_MSG
fromID != TSK_MESSAGE)) { && fromID != TSK_MESSAGE)) {
return sheet; return sheet;
} }
if (threadID != null) {
sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",threadID == null ? UNTHREADED_ID : threadID)); //NON-NLS sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID", "", threadID)); //NON-NLS
}
sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "", sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "",
getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS
try { try {
@ -105,26 +110,28 @@ class MessageNode extends BlackboardArtifactNode {
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS
} }
switch (fromID) { String msg_from = getAttributeDisplayString(artifact, TSK_EMAIL_FROM);
case TSK_EMAIL_MSG: String msg_to = getAttributeDisplayString(artifact, TSK_EMAIL_TO);
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "", String date = getAttributeDisplayString(artifact, TSK_DATETIME_SENT);
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"))); //NON-NLS
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "", if (msg_from.isEmpty()) {
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;"))); //NON-NLS msg_from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "",
getAttributeDisplayString(artifact, TSK_DATETIME_SENT))); //NON-NLS
break;
case TSK_MESSAGE:
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "",
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM))); //NON-NLS
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "",
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO))); //NON-NLS
sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "",
getAttributeDisplayString(artifact, TSK_DATETIME))); //NON-NLS
break;
default:
break;
} }
if (msg_to.isEmpty()) {
msg_to = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO);
}
if (date.isEmpty()) {
date = getAttributeDisplayString(artifact, TSK_DATETIME);
}
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "",
msg_from)); //NON-NLS
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "",
msg_to)); //NON-NLS
sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "",
date)); //NON-NLS
return sheet; return sheet;
} }
@ -145,7 +152,7 @@ class MessageNode extends BlackboardArtifactNode {
@Override @Override
public Action getPreferredAction() { public Action getPreferredAction() {
return preferredAction; return preferredAction != null ? preferredAction : defaultNoopAction;
} }
private int getAttachmentsCount() throws TskCoreException { private int getAttachmentsCount() throws TskCoreException {
@ -158,8 +165,7 @@ class MessageNode extends BlackboardArtifactNode {
try { try {
MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class); MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class);
return msgAttachments.getAttachmentsCount(); return msgAttachments.getAttachmentsCount();
} } catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
logger.log(Level.WARNING, String.format("Unable to parse json for MessageAttachments object in artifact: %s", artifact.getName()), ex); logger.log(Level.WARNING, String.format("Unable to parse json for MessageAttachments object in artifact: %s", artifact.getName()), ex);
return 0; return 0;
} }
@ -169,4 +175,17 @@ class MessageNode extends BlackboardArtifactNode {
return attachmentsCount; return attachmentsCount;
} }
/**
* A no op action to override the default action of BlackboardArtifactNode
*/
private class DefaultMessageAction extends AbstractAction {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
// Do Nothing.
}
}
} }

View File

@ -38,6 +38,8 @@ import javax.swing.JPanel;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import static javax.swing.SwingUtilities.isDescendingFrom; import static javax.swing.SwingUtilities.isDescendingFrom;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline; import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
@ -50,6 +52,7 @@ import org.openide.nodes.Node.PropertySet;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.communications.Utils;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
/** /**
@ -57,18 +60,19 @@ import org.sleuthkit.autopsy.coreutils.Logger;
* *
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class MessageViewer extends JPanel implements RelationshipsViewer { final class MessageViewer extends JPanel implements RelationshipsViewer {
private static final Logger logger = Logger.getLogger(MessageViewer.class.getName()); private static final Logger logger = Logger.getLogger(MessageViewer.class.getName());
private static final long serialVersionUID = 1L;
private final ModifiableProxyLookup proxyLookup; private final ModifiableProxyLookup proxyLookup;
private final PropertyChangeListener focusPropertyListener; private PropertyChangeListener focusPropertyListener;
private final ThreadChildNodeFactory rootMessageFactory; private final ThreadChildNodeFactory rootMessageFactory;
private final MessagesChildNodeFactory threadMessageNodeFactory; private final MessagesChildNodeFactory threadMessageNodeFactory;
private SelectionInfo currentSelectionInfo = null; private SelectionInfo currentSelectionInfo = null;
OutlineViewPanel currentPanel; private OutlineViewPanel currentPanel;
@Messages({ @Messages({
"MessageViewer_tabTitle=Messages", "MessageViewer_tabTitle=Messages",
@ -87,7 +91,7 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
/** /**
* Creates new form MessageViewer * Creates new form MessageViewer
*/ */
public MessageViewer() { MessageViewer() {
initComponents(); initComponents();
currentPanel = rootTablePane; currentPanel = rootTablePane;
@ -95,23 +99,6 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
rootMessageFactory = new ThreadChildNodeFactory(new ShowThreadMessagesAction()); rootMessageFactory = new ThreadChildNodeFactory(new ShowThreadMessagesAction());
threadMessageNodeFactory = new MessagesChildNodeFactory(); threadMessageNodeFactory = new MessagesChildNodeFactory();
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
final Component newFocusOwner = (Component) focusEvent.getNewValue();
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, rootTablePane)) {
proxyLookup.setNewLookups(createLookup(rootTablePane.getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, MessageViewer.this)) {
proxyLookup.setNewLookups(createLookup(currentPanel.getExplorerManager(), getActionMap()));
}
}
};
rootTablePane.getExplorerManager().setRootContext( rootTablePane.getExplorerManager().setRootContext(
new AbstractNode(Children.create(rootMessageFactory, true))); new AbstractNode(Children.create(rootMessageFactory, true)));
@ -132,12 +119,19 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
} }
}); });
rootTablePane.getOutlineView().getOutline().getModel().addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
Utils.setColumnWidths(rootTablePane.getOutlineView().getOutline());
}
});
threadMessagesPanel.setChildFactory(threadMessageNodeFactory); threadMessagesPanel.setChildFactory(threadMessageNodeFactory);
rootTablePane.setTableColumnsWidth(10, 20, 70); rootTablePane.setTableColumnsWidth(10, 20, 70);
Image image = getScaledImage((new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/timeline/images/arrow-180.png"))).getImage(), 16, 16); Image image = getScaledImage((new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/timeline/images/arrow-180.png"))).getImage(), 16, 16);
backButton.setIcon(new ImageIcon(image) ); backButton.setIcon(new ImageIcon(image));
} }
@Override @Override
@ -170,11 +164,38 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
if (focusPropertyListener == null) {
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
handleFocusChange((Component) focusEvent.getNewValue());
}
};
}
//add listener that maintains correct selection in the Global Actions Context //add listener that maintains correct selection in the Global Actions Context
KeyboardFocusManager.getCurrentKeyboardFocusManager() KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("focusOwner", focusPropertyListener); .addPropertyChangeListener("focusOwner", focusPropertyListener);
} }
/**
* Handle the switching of the proxyLookup due to focus change.
*
* @param newFocusOwner
*/
private void handleFocusChange(Component newFocusOwner) {
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, rootTablePane)) {
proxyLookup.setNewLookups(createLookup(rootTablePane.getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, this)) {
proxyLookup.setNewLookups(createLookup(currentPanel.getExplorerManager(), getActionMap()));
}
}
@Override @Override
public void removeNotify() { public void removeNotify() {
super.removeNotify(); super.removeNotify();
@ -247,8 +268,6 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
*/ */
private void showMessagesPane() { private void showMessagesPane() {
switchCard("messages"); switchCard("messages");
Outline outline = rootTablePane.getOutlineView().getOutline();
outline.clearSelection();
} }
/** /**
@ -260,7 +279,7 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
public void run() { public void run() {
CardLayout layout = (CardLayout)getLayout(); CardLayout layout = (CardLayout) getLayout();
layout.show(MessageViewer.this, cardName); layout.show(MessageViewer.this, cardName);
} }
}); });
@ -275,7 +294,7 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
* *
* @return Scaled version of srcImg * @return Scaled version of srcImg
*/ */
private Image getScaledImage(Image srcImg, int w, int h){ private Image getScaledImage(Image srcImg, int w, int h) {
BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = resizedImg.createGraphics(); Graphics2D g2 = resizedImg.createGraphics();

View File

@ -35,16 +35,6 @@
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component> </Component>
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="messageContentViewer">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>

View File

@ -23,6 +23,8 @@ import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import static javax.swing.SwingUtilities.isDescendingFrom; import static javax.swing.SwingUtilities.isDescendingFrom;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline; import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
@ -39,42 +41,29 @@ import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
/** /**
* *
* General Purpose class for panels that need OutlineView of message nodes at * General Purpose class for panels that need OutlineView of message nodes at
* the top with a MessageContentViewer at the bottom. * the top with a MessageDataContent at the bottom.
*/ */
public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider { class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
private static final long serialVersionUID = 1L;
private final Outline outline; private final Outline outline;
private final ModifiableProxyLookup proxyLookup; private final ModifiableProxyLookup proxyLookup;
private final PropertyChangeListener focusPropertyListener; private PropertyChangeListener focusPropertyListener;
private final MessageDataContent messageContentViewer;
/** /**
* Creates new form MessagesPanel * Creates new form MessagesPanel
*/ */
public MessagesPanel() { MessagesPanel() {
initComponents(); initComponents();
messageContentViewer = new MessageDataContent();
splitPane.setRightComponent(messageContentViewer);
proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap())); proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
final Component newFocusOwner = (Component) focusEvent.getNewValue();
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, messageContentViewer)) {
//if the focus owner is within the MessageContentViewer (the attachments table)
proxyLookup.setNewLookups(createLookup(((MessageDataContent) messageContentViewer).getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, MessagesPanel.this)) {
//... or if it is within the Results table.
proxyLookup.setNewLookups(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
}
}
};
outline = outlineViewPanel.getOutlineView().getOutline(); outline = outlineViewPanel.getOutlineView().getOutline();
outlineViewPanel.getOutlineView().setPropertyColumns( outlineViewPanel.getOutlineView().setPropertyColumns(
"From", Bundle.MessageViewer_columnHeader_From(), "From", Bundle.MessageViewer_columnHeader_From(),
@ -92,16 +81,27 @@ public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider
if (nodes != null && nodes.length == 1) { if (nodes != null && nodes.length == 1) {
messageContentViewer.setNode(nodes[0]); messageContentViewer.setNode(nodes[0]);
} } else {
else {
messageContentViewer.setNode(null); messageContentViewer.setNode(null);
} }
}
});
// This is a trick to get the first message to be selected after the ChildFactory has added
// new data to the table.
outlineViewPanel.getOutlineView().getOutline().getOutlineModel().addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.INSERT) {
outline.setRowSelectionInterval(0, 0);
}
} }
}); });
splitPane.setResizeWeight(0.5); splitPane.setResizeWeight(0.5);
splitPane.setDividerLocation(0.5); splitPane.setDividerLocation(0.5);
outlineViewPanel.setTableColumnsWidth(5,10,10,15,50,10); outlineViewPanel.setTableColumnsWidth(5, 10, 10, 15, 50, 10);
} }
public MessagesPanel(ChildFactory<?> nodeFactory) { public MessagesPanel(ChildFactory<?> nodeFactory) {
@ -117,11 +117,38 @@ public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
if (focusPropertyListener == null) {
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
handleFocusChange((Component) focusEvent.getNewValue());
}
};
}
//add listener that maintains correct selection in the Global Actions Context //add listener that maintains correct selection in the Global Actions Context
KeyboardFocusManager.getCurrentKeyboardFocusManager() KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("focusOwner", focusPropertyListener); .addPropertyChangeListener("focusOwner", focusPropertyListener);
} }
private void handleFocusChange(Component newFocusOwner) {
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, messageContentViewer)) {
//if the focus owner is within the MessageContentViewer (the attachments table)
proxyLookup.setNewLookups(createLookup((messageContentViewer).getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, MessagesPanel.this)) {
//... or if it is within the Results table.
proxyLookup.setNewLookups(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
}
}
@Override @Override
public void removeNotify() { public void removeNotify() {
super.removeNotify(); super.removeNotify();
@ -135,7 +162,7 @@ public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider
new DataResultFilterNode( new DataResultFilterNode(
new AbstractNode( new AbstractNode(
Children.create(nodeFactory, true)), Children.create(nodeFactory, true)),
outlineViewPanel.getExplorerManager()),true)); outlineViewPanel.getExplorerManager()), true));
} }
/** /**
@ -149,20 +176,17 @@ public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider
splitPane = new javax.swing.JSplitPane(); splitPane = new javax.swing.JSplitPane();
outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
messageContentViewer = new MessageDataContent();
setLayout(new java.awt.BorderLayout()); setLayout(new java.awt.BorderLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setLeftComponent(outlineViewPanel); splitPane.setLeftComponent(outlineViewPanel);
splitPane.setRightComponent(messageContentViewer);
add(splitPane, java.awt.BorderLayout.CENTER); add(splitPane, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer messageContentViewer;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel; private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;
private javax.swing.JSplitPane splitPane; private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View File

@ -253,22 +253,6 @@
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="fileReferencesPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="File References in Current Case">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.fileReferencesPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="caseReferencesPanel"> <Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="caseReferencesPanel">
<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">
@ -285,5 +269,99 @@
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component> </Component>
<Container class="javax.swing.JPanel" name="fileRefPane">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="File References in Current Case">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.fileRefPane.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="fileRefScrolPanel">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="listPanelCard"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="scrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<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.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="fileRefList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="5">
<StringItem index="0" value="Item 1"/>
<StringItem index="1" value="Item 2"/>
<StringItem index="2" value="Item 3"/>
<StringItem index="3" value="Item 4"/>
<StringItem index="4" value="Item 5"/>
</StringArray>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="selectAccountPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="selectAccountCard"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="selectAccountFileRefLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.selectAccountFileRefLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents> </SubComponents>
</Form> </Form>

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