Merge branch 'develop' of github.com:sleuthkit/autopsy into develop

This commit is contained in:
esaunders 2020-07-13 15:39:07 -04:00
commit baf336b218
474 changed files with 27112 additions and 10754 deletions

View File

@ -54,6 +54,11 @@
<fileset dir="${thirdparty.dir}/7-Zip"/>
</copy>
<!--Copy InterestingFileSetRules to release-->
<copy todir="${basedir}/release/InterestingFileSetRules" >
<fileset dir="${thirdparty.dir}/InterestingFileSetRules"/>
</copy>
<!-- 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
the ZIP file afterward. -->

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.core/10
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
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
AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: true

View File

@ -138,5 +138,5 @@ nbm.homepage=http://www.sleuthkit.org/
nbm.module.author=Brian Carrier
nbm.needs.restart=true
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

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

View File

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

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* 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()) {
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
TagName tagName = entry.getValue();
TagSet tagSet = tagName.getTagSet();
TagSet tagSet = tagsManager.getTagSet(tagName);
// Show custom tags before predefined tags in the menu
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.imagewriter.ImageWriterService;
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
import org.sleuthkit.datamodel.AddDataSourceCallbacks;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitJNI;
@ -42,17 +43,10 @@ import org.sleuthkit.datamodel.TskDataException;
class AddImageTask implements Runnable {
private final Logger logger = Logger.getLogger(AddImageTask.class.getName());
private final String deviceId;
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 ImageDetails imageDetails;
private final DataSourceProcessorProgressMonitor progressMonitor;
private final DataSourceProcessorCallback callback;
private final AddDataSourceCallbacks addDataSourceCallbacks;
private final AddImageTaskCallback addImageTaskCallback;
private boolean criticalErrorOccurred;
/*
@ -73,40 +67,18 @@ class AddImageTask implements Runnable {
/**
* Constructs a runnable task that adds an image to the case database.
*
* @param deviceId An ASCII-printable identifier for the device
* 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 imageDetails Holds all data about the image.
* @param progressMonitor Progress monitor to report progress during
* 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,
DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
this.deviceId = deviceId;
this.imagePath = imagePath;
this.sectorSize = sectorSize;
this.timeZone = timeZone;
this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
this.md5 = md5;
this.sha1 = sha1;
this.sha256 = sha256;
this.imageWriterSettings = imageWriterSettings;
this.callback = callback;
AddImageTask(ImageDetails imageDetails, DataSourceProcessorProgressMonitor progressMonitor, AddDataSourceCallbacks addDataSourceCallbacks,
AddImageTaskCallback addImageTaskCallback) {
this.imageDetails = imageDetails;
this.addDataSourceCallbacks = addDataSourceCallbacks;
this.addImageTaskCallback = addImageTaskCallback;
this.progressMonitor = progressMonitor;
tskAddImageProcessLock = new Object();
}
@ -120,21 +92,21 @@ class AddImageTask implements Runnable {
try {
currentCase = Case.getCurrentCaseThrows();
} 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;
}
progressMonitor.setIndeterminate(true);
progressMonitor.setProgress(0);
String imageWriterPath = "";
if (imageWriterSettings != null) {
imageWriterPath = imageWriterSettings.getPath();
if (imageDetails.imageWriterSettings != null) {
imageWriterPath = imageDetails.imageWriterSettings.getPath();
}
List<String> errorMessages = new ArrayList<>();
List<Content> newDataSources = new ArrayList<>();
try {
synchronized (tskAddImageProcessLock) {
if (!tskAddImageProcessStopped) {
tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles, imageWriterPath);
tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(imageDetails.timeZone, true, imageDetails.ignoreFatOrphanFiles, imageWriterPath);
} else {
return;
}
@ -143,7 +115,7 @@ class AddImageTask implements Runnable {
progressUpdateThread.start();
runAddImageProcess(errorMessages);
progressUpdateThread.interrupt();
commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources);
finishAddImageProcess(errorMessages, newDataSources);
progressMonitor.setProgress(100);
} finally {
DataSourceProcessorCallback.DataSourceProcessorResult result;
@ -154,7 +126,7 @@ class AddImageTask implements Runnable {
} else {
result = DataSourceProcessorResult.NO_ERRORS;
}
callback.done(result, errorMessages, newDataSources);
addImageTaskCallback.onCompleted(result, errorMessages, newDataSources);
}
}
@ -177,7 +149,7 @@ class AddImageTask implements Runnable {
tskAddImageProcess.stop();
} 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) {
try {
tskAddImageProcess.run(deviceId, new String[]{imagePath}, sectorSize);
tskAddImageProcess.run(imageDetails.deviceId, imageDetails.image, imageDetails.sectorSize, this.addDataSourceCallbacks);
} 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;
errorMessages.add(ex.getMessage());
} 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());
}
}
/**
* Commits or reverts the results of the TSK add image process. If the
* process was stopped before it completed or there was a critical error the
* results are reverted, otherwise they are committed.
* Handle the results of the TSK add image process.
* The image will be in the database even if a critical error occurred or
* the user canceled.
*
* @param currentCase The current case.
* @param errorMessages Error messages, if any, are added to this list for
* eventual return via the callback.
* @param newDataSources If the new image is successfully committed, it is
@ -216,84 +187,66 @@ class AddImageTask implements Runnable {
*
* @return
*/
private void commitOrRevertAddImageProcess(Case currentCase, List<String> errorMessages, List<Content> newDataSources) {
private void finishAddImageProcess(List<String> errorMessages, List<Content> newDataSources) {
synchronized (tskAddImageProcessLock) {
if (tskAddImageProcessStopped || criticalErrorOccurred) {
Image newImage = imageDetails.image;
String verificationError = newImage.verifyImageSize();
if (!verificationError.isEmpty()) {
errorMessages.add(verificationError);
}
if (imageDetails.imageWriterSettings != null) {
ImageWriterService.createImageWriter(newImage.getId(), imageDetails.imageWriterSettings);
}
newDataSources.add(newImage);
// If the add image process was cancelled don't do any further processing here
if (tskAddImageProcessStopped) {
return;
}
if (!StringUtils.isBlank(imageDetails.md5)) {
try {
tskAddImageProcess.revert();
newImage.setMD5(imageDetails.md5);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error reverting after adding image %s to the case database", imagePath), ex); //NON-NLS
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());
criticalErrorOccurred = true;
} catch (TskDataException ignored) {
/*
* The only reasonable way for this to happen at
* present is through C/C++ processing of an EWF
* image, which is not an error.
*/
}
} else {
}
if (!StringUtils.isBlank(imageDetails.sha1)) {
try {
long imageId = tskAddImageProcess.commit();
if (imageId != 0) {
Image newImage = currentCase.getSleuthkitCase().getImageById(imageId);
String verificationError = newImage.verifyImageSize();
if (!verificationError.isEmpty()) {
errorMessages.add(verificationError);
}
if (imageWriterSettings != null) {
ImageWriterService.createImageWriter(imageId, imageWriterSettings);
}
newDataSources.add(newImage);
if (!StringUtils.isBlank(md5)) {
try {
newImage.setMD5(md5);
} 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);
errorMessages.add(ex.getMessage());
criticalErrorOccurred = true;
} catch (TskDataException ignored) {
/*
* The only reasonable way for this to happen at
* present is through C/C++ processing of an EWF
* image, which is not an error.
*/
}
}
if (!StringUtils.isBlank(sha1)) {
try {
newImage.setSha1(sha1);
} 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);
errorMessages.add(ex.getMessage());
criticalErrorOccurred = true;
} catch (TskDataException ignored) {
/*
* The only reasonable way for this to happen at
* present is through C/C++ processing of an EWF
* image, which is not an error.
*/
}
}
if (!StringUtils.isBlank(sha256)) {
try {
newImage.setSha256(sha256);
} 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);
errorMessages.add(ex.getMessage());
criticalErrorOccurred = true;
} catch (TskDataException ignored) {
/*
* The only reasonable way for this to happen at
* present is through C/C++ processing of an EWF
* image, which is not an error.
*/
}
}
} 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;
}
newImage.setSha1(imageDetails.sha1);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error committing adding image %s to the case database", imagePath), ex); //NON-NLS
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());
criticalErrorOccurred = true;
} catch (TskDataException ignored) {
/*
* The only reasonable way for this to happen at
* present is through C/C++ processing of an EWF
* image, which is not an error.
*/
}
}
if (!StringUtils.isBlank(imageDetails.sha256)) {
try {
newImage.setSha256(imageDetails.sha256);
} 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);
errorMessages.add(ex.getMessage());
criticalErrorOccurred = true;
} catch (TskDataException ignored) {
/*
* The only reasonable way for this to happen at
* present is through C/C++ processing of an EWF
* image, which is not an error.
*/
}
}
}
@ -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() {
if (!newContents.isEmpty() && readyToIngest && !ingested) {
ingested = true;
IngestManager.getInstance().queueIngestJob(newContents, ingestJobSettings);
if (dsProcessor != null && ! dsProcessor.supportsIngestStream()) {
IngestManager.getInstance().queueIngestJob(newContents, ingestJobSettings);
}
setStateFinished();
}
}
@ -360,8 +362,12 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
setStateStarted();
// Kick off the DSProcessor
dsProcessor.run(getDSPProgressMonitorImpl(), cbObj);
// Kick off the DSProcessor
if (dsProcessor.supportsIngestStream()) {
dsProcessor.runWithIngestStream(ingestJobSettings, getDSPProgressMonitorImpl(), cbObj);
} else {
dsProcessor.run(getDSPProgressMonitorImpl(), cbObj);
}
}
}

View File

@ -41,6 +41,7 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescript
private final AddImageAction action;
private int progressPanelIndex;
private int dsPanelIndex;
private int ingestPanelIndex;
private final static String PROP_LASTPROFILE_NAME = "AIW_LASTPROFILE_NAME"; //NON-NLS
AddImageWizardIterator(AddImageAction action) {
@ -69,6 +70,7 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescript
panels.add(progressPanel);
progressPanelIndex = panels.indexOf(progressPanel); //Doing programatically because number of panels is variable
dsPanelIndex = panels.indexOf(dsPanel);
ingestPanelIndex = panels.indexOf(ingestConfigPanel);
String[] steps = new String[panels.size()];
for (int i = 0; i < panels.size(); i++) {
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,
// 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
if (index == dsPanelIndex) {
if (index == ingestPanelIndex) {
((AddImageWizardAddingProgressPanel) panels.get(progressPanelIndex)).
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.AutopsyEventException;
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.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestServices;
@ -1124,7 +1124,7 @@ public class Case {
CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);
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
@ -1179,7 +1179,7 @@ public class Case {
CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
CallableSystemAction.get(OpenOutputFolderAction.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

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.Calendar;
import java.util.List;
import java.util.logging.Level;
import java.util.UUID;
import javax.swing.filechooser.FileFilter;
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.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.DataSourceUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
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
@ -49,6 +57,7 @@ import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSourceProcessor {
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 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);
@ -58,6 +67,8 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
private static final List<FileFilter> filtersList = new ArrayList<>();
private final ImageFilePanel configPanel;
private AddImageTask addImageTask;
private IngestStream ingestStream = null;
private Image image = null;
/*
* TODO: Remove the setDataSourceOptionsCalled flag and the settings fields
* when the deprecated method setDataSourceOptions is removed.
@ -170,6 +181,68 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
*/
@Override
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
this.ingestStream = IngestManager.getInstance().openIngestStream(image, settings);
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progress, callBack);
}
/**
* Store the options from the config panel.
*/
private void readConfigSettings() {
if (!setDataSourceOptionsCalled) {
configPanel.storeSettings();
deviceId = UUID.randomUUID().toString();
@ -190,8 +263,17 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
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;
}
/**
* Adds a data source to the case database using a background task in a
@ -215,7 +297,19 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
* @param callback Callback to call when processing is done.
*/
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);
}
/**
@ -224,6 +318,10 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
* selection and configuration panel. Returns as soon as the background task
* is started and uses the callback object to signal task completion and
* 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
* associated with the data source that is
@ -243,8 +341,31 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
* during processing.
* @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) {
addImageTask = new AddImageTask(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, progressMonitor, callback);
private void doAddImageProcess(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback 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();
}
@ -260,6 +381,9 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
if (null != addImageTask) {
addImageTask.cancelTask();
}
if (ingestStream != null) {
ingestStream.stop();
}
}
/**
@ -316,7 +440,20 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
this.timeZone = Calendar.getInstance().getTimeZone().getID();
this.ignoreFatOrphanFiles = false;
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

@ -18,15 +18,22 @@
*/
package org.sleuthkit.autopsy.casemodule;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import javax.swing.JPanel;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.Logger;
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
@ -37,6 +44,7 @@ import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
@ServiceProvider(service = DataSourceProcessor.class)
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 final LocalDiskPanel configPanel;
private AddImageTask addDiskTask;
@ -139,7 +147,25 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
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();
}
@ -191,7 +217,23 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
* @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) {
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();
}

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,4 @@
OptionsCategory_Name_TagNamesOptions=Tags
OptionsCategory_Name_TagNamesOptions=Custom Tags
OptionsCategory_TagNames=TagNames
TagNameDialog.title.text=New Tag
TagNameDialog.JOptionPane.tagNameIllegalCharacters.message=Tag name may not contain any of the following symbols: \\ : * ? " < > | , ;

View File

@ -1,4 +1,4 @@
OptionsCategory_Name_TagNamesOptions=Tags
OptionsCategory_Name_TagNamesOptions=Custom Tags
OptionsCategory_TagNames=TagNames
TagNameDefinition.predefTagNames.bookmark.text=Bookmark
TagNameDefinition.predefTagNames.followUp.text=Follow Up

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2020 Basis Technology Corp.
* Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -56,7 +56,7 @@ public class TagsManager implements Closeable {
private final SleuthkitCase caseDb;
private static String DEFAULT_TAG_SET_NAME = "Project VIC";
private static final Object lock = new Object();
static {
@ -235,16 +235,16 @@ public class TagsManager implements Closeable {
public static String getNotableTagDisplayName() {
return TagNameDefinition.getNotableTagDisplayName();
}
/**
* Creates a new TagSetDefinition file.
*
*
* @param tagSetDef The tag set definition.
*
* @throws IOException
*
* @throws IOException
*/
public static void addTagSetDefinition(TagSetDefinition tagSetDef) throws IOException {
synchronized(lock) {
synchronized (lock) {
TagSetDefinition.writeTagSetDefinition(tagSetDef);
}
}
@ -267,20 +267,20 @@ public class TagsManager implements Closeable {
caseDb.addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus());
}
//Assume new case and add tag sets
for(TagSetDefinition setDef: TagSetDefinition.readTagSetDefinitions()) {
for (TagSetDefinition setDef : TagSetDefinition.readTagSetDefinitions()) {
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()));
}
if(!tagNameList.isEmpty()) {
if (!tagNameList.isEmpty()) {
taggingMgr.addTagSet(setDef.getName(), tagNameList);
}
}
}
} catch (TskCoreException 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);
}
@ -288,28 +288,41 @@ public class TagsManager implements Closeable {
tagName.saveToCase(caseDb);
}
}
/**
* Get a list of all tag sets currently in the case database.
*
*
* @return A list, possibly empty, of TagSet objects.
*
*
* @throws TskCoreException
*/
public List<TagSet> getAllTagSets() throws TskCoreException {
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
* which they are passed to this method.
*
* @param name Tag set name.
*
* @param name Tag set name.
* @param tagNameList List of TagName in rank order.
*
*
* @return A new TagSet object.
*
* @throws TskCoreException
*
* @throws TskCoreException
*/
public TagSet addTagSet(String name, List<TagName> tagNameList) throws TskCoreException {
return caseDb.getTaggingManager().addTagSet(name, tagNameList);
@ -501,7 +514,7 @@ public class TagsManager implements Closeable {
* name to the case database.
*/
public TagName addTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TagNameAlreadyExistsException, TskCoreException {
synchronized(lock) {
synchronized (lock) {
try {
TagName tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatus);
Set<TagNameDefinition> customTypes = TagNameDefinition.getTagNameDefinitions();

View File

@ -1,9 +1,9 @@
OpenIDE-Module-Name=Central Repository
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=\
Correlation Engine 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\
Central Repository ingest module and central database. \n\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.
CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.okButton.text=&OK

View File

@ -4,10 +4,10 @@ AddEditCentralRepoCommentAction.menuItemText.addEditCentralRepoCommentNoMD5=Add/
CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Repository Comment
OpenIDE-Module-Name=Central Repository
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=\
Correlation Engine 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\
Central Repository ingest module and central database. \n\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.
CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.okButton.text=&OK

View File

@ -23,25 +23,29 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.CommunicationsUtils;
import static org.sleuthkit.datamodel.CommunicationsUtils.normalizeEmailAddress;
import org.sleuthkit.datamodel.TskCoreException;
/**
* This class abstracts an Account as stored in the CR database.
*/
public final class CentralRepoAccount {
// primary key in the Accounts table in CR database
private final long accountId;
private final CentralRepoAccountType accountType;
// type specific unique account identifier
private final String typeSpecificIdentifier;
// primary key in the Accounts table in CR database
private final long accountId;
private final CentralRepoAccountType accountType;
// type specific unique account identifier
private final String typeSpecificIdentifier;
/**
* Encapsulates a central repo account type and the correlation type
* that it maps to.
* Encapsulates a central repo account type and the correlation type that it
* maps to.
*/
public static final class CentralRepoAccountType {
@ -55,7 +59,6 @@ public final class CentralRepoAccount {
this.correlationTypeId = correlationTypeId;
this.accountTypeId = acctTypeID;
}
/**
* @return the acctType
@ -71,72 +74,103 @@ public final class CentralRepoAccount {
public int getAccountTypeId() {
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) {
this.accountId = accountId;
this.accountType = accountType;
this.typeSpecificIdentifier = typeSpecificIdentifier;
}
this.accountId = accountId;
this.accountType = accountType;
this.typeSpecificIdentifier = typeSpecificIdentifier;
}
/**
* Gets unique identifier (assigned by a provider) for the account. Example
* includes an email address, a phone number, or a website username.
*
* @return type specific account id.
*/
public String getIdentifier() {
return this.typeSpecificIdentifier;
}
/**
* Gets unique identifier (assigned by a provider) for the account. Example
* includes an email address, a phone number, or a website username.
*
* @return type specific account id.
*/
public String getIdentifier() {
return this.typeSpecificIdentifier;
}
/**
* Gets the account type
*
* @return account type
*/
public CentralRepoAccountType getAccountType() {
return this.accountType;
}
/**
* Gets the account type
*
* @return account type
*/
public CentralRepoAccountType getAccountType() {
return this.accountType;
}
/**
* Gets the unique row id for this account in the database.
*
* @return unique row id.
*/
public long getId() {
return this.accountId;
}
/**
* Gets the unique row id for this account in the database.
*
* @return unique row id.
*/
public long getId() {
return this.accountId;
}
@Override
public int hashCode() {
int hash = 5;
hash = 43 * hash + (int) (this.accountId ^ (this.accountId >>> 32));
hash = 43 * hash + (this.accountType != null ? this.accountType.hashCode() : 0);
hash = 43 * hash + (this.typeSpecificIdentifier != null ? this.typeSpecificIdentifier.hashCode() : 0);
return hash;
}
@Override
public int hashCode() {
int hash = 5;
hash = 43 * hash + (int) (this.accountId ^ (this.accountId >>> 32));
hash = 43 * hash + (this.accountType != null ? this.accountType.hashCode() : 0);
hash = 43 * hash + (this.typeSpecificIdentifier != null ? this.typeSpecificIdentifier.hashCode() : 0);
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 CentralRepoAccount other = (CentralRepoAccount) obj;
if (this.accountId != other.getId()) {
return false;
}
if (!Objects.equals(this.typeSpecificIdentifier, other.getIdentifier())) {
return false;
}
return Objects.equals(this.accountType, other.getAccountType());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CentralRepoAccount other = (CentralRepoAccount) obj;
if (this.accountId != other.getId()) {
return false;
}
if ((this.typeSpecificIdentifier == null) ? (other.getIdentifier() != null) : !this.typeSpecificIdentifier.equals(other.getIdentifier())) {
return false;
}
return !(this.accountType != other.getAccountType() && (this.accountType == null || !this.accountType.equals(other.getAccountType())));
}
/**
* Callback to process a query that gets accounts
*/
@ -152,7 +186,7 @@ public final class CentralRepoAccount {
// create account
Account.Type acctType = new Account.Type(rs.getString("type_name"), rs.getString("display_name"));
CentralRepoAccountType crAccountType = new CentralRepoAccountType(rs.getInt("account_type_id"), acctType, rs.getInt("correlation_type_id"));
CentralRepoAccount account = new CentralRepoAccount(
rs.getInt("account_id"),
crAccountType,
@ -167,7 +201,6 @@ public final class CentralRepoAccount {
}
};
private static final String ACCOUNTS_QUERY_CLAUSE
= "SELECT accounts.id as account_id, "
+ " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier,"
@ -175,66 +208,103 @@ public final class CentralRepoAccount {
+ " account_types.type_name as type_name, account_types.display_name as display_name, account_types.correlation_type_id as correlation_type_id "
+ " FROM accounts "
+ " JOIN account_types as account_types on accounts.account_type_id = account_types.id ";
/**
/**
* 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
* be empty.
*
* @throws CentralRepoException If there is an error in getting the accounts.
* @return Collection of all accounts with identifier matching the given
* substring, may be empty.
*
* @throws CentralRepoException If there is an error in getting the
* accounts.
*/
public static Collection<CentralRepoAccount> getAccountsWithIdentifierLike(String accountIdentifierSubstring) throws CentralRepoException {
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();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
CentralRepository.getInstance().executeQuery(queryClause, params, queryCallback);
return queryCallback.getAccountsList();
}
/**
* Get all accounts with account identifier matching the given identifier.
*
* @param accountIdentifier Account identifier to look for.
*
* @return Collection of all accounts with identifier matching the given identifier, may
* be empty.
*
* @throws CentralRepoException If there is an error in getting the accounts.
* @return Collection of all accounts with identifier matching the given
* identifier, may be empty.
*
* @throws CentralRepoException If there is an error in getting the
* accounts.
*/
public static Collection<CentralRepoAccount> getAccountsWithIdentifier(String accountIdentifier) throws CentralRepoException {
String normalizedAccountIdentifier;
try {
normalizedAccountIdentifier = normalizeAccountIdentifier(accountIdentifier);
} catch (TskCoreException ex) {
throw new CentralRepoException("Failed to normalize account identifier.", ex);
}
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();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
CentralRepository.getInstance().executeQuery(queryClause, params, queryCallback);
return queryCallback.getAccountsList();
}
/**
* Get all central repo accounts.
*
* @return Collection of all accounts with identifier matching the given identifier, may
* be empty.
*
* @throws CentralRepoException If there is an error in getting the accounts.
* @return Collection of all accounts with identifier matching the given
* identifier, may be empty.
*
* @throws CentralRepoException If there is an error in getting the
* accounts.
*/
public static Collection<CentralRepoAccount> getAllAccounts() throws CentralRepoException {
String queryClause = ACCOUNTS_QUERY_CLAUSE;
List<Object> params = new ArrayList<>(); // empty param list
AccountsQueryCallback queryCallback = new AccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
CentralRepository.getInstance().executeQuery(queryClause, params, queryCallback);
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 TskCoreException
*/
private static String normalizeAccountIdentifier(String accountIdentifier) throws TskCoreException {
String normalizedAccountIdentifier = accountIdentifier;
if (CommunicationsUtils.isValidPhoneNumber(accountIdentifier)) {
normalizedAccountIdentifier = CommunicationsUtils.normalizePhoneNum(accountIdentifier);
} else if (CommunicationsUtils.isValidEmailAddress(accountIdentifier)) {
normalizedAccountIdentifier = normalizeEmailAddress(accountIdentifier);
}
return normalizedAccountIdentifier;
}
}

View File

@ -25,6 +25,9 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
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.ModuleSettings;
import static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo.SOFTWARE_CR_DB_SCHEMA_VERSION;
@ -259,8 +262,23 @@ public class CentralRepoDbUtil {
* used
*/
public static void setUseCentralRepo(boolean centralRepoCheckBoxIsSelected) {
if (!centralRepoCheckBoxIsSelected) {
closePersonasTopComponent();
}
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

View File

@ -64,7 +64,8 @@ public interface CentralRepository {
* It will not close active/in-use connections. Thus, it is vital that there
* 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;
@ -101,7 +102,7 @@ public interface CentralRepository {
/**
* Add a new name/value pair in the db_info table.
*
* @param name Key to set
* @param name Key to set
* @param value Value to set
*
* @throws CentralRepoException
@ -112,9 +113,9 @@ public interface CentralRepository {
* Set the data source object id for a specific entry in the data_sources
* table
*
* @param rowId - the row id for the data_sources table entry
* @param rowId - the row id for the data_sources table entry
* @param dataSourceObjectId - the object id for the data source from the
* caseDb
* caseDb
*/
void addDataSourceObjectId(int rowId, long dataSourceObjectId) throws CentralRepoException;
@ -132,7 +133,7 @@ public interface CentralRepository {
/**
* Update the value for a name in the name/value db_info table.
*
* @param name Name to find
* @param name Name to find
* @param value Value to assign to name.
*
* @throws CentralRepoException
@ -161,10 +162,10 @@ public interface CentralRepository {
* @param eamCase The case to update
*/
void updateCase(CorrelationCase eamCase) throws CentralRepoException;
/**
* Queries the examiner table for the given user name.
* Adds a row if the user is not found in the examiner table.
* Queries the examiner table for the given user name. Adds a row if the
* user is not found in the examiner table.
*
* @param examinerLoginName user name to look for.
* @return CentralRepoExaminer for the given user name.
@ -215,7 +216,7 @@ public interface CentralRepository {
* @param eamDataSource the data source to add
*
* @return - A CorrelationDataSource object with data source's central
* repository id
* repository id
*/
CorrelationDataSource newDataSource(CorrelationDataSource eamDataSource) throws CentralRepoException;
@ -244,8 +245,8 @@ public interface CentralRepository {
/**
* Retrieves Data Source details based on data source device ID
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
* @param caseDbDataSourceId the data source device ID number
*
* @return The data source
@ -256,8 +257,8 @@ public interface CentralRepository {
* Retrieves Data Source details based on data source ID
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
* @param dataSourceId the data source ID number
* uniqueness of DataSource
* @param dataSourceId the data source ID number
*
* @return The data source
*/
@ -274,7 +275,7 @@ public interface CentralRepository {
* Changes the name of a data source in the DB
*
* @param eamDataSource The data source
* @param newName The new name
* @param newName The new name
*
* @throws CentralRepoException
*/
@ -292,12 +293,12 @@ public interface CentralRepository {
* Retrieves eamArtifact instances from the database that are associated
* with the eamArtifactType and eamArtifactValues of the given eamArtifact.
*
* @param aType EamArtifact.Type to search for
* @param aType EamArtifact.Type to search for
* @param values The list of correlation values to get
* CorrelationAttributeInstances for
* CorrelationAttributeInstances for
*
* @return List of artifact instances for a given type with the specified
* values
* values
*
* @throws CorrelationAttributeNormalizationException
* @throws CentralRepoException
@ -323,14 +324,14 @@ public interface CentralRepository {
* with the eamArtifactType and eamArtifactValues of the given eamArtifact
* for the specified cases.
*
* @param aType The type of the artifact
* @param values The list of correlation values to get
* CorrelationAttributeInstances for
* @param aType The type of the artifact
* @param values The list of correlation values to get
* CorrelationAttributeInstances for
* @param caseIds The list of central repository case ids to get
* CorrelationAttributeInstances for
* CorrelationAttributeInstances for
*
* @return List of artifact instances for a given type with the specified
* values for the specified cases
* values for the specified cases
*
* @throws CorrelationAttributeNormalizationException
* @throws CentralRepoException
@ -345,7 +346,7 @@ public interface CentralRepository {
* @param value Value to search for
*
* @return Number of artifact instances having ArtifactType and
* ArtifactValue.
* ArtifactValue.
*/
Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException, CorrelationAttributeNormalizationException;
@ -384,7 +385,7 @@ public interface CentralRepository {
* @param correlationDataSource Data source to search for
*
* @return Number of artifact instances having caseDisplayName and
* dataSource
* dataSource
*/
Long getCountArtifactInstancesByCaseDataSource(CorrelationDataSource correlationDataSource) throws CentralRepoException;
@ -413,7 +414,7 @@ public interface CentralRepository {
* in the associated CorrelationAttribute object.
*
* @param eamArtifact The correlation attribute whose database instance will
* be updated.
* be updated.
*
* @throws CentralRepoException
*/
@ -426,11 +427,11 @@ public interface CentralRepository {
* Method exists to support instances added using Central Repository version
* 1,1 and older
*
* @param type The type of instance.
* @param correlationCase The case tied to the instance.
* @param type The type of instance.
* @param correlationCase The case tied to the instance.
* @param correlationDataSource The data source tied to the instance.
* @param value The value tied to the instance.
* @param filePath The file path tied to the instance.
* @param value The value tied to the instance.
* @param filePath The file path tied to the instance.
*
* @return The correlation attribute if it exists; otherwise null.
*
@ -443,11 +444,10 @@ public interface CentralRepository {
* Find a correlation attribute in the Central Repository database given the
* instance type, case, data source, object id.
*
* @param type The type of instance.
* @param correlationCase The case tied to the instance.
* @param type The type of instance.
* @param correlationCase The case tied to the instance.
* @param correlationDataSource The data source tied to the instance.
* @param objectID The object id of the file tied to the
* instance.
* @param objectID The object id of the file tied to the instance.
*
* @return The correlation attribute if it exists; otherwise null.
*
@ -483,7 +483,7 @@ public interface CentralRepository {
* @param value Value to search for
*
* @return List of cases containing this artifact with instances marked as
* bad
* bad
*
* @throws CentralRepoException
*/
@ -497,7 +497,7 @@ public interface CentralRepository {
* @param value Value to search for
*
* @return List of cases containing this artifact with instances marked as
* bad
* bad
*
* @throws CentralRepoException
*/
@ -554,13 +554,13 @@ public interface CentralRepository {
*/
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.
*
* @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.
*
@ -569,7 +569,6 @@ public interface CentralRepository {
*/
HashHitInfo lookupHash(String hash, int referenceSetID) throws CentralRepoException, CorrelationAttributeNormalizationException;
/**
* Check if the given value is in a specific reference set
*
@ -637,7 +636,7 @@ public interface CentralRepository {
* Update an existing organization.
*
* @param updatedOrganization the values the Organization with the same ID
* will be updated to in the database.
* will be updated to in the database.
*
* @throws CentralRepoException
*/
@ -689,8 +688,7 @@ public interface CentralRepository {
* Add a new reference instance
*
* @param eamGlobalFileInstance The reference instance to add
* @param correlationType Correlation Type that this Reference
* Instance is
* @param correlationType Correlation Type that this Reference Instance is
*
* @throws CentralRepoException
*/
@ -700,8 +698,8 @@ public interface CentralRepository {
* Insert the bulk collection of Global File Instances
*
* @param globalInstances a Set of EamGlobalFileInstances to insert into the
* db.
* @param contentType the Type of the global instances
* db.
* @param contentType the Type of the global instances
*
* @throws CentralRepoException
*/
@ -710,7 +708,7 @@ public interface CentralRepository {
/**
* Get all reference entries having a given correlation type and value
*
* @param aType Type to use for matching
* @param aType Type to use for matching
* @param aValue Value to use for matching
*
* @return List of all global file instances with a type and value
@ -735,7 +733,7 @@ public interface CentralRepository {
* used to correlate artifacts.
*
* @return List of EamArtifact.Type's. If none are defined in the database,
* the default list will be returned.
* the default list will be returned.
*
* @throws CentralRepoException
*/
@ -746,7 +744,7 @@ public interface CentralRepository {
* artifacts.
*
* @return List of enabled EamArtifact.Type's. If none are defined in the
* database, the default list will be returned.
* database, the default list will be returned.
*
* @throws CentralRepoException
*/
@ -757,7 +755,7 @@ public interface CentralRepository {
* correlate artifacts.
*
* @return List of supported EamArtifact.Type's. If none are defined in the
* database, the default list will be returned.
* database, the default list will be returned.
*
* @throws CentralRepoException
*/
@ -798,15 +796,15 @@ public interface CentralRepository {
*
* @return the lock, or null if locking is not supported
*
* @throws CentralRepoException if the coordination service is running but we fail
* to get the lock
* @throws CentralRepoException if the coordination service is running but
* we fail to get the lock
*/
public CoordinationService.Lock getExclusiveMultiUserDbLock() throws CentralRepoException;
/**
* Process the Artifact instance in the EamDb
*
* @param type EamArtifact.Type to search for
* @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance
*
* @throws CentralRepoException
@ -816,9 +814,9 @@ public interface CentralRepository {
/**
* Process the Artifact instance in the EamDb
*
* @param type EamArtifact.Type to search for
* @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance
* @param whereClause query string to execute
* @param whereClause query string to execute
*
* @throws CentralRepoException
*/
@ -827,60 +825,64 @@ public interface CentralRepository {
/**
* Process a SELECT query
*
* @param selectClause query string to execute
* @param selectClause query string to execute
* @param instanceTableCallback callback to process the instance
*
* @throws CentralRepoException
*/
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.
* @param sql INSERT sql to execute.
*
* @throws CentralRepoException If there is an error.
*/
void executeInsertSQL(String sql) throws CentralRepoException;
/**
* Executes a SELECT sql statement on the central repository database.
*
* @param sql SELECT sql to execute.
* @param queryCallback Query callback to handle the result of the query.
*
* Executes an INSERT/UPDATE/DELETE sql as a prepared statement, on the
* 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.
*/
void executeSelectSQL(String sql, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException;
void executeCommand(String sql, List<Object> params) throws CentralRepoException;
/**
* Executes a SELECT query sql as a prepared statement, on the central
* repository database.
*
* @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.
*
* @throws CentralRepoException If there is an error.
*/
void executeQuery(String sql, List<Object> params, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException;
/**
* Get account type by type name.
*
*
* @param accountTypeName account type name to look for
* @return CR account type
* @throws CentralRepoException
* @throws CentralRepoException
*/
CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException;
/**
* Gets all account types.
*
*
* @return Collection of all CR account types in the database.
*
* @throws CentralRepoException
*
* @throws CentralRepoException
*/
Collection<CentralRepoAccountType> getAllAccountTypes() throws CentralRepoException;
/**
* Get an account from the accounts table matching the given type/ID.
* Get an account from the accounts table matching the given type/ID.
* Inserts a row if one doesn't exists.
*
*
* @param crAccountType CR account type to look for or create
* @param accountUniqueID type specific unique account id
* @return CR account
*
* @throws CentralRepoException
*
* @throws CentralRepoException
*/
CentralRepoAccount getOrCreateAccount(CentralRepoAccount.CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException;
}

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 {

View File

@ -24,11 +24,11 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* This class abstracts a persona.
@ -42,11 +42,9 @@ public class Persona {
* Defines level of confidence in assigning a persona to an account.
*/
public enum Confidence {
UNKNOWN(1, "Unknown"),
LOW(2, "Low confidence"),
MEDIUM(3, "Medium confidence"),
HIGH(4, "High confidence"),
DERIVED(5, "Derived directly");
LOW(1, "Low confidence"),
MODERATE(2, "Moderate confidence"),
HIGH(3, "High confidence");
private final String name;
private final int level_id;
@ -72,7 +70,7 @@ public class Persona {
return confidence;
}
}
return Confidence.UNKNOWN;
return Confidence.LOW;
}
}
@ -124,7 +122,7 @@ public class Persona {
private final long modifiedDate;
private final PersonaStatus status;
private final CentralRepoExaminer examiner;
@NbBundle.Messages("Persona.defaultName=Unnamed")
public static String getDefaultName() {
return Bundle.Persona_defaultName();
@ -202,88 +200,155 @@ public class Persona {
/**
* Creates a Persona and associates the specified account with it.
*
* @param personaName Persona name.
* @param comment Comment to associate with persona, may be null.
* @param status Persona status
* @param account Account for which the persona is being created.
* @param personaName Persona name.
* @param comment Comment to associate with persona, may be null.
* @param status Persona status
* @param account Account for which the persona is being created.
* @param justification Justification for why this account belongs to this
* persona, may be null.
* @param confidence Confidence level for this association of Persona &
* account.
* persona, may be null.
* @param confidence Confidence level for this association of Persona &
* account.
*
* @return Persona Persona created.
*
* @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 {
Persona persona = createPersona(personaName, comment, status);
persona.addAccountToPersona(account, justification, confidence);
persona.addAccount(account, justification, confidence);
return persona;
}
/**
* Inserts a row in the Persona tables.
*
* @param name Persona name, may be null - default name is used in that
* case.
* @param name Persona name, may be null - default name is used in that
* case.
* @param comment Comment to associate with persona, may be null.
* @param status Persona status.
* @param status Persona status.
*
* @return Persona corresponding to the row inserted in the personas table.
*
* @throws CentralRepoException If there is an error in adding a row to
* personas table.
* personas table.
*/
private static Persona createPersona(String name, String comment, PersonaStatus status) throws CentralRepoException {
// generate a UUID for the persona
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();
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);
}
/**
* 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
*
* @param name The new name.
*
* @throws CentralRepoException If there is an error.
*/
public void setName(String name) throws CentralRepoException {
String updateSQL = "UPDATE personas SET name = ? WHERE id = ?";
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);
}
}
/**
* Associates an account with a persona by creating a row in the
* PersonaAccounts table.
*
* @param account Account to add to persona.
* @param account Account to add to persona.
* @param justification Reason for adding the account to persona, may be
* null.
* @param confidence Confidence level.
* null.
* @param confidence Confidence level.
*
* @return PersonaAccount
*
* @throws CentralRepoException If there is an error.
*/
public PersonaAccount addAccountToPersona(CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException {
public PersonaAccount addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException {
return PersonaAccount.addPersonaAccount(this, account, justification, confidence);
}
CentralRepoExaminer currentExaminer = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name"));
/**
* Removes the given PersonaAccount (persona/account association)
*
* @param account account to remove
*
* @throws CentralRepoException If there is an error in querying the
* Personas table.
*/
public void removeAccount(PersonaAccount account) throws CentralRepoException {
PersonaAccount.removePersonaAccount(account.getId());
}
Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli();
String insertClause = " INTO persona_accounts (persona_id, account_id, justification, confidence_id, date_added, examiner_id ) "
+ "VALUES ( "
+ this.getId() + ", "
+ account.getId() + ", "
+ "'" + ((StringUtils.isBlank(justification) ? "" : SleuthkitCase.escapeSingleQuotes(justification))) + "', "
+ confidence.getLevelId() + ", "
+ timeStampMillis.toString() + ", "
+ currentExaminer.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);
}
CentralRepository.getInstance().executeInsertSQL(insertClause);
return new PersonaAccount(this, account, justification, confidence, timeStampMillis, currentExaminer);
/**
* Marks this persona as deleted
*/
public void delete() throws CentralRepoException {
String deleteSQL = "UPDATE personas SET status_id = ? WHERE id = ?";
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
List<Object> params = new ArrayList<>();
params.add(PersonaStatus.DELETED.getStatusId());
params.add(id);
getCRInstance().executeCommand(deleteSQL, params);
}
}
/**
@ -291,7 +356,7 @@ public class Persona {
*/
private static class PersonaQueryCallback implements CentralRepositoryDbQueryCallback {
private final Collection<Persona> personaList = new ArrayList<>();
private final Collection<Persona> personaList = new ArrayList<>();
@Override
public void process(ResultSet rs) throws SQLException {
@ -312,7 +377,7 @@ public class Persona {
status,
examiner
);
personaList.add(persona);
}
}
@ -324,72 +389,166 @@ public class Persona {
// Partial query string to select from personas table,
// just supply the where clause.
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 "
+ "FROM personas as p "
+ "INNER JOIN examiners as e ON e.id = p.examiner_id ";
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 "
+ "FROM personas as p "
+ "INNER JOIN examiners as e ON e.id = p.examiner_id ";
/**
* Gets the row from the Personas table with the given UUID, creates and
* returns the Persona from that data.
*
* @param uuid Persona UUID to match.
*
* @return Persona matching the given UUID, may be null if no match is
* found.
* found.
*
* @throws CentralRepoException If there is an error in querying the
* Personas table.
* Personas table.
*/
private static Persona getPersonaByUUID(String uuid) throws CentralRepoException {
String queryClause =
PERSONA_QUERY
+ "WHERE p.uuid = '" + uuid + "'";
String queryClause
= PERSONA_QUERY
+ "WHERE p.uuid = ?";
List<Object> params = new ArrayList<>();
params.add(uuid);
PersonaQueryCallback queryCallback = new PersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeQuery(queryClause, params, queryCallback);
Collection<Persona> personas = queryCallback.getPersonas();
return personas.isEmpty() ? null : personas.iterator().next();
}
/**
* Gets the rows from the Personas table with matching name.
* Gets the rows from the Personas table with matching name. 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.
* empty if no match is found.
*
* @throws CentralRepoException If there is an error in querying the
* Personas table.
* Personas table.
*/
public static Collection<Persona> getPersonaByName(String partialName) throws CentralRepoException {
String queryClause = PERSONA_QUERY
+ "WHERE LOWER(p.name) LIKE " + "LOWER('%" + partialName + "%')" ;
+ "WHERE p.status_id != ? "
+ " 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();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeQuery(queryClause, params, queryCallback);
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.
*
* @param alias Alias name.
* @param alias Alias name.
* @param justification Reason for assigning the alias, may be null.
* @param confidence Confidence level.
* @param confidence Confidence level.
*
* @return PersonaAlias
*
* @throws CentralRepoException If there is an error in creating the alias.
*/
public PersonaAlias addAlias(String alias, String justification, Persona.Confidence confidence) throws CentralRepoException {
return PersonaAlias.addPersonaAlias(this, alias, justification, confidence);
}
/**
* Removes the given alias.
*
* @param alias alias to remove
*
* @throws CentralRepoException If there is an error in querying the
* Personas table.
*/
public void removeAlias(PersonaAlias alias) throws CentralRepoException {
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.
*
@ -404,18 +563,45 @@ public class Persona {
/**
* Adds specified metadata to the persona.
*
* @param name Metadata name.
* @param value Metadata value.
* @param name Metadata name.
* @param value Metadata value.
* @param justification Reason for adding the metadata, may be null.
* @param confidence Confidence level.
* @param confidence Confidence level.
*
* @return PersonaMetadata
*
* @throws CentralRepoException If there is an error in adding metadata.
*/
public PersonaMetadata addMetadata(String name, String value, String justification, Persona.Confidence confidence) throws CentralRepoException {
return PersonaMetadata.addPersonaMetadata(this.getId(), name, value, justification, confidence);
}
/**
* Removes the given metadata from this persona.
*
* @param metadata metadata to remove
*
* @throws CentralRepoException If there is an error in querying the
* Personas table.
*/
public void removeMetadata(PersonaMetadata metadata) throws CentralRepoException {
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.
*
@ -433,12 +619,12 @@ public class Persona {
* @return Collection of PersonaAccounts, may be empty.
*
* @throws CentralRepoException If there is an error in getting the
* persona_account.
* persona_account.
*/
public Collection<PersonaAccount> getPersonaAccounts() throws CentralRepoException {
return PersonaAccount.getPersonaAccountsForPersona(this.getId());
}
/**
* Callback to process a query that gets cases for account instances of an
* account
@ -452,7 +638,7 @@ public class Persona {
while (resultSet.next()) {
// 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);
}
}
@ -466,8 +652,9 @@ public class Persona {
* Gets a list of cases that the persona appears in.
*
* @return Collection of cases that the persona appears in, may be empty.
*
* @throws CentralRepoException If there is an error in getting the cases
* from the database.
* from the database.
*/
public Collection<CorrelationCase> getCases() throws CentralRepoException {
@ -477,14 +664,17 @@ public class Persona {
Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId());
for (CentralRepoAccount account : accounts) {
int corrTypeId = account.getAccountType().getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
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();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback);
getCRInstance().executeQuery(querySql, params, queryCallback);
// Add any cases that aren't already on the list.
for (CorrelationCase corrCase : queryCallback.getCases()) {
@ -511,8 +701,8 @@ public class Persona {
while (resultSet.next()) {
// get Case for case_id
CorrelationCase correlationCase = CentralRepository.getInstance().getCaseById(resultSet.getInt("case_id"));
CorrelationDataSource correlationDatasource = CentralRepository.getInstance().getDataSourceById(correlationCase, resultSet.getInt("data_source_id"));
CorrelationCase correlationCase = getCRInstance().getCaseById(resultSet.getInt("case_id"));
CorrelationDataSource correlationDatasource = getCRInstance().getDataSourceById(correlationCase, resultSet.getInt("data_source_id"));
// Add data source to list if not already on it.
if (!correlationDataSources.stream().anyMatch(p -> Objects.equals(p.getDataSourceObjectID(), correlationDatasource.getDataSourceObjectID()))) {
@ -530,7 +720,7 @@ public class Persona {
* Gets all data sources that the persona appears in.
*
* @return Collection of data sources that the persona appears in, may be
* empty.
* empty.
*
* @throws CentralRepoException
*/
@ -540,14 +730,17 @@ public class Persona {
Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId());
for (CentralRepoAccount account : accounts) {
int corrTypeId = account.getAccountType().getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
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();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback);
getCRInstance().executeQuery(querySql, params, queryCallback);
// Add any data sources that aren't already on the list.
for (CorrelationDataSource correlationDatasource : queryCallback.getDataSources()) {
@ -604,13 +797,15 @@ public class Persona {
* the X_instance table for the given account type.
*
* @param crAccountType Account type to generate the query string for.
*
* @return Query substring.
*
* @throws CentralRepoException
*/
private static String getPersonaFromInstanceTableQueryTemplate(CentralRepoAccount.CentralRepoAccountType crAccountType) throws CentralRepoException {
int corrTypeId = crAccountType.getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String instanceTableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
return "SELECT " + instanceTableName + ".account_id, case_id, data_source_id, "
@ -629,19 +824,25 @@ public class Persona {
* @param correlationCase Case to look the persona in.
*
* @return Collection of personas, may be empty.
*
* @throws CentralRepoException
*/
public static Collection<Persona> getPersonasForCase(CorrelationCase correlationCase) throws CentralRepoException {
Collection<Persona> personaList = new ArrayList<>();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = CentralRepository.getInstance().getAllAccountTypes();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = getCRInstance().getAllAccountTypes();
for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) {
String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType)
+ " WHERE case_id = " + correlationCase.getID();
+ " WHERE case_id = ?" // param 1
+ " 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();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback);
getCRInstance().executeQuery(querySql, params, queryCallback);
// Add persona that aren't already on the list.
for (Persona persona : queryCallback.getPersonasList()) {
@ -660,19 +861,25 @@ public class Persona {
* @param dataSource Data source to look the persona in.
*
* @return Collection of personas, may be empty.
*
* @throws CentralRepoException
*/
public static Collection<Persona> getPersonasForDataSource(CorrelationDataSource dataSource) throws CentralRepoException {
Collection<Persona> personaList = new ArrayList<>();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = CentralRepository.getInstance().getAllAccountTypes();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = getCRInstance().getAllAccountTypes();
for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) {
String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType)
+ " WHERE data_source_id = " + dataSource.getID();
+ " WHERE data_source_id = ?"
+ " AND personas.status_id != ?";
List<Object> params = new ArrayList<>();
params.add(dataSource.getID());
params.add(Persona.PersonaStatus.DELETED.getStatusId());
PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback);
getCRInstance().executeQuery(querySql, params, queryCallback);
// Add persona that aren't already on the list.
for (Persona persona : queryCallback.getPersonasList()) {
@ -684,4 +891,22 @@ public class Persona {
}
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

@ -20,20 +20,25 @@ package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.datamodel.Account;
/**
* This class represents an association between a Persona and an Account.
*
*
* A Persona has at least one, possibly more, accounts associated with it.
*
*
*
*
*/
public class PersonaAccount {
private final long id;
private final Persona persona;
private final CentralRepoAccount account;
private final String justification;
@ -41,7 +46,8 @@ public class PersonaAccount {
private final long dateAdded;
private final CentralRepoExaminer examiner;
public PersonaAccount(Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
public PersonaAccount(long id, Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
this.id = id;
this.persona = persona;
this.account = account;
this.justification = justification;
@ -50,6 +56,10 @@ public class PersonaAccount {
this.examiner = examiner;
}
public long getId() {
return id;
}
public Persona getPersona() {
return persona;
}
@ -108,7 +118,59 @@ public class PersonaAccount {
return Objects.equals(this.examiner, other.getExaminer());
}
/**
* Creates an account for the specified Persona.
*
* @param persona Persona for which the account is being added.
* @param account Account.
* @param justification Reason for assigning the alias, may be null.
* @param confidence Confidence level.
*
* @return PersonaAccount
*
* @throws CentralRepoException If there is an error in creating the
* account.
*/
static PersonaAccount addPersonaAccount(Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException {
CentralRepoExaminer currentExaminer = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli();
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());
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
getCRInstance().executeQuery(querySQL, queryParams, queryCallback);
Collection<PersonaAccount> accounts = queryCallback.getPersonaAccountsList();
if (accounts.size() != 1) {
throw new CentralRepoException("Account add query failed");
}
return accounts.iterator().next();
}
/**
* Callback to process a Persona Accounts query.
*/
@ -144,14 +206,14 @@ public class PersonaAccount {
);
// 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(
rs.getInt("account_id"),
crAccountType,
rs.getString("account_unique_identifier"));
// create persona account
PersonaAccount personaAccount = new PersonaAccount(persona, account,
PersonaAccount personaAccount = new PersonaAccount(rs.getLong("persona_accounts_id"), persona, account,
rs.getString("justification"),
Persona.Confidence.fromId(rs.getInt("confidence_id")),
Long.parseLong(rs.getString("date_added")),
@ -166,8 +228,8 @@ public class PersonaAccount {
}
};
// Query clause to select from persona_accounts table to create PersonaAccount(s)
private static final String PERSONA_ACCOUNTS_QUERY_CALUSE = "SELECT 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,"
// Query clause to select from persona_accounts table to create PersonaAccount(s)
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.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,"
@ -178,67 +240,141 @@ public class PersonaAccount {
+ " JOIN account_types as account_types on accounts.account_type_id = account_types.id "
+ " JOIN examiners as pa_examiner ON pa_examiner.id = persona_accounts.examiner_id "
+ " JOIN examiners as persona_examiner ON persona_examiner.id = personas.examiner_id ";
/**
/**
* Gets all the Accounts for the specified Persona.
*
* @param personaId Id of persona for which to get the accounts for.
*
* @return Collection of PersonaAccounts, may be empty.
*
* @throws CentralRepoException If there is an error in getting the
* persona_account.
*/
static Collection<PersonaAccount> getPersonaAccountsForPersona(long personaId) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ " WHERE persona_accounts.persona_id = " + personaId;
String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " WHERE persona_accounts.persona_id = ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add(personaId);
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeQuery(querySQL, queryParams, queryCallback);
return queryCallback.getPersonaAccountsList();
}
/**
* Gets all the Persona for the specified Account.
*
* @param accountId Id of account for which to get the Personas for.
*
* @return Collection of PersonaAccounts. may be empty.
*
* @throws CentralRepoException If there is an error in getting the
* persona_account.
*/
public static Collection<PersonaAccount> getPersonaAccountsForAccount(long accountId) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ " WHERE persona_accounts.account_id = " + accountId;
String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " WHERE persona_accounts.account_id = ?"
+ " AND personas.status_id != ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add(accountId);
queryParams.add(Persona.PersonaStatus.DELETED.getStatusId());
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeQuery(querySQL, queryParams, queryCallback);
return queryCallback.getPersonaAccountsList();
}
/**
* Gets all the Persona associated with all the accounts matching the given
* account identifier substring.
*
* @param accountIdentifierSubstring Account identifier substring to search
* for.
*
* @return Collection of PersonaAccounts. may be empty.
*
* @throws CentralRepoException If there is an error in getting the
* persona_account.
*/
public static Collection<PersonaAccount> getPersonaAccountsForIdentifierLike(String accountIdentifierSubstring) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')";
String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER(?)"
+ " AND personas.status_id != ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add("%" + accountIdentifierSubstring + "%"); // substring match
queryParams.add(Persona.PersonaStatus.DELETED.getStatusId());
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeQuery(querySQL, queryParams, queryCallback);
return queryCallback.getPersonaAccountsList();
}
/**
* Gets all the Persona associated with the given account.
*
* @param account Account to search for.
*
* @return Collection of PersonaAccounts, maybe empty if none were found or
* CR is not enabled.
*
* @throws CentralRepoException
*/
public static Collection<PersonaAccount> getPersonaAccountsForAccount(Account account) throws CentralRepoException {
String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " 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());
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
getCRInstance().executeQuery(querySQL, queryParams, queryCallback);
return queryCallback.getPersonaAccountsList();
}
/**
* Removes 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 removePersonaAccount(long id) throws CentralRepoException {
String deleteSQL = " DELETE FROM persona_accounts WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(id);
getCRInstance().executeCommand(deleteSQL, params);
}
/**
* 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);
}
/**
* Callback to process a query that gets all accounts belonging to a
* persona.
@ -253,7 +389,7 @@ public class PersonaAccount {
while (rs.next()) {
// 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(
rs.getInt("account_id"),
crAccountType,
@ -275,7 +411,9 @@ public class PersonaAccount {
*
* @return Collection of all accounts associated with the given persona, 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.
*/
static Collection<CentralRepoAccount> getAccountsForPersona(long personaId) throws CentralRepoException {
String queryClause = "SELECT account_id, "
@ -284,11 +422,32 @@ public class PersonaAccount {
+ " FROM persona_accounts "
+ " 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 "
+ " WHERE persona_accounts.persona_id = " + personaId;
+ " WHERE persona_accounts.persona_id = ?";
List<Object> queryParams = new ArrayList<>();
queryParams.add(personaId);
AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeQuery(queryClause, queryParams, queryCallback);
return queryCallback.getAccountsList();
}
/**
* 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,16 +24,22 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* This class abstracts an alias assigned to a Persona.
* A Persona may have multiple aliases.
*
* This class abstracts an alias assigned to a Persona. A Persona may have
* multiple aliases.
*
*/
public class PersonaAlias {
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 "
+ "FROM persona_alias as pa "
+ "INNER JOIN examiners as e ON e.id = pa.examiner_id ";
private final long id;
private final long personaId;
private final String alias;
private final String justification;
@ -41,6 +47,10 @@ public class PersonaAlias {
private final long dateAdded;
private final CentralRepoExaminer examiner;
public long getId() {
return id;
}
public long getPersonaId() {
return personaId;
}
@ -64,8 +74,9 @@ public class PersonaAlias {
public CentralRepoExaminer getExaminer() {
return examiner;
}
public PersonaAlias(long personaId, String alias, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
public PersonaAlias(long id, long personaId, String alias, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
this.id = id;
this.personaId = personaId;
this.alias = alias;
this.justification = justification;
@ -73,8 +84,8 @@ public class PersonaAlias {
this.dateAdded = dateAdded;
this.examiner = examiner;
}
/**
/**
* Creates an alias for the specified Persona.
*
* @param persona Persona for which the alias is being added.
@ -87,26 +98,88 @@ public class PersonaAlias {
*/
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();
Long timeStampMillis = instant.toEpochMilli();
String insertClause = " INTO persona_alias (persona_id, alias, justification, confidence_id, date_added, examiner_id ) "
+ "VALUES ( "
+ persona.getId() + ", "
+ "'" + alias + "', "
+ "'" + ((StringUtils.isBlank(justification) ? "" : SleuthkitCase.escapeSingleQuotes(justification))) + "', "
+ confidence.getLevelId() + ", "
+ timeStampMillis.toString() + ", "
+ examiner.getId()
+ ")";
String insertSQL = "INSERT INTO persona_alias (persona_id, alias, justification, confidence_id, date_added, examiner_id ) "
+ " VALUES ( ?, ?, ?, ?, ?, ?)";
CentralRepository.getInstance().executeInsertSQL(insertClause);
return new PersonaAlias(persona.getId(), alias, justification, confidence, timeStampMillis, examiner);
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
+ "WHERE pa.persona_id = ?"
+ " AND pa.alias = ?"
+ " AND pa.date_added = ?"
+ " 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();
getCRInstance().executeQuery(queryClause, queryParams, queryCallback);
Collection<PersonaAlias> aliases = queryCallback.getAliases();
if (aliases.size() != 1) {
throw new CentralRepoException("Alias add query failed");
}
return aliases.iterator().next();
}
/**
/**
* Removes a PersonaAlias.
*
* @param alias Alias to remove.
*
* @throws CentralRepoException If there is an error in removing the alias.
*/
static void removePersonaAlias(PersonaAlias alias) throws CentralRepoException {
String deleteSQL = " DELETE FROM persona_alias WHERE id = ?";
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);
}
/**
* Callback to process a Persona aliases query.
*/
static class PersonaAliasesQueryCallback implements CentralRepositoryDbQueryCallback {
@ -122,6 +195,7 @@ public class PersonaAlias {
rs.getString("login_name"));
PersonaAlias alias = new PersonaAlias(
rs.getLong("id"),
rs.getLong("persona_id"),
rs.getString("alias"),
rs.getString("justification"),
@ -137,7 +211,7 @@ public class PersonaAlias {
return Collections.unmodifiableCollection(personaAliases);
}
};
/**
* Gets all aliases for the persona with specified id.
*
@ -147,15 +221,33 @@ public class PersonaAlias {
* @throws CentralRepoException If there is an error in retrieving aliases.
*/
public static Collection<PersonaAlias> getPersonaAliases(long personaId) throws CentralRepoException {
String queryClause = "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 "
+ "INNER JOIN examiners as e ON e.id = pa.examiner_id "
+ "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();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeQuery(queryClause, params, queryCallback);
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,18 +24,24 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* This class abstracts metadata associated with a Persona.
* Metadata is in the form of a name/value pair.
*
* This class abstracts metadata associated with a Persona. Metadata is in the
* form of a name/value pair.
*
* A Persona may have zero or more metadata.
*
*
*/
public class PersonaMetadata {
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 "
+ "FROM persona_metadata as pmd "
+ "INNER JOIN examiners as e ON e.id = pmd.examiner_id ";
private final long id;
private final long personaId;
private final String name;
private final String value;
@ -44,6 +50,10 @@ public class PersonaMetadata {
private final long dateAdded;
private final CentralRepoExaminer examiner;
public long getId() {
return id;
}
public long getPersonaId() {
return personaId;
}
@ -71,8 +81,9 @@ public class PersonaMetadata {
public CentralRepoExaminer getExaminer() {
return examiner;
}
public PersonaMetadata(long personaId, String name, String value, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
public PersonaMetadata(long id, long personaId, String name, String value, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
this.id = id;
this.personaId = personaId;
this.name = name;
this.value = value;
@ -81,8 +92,8 @@ public class PersonaMetadata {
this.dateAdded = dateAdded;
this.examiner = examiner;
}
/**
/**
* Adds specified metadata to the given persona.
*
* @param personaId Id of persona to add metadata for.
@ -96,26 +107,92 @@ public class PersonaMetadata {
*/
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();
Long timeStampMillis = instant.toEpochMilli();
String insertClause = " INTO persona_metadata (persona_id, name, value, justification, confidence_id, date_added, examiner_id ) "
+ "VALUES ( "
+ personaId + ", "
+ "'" + name + "', "
+ "'" + value + "', "
+ "'" + ((StringUtils.isBlank(justification) ? "" : SleuthkitCase.escapeSingleQuotes(justification))) + "', "
+ confidence.getLevelId() + ", "
+ timeStampMillis.toString() + ", "
+ examiner.getId()
+ ")";
String insertSQL = "INSERT INTO persona_metadata (persona_id, name, value, justification, confidence_id, date_added, examiner_id ) "
+ "VALUES ( ?, ?, ?, ?, ?, ?, ?)";
CentralRepository.getInstance().executeInsertSQL(insertClause);
return new PersonaMetadata(personaId, name, value, justification, confidence, timeStampMillis, examiner);
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
+ "WHERE pmd.persona_id = ?"
+ " AND pmd.name = ?"
+ " AND pmd.value = ?"
+ " AND pmd.date_added = ?"
+ " 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();
getCRInstance().executeQuery(queryClause, queryParams, queryCallback);
Collection<PersonaMetadata> metadata = queryCallback.getMetadataList();
if (metadata.size() != 1) {
throw new CentralRepoException("Metadata add query failed");
}
return metadata.iterator().next();
}
/**
* Removes the given PersonaMetadata
*
* @param metadata Metadata to remove.
*
* @throws CentralRepoException If there is an error in removing the
* metadata.
*/
static void removePersonaMetadata(PersonaMetadata metadata) throws CentralRepoException {
String deleteSql = " DELETE FROM persona_metadata WHERE id = ?";
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);
}
/**
* Callback to process a Persona metadata query.
*/
@ -132,6 +209,7 @@ public class PersonaMetadata {
rs.getString("login_name"));
PersonaMetadata metaData = new PersonaMetadata(
rs.getLong("id"),
rs.getLong("persona_id"),
rs.getString("name"),
rs.getString("value"),
@ -148,8 +226,8 @@ public class PersonaMetadata {
return Collections.unmodifiableCollection(personaMetadataList);
}
};
/**
/**
* Gets all metadata for the persona with specified id.
*
* @param personaId Id of the persona for which to get the metadata.
@ -158,16 +236,34 @@ public class PersonaMetadata {
* @throws CentralRepoException If there is an error in retrieving aliases.
*/
static Collection<PersonaMetadata> getPersonaMetadata(long personaId) throws CentralRepoException {
String queryClause = "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 "
+ "INNER JOIN examiners as e ON e.id = pmd.examiner_id "
+ "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();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeQuery(queryClause, params, queryCallback);
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.TimeUnit;
import java.util.logging.Level;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
@ -1082,20 +1081,25 @@ abstract class RdbmsCentralRepo implements CentralRepository {
*/
@Override
public CentralRepoAccount getOrCreateAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
// TBD: normalize the account id - waiting for a PR to be merged
// Get the account fom the accounts table
CentralRepoAccount account = getAccount(crAccountType, accountUniqueID);
// account not found in the table, create it
if (null == account) {
String query = "INSERT INTO accounts (account_type_id, account_unique_identifier) "
+ "VALUES ( " + crAccountType.getAccountTypeId() + ", '"
+ accountUniqueID + "' )";
String insertSQL = "INSERT INTO accounts (account_type_id, account_unique_identifier) "
+ "VALUES (?, ?)";
try (Connection connection = connect();
Statement s = connection.createStatement();) {
PreparedStatement preparedStatement = connection.prepareStatement(insertSQL);) {
preparedStatement.setInt(1, crAccountType.getAccountTypeId());
preparedStatement.setString(2, accountUniqueID); // TBD: fill in the normalized ID
preparedStatement.executeUpdate();
s.execute(query);
// get the account from the db - should exist now.
account = getAccount(crAccountType, accountUniqueID);
} catch (SQLException ex) {
@ -1679,7 +1683,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
bulkArtifacts.get(tableName).clear();
}
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Bulk insert");
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Central Repository: Bulk insert");
HealthMonitor.submitTimingMetric(timingMetric);
// Reset state
@ -2543,45 +2547,52 @@ abstract class RdbmsCentralRepo implements CentralRepository {
}
@Override
public void executeInsertSQL(String insertClause) throws CentralRepoException {
if (insertClause == null) {
throw new CentralRepoException("Insert SQL is null");
}
String sql = getPlatformSpecificInsertSQL(insertClause);
try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
public void executeCommand(String sql, List<Object> params) throws CentralRepoException {
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;
}
}
// execute the prepared statement
preparedStatement.executeUpdate();
} 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
public void executeSelectSQL(String selectSQL, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException {
public void executeQuery(String sql, List<Object> params, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException {
if (queryCallback == null) {
throw new CentralRepoException("Query callback is null");
}
if (selectSQL == null) {
throw new CentralRepoException("Select SQL is null");
}
StringBuilder sqlSb = new StringBuilder(QUERY_STR_MAX_LEN);
if (selectSQL.trim().toUpperCase().startsWith("SELECT") == false) {
sqlSb.append("SELECT ");
}
sqlSb.append(selectSQL);
try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sqlSb.toString());
ResultSet resultSet = preparedStatement.executeQuery();) {
queryCallback.process(resultSet);
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;
}
}
// execute query, and the callback to process result
try (ResultSet resultSet = preparedStatement.executeQuery();) {
queryCallback.process(resultSet);
}
} 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

View File

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

View File

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

View File

@ -73,7 +73,7 @@ import org.sleuthkit.datamodel.CommunicationsUtils;
* Listen for ingest events and update entries in the Central Repository
* database accordingly
*/
@NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Correlation Engine"})
@NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Central Repository"})
public class IngestEventsListener {
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
* Correlation Engine.
* Central Repository.
*/
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
* Correlation Engine.
* Central Repository.
*/
public synchronized static void decrementCorrelationEngineModuleCount() {
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.
*/
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.
*
* @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() {
return correlationModuleInstanceCount;
@ -282,7 +282,7 @@ public class IngestEventsListener {
@Override
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
//in these cases we still want to create correlation attributesForNewArtifact for those artifacts when appropriate
if (!IngestManager.getInstance().isIngestRunning() || getCeModuleInstanceCount() > 0) {
@ -342,101 +342,14 @@ public class IngestEventsListener {
dbManager = db;
event = evt;
}
/**
* Automatically creates personas from all the TSK_CONTACT artifacts
* found in a data source.
*
* @param dataSource Data source that was just analyzed.
* @throws TskCoreException If there is any error getting contact
* artifacts from case database.
* @throws CentralRepoException If there is an error in creating
* personas in the Central Repo.
*/
private void autoGenerateContactPersonas(Content dataSource) throws TskCoreException, CentralRepoException {
Blackboard blackboard;
try {
blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
} catch (NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex);
return;
}
// get all TSK_CONTACT artifacts in this data source.
List<BlackboardArtifact> contactArtifacts = blackboard.getArtifacts(TSK_CONTACT.getTypeID(), dataSource.getId());
for (BlackboardArtifact artifact : contactArtifacts) {
BlackboardAttribute nameAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME));
String personaName = (nameAttr != null) ? nameAttr.getValueString() : null;
// Get phone number and email attributes.
BlackboardAttribute phoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER));
BlackboardAttribute homePhoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME));
BlackboardAttribute mobilePhoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE));
BlackboardAttribute emailAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL));
Persona persona = personaFromContactAttribute(null, Account.Type.PHONE, phoneAttr, personaName);
persona = personaFromContactAttribute(persona, Account.Type.PHONE, homePhoneAttr, personaName);
persona = personaFromContactAttribute(persona, Account.Type.PHONE, mobilePhoneAttr, personaName);
personaFromContactAttribute(persona, Account.Type.EMAIL, emailAttr, personaName);
}
}
/**
* Gets central repo account for the given attribute for a TSK_CONTACT
* artifact. Associates the given persona with that account. Creates a
* Persona, if one isn't provided.
*
* @param persona Persona to associate with the account. May be null, in
* which case a persona is created first.
* @param accountType Account type of account to be associated.
* @param attribute Attribute form which get the account id.
* @param personaName Persona name, if a persona needs to be created.
* @return Persona created or associated with the account.
*
* @throws TskCoreException If there is an error in normalizing the
* account id.
* @throws CentralRepoException If there is an erorr is getting the
* account or associating the persona with it.
*/
private Persona personaFromContactAttribute(Persona persona, Account.Type accountType, BlackboardAttribute attribute, String personaName) throws CentralRepoException, TskCoreException {
Persona personaToReturn = persona;
if (attribute != null) {
String accountId = attribute.getValueString();
if (CommunicationsUtils.isValidAccountId(accountType, accountId)) {
if (accountType == Account.Type.PHONE) {
accountId = CommunicationsUtils.normalizePhoneNum(accountId);
} else if (accountType == Account.Type.EMAIL) {
accountId = CommunicationsUtils.normalizeEmailAddress(accountId);
}
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountType.getTypeName());
CentralRepoAccount crAccount = CentralRepository.getInstance().getOrCreateAccount(crAccountType, accountId);
PersonaAccount personaAccount;
// If persona doesnt exist, create one
if (persona == null) {
personaToReturn = Persona.createPersonaForAccount(personaName, "Auto generated contact persona", Persona.PersonaStatus.UNKNOWN, crAccount, "Found in contact book entry", Persona.Confidence.DERIVED);
} else {
persona.addAccountToPersona(crAccount, "Found in contact book entry", Persona.Confidence.DERIVED);
}
}
}
return personaToReturn;
}
@Override
public void run() {
// clear the tracker to reduce memory usage
if (getCeModuleInstanceCount() == 0) {
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
@ -504,8 +417,6 @@ public class IngestEventsListener {
correlationDataSource.setSha256(imageSha256Hash);
}
}
// automatically generate persona from contact artifacts.
autoGenerateContactPersonas(dataSource);
}
} catch (CentralRepoException ex) {
LOGGER.log(Level.SEVERE, String.format(

View File

@ -1,6 +1,6 @@
CentralRepoIngestModel_name_header=Name:<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.prevCaseComment.text=Previous Case:
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
CentralRepoIngestModule_postToBB_knownBadMsg=Notable: {0}
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.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable
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;
/**
* Instantiate the Correlation Engine ingest module.
* Instantiate the Central Repository ingest module.
*
* @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) {
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);
HealthMonitor.submitTimingMetric(timingMetric);
if (!caseDisplayNamesList.isEmpty()) {
@ -220,7 +220,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
// see ArtifactManagerTimeTester for details
@Messages({
"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
public void startUp(IngestJobContext context) throws IngestModuleException {
@ -235,7 +235,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
* posited.
*
* 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
* notable items, and the proceeding module (with flagging disabled)
* 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
if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
&& (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
}
jobId = context.getJobId();

View File

@ -33,7 +33,7 @@ import org.sleuthkit.autopsy.ingest.NoIngestModuleIngestJobSettings;
* Factory for Central Repository ingest modules
*/
@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"})
public class CentralRepoIngestModuleFactory extends IngestModuleFactoryAdapter {

View File

@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.ingestmodule;
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 {

View File

@ -22,7 +22,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
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
final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {

View File

@ -1,21 +1,8 @@
CTL_OpenPersonaManager=Persona Manager
CTL_PersonaManagerTopComponentAction=Persona Manager
CTL_OpenPersonas=Personas
CTL_PersonasTopComponentAction=Personas
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.okBtn.text=OK
PersonaDetailsPanel.deleteCaseBtn.text=Delete
PersonaDetailsPanel.addCaseBtn.text=Add
PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add
@ -31,25 +18,66 @@ PersonaDetailsPanel.nameLbl.text=Name:
AddAliasDialog.accountsLbl.text=Account:
AddAliasDialog.okBtn.text=OK
AddAliasDialog.cancelBtn.text=Cancel
AddMetadataDialog.cancelBtn.text=Cancel
AddMetadataDialog.okBtn.text=OK
AddMetadataDialog.nameLbl.text=Name:
AddMetadataDialog.nameTextField.text=
AddMetadataDialog.valueLbl.text=Value:
AddMetadataDialog.valueTextField.text=
AddMetadataDialog.justificationLbl.text=Justification:
AddMetadataDialog.justificationTextField.text=
AddMetadataDialog.confidenceLbl.text=Confidence:
AddAliasDialog.justificationLbl.text=Justification:
AddAliasDialog.okBtn.text_1=OK
AddAliasDialog.cancelBtn.text_1=Cancel
AddAliasDialog.confidenceLbl.text=Confidence:
AddAliasDialog.justificationTextField.text=
AddAliasDialog.aliasLbl.text=Alias:
AddAliasDialog.aliasTextField.text=
AddAccountDialog.justificationTextField.text=
AddAccountDialog.justificationLbl.text=Justification:
AddAccountDialog.confidenceLbl.text=Confidence:
AddAccountDialog.typeLbl.text=Type:
AddAccountDialog.identiferLbl.text=Identifier:
AddAccountDialog.identifierTextField.text=
PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add
PersonaDetailsPanel.aliasesLabel.text=Aliases:
PersonaDetailsPanel.deleteMetadataBtn.text=Delete
PersonaDetailsPanel.addMetadataBtn.text=Add
PersonaDetailsPanel.metadataLabel.text=Metadata:
PersonaDetailsPanel.deleteAccountBtn.text=Delete
PersonaDetailsPanel.addAccountBtn.text=Add
PersonaDetailsPanel.accountsLbl.text=Accounts:
PersonaDetailsPanel.commentField.text=
PersonaDetailsPanel.commentLbl.text=Comment:
PersonaDetailsPanel.nameField.text=
PersonaDetailsPanel.nameLbl.text=Name:
PersonaDetailsPanel.examinerLbl.text=Created by:
PersonaDetailsPanel.examinerField.text=
PersonaDetailsPanel.creationDateLbl.text=Created on:
PersonaDetailsPanel.creationDateField.text=
PersonaAccountDialog.confidenceLbl.text=Confidence:
PersonaAccountDialog.justificationTextField.text=
PersonaAccountDialog.justificationLbl.text=Justification:
PersonaAccountDialog.typeLbl.text=Type:
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,25 +1,42 @@
AddAccountDialog.title.text=Add Account
AddAccountDialog_get_types_exception_msg=Failed to access central repository
AddAccountDialog_get_types_exception_Title=Central Repository failure
AddAccountDialog_validate_id_failure=Account ID must not be empty
AddAccountDialog_validate_id_failure_title=Account ID issue
CTL_OpenPersonaManager=Persona Manager
CTL_PersonaManagerTopComponentAction=Persona Manager
AddMetadataDialog.title.text=Add Metadata
AddMetadataDialog_dup_msg=A metadata entry with this name has already been added to this persona.
AddMetadataDialog_dup_Title=Metadata add failure
AddMetadataDialog_empty_name_msg=A metadata entry cannot have an empty name or value.
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_success_msg=Account added.
CreatePersonaAccountDialog_success_title=Account added
CTL_OpenPersonas=Personas
CTL_PersonasTopComponentAction=Personas
CTL_PersonaDetailsTopComponent=Persona Details
OpenPersonasAction.displayName=Persona Manager
PersonaDetailsPanel_load_exception_msg=Failed to load persona
PersonaDetailsPanel_load_exception_Title=Initialization failure
PersonaDetailsPanel_NameCreate=Create Persona
PersonaDetailsPanel_NameEdit=Edit Persona
PersonaDetailsPanel_NameView=View Persona
PersonaManagerTopComponent.createBtn.text=Create New
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=
OpenPersonasAction.displayName=Personas
PersonaAccountDialog.title.text=Add Account
PersonaAccountDialog_dup_msg=This account is already added to the persona.
PersonaAccountDialog_dup_Title=Account add failure
PersonaAccountDialog_get_types_exception_msg=Failed to access central repository.
PersonaAccountDialog_get_types_exception_Title=Central Repository failure
PersonaAccountDialog_identifier_empty_msg=The identifier field cannot be empty.
PersonaAccountDialog_identifier_empty_Title=Empty identifier
PersonaAccountDialog_search_empty_msg=Account not found for given identifier and type.
PersonaAccountDialog_search_empty_Title=Account not found
PersonaAccountDialog_search_failure_msg=Central Repository account search failed.
PersonaAccountDialog_search_failure_Title=Account add failure
PersonaAliasDialog.title.text=Add Alias
PersonaAliasDialog_dup_msg=This alias has already been added to this persona.
PersonaAliasDialog_dup_Title=Alias add failure
PersonaAliasDialog_empty_msg=An alias cannot be empty.
PersonaAliasDialog_empty_Title=Empty alias
PersonaDetailsDialog.cancelBtn.text=Cancel
PersonaDetailsDialog.okBtn.text=OK
PersonaDetailsDialogCreateTitle=Create Persona
PersonaDetailsDialogEditTitle=Edit Persona
PersonaDetailsDialogViewTitle=View Persona
PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add
PersonaDetailsPanel.aliasesLabel.text=Aliases:
PersonaDetailsPanel.deleteMetadataBtn.text=Delete
PersonaDetailsPanel.addMetadataBtn.text=Add
PersonaDetailsPanel.metadataLabel.text=Metadata:
@ -27,18 +44,90 @@ PersonaDetailsPanel.deleteAccountBtn.text=Delete
PersonaDetailsPanel.addAccountBtn.text=Add
PersonaDetailsPanel.accountsLbl.text=Accounts:
PersonaDetailsPanel.nameField.text=
PersonaDetailsPanel.saveBtn.toolTipText=
PersonaDetailsPanel.saveBtn.text=Save Changes
PersonaDetailsPanel.nameLbl.text=Name:
PersonaDetailsPanel.deleteCaseBtn.text=Delete
PersonaDetailsPanel.addCaseBtn.text=Add
AddAliasDialog.accountsLbl.text=Account:
AddAliasDialog.okBtn.text=OK
AddAliasDialog.cancelBtn.text=Cancel
PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add
PersonaDetailsPanel.aliasesLabel.text=Aliases:
AddAccountDialog.cancelBtn.text=Cancel
AddAccountDialog.okBtn.text=OK
PersonaManagerTopComponent.editBtn.text=Edit
PMTopComponent_Name=Persona Manager
PMTopComponent_search_exception_msg=Failed to search personas
PMTopComponent_search_exception_Title=Search failure
PersonaDetailsPanel.deleteMetadataBtn.text=Delete
PersonaDetailsPanel.addMetadataBtn.text=Add
PersonaDetailsPanel.metadataLabel.text=Metadata:
PersonaDetailsPanel.deleteAccountBtn.text=Delete
PersonaDetailsPanel.addAccountBtn.text=Add
PersonaDetailsPanel.accountsLbl.text=Accounts:
PersonaDetailsPanel.commentField.text=
PersonaDetailsPanel.commentLbl.text=Comment:
PersonaDetailsPanel.nameField.text=
PersonaDetailsPanel.nameLbl.text=Name:
PersonaDetailsPanel.examinerLbl.text=Created by:
PersonaDetailsPanel.examinerField.text=
PersonaDetailsPanel.creationDateLbl.text=Created on:
PersonaDetailsPanel.creationDateField.text=
PersonaAccountDialog.confidenceLbl.text=Confidence:
PersonaAccountDialog.justificationTextField.text=
PersonaAccountDialog.justificationLbl.text=Justification:
PersonaAccountDialog.typeLbl.text=Type:
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
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=Search failure

View File

@ -1,10 +1,12 @@
AddAccountDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
AddAccountDialog.okBtn.text=OK
PersonaDetailsDialog.okBtn.text=OK
PersonaDetailsDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
AddAliasDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
AddAliasDialog.okBtn.text=OK
AddMetadataDialog.okBtn.text=OK
AddMetadataDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
AddAliasDialog.cancelBtn.text_1=\u53d6\u308a\u6d88\u3057
AddAliasDialog.okBtn.text_1=OK
PersonaAccountDialog.okBtn.text=OK
PersonaAccountDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
PersonaAliasDialog.cancelBtn.text_1=\u53d6\u308a\u6d88\u3057
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,282 @@
/*
* 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;
/**
* 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.",
})
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_title(),
Bundle.CreatePersonaAccountDialog_error_msg(),
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.
*/
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.centralrepository.persona.OpenPersonaManagerAction")
@ActionRegistration(displayName = "#CTL_OpenPersonaManager", lazy = false)
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.centralrepository.persona.OpenPersonasAction")
@ActionRegistration(displayName = "#CTL_OpenPersonas", lazy = false)
@ActionReferences(value = {
@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 final JMenuItem menuItem;
public OpenPersonaManagerAction() {
public OpenPersonasAction() {
menuItem = super.getMenuPresenter();
this.setEnabled(CentralRepository.isEnabled());
}
@ -54,7 +54,7 @@ public final class OpenPersonaManagerAction extends CallableSystemAction {
@Override
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void performAction() {
final TopComponent topComponent = WindowManager.getDefault().findTopComponent("PersonaManagerTopComponent");
final TopComponent topComponent = WindowManager.getDefault().findTopComponent("PersonasTopComponent");
if (topComponent != null) {
if (topComponent.isOpened() == false) {
topComponent.open();
@ -65,7 +65,7 @@ public final class OpenPersonaManagerAction extends CallableSystemAction {
}
@Override
@NbBundle.Messages("OpenPersonasAction.displayName=Persona Manager")
@NbBundle.Messages("OpenPersonasAction.displayName=Personas")
public String getName() {
return Bundle.OpenPersonasAction_displayName();
}

View File

@ -25,24 +25,21 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" 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"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelBtn" linkSize="2" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<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 max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="okBtn" 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">
<Component id="confidenceLbl" min="-2" 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 type="102" alignment="0" attributes="0">
<Component id="justificationLbl" min="-2" 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>
<EmptySpace max="-2" attributes="0"/>
@ -101,21 +98,21 @@
<Component id="identiferLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="identifierTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<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 type="unrelated" 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"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="confidenceLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="confidenceComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
</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"/>
</Group>
</Group>
@ -125,14 +122,14 @@
<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="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>
</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="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>
</Properties>
<Events>
@ -142,7 +139,7 @@
<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="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>
</Properties>
</Component>
@ -156,24 +153,10 @@
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType&gt;"/>
</AuxValues>
</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">
<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.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>
</Properties>
</Component>
@ -187,12 +170,26 @@
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence&gt;"/>
</AuxValues>
</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>
</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="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 name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[79, 23]"/>
@ -211,7 +208,7 @@
<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="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>
</Properties>
<Events>

View File

@ -23,11 +23,12 @@ import java.io.Serializable;
import java.util.Collection;
import java.util.logging.Level;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
@ -41,32 +42,55 @@ import org.sleuthkit.autopsy.coreutils.Logger;
* Configuration dialog for adding an account to a persona.
*/
@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 final TypeChoiceRenderer TYPE_CHOICE_RENDERER = new TypeChoiceRenderer();
private final PersonaDetailsPanel pdp;
private PersonaDetailsPanel.PAccount currentAccount = null;
/**
* Creates new add account dialog
*/
@Messages({"AddAccountDialog.title.text=Add Account",})
public AddAccountDialog(PersonaDetailsPanel pdp) {
super((JFrame) WindowManager.getDefault().getMainWindow(),
Bundle.AddAccountDialog_title_text(),
false);
@Messages({"PersonaAccountDialog.title.text=Add Account",})
public PersonaAccountDialog(PersonaDetailsPanel pdp) {
super(SwingUtilities.windowForComponent(pdp),
Bundle.PersonaAccountDialog_title_text(),
ModalityType.APPLICATION_MODAL);
this.pdp = pdp;
initComponents();
typeComboBox.setRenderer(TYPE_CHOICE_RENDERER);
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 {
@ -80,11 +104,10 @@ public class AddAccountDialog extends JDialog {
return this;
}
}
@Messages({
"AddAccountDialog_get_types_exception_Title=Central Repository failure",
"AddAccountDialog_get_types_exception_msg=Failed to access central repository",
})
"PersonaAccountDialog_get_types_exception_Title=Central Repository failure",
"PersonaAccountDialog_get_types_exception_msg=Failed to access central repository.",})
private CentralRepoAccountType[] getAllAccountTypes() {
Collection<CentralRepoAccountType> allAccountTypes;
try {
@ -92,9 +115,9 @@ public class AddAccountDialog extends JDialog {
} catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to access central repository", e);
JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_get_types_exception_Title(),
Bundle.AddAccountDialog_get_types_exception_msg(),
JOptionPane.ERROR_MESSAGE);
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]);
@ -114,10 +137,10 @@ public class AddAccountDialog extends JDialog {
identifierTextField = new javax.swing.JTextField();
typeLbl = new javax.swing.JLabel();
typeComboBox = new javax.swing.JComboBox<>();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
confidenceLbl = new javax.swing.JLabel();
confidenceComboBox = new javax.swing.JComboBox<>();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
cancelBtn = new javax.swing.JButton();
okBtn = new javax.swing.JButton();
@ -126,27 +149,27 @@ public class AddAccountDialog extends JDialog {
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() {
public void actionPerformed(java.awt.event.ActionEvent 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()));
org.openide.awt.Mnemonics.setLocalizedText(justificationLbl, org.openide.util.NbBundle.getMessage(AddAccountDialog.class, "AddAccountDialog.justificationLbl.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
org.openide.awt.Mnemonics.setLocalizedText(confidenceLbl, org.openide.util.NbBundle.getMessage(PersonaAccountDialog.class, "PersonaAccountDialog.confidenceLbl.text")); // NOI18N
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);
settingsPanel.setLayout(settingsPanelLayout);
settingsPanelLayout.setHorizontalGroup(
@ -165,11 +188,11 @@ public class AddAccountDialog extends JDialog {
.addGroup(settingsPanelLayout.createSequentialGroup()
.addComponent(confidenceLbl)
.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()
.addComponent(justificationLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(justificationTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 264, Short.MAX_VALUE)))
.addComponent(justificationTextField)))
.addContainerGap())
);
settingsPanelLayout.setVerticalGroup(
@ -179,22 +202,22 @@ public class AddAccountDialog extends JDialog {
.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.UNRELATED)
.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))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.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)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(confidenceLbl)
.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))
);
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.setMinimumSize(new java.awt.Dimension(79, 23));
cancelBtn.setPreferredSize(new java.awt.Dimension(79, 23));
@ -204,7 +227,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() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
okBtnActionPerformed(evt);
@ -216,14 +239,12 @@ public class AddAccountDialog extends JDialog {
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.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)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelBtn, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.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});
@ -231,8 +252,9 @@ public class AddAccountDialog extends JDialog {
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, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.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))
@ -248,19 +270,26 @@ public class AddAccountDialog extends JDialog {
}
@Messages({
"AddAccountDialog_dup_Title=Account add failure",
"AddAccountDialog_dup_msg=This account is already added to the persona",
"AddAccountDialog_empty_Title=Empty identifier",
"AddAccountDialog_empty_msg=The identifier field cannot be empty",
"AddAccountDialog_search_failure_Title=Account add failure",
"AddAccountDialog_search_failure_msg=Central Repository account search failed",
"AddAccountDialog_search_empty_Title=Account not found",
"AddAccountDialog_search_empty_msg=Account not found for given identifier and type",})
"PersonaAccountDialog_dup_Title=Account add failure",
"PersonaAccountDialog_dup_msg=This account is already added to the persona.",
"PersonaAccountDialog_identifier_empty_Title=Empty identifier",
"PersonaAccountDialog_identifier_empty_msg=The identifier field cannot be empty.",
"PersonaAccountDialog_search_failure_Title=Account add failure",
"PersonaAccountDialog_search_failure_msg=Central Repository account search failed.",
"PersonaAccountDialog_search_empty_Title=Account not found",
"PersonaAccountDialog_search_empty_msg=Account not found for given identifier and type.",})
private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed
if (identifierTextField.getText().isEmpty()) {
if (StringUtils.isBlank(identifierTextField.getText())) {
JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_empty_msg(),
Bundle.AddAccountDialog_empty_Title(),
Bundle.PersonaAccountDialog_identifier_empty_msg(),
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);
return;
}
@ -270,15 +299,15 @@ public class AddAccountDialog extends JDialog {
} catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to access central repository", e);
JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_search_failure_msg(),
Bundle.AddAccountDialog_search_failure_Title(),
Bundle.PersonaAccountDialog_search_failure_msg(),
Bundle.PersonaAccountDialog_search_failure_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
if (candidates.isEmpty()) {
JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_search_empty_msg(),
Bundle.AddAccountDialog_search_empty_Title(),
Bundle.PersonaAccountDialog_search_empty_msg(),
Bundle.PersonaAccountDialog_search_empty_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
@ -292,22 +321,28 @@ public class AddAccountDialog extends JDialog {
}
if (result == null) {
JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_search_empty_msg(),
Bundle.AddAccountDialog_search_empty_Title(),
Bundle.PersonaAccountDialog_search_empty_msg(),
Bundle.PersonaAccountDialog_search_empty_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
if (pdp.addAccount(
result,
justificationTextField.getText(),
(Persona.Confidence) confidenceComboBox.getSelectedItem())) {
Persona.Confidence confidence = (Persona.Confidence) confidenceComboBox.getSelectedItem();
String justification = justificationTextField.getText();
if (currentAccount != null) {
currentAccount.confidence = confidence;
currentAccount.justification = justification;
dispose();
} else {
JOptionPane.showMessageDialog(this,
Bundle.AddAccountDialog_dup_msg(),
Bundle.AddAccountDialog_dup_Title(),
JOptionPane.ERROR_MESSAGE);
if (pdp.addAccount(result, justification, confidence)) {
dispose();
} else {
JOptionPane.showMessageDialog(this,
Bundle.PersonaAccountDialog_dup_msg(),
Bundle.PersonaAccountDialog_dup_Title(),
JOptionPane.ERROR_MESSAGE);
}
}
}//GEN-LAST:event_okBtnActionPerformed
@ -318,7 +353,7 @@ public class AddAccountDialog extends JDialog {
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.JComboBox<org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence> confidenceComboBox;

View File

@ -38,7 +38,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" 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">
<Component id="okBtn" 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="aliasTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" 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"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="confidenceComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="confidenceLbl" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<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>
</DimensionLayout>
@ -111,35 +111,21 @@
<Component class="javax.swing.JLabel" name="aliasLbl">
<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.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>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="aliasTextField">
<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.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;)"/>
<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="confidenceLbl">
<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.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>
</Properties>
</Component>
@ -153,12 +139,26 @@
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence&gt;"/>
</AuxValues>
</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>
</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="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 name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[79, 23]"/>
@ -177,7 +177,7 @@
<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="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>
</Properties>
<Events>

View File

@ -19,8 +19,9 @@
package org.sleuthkit.autopsy.centralrepository.persona;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
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.
*/
@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 final PersonaDetailsPanel pdp;
private PersonaDetailsPanel.PAlias currentAlias = null;
/**
* Creates new add alias dialog
*/
@Messages({"AddAliasDialog.title.text=Add Alias",})
public AddAliasDialog(PersonaDetailsPanel pdp) {
super((JFrame) WindowManager.getDefault().getMainWindow(),
Bundle.AddAliasDialog_title_text(),
false);
@Messages({"PersonaAliasDialog.title.text=Add Alias",})
public PersonaAliasDialog(PersonaDetailsPanel pdp) {
super(SwingUtilities.windowForComponent(pdp),
Bundle.PersonaAliasDialog_title_text(),
ModalityType.APPLICATION_MODAL);
this.pdp = pdp;
initComponents();
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.
* 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();
aliasLbl = new javax.swing.JLabel();
aliasTextField = new javax.swing.JTextField();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
confidenceLbl = new javax.swing.JLabel();
confidenceComboBox = new javax.swing.JComboBox<>();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
cancelBtn = new javax.swing.JButton();
okBtn = new javax.swing.JButton();
@ -73,18 +93,18 @@ public class AddAliasDialog extends JDialog {
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
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
org.openide.awt.Mnemonics.setLocalizedText(confidenceLbl, org.openide.util.NbBundle.getMessage(PersonaAliasDialog.class, "PersonaAliasDialog.confidenceLbl.text")); // NOI18N
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);
settingsPanel.setLayout(settingsPanelLayout);
settingsPanelLayout.setHorizontalGroup(
@ -114,17 +134,17 @@ public class AddAliasDialog extends JDialog {
.addComponent(aliasLbl)
.addComponent(aliasTextField, 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(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)
.addComponent(confidenceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.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.setMinimumSize(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() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
okBtnActionPerformed(evt);
@ -176,19 +196,42 @@ public class AddAliasDialog extends JDialog {
}
@Messages({
"AddAliasDialog_dup_Title=Alias add failure",
"AddAliasDialog_dup_msg=This alias has already been added to this persona",})
"PersonaAliasDialog_empty_Title=Empty alias",
"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
if (pdp.addAlias(
aliasTextField.getText(),
justificationTextField.getText(),
(Persona.Confidence) confidenceComboBox.getSelectedItem())) {
if (StringUtils.isBlank(aliasTextField.getText())) {
JOptionPane.showMessageDialog(this,
Bundle.PersonaAliasDialog_empty_msg(),
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 {
JOptionPane.showMessageDialog(this,
Bundle.AddAliasDialog_dup_msg(),
Bundle.AddAliasDialog_dup_Title(),
if (pdp.addAlias(aliasTextField.getText(), justification, confidence)) {
dispose();
} else {
JOptionPane.showMessageDialog(this,
Bundle.PersonaAliasDialog_dup_msg(),
Bundle.PersonaAliasDialog_dup_Title(),
JOptionPane.ERROR_MESSAGE);
}
}
}//GEN-LAST:event_okBtnActionPerformed

View File

@ -19,11 +19,13 @@
package org.sleuthkit.autopsy.centralrepository.persona;
import java.awt.Component;
import java.util.logging.Level;
import javax.swing.JDialog;
import javax.swing.JFrame;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Configuration dialog for editing or creating a persona
@ -32,25 +34,46 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
public class PersonaDetailsDialog extends JDialog {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(PersonaDetailsDialog.class.getName());
private final PersonaDetailsDialogCallback callback;
@NbBundle.Messages({
"PersonaDetailsDialogCreateTitle=Create Persona",
"PersonaDetailsDialogEditTitle=Edit Persona",})
PersonaDetailsDialog(Component parent, PersonaDetailsMode mode, Persona persona, PersonaDetailsDialogCallback callback) {
"PersonaDetailsDialogEditTitle=Edit Persona",
"PersonaDetailsDialogViewTitle=View Persona",})
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(),
mode == PersonaDetailsMode.CREATE ?
Bundle.PersonaDetailsDialogCreateTitle() :
Bundle.PersonaDetailsDialogEditTitle(),
false);
getTitle(mode),
true);
this.callback = callback;
initComponents();
pdp.setMode(parent, mode, persona);
display();
if (displayDialog) {
display();
}
}
private static String getTitle(PersonaDetailsMode mode) {
switch (mode) {
case CREATE:
return Bundle.PersonaDetailsDialogCreateTitle();
case EDIT:
return Bundle.PersonaDetailsDialogEditTitle();
case VIEW:
return Bundle.PersonaDetailsDialogViewTitle();
default:
logger.log(Level.WARNING, "Unsupported mode: {0}", mode);
return Bundle.PersonaDetailsDialogViewTitle();
}
}
/**
@ -119,7 +142,7 @@ public class PersonaDetailsDialog extends JDialog {
pack();
}// </editor-fold>//GEN-END:initComponents
private void display() {
public final void display() {
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setVisible(true);
}
@ -135,6 +158,10 @@ public class PersonaDetailsDialog extends JDialog {
private void cancelBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelBtnActionPerformed
dispose();
}//GEN-LAST:event_cancelBtnActionPerformed
public PersonaDetailsPanel getDetailsPanel() {
return this.pdp;
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton cancelBtn;

View File

@ -37,17 +37,20 @@
<Layout>
<DimensionLayout dim="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">
<Component id="detailsPanel" alignment="0" max="32767" attributes="0"/>
<EmptySpace min="0" pref="617" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<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">
<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>
</DimensionLayout>
@ -61,12 +64,7 @@
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="accountsTablePane" alignment="1" pref="549" 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="accountsTablePane" alignment="1" pref="605" max="32767" attributes="0"/>
<Component id="accountsLbl" alignment="0" max="32767" attributes="0"/>
<Component id="metadataLabel" alignment="0" 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="casesLbl" alignment="0" 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="103" groupAlignment="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"/>
<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"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="addMetadataBtn" min="-2" 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"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="addAliasBtn" min="-2" 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"/>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</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>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -107,7 +125,19 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="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"/>
<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">
<Component id="nameLbl" 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">
<Component id="addAccountBtn" 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>
<EmptySpace max="-2" attributes="0"/>
<Component id="metadataLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="metadataTablePane" min="-2" pref="66" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="deleteMetadataBtn" min="-2" max="-2" attributes="0"/>
<Component id="addMetadataBtn" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="addMetadataBtn" alignment="3" 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>
<EmptySpace max="-2" attributes="0"/>
<Component id="aliasesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="aliasesTablePane" min="-2" pref="74" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="deleteAliasBtn" min="-2" max="-2" attributes="0"/>
<Component id="addAliasBtn" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="addAliasBtn" alignment="3" 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>
<EmptySpace max="-2" attributes="0"/>
<Component id="casesLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="casesTablePane" min="-2" pref="63" max="-2" attributes="0"/>
<EmptySpace 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"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<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">
<Properties>
<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;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="nameFieldActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="accountsLbl">
<Properties>
@ -203,6 +273,14 @@
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</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">
<Properties>
<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"/>
</Properties>
</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">
<Properties>
<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"/>
</Properties>
</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">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
@ -312,22 +406,6 @@
</Component>
</SubComponents>
</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>
</Container>
</SubComponents>

View File

@ -20,22 +20,28 @@ package org.sleuthkit.autopsy.centralrepository.persona;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.DefaultTableModel;
import org.apache.commons.lang.StringUtils;
import org.openide.windows.TopComponent;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.RetainLocation;
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.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
@ -57,14 +63,21 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(PersonaDetailsPanel.class.getName());
private PersonaDetailsMode mode;
// Not-yet-created
private final List<PAccount> accountsToAdd = new ArrayList<>();
private final List<PMetadata> metadataToAdd = new ArrayList<>();
private final List<PAlias> aliasesToAdd = new ArrayList<>();
private final List<PersonaAccount> accountsToRemove = new ArrayList<>();
private final List<PersonaMetadata> metadataToRemove = 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 List<CentralRepoAccount> currentAccounts = new ArrayList<>();
private List<PersonaAccount> currentAccounts = new ArrayList<>();
private List<PersonaMetadata> currentMetadata = new ArrayList<>();
private List<PersonaAlias> currentAliases = new ArrayList<>();
private List<CorrelationCase> currentCases = new ArrayList<>();
@ -75,16 +88,28 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
private PersonaDetailsTableModel casesModel;
@Messages({
"PersonaDetailsPanel_NameEdit=Edit Persona",
"PersonaDetailsPanel_NameCreate=Create Persona",
"PersonaDetailsPanel_NameView=View Persona",})
"PersonaDetailsPanel_empty_justification_Title=Empty justification",
"PersonaDetailsPanel_empty_justification_msg=The justification field cannot be empty",})
public PersonaDetailsPanel() {
initComponents();
clear();
// Accounts
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) -> {
int selectedRow = accountsTable.getSelectedRow();
@ -93,19 +118,35 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
if (selectedRow >= currentAccounts.size()) {
accountsToAdd.remove(selectedRow - currentAccounts.size());
} else {
currentAccounts.remove(selectedRow);
PersonaAccount toRemove = currentAccounts.get(selectedRow);
accountsToEdit.remove(toRemove);
accountsToRemove.add(toRemove);
currentAccounts.remove(toRemove);
}
updateAccountsTable();
}
});
accountsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
accountsTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
handleSelectionChange(e, deleteAccountBtn, accountsTable);
handleSelectionChange(e, editAccountBtn, deleteAccountBtn, accountsTable);
});
// Metadata
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) -> {
int selectedRow = metadataTable.getSelectedRow();
@ -114,19 +155,35 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
if (selectedRow >= currentMetadata.size()) {
metadataToAdd.remove(selectedRow - currentMetadata.size());
} else {
currentMetadata.remove(selectedRow);
PersonaMetadata toRemove = currentMetadata.get(selectedRow);
metadataToEdit.remove(toRemove);
metadataToRemove.add(toRemove);
currentMetadata.remove(toRemove);
}
updateMetadataTable();
}
});
metadataTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
metadataTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
handleSelectionChange(e, deleteMetadataBtn, metadataTable);
handleSelectionChange(e, editMetadataBtn, deleteMetadataBtn, metadataTable);
});
// Aliases
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) -> {
int selectedRow = aliasesTable.getSelectedRow();
@ -135,32 +192,48 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
if (selectedRow >= currentAliases.size()) {
aliasesToAdd.remove(selectedRow - currentAliases.size());
} else {
currentAliases.remove(selectedRow);
PersonaAlias toRemove = currentAliases.get(selectedRow);
aliasesToEdit.remove(toRemove);
aliasesToRemove.add(toRemove);
currentAliases.remove(toRemove);
}
updateAliasesTable();
}
});
aliasesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
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()) {
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
*/
private class PAccount {
class PAccount {
private final CentralRepoAccount account;
private final String justification;
private final Persona.Confidence confidence;
CentralRepoAccount account;
String justification;
Persona.Confidence confidence;
PAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) {
this.account = account;
@ -168,10 +241,10 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
this.confidence = confidence;
}
}
boolean accountExists(CentralRepoAccount account) {
for (CentralRepoAccount acc : currentAccounts) {
if (acc.getId() == account.getId()) {
for (PersonaAccount acc : currentAccounts) {
if (acc.getAccount().getId() == account.getId()) {
return true;
}
}
@ -183,7 +256,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
return false;
}
boolean addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) {
public boolean addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) {
if (!accountExists(account)) {
accountsToAdd.add(new PAccount(account, justification, confidence));
updateAccountsTable();
@ -192,19 +265,15 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
return false;
}
Collection<CentralRepoAccount> getCurrentAccounts() {
return currentAccounts;
}
/**
* A data bucket class for yet-to-be-created PersonaMetadata
*/
private class PMetadata {
class PMetadata {
private final String name;
private final String value;
private final String justification;
private final Persona.Confidence confidence;
String name;
String value;
String justification;
Persona.Confidence confidence;
PMetadata(String name, String value, String justification, Persona.Confidence confidence) {
this.name = name;
@ -213,7 +282,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
this.confidence = confidence;
}
}
boolean metadataExists(String name) {
for (PersonaMetadata pm : currentMetadata) {
if (pm.getName().equals(name)) {
@ -240,11 +309,11 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
/**
* A data bucket class for yet-to-be-created PersonaAlias
*/
private class PAlias {
class PAlias {
private final String alias;
private final String justification;
private final Persona.Confidence confidence;
String alias;
String justification;
Persona.Confidence confidence;
PAlias(String alias, String justification, Persona.Confidence confidence) {
this.alias = alias;
@ -252,7 +321,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
this.confidence = confidence;
}
}
boolean aliasExists(String alias) {
for (PersonaAlias pa : currentAliases) {
if (pa.getAlias().equals(alias)) {
@ -285,38 +354,55 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
private void initComponents() {
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();
nameField = new javax.swing.JTextField();
accountsLbl = new javax.swing.JLabel();
accountsTablePane = new javax.swing.JScrollPane();
accountsTable = new javax.swing.JTable();
addAccountBtn = new javax.swing.JButton();
editAccountBtn = new javax.swing.JButton();
deleteAccountBtn = new javax.swing.JButton();
metadataLabel = new javax.swing.JLabel();
metadataTablePane = new javax.swing.JScrollPane();
metadataTable = new javax.swing.JTable();
addMetadataBtn = new javax.swing.JButton();
editMetadataBtn = new javax.swing.JButton();
deleteMetadataBtn = new javax.swing.JButton();
aliasesLabel = new javax.swing.JLabel();
aliasesTablePane = new javax.swing.JScrollPane();
aliasesTable = new javax.swing.JTable();
addAliasBtn = new javax.swing.JButton();
editAliasBtn = new javax.swing.JButton();
deleteAliasBtn = new javax.swing.JButton();
casesLbl = new javax.swing.JLabel();
casesTablePane = new javax.swing.JScrollPane();
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
nameField.setEditable(false);
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
@ -336,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
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
deleteAccountBtn.setEnabled(false);
@ -357,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
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
deleteMetadataBtn.setEnabled(false);
@ -378,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
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
deleteAliasBtn.setEnabled(false);
@ -396,12 +491,6 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
));
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);
detailsPanel.setLayout(detailsPanelLayout);
detailsPanelLayout.setHorizontalGroup(
@ -409,11 +498,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
.addGroup(detailsPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(accountsTablePane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 549, Short.MAX_VALUE)
.addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(nameLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(nameField))
.addComponent(accountsTablePane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 605, 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(metadataTablePane, javax.swing.GroupLayout.Alignment.TRAILING)
@ -421,31 +506,59 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
.addComponent(aliasesTablePane)
.addComponent(casesLbl, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.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.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(addCaseBtn)
.addGap(18, 18, 18)
.addComponent(deleteCaseBtn))
.addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(addAccountBtn)
.addGap(18, 18, 18)
.addComponent(editAccountBtn)
.addGap(18, 18, 18)
.addComponent(deleteAccountBtn))
.addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(addMetadataBtn)
.addGap(18, 18, 18)
.addComponent(editMetadataBtn)
.addGap(18, 18, 18)
.addComponent(deleteMetadataBtn))
.addGroup(detailsPanelLayout.createSequentialGroup()
.addComponent(addAliasBtn)
.addGap(18, 18, 18)
.addComponent(editAliasBtn)
.addGap(18, 18, 18)
.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())
);
detailsPanelLayout.setVerticalGroup(
detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.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)
.addComponent(nameLbl)
.addComponent(nameField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
@ -456,61 +569,57 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(addAccountBtn)
.addComponent(deleteAccountBtn))
.addComponent(deleteAccountBtn)
.addComponent(editAccountBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(metadataLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(metadataTablePane, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE)
.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(addMetadataBtn))
.addComponent(editMetadataBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(aliasesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(aliasesTablePane, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE)
.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(addAliasBtn))
.addComponent(editAliasBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(casesLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(casesTablePane, javax.swing.GroupLayout.PREFERRED_SIZE, 63, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(addCaseBtn)
.addComponent(deleteCaseBtn))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
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)
.addComponent(detailsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(0, 617, Short.MAX_VALUE))
);
layout.setVerticalGroup(
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)
.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
private void nameFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nameFieldActionPerformed
}//GEN-LAST:event_nameFieldActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel accountsLbl;
private javax.swing.JTable accountsTable;
private javax.swing.JScrollPane accountsTablePane;
private javax.swing.JButton addAccountBtn;
private javax.swing.JButton addAliasBtn;
private javax.swing.JButton addCaseBtn;
private javax.swing.JButton addMetadataBtn;
private javax.swing.JLabel aliasesLabel;
private javax.swing.JTable aliasesTable;
@ -518,11 +627,19 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
private javax.swing.JLabel casesLbl;
private javax.swing.JTable casesTable;
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 deleteAliasBtn;
private javax.swing.JButton deleteCaseBtn;
private javax.swing.JButton deleteMetadataBtn;
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.JTable metadataTable;
private javax.swing.JScrollPane metadataTablePane;
@ -532,17 +649,26 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
@Messages({
"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) {
String examiner;
String creationDate;
String comment;
String name;
Collection<CentralRepoAccount> accounts;
Collection<PersonaAccount> accounts;
Collection<PersonaMetadata> metadata;
Collection<PersonaAlias> aliases;
Collection<CorrelationCase> cases;
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();
accounts = persona.getPersonaAccounts().stream().map(PersonaAccount::getAccount)
.collect(Collectors.toList());
accounts = persona.getPersonaAccounts();
metadata = persona.getMetadata();
aliases = persona.getAliases();
cases = persona.getCases();
@ -555,6 +681,9 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
return;
}
this.currentPersona = persona;
this.examinerField.setText(examiner);
this.creationDateField.setText(creationDate);
this.commentField.setText(comment);
this.nameField.setText(name);
this.currentAccounts.addAll(accounts);
this.currentMetadata.addAll(metadata);
@ -564,7 +693,10 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
void clear() {
currentPersona = null;
nameField.setText(Persona.getDefaultName());
examinerField.setText("");
creationDateField.setText("");
commentField.setText("");
nameField.setText(mode == PersonaDetailsMode.CREATE ? Persona.getDefaultName() : "");
currentAccounts = new ArrayList<>();
currentMetadata = new ArrayList<>();
currentAliases = new ArrayList<>();
@ -573,17 +705,19 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
metadataToAdd.clear();
aliasesToAdd.clear();
nameField.setEditable(false);
commentField.setEditable(false);
initializeFields();
addAccountBtn.setEnabled(false);
addMetadataBtn.setEnabled(false);
addAliasBtn.setEnabled(false);
addCaseBtn.setEnabled(false);
deleteAccountBtn.setEnabled(false);
deleteMetadataBtn.setEnabled(false);
deleteAliasBtn.setEnabled(false);
deleteCaseBtn.setEnabled(false);
editAccountBtn.setEnabled(false);
editMetadataBtn.setEnabled(false);
editAliasBtn.setEnabled(false);
}
/**
@ -606,10 +740,10 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
private void updateAccountsTable() {
Object[][] rows = new Object[currentAccounts.size() + accountsToAdd.size()][2];
int i = 0;
for (CentralRepoAccount acc : currentAccounts) {
for (PersonaAccount acc : currentAccounts) {
rows[i] = new Object[]{
acc.getAccountType().getAcctType().getDisplayName(),
acc.getIdentifier()
acc.getAccount().getAccountType().getAcctType().getDisplayName(),
acc.getAccount().getIdentifier()
};
i++;
}
@ -681,15 +815,37 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
casesTable.setModel(casesModel);
}
void enableEditUIComponents() {
nameField.setEditable(true);
addAccountBtn.setEnabled(true);
addMetadataBtn.setEnabled(true);
addAliasBtn.setEnabled(true);
//addCaseBtn.setEnabled(true); //todo
void configureEditComponents(boolean enabled) {
commentField.setEditable(enabled);
nameField.setEditable(enabled);
addAccountBtn.setEnabled(enabled);
addMetadataBtn.setEnabled(enabled);
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() {
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();
updateMetadataTable();
updateAliasesTable();
@ -701,14 +857,15 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
this.mode = mode;
switch (mode) {
case CREATE:
enableEditUIComponents();
configureEditComponents(true);
break;
case EDIT:
loadPersona(parent, persona);
enableEditUIComponents();
configureEditComponents(true);
break;
case VIEW:
loadPersona(parent, persona);
configureEditComponents(false);
break;
default:
logger.log(Level.WARNING, "Unsupported mode: {0}", mode);
@ -716,39 +873,50 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
}
initializeFields();
}
@Messages({
"PersonaDetailsPanel_NotEnoughAccounts_msg=Two or more accounts are necessary to create a persona",
"PersonaDetailsPanel_NotEnoughAccounts_Title=Not enough accounts",
"PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository",
@Messages({
"PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs at least one account.",
"PersonaDetailsPanel_NotEnoughAccounts_Title=Missing account",
"PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository.",
"PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure",
"PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty",
"PersonaDetailsPanel_EmptyName_Title=Empty persona name",})
"PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty.",
"PersonaDetailsPanel_EmptyName_Title=Empty persona name",
"PersonaDetailsPanel_EmptyComment_msg=Persona comment cannot be empty.",
"PersonaDetailsPanel_EmptyComment_Title=Empty persona comment",})
Persona okHandler() {
if (accountsToAdd.size() + currentAccounts.size() < 1) {
JOptionPane.showMessageDialog(this,
Bundle.PersonaDetailsPanel_NotEnoughAccounts_msg(),
Bundle.PersonaDetailsPanel_NotEnoughAccounts_Title(),
JOptionPane.ERROR_MESSAGE);
return null;
}
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,
Bundle.PersonaDetailsPanel_EmptyName_msg(),
Bundle.PersonaDetailsPanel_EmptyName_Title(),
JOptionPane.ERROR_MESSAGE);
return null;
}
Persona ret = null;
switch (mode) {
case CREATE:
if (accountsToAdd.size() < 2) {
JOptionPane.showMessageDialog(this,
Bundle.PersonaDetailsPanel_NotEnoughAccounts_msg(),
Bundle.PersonaDetailsPanel_NotEnoughAccounts_Title(),
JOptionPane.ERROR_MESSAGE);
break;
}
if (nameField.getText().isEmpty()) {
JOptionPane.showMessageDialog(this,
Bundle.PersonaDetailsPanel_EmptyName_msg(),
Bundle.PersonaDetailsPanel_EmptyName_Title(),
JOptionPane.ERROR_MESSAGE);
break;
}
try {
PAccount firstAccount = accountsToAdd.get(0);
ret = Persona.createPersonaForAccount(nameField.getText(),
"", Persona.PersonaStatus.ACTIVE, firstAccount.account,
commentField.getText(), Persona.PersonaStatus.ACTIVE, firstAccount.account,
firstAccount.justification, firstAccount.confidence);
for (int i = 1; i < accountsToAdd.size(); i++) {
ret.addAccountToPersona(accountsToAdd.get(i).account,
ret.addAccount(accountsToAdd.get(i).account,
accountsToAdd.get(i).justification,
accountsToAdd.get(i).confidence);
}
@ -761,18 +929,69 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel {
} 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);
Bundle.PersonaDetailsPanel_CentralRepoErr_msg(),
Bundle.PersonaDetailsPanel_CentralRepoErr_Title(),
JOptionPane.ERROR_MESSAGE);
break;
}
break;
case EDIT:
// todo implement
try {
ret = currentPersona;
currentPersona.setComment(commentField.getText());
currentPersona.setName(nameField.getText());
for (PAccount acc : accountsToAdd) {
ret.addAccount(acc.account, acc.justification, acc.confidence);
}
for (PersonaAccount acc : accountsToRemove) {
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) {
ret.addMetadata(md.name, md.value, md.justification, md.confidence);
}
for (PersonaMetadata md : metadataToRemove) {
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) {
ret.addAlias(pa.alias, pa.justification, pa.confidence);
}
for (PersonaAlias pa : aliasesToRemove) {
ret.removeAlias(pa);
}
for (HashMap.Entry<PersonaAlias, PAlias> entry : aliasesToEdit.entrySet()) {
ret.modifyAlias(entry.getKey(), entry.getValue().confidence, entry.getValue().justification);
}
} 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);
break;
}
break;
case VIEW:
ret = currentPersona;
break;
default:
logger.log(Level.SEVERE, "Unsupported mode: {0}", mode);
}
return ret;
}
/**
* Sets the persona name field.
*
* @param name Persona name.
*/
public void setPersonaName(String name) {
nameField.setText(name);
}
}

View File

@ -1,347 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.centralrepository.persona;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.RetainLocation;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Top component for the Personas tool
*
*/
@TopComponent.Description(preferredID = "PersonaManagerTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "personamanager", openAtStartup = false)
@RetainLocation("personamanager")
@SuppressWarnings("PMD.SingularField")
public final class PersonaManagerTopComponent extends TopComponent {
private static final Logger logger = Logger.getLogger(PersonaManagerTopComponent.class.getName());
private List<Persona> currentResults = null;
private Persona selectedPersona = null;
@Messages({
"PMTopComponent_Name=Persona Manager"
})
public PersonaManagerTopComponent() {
initComponents();
setName(Bundle.PMTopComponent_Name());
executeSearch();
searchBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
executeSearch();
}
});
editBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new PersonaDetailsDialog(PersonaManagerTopComponent.this,
PersonaDetailsMode.EDIT, selectedPersona, new EditCallbackImpl());
}
});
createBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new PersonaDetailsDialog(PersonaManagerTopComponent.this,
PersonaDetailsMode.CREATE, selectedPersona, new CreateCallbackImpl());
}
});
// Results table
resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
resultsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
handleSelectionChange(e);
}
});
}
/**
* Callback method for the create mode of the PersonaDetailsDialog
*/
class CreateCallbackImpl implements PersonaDetailsDialogCallback {
@Override
public void callback(Persona persona) {
if (persona != null) {
searchField.setText("");
executeSearch();
int newPersonaRow = currentResults.size() - 1;
resultsTable.getSelectionModel().setSelectionInterval(newPersonaRow, newPersonaRow);
handleSelectionChange();
}
createBtn.setEnabled(true);
}
}
/**
* Callback method for the edit mode of the PersonaDetailsDialog
*/
class EditCallbackImpl implements PersonaDetailsDialogCallback {
@Override
public void callback(Persona persona) {
createBtn.setEnabled(true);
}
}
void setPersona(int index) {
Persona persona = currentResults.get(index);
selectedPersona = persona;
//editBtn.setEnabled(true); todo uncomment when we add edit support
}
/**
* Table model for the persona search results
*/
final class PersonaFilterTableModel extends DefaultTableModel {
private static final long serialVersionUID = 1L;
PersonaFilterTableModel(Object[][] rows, String[] colNames) {
super(rows, colNames);
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
}
private void handleSelectionChange(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
handleSelectionChange();
}
private void handleSelectionChange() {
int selectedRow = resultsTable.getSelectedRow();
if (selectedRow != -1) {
setPersona(resultsTable.getSelectedRow());
detailsPanel.setMode(this, PersonaDetailsMode.VIEW, selectedPersona);
}
}
private void updateResultsTable(Collection<Persona> results) {
Object[][] rows = new Object[results.size()][2];
int i = 0;
for (Persona result : results) {
rows[i] = new Object[]{result.getId(), result.getName()};
i++;
}
PersonaFilterTableModel updatedTableModel = new PersonaFilterTableModel(
rows,
new String[]{"ID", "Name"}
);
resultsTable.setModel(updatedTableModel);
currentResults = new ArrayList<>(results);
// Formatting
resultsTable.getColumnModel().getColumn(0).setMaxWidth(100);
resultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
}
@Messages({
"PMTopComponent_search_exception_Title=Search failure",
"PMTopComponent_search_exception_msg=Failed to search personas",})
private void executeSearch() {
Collection<Persona> results;
try {
results = Persona.getPersonaByName(searchField.getText());
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to search personas", ex);
JOptionPane.showMessageDialog(this,
Bundle.PMTopComponent_search_exception_Title(),
Bundle.PMTopComponent_search_exception_msg(),
JOptionPane.ERROR_MESSAGE);
return;
}
resultsTable.clearSelection();
updateResultsTable(results);
editBtn.setEnabled(false);
}
@Override
public void componentOpened() {
super.componentOpened();
WindowManager.getDefault().setTopComponentFloating(this, true);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
searchButtonGroup = new javax.swing.ButtonGroup();
jSplitPane1 = new javax.swing.JSplitPane();
searchPanel = new javax.swing.JPanel();
searchField = new javax.swing.JTextField();
searchNameRadio = new javax.swing.JRadioButton();
searchAccountRadio = new javax.swing.JRadioButton();
resultsPane = new javax.swing.JScrollPane();
resultsTable = new javax.swing.JTable();
searchBtn = new javax.swing.JButton();
editBtn = new javax.swing.JButton();
createBtn = new javax.swing.JButton();
detailsPanel = new org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel();
setMinimumSize(new java.awt.Dimension(400, 400));
searchField.setText(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchField.text")); // NOI18N
searchButtonGroup.add(searchNameRadio);
searchNameRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(searchNameRadio, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchNameRadio.text")); // NOI18N
searchNameRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
searchNameRadioActionPerformed(evt);
}
});
searchButtonGroup.add(searchAccountRadio);
org.openide.awt.Mnemonics.setLocalizedText(searchAccountRadio, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchAccountRadio.text")); // NOI18N
searchAccountRadio.setEnabled(false);
resultsTable.setToolTipText(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.resultsTable.toolTipText")); // NOI18N
resultsTable.getTableHeader().setReorderingAllowed(false);
resultsPane.setViewportView(resultsTable);
if (resultsTable.getColumnModel().getColumnCount() > 0) {
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(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.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(editBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.editBtn.text")); // NOI18N
editBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(createBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.createBtn.text")); // NOI18N
createBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
createBtnActionPerformed(evt);
}
});
javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel);
searchPanel.setLayout(searchPanelLayout);
searchPanelLayout.setHorizontalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addComponent(createBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editBtn)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(searchField)
.addGroup(searchPanelLayout.createSequentialGroup()
.addComponent(searchNameRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchAccountRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(searchBtn)))
.addContainerGap())
);
searchPanelLayout.setVerticalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(searchField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(searchNameRadio)
.addComponent(searchAccountRadio)
.addComponent(searchBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(resultsPane, javax.swing.GroupLayout.DEFAULT_SIZE, 528, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(editBtn)
.addComponent(createBtn))
.addContainerGap())
);
jSplitPane1.setLeftComponent(searchPanel);
jSplitPane1.setRightComponent(detailsPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 692, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 650, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
private void searchNameRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchNameRadioActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_searchNameRadioActionPerformed
private void createBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createBtnActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_createBtnActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton createBtn;
private org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel detailsPanel;
private javax.swing.JButton editBtn;
private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JScrollPane resultsPane;
private javax.swing.JTable resultsTable;
private javax.swing.JRadioButton searchAccountRadio;
private javax.swing.JButton searchBtn;
private javax.swing.ButtonGroup searchButtonGroup;
private javax.swing.JTextField searchField;
private javax.swing.JRadioButton searchNameRadio;
private javax.swing.JPanel searchPanel;
// End of variables declaration//GEN-END:variables
}

View File

@ -103,16 +103,16 @@
<Component id="valueTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<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">
<Component id="confidenceComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="confidenceLbl" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<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>
</DimensionLayout>
@ -121,49 +121,35 @@
<Component class="javax.swing.JLabel" name="nameLbl">
<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.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>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="nameTextField">
<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.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>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="valueLbl">
<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.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>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="valueTextField">
<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.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;)"/>
<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="confidenceLbl">
<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.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>
</Properties>
</Component>
@ -177,12 +163,26 @@
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence&gt;"/>
</AuxValues>
</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>
</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="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 name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[79, 23]"/>
@ -201,7 +201,7 @@
<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="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>
</Properties>
<Events>

View File

@ -19,8 +19,9 @@
package org.sleuthkit.autopsy.centralrepository.persona;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
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.Persona;
@ -29,26 +30,47 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
* Configuration dialog for adding metadata to a persona.
*/
@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 final PersonaDetailsPanel pdp;
private PersonaDetailsPanel.PMetadata currentMetadata;
/**
* Creates new add metadata dialog
*/
@Messages({"AddMetadataDialog.title.text=Add Metadata",})
public AddMetadataDialog(PersonaDetailsPanel pdp) {
super((JFrame) WindowManager.getDefault().getMainWindow(),
public PersonaMetadataDialog(PersonaDetailsPanel pdp) {
super(SwingUtilities.windowForComponent(pdp),
Bundle.AddMetadataDialog_title_text(),
false);
ModalityType.APPLICATION_MODAL);
this.pdp = pdp;
initComponents();
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.
* 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();
valueLbl = new javax.swing.JLabel();
valueTextField = new javax.swing.JTextField();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
confidenceLbl = new javax.swing.JLabel();
confidenceComboBox = new javax.swing.JComboBox<>();
justificationLbl = new javax.swing.JLabel();
justificationTextField = new javax.swing.JTextField();
cancelBtn = new javax.swing.JButton();
okBtn = new javax.swing.JButton();
@ -75,22 +97,22 @@ public class AddMetadataDialog extends JDialog {
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
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
org.openide.awt.Mnemonics.setLocalizedText(confidenceLbl, org.openide.util.NbBundle.getMessage(PersonaMetadataDialog.class, "PersonaMetadataDialog.confidenceLbl.text")); // NOI18N
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);
settingsPanel.setLayout(settingsPanelLayout);
settingsPanelLayout.setHorizontalGroup(
@ -128,17 +150,17 @@ public class AddMetadataDialog extends JDialog {
.addComponent(valueLbl)
.addComponent(valueTextField, 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(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)
.addComponent(confidenceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.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.setMinimumSize(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() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
okBtnActionPerformed(evt);
@ -191,19 +213,41 @@ public class AddMetadataDialog extends JDialog {
@Messages({
"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
if (pdp.addMetadata(
nameTextField.getText(),
valueTextField.getText(),
justificationTextField.getText(),
(Persona.Confidence) confidenceComboBox.getSelectedItem())) {
if (StringUtils.isBlank(nameTextField.getText()) || StringUtils.isBlank(valueTextField.getText())) {
JOptionPane.showMessageDialog(this,
Bundle.AddMetadataDialog_empty_name_msg(),
Bundle.AddMetadataDialog_empty_name_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 (currentMetadata != null) {
currentMetadata.confidence = confidence;
currentMetadata.justification = justification;
dispose();
} else {
JOptionPane.showMessageDialog(this,
if (pdp.addMetadata(nameTextField.getText(), valueTextField.getText(), justification, confidence)) {
dispose();
} else {
JOptionPane.showMessageDialog(this,
Bundle.AddMetadataDialog_dup_msg(),
Bundle.AddMetadataDialog_dup_Title(),
JOptionPane.ERROR_MESSAGE);
}
}
}//GEN-LAST:event_okBtnActionPerformed

View File

@ -1,35 +1,12 @@
<?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">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="searchButtonGroup">
</Component>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 400]"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
@ -46,17 +23,52 @@
<Layout>
<DimensionLayout dim="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>
</DimensionLayout>
<DimensionLayout dim="1">
<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>
</DimensionLayout>
</Layout>
<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"/>
<SubComponents>
@ -73,12 +85,7 @@
<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="createBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="editBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Component id="createButtonSeparator" max="32767" attributes="0"/>
<Component id="resultsPane" pref="0" max="32767" attributes="0"/>
<Component id="searchField" max="32767" attributes="0"/>
<Group type="102" attributes="0">
@ -88,6 +95,20 @@
<EmptySpace max="32767" attributes="0"/>
<Component id="searchBtn" min="-2" max="-2" attributes="0"/>
</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>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -96,7 +117,8 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="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"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
@ -105,13 +127,18 @@
<Component id="searchBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<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"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="editBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="createBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="deleteBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<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>
</DimensionLayout>
@ -120,7 +147,7 @@
<Component class="javax.swing.JTextField" name="searchField">
<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.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>
</Properties>
</Component>
@ -131,12 +158,9 @@
</Property>
<Property name="selected" type="boolean" value="true"/>
<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>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="searchNameRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="searchAccountRadio">
<Properties>
@ -144,9 +168,15 @@
<ComponentRef name="searchButtonGroup"/>
</Property>
<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 name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="resultsPane">
@ -159,20 +189,20 @@
<Component class="javax.swing.JTable" name="resultsTable">
<Properties>
<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 name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0">
<Column maxWidth="25" minWidth="-1" prefWidth="-1" resizable="true">
<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>
<Editor/>
<Renderer/>
</Column>
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
<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>
<Editor/>
<Renderer/>
@ -186,40 +216,69 @@
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="searchBtn">
<Component class="javax.swing.JButton" name="createAccountBtn">
<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.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>
<AuxValues>
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
</AuxValues>
</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;)"/>
<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 name="enabled" type="boolean" value="false"/>
</Properties>
</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="PersonaManagerTopComponent.createBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<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="createBtnActionPerformed"/>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbFilterByKeywordActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel">
<Container class="javax.swing.JScrollPane" name="detailsScrollPane">
<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>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel">
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>

View File

@ -0,0 +1,507 @@
/*
* 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.centralrepository.persona;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.AncestorListener;
import javax.swing.event.AncestorEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.RetainLocation;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
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.coreutils.Logger;
/**
* Top component for the Personas tool
*
*/
@TopComponent.Description(preferredID = "PersonasTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "personas", openAtStartup = false)
@RetainLocation("personas")
@SuppressWarnings("PMD.SingularField")
public final class PersonasTopComponent extends TopComponent {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(PersonasTopComponent.class.getName());
private List<Persona> currentResults = null;
private Persona selectedPersona = null;
/**
* Listens for when this component will be rendered and executes a search to
* update gui when it is displayed.
*/
private final AncestorListener onAddListener = new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent event) {
resetSearchControls();
setKeywordSearchEnabled(false, true);
}
@Override
public void ancestorRemoved(AncestorEvent event) {
//Empty
}
@Override
public void ancestorMoved(AncestorEvent event) {
//Empty
}
};
@Messages({
"PersonasTopComponent_Name=Personas",
"PersonasTopComponent_delete_exception_Title=Delete failure",
"PersonasTopComponent_delete_exception_msg=Failed to delete persona.",
"PersonasTopComponent_delete_confirmation_Title=Are you sure?",
"PersonasTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?",})
public PersonasTopComponent() {
initComponents();
setName(Bundle.PersonasTopComponent_Name());
searchBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
executeSearch();
}
});
editBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new PersonaDetailsDialog(PersonasTopComponent.this,
PersonaDetailsMode.EDIT, selectedPersona, new CreateEditCallbackImpl());
}
});
createBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new PersonaDetailsDialog(PersonasTopComponent.this,
PersonaDetailsMode.CREATE, selectedPersona, new CreateEditCallbackImpl());
}
});
deleteBtn.addActionListener(new ActionListener() {
@Override
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 {
if (selectedPersona != null) {
selectedPersona.delete();
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to delete persona: " + selectedPersona.getName(), ex);
JOptionPane.showMessageDialog(PersonasTopComponent.this,
Bundle.PersonasTopComponent_delete_exception_msg(),
Bundle.PersonasTopComponent_delete_exception_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
executeSearch();
}
}
});
// Results table
resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
resultsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent 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);
}
});
addAncestorListener(onAddListener);
}
/**
* Callback method for the create/edit mode of the PersonaDetailsDialog
*/
class CreateEditCallbackImpl implements PersonaDetailsDialogCallback {
@Override
public void callback(Persona persona) {
if (persona != null) {
searchField.setText("");
executeSearch();
int personaRow = currentResults.indexOf(persona);
resultsTable.getSelectionModel().setSelectionInterval(personaRow, personaRow);
handleSelectionChange();
}
createBtn.setEnabled(true);
}
}
/**
* 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) {
Persona persona = currentResults.get(index);
selectedPersona = persona;
editBtn.setEnabled(true);
deleteBtn.setEnabled(true);
}
/**
* Table model for the persona search results
*/
final class PersonaFilterTableModel extends DefaultTableModel {
private static final long serialVersionUID = 1L;
PersonaFilterTableModel(Object[][] rows, String[] colNames) {
super(rows, colNames);
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
}
private void handleSelectionChange(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
handleSelectionChange();
}
private void handleSelectionChange() {
int selectedRow = resultsTable.getSelectedRow();
if (selectedRow != -1) {
setPersona(resultsTable.getSelectedRow());
detailsPanel.setMode(this, PersonaDetailsMode.VIEW, selectedPersona);
} else {
detailsPanel.clear();
}
}
private void updateResultsTable(Collection<Persona> results) {
Object[][] rows = new Object[results.size()][2];
int i = 0;
for (Persona result : results) {
rows[i] = new Object[]{result.getId(), result.getName()};
i++;
}
PersonaFilterTableModel updatedTableModel = new PersonaFilterTableModel(
rows,
new String[]{"ID", "Name"}
);
resultsTable.setModel(updatedTableModel);
currentResults = new ArrayList<>(results);
// Formatting
resultsTable.getColumnModel().getColumn(0).setMaxWidth(100);
resultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
}
@Messages({
"PersonasTopComponent_search_exception_Title=Search failure",
"PersonasTopComponent_search_exception_msg=Failed to search personas.",
"PersonasTopComponent_noCR_msg=Central Repository is not enabled.",})
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;
try {
if (cbFilterByKeyword.isSelected()) {
if (searchNameRadio.isSelected()) {
results = Persona.getPersonaByName(searchField.getText());
} else {
results = Persona.getPersonaByAccountIdentifierLike(searchField.getText());
}
} else {
results = Persona.getPersonaByName("");
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to search personas", ex);
JOptionPane.showMessageDialog(this,
Bundle.PersonasTopComponent_search_exception_Title(),
Bundle.PersonasTopComponent_search_exception_msg(),
JOptionPane.ERROR_MESSAGE);
return;
}
resultsTable.clearSelection();
updateResultsTable(results);
editBtn.setEnabled(false);
deleteBtn.setEnabled(false);
}
@Override
public void componentOpened() {
super.componentOpened();
WindowManager.getDefault().setTopComponentFloating(this, true);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
searchButtonGroup = new javax.swing.ButtonGroup();
introTextScrollPane = new javax.swing.JScrollPane();
introText = new javax.swing.JTextArea();
mainSplitPane = new javax.swing.JSplitPane();
searchPanel = new javax.swing.JPanel();
searchField = new javax.swing.JTextField();
searchNameRadio = new javax.swing.JRadioButton();
searchAccountRadio = new javax.swing.JRadioButton();
searchBtn = new javax.swing.JButton();
resultsPane = new javax.swing.JScrollPane();
resultsTable = new javax.swing.JTable();
createAccountBtn = new javax.swing.JButton();
editBtn = 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();
setName(""); // 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);
searchNameRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(searchNameRadio, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchNameRadio.text")); // NOI18N
searchButtonGroup.add(searchAccountRadio);
org.openide.awt.Mnemonics.setLocalizedText(searchAccountRadio, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchAccountRadio.text")); // 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);
resultsPane.setViewportView(resultsTable);
if (resultsTable.getColumnModel().getColumnCount() > 0) {
resultsTable.getColumnModel().getColumn(0).setMaxWidth(25);
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(PersonasTopComponent.class, "PersonasTopComponent.resultsTable.columnModel.title1")); // 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(PersonasTopComponent.class, "PersonasTopComponent.editBtn.text")); // NOI18N
editBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(deleteBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.deleteBtn.text")); // NOI18N
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);
searchPanel.setLayout(searchPanelLayout);
searchPanelLayout.setHorizontalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(createButtonSeparator)
.addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(searchField)
.addGroup(searchPanelLayout.createSequentialGroup()
.addComponent(searchNameRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchAccountRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.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())
);
searchPanelLayout.setVerticalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addComponent(cbFilterByKeyword)
.addGap(1, 1, 1)
.addComponent(searchField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(searchNameRadio)
.addComponent(searchAccountRadio)
.addComponent(searchBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 302, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(editBtn)
.addComponent(createBtn)
.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())
);
mainSplitPane.setLeftComponent(searchPanel);
detailsScrollPane.setViewportView(detailsPanel);
mainSplitPane.setRightComponent(detailsScrollPane);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(introTextScrollPane)
.addComponent(mainSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 724, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.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
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
private javax.swing.JCheckBox cbFilterByKeyword;
private javax.swing.JButton createAccountBtn;
private javax.swing.JButton createBtn;
private javax.swing.JSeparator createButtonSeparator;
private javax.swing.JButton deleteBtn;
private org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel detailsPanel;
private javax.swing.JScrollPane detailsScrollPane;
private javax.swing.JButton editBtn;
private javax.swing.JTextArea introText;
private javax.swing.JScrollPane introTextScrollPane;
private javax.swing.JSplitPane mainSplitPane;
private javax.swing.JScrollPane resultsPane;
private javax.swing.JTable resultsTable;
private javax.swing.JRadioButton searchAccountRadio;
private javax.swing.JButton searchBtn;
private javax.swing.ButtonGroup searchButtonGroup;
private javax.swing.JTextField searchField;
private javax.swing.JRadioButton searchNameRadio;
private javax.swing.JPanel searchPanel;
// End of variables declaration//GEN-END:variables
}

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,
* 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
* any data sources are unprocessed. If the settings reflect that a
* 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
* current central repository instead of another. - Does not check that the
* 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.
*/
@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
for (CorrelationDataSource correlatedDataSource : correlatedDataSources) {
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);
break;
}
}
}
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()) {
if (correlationEngineModuleName.equals(ingestModuleInfo.getDisplayName())) {
dataSourceCorrelationMap.put(dataSource, CorrelatedStatus.CORRELATED);

View File

@ -20,12 +20,18 @@ package org.sleuthkit.autopsy.communications;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.Action;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.AccountDeviceInstance;
@ -37,6 +43,8 @@ import org.sleuthkit.datamodel.CommunicationsManager;
*/
final class AccountDeviceInstanceNode extends AbstractNode {
private static final Logger logger = Logger.getLogger(AccountDeviceInstanceNode.class.getName());
private final AccountDeviceInstanceKey accountDeviceInstanceKey;
private final CommunicationsManager commsManager;
private final Account account;
@ -103,4 +111,34 @@ final class AccountDeviceInstanceNode extends AbstractNode {
actions.add(ResetAndPinAccountsAction.getInstance());
return actions.toArray(new Action[actions.size()]);
}
@Messages({
"# {0} - Contact Name",
"# {1} - Persona Name",
"AccountInstanceNode_Tooltip_Template=Contact: {0} - Persona: {1}",
"# {0} - PersonaAccount count",
"AccountInstanceNode_Tooltip_suffix=(1 of {0})"
})
@Override
public String getShortDescription() {
List<PersonaAccount> personaList;
try {
personaList = CVTPersonaCache.getPersonaAccounts(account);
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex);
return getDisplayName();
}
String personaName;
if (!personaList.isEmpty()) {
personaName = personaList.get(0).getPersona().getName();
if (personaList.size() > 1) {
personaName += Bundle.AccountInstanceNode_Tooltip_suffix(Integer.toString(personaList.size()));
}
} else {
personaName = "None";
}
return Bundle.AccountInstanceNode_Tooltip_Template(getDisplayName(), personaName);
}
}

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.accountType=Type
AccountNode.device=Device

View File

@ -0,0 +1,96 @@
/*
* 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 com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Account;
/**
* A singleton cache of the PersonaAccount information. The list of
* PersonaAccounts for a given Account typeSpecificID retrieved on first access
* and evicted from the cache after 5 minutes.
*/
final class CVTPersonaCache {
private static final Logger logger = Logger.getLogger(CVTPersonaCache.class.getName());
private final LoadingCache<Account, List<PersonaAccount>> accountMap;
private static CVTPersonaCache instance;
/**
* Cache constructor.
*/
private CVTPersonaCache() {
accountMap = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build(
new CacheLoader<Account, List<PersonaAccount>>() {
@Override
public List<PersonaAccount> load(Account key) {
List<PersonaAccount> accountList = new ArrayList<>();
try {
if (CentralRepository.isEnabled()) {
Collection<PersonaAccount> accounts = PersonaAccount.getPersonaAccountsForAccount(key);
accountList.addAll(accounts);
}
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, String.format("Unable to load Persona information for account: %s", key), ex);
}
return accountList;
}
}
);
}
/**
* Returns the singleton instance of the cache.
*
* @return CVTPersonaCache instance.
*/
private static synchronized CVTPersonaCache getInstance() {
if (instance == null) {
instance = new CVTPersonaCache();
}
return instance;
}
/**
* Returns the list of PersonaAccounts for the given Account typeSpecificId.
*
* @param typeSpecificID Account typeSpecificId.
*
* @return List of PersonaAccounts for id or empty list if none were found.
*
* @throws ExecutionException
*/
static synchronized List<PersonaAccount> getPersonaAccounts(Account account) throws ExecutionException {
return getInstance().accountMap.get(account);
}
}

View File

@ -222,9 +222,14 @@
</SubComponents>
</Container>
<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>
<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>
</Constraints>
@ -277,7 +282,6 @@
</Component>
<Container class="javax.swing.JScrollPane" name="devicesScrollPane">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 30]"/>
</Property>

View File

@ -28,7 +28,9 @@ import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
@ -355,16 +357,25 @@ final public class FiltersPanel extends JPanel {
final JCheckBox jCheckBox = new JCheckBox(dsName, selected);
jCheckBox.addItemListener(validationListener);
devicesListPane.add(jCheckBox);
jCheckBox.setToolTipText(dsName);
devicesMap.put(dataSource.getDeviceId(), jCheckBox);
newOneFound = true;
}
} catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", ex);
}
if (newOneFound) {
devicesListPane.removeAll();
List<JCheckBox> checkList = new ArrayList<>(devicesMap.values());
checkList.sort(new DeviceCheckBoxComparator());
for (JCheckBox cb : checkList) {
devicesListPane.add(cb);
}
devicesListPane.revalidate();
}
@ -613,6 +624,7 @@ final public class FiltersPanel extends JPanel {
gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 25);
mainPanel.add(dateRangePane, gridBagConstraints);
devicesPane.setPreferredSize(new java.awt.Dimension(300, 300));
devicesPane.setLayout(new java.awt.GridBagLayout());
unCheckAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllDevicesButton.text")); // NOI18N
@ -652,7 +664,6 @@ final public class FiltersPanel extends JPanel {
gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0);
devicesPane.add(checkAllDevicesButton, gridBagConstraints);
devicesScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
devicesScrollPane.setMaximumSize(new java.awt.Dimension(32767, 30));
devicesScrollPane.setMinimumSize(new java.awt.Dimension(27, 30));
devicesScrollPane.setPreferredSize(new java.awt.Dimension(3, 30));
@ -686,10 +697,10 @@ final public class FiltersPanel extends JPanel {
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.ipady = 100;
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(15, 0, 0, 25);
mainPanel.add(devicesPane, gridBagConstraints);
@ -1224,4 +1235,16 @@ final public class FiltersPanel extends JPanel {
}
}
/**
* 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

@ -17,7 +17,7 @@ ContactsViewer_columnHeader_Phone=Phone
ContactsViewer_noContacts_message=<No contacts found for selected account>
ContactsViewer_tabTitle=Contacts
MediaViewer_Name=Media Attachments
MessageNode_Node_Property_Attms=Attachments
MessageNode_Node_Property_Attms=Attachment Count
MessageNode_Node_Property_Date=Date
MessageNode_Node_Property_From=From
MessageNode_Node_Property_Subject=Subject

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"/>
<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>
<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>
</Component>
<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>
</Constraints>
</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>
</Form>

View File

@ -18,15 +18,24 @@
*/
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 static javax.swing.SwingUtilities.isDescendingFrom;
import org.netbeans.swing.outline.DefaultOutlineModel;
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.Children;
import org.openide.nodes.Node;
import org.openide.nodes.NodeAdapter;
import org.openide.nodes.NodeMemberEvent;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER;
@ -35,46 +44,68 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIR
/**
*
* CallLogViewer Panel
* CallLogViewer Panel
*/
final class CallLogViewer extends javax.swing.JPanel implements RelationshipsViewer {
private static final long serialVersionUID = 1L;
private final CallLogsChildNodeFactory nodeFactory;
private final CallLogDataViewer callLogDataViewer;
private final ModifiableProxyLookup proxyLookup;
private PropertyChangeListener focusPropertyListener;
@Messages({
"CallLogViewer_title=Call Logs",
"CallLogViewer_noCallLogs=<No call logs found for selected account>",
"CallLogViewer_recipient_label=To/From",
"CallLogViewer_duration_label=Duration(seconds)",
"CallLogViewer_device_label=Device"
"CallLogViewer_device_label=Device"
})
/**
* Creates new form CallLogViewer
*/
public CallLogViewer() {
CallLogViewer() {
initComponents();
callLogDataViewer = new CallLogDataViewer();
bottomScrollPane.setViewportView(callLogDataViewer);
splitPane.setResizeWeight(0.5);
splitPane.setDividerLocation(0.5);
nodeFactory = new CallLogsChildNodeFactory(null);
proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
outlineViewPanel.hideOutlineView(Bundle.CallLogViewer_noCallLogs());
outlineViewPanel.getOutlineView().setPropertyColumns(
TSK_DIRECTION.getLabel(), TSK_DIRECTION.getDisplayName(),
TSK_PHONE_NUMBER.getLabel(), Bundle.CallLogViewer_recipient_label(),
TSK_DATETIME_START.getLabel(), TSK_DATETIME_START.getDisplayName(),
CallLogNode.DURATION_PROP, Bundle.CallLogViewer_duration_label()
);
Outline outline = outlineViewPanel.getOutlineView().getOutline();
outline.setRootVisible(false);
((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(
new TableFilterNode(
new DataResultFilterNode(
new AbstractNode(Children.create(nodeFactory, true)), outlineViewPanel.getExplorerManager()), true));
outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter(){
outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter() {
@Override
public void childrenAdded(NodeMemberEvent nme) {
updateOutlineViewPanel();
@ -83,9 +114,9 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
@Override
public void childrenRemoved(NodeMemberEvent nme) {
updateOutlineViewPanel();
}
}
});
}
/**
@ -98,16 +129,21 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
splitPane = new javax.swing.JSplitPane();
outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
bottomScrollPane = new javax.swing.JScrollPane();
setLayout(new java.awt.GridBagLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setLeftComponent(outlineViewPanel);
splitPane.setRightComponent(bottomScrollPane);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(15, 15, 15, 15);
add(outlineViewPanel, gridBagConstraints);
add(splitPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
@Override
@ -127,12 +163,60 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
@Override
public Lookup getLookup() {
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() {
int nodeCount = outlineViewPanel.getExplorerManager().getRootContext().getChildren().getNodesCount();
if(nodeCount == 0) {
if (nodeCount == 0) {
outlineViewPanel.hideOutlineView(Bundle.ContactsViewer_noContacts_message());
} else {
outlineViewPanel.showOutlineView();
@ -141,6 +225,8 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane bottomScrollPane;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;
private javax.swing.JSplitPane splitPane;
// 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"/>
<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">
<Constraints>
<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.
*
*/
@ServiceProvider(service = RelationshipsViewer.class)
public final class ContactsViewer extends JPanel implements RelationshipsViewer{
final class ContactsViewer extends JPanel implements RelationshipsViewer {
private static final long serialVersionUID = 1L;
// private final ExplorerManager tableEM;
private final Outline outline;
private final ModifiableProxyLookup proxyLookup;
private final PropertyChangeListener focusPropertyListener;
private PropertyChangeListener focusPropertyListener;
private final ContactsChildNodeFactory nodeFactory;
private final ContactDataViewer contactPane;
@NbBundle.Messages({
"ContactsViewer_tabTitle=Contacts",
@ -65,34 +66,17 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
/**
* Visualization for contact nodes.
*/
public ContactsViewer() {
ContactsViewer() {
initComponents();
contactPane = new ContactDataViewer();
splitPane.setRightComponent(contactPane);
outlineViewPanel.hideOutlineView(Bundle.ContactsViewer_noContacts_message());
proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
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();
outlineViewPanel.getOutlineView().setPropertyColumns(
"TSK_EMAIL", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getDisplayName(),
@ -100,21 +84,21 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
);
outline.setRootVisible(false);
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.ContactsViewer_columnHeader_Name());
outlineViewPanel.hideOutlineView("<No contacts for selected account>");
outlineViewPanel.getExplorerManager().addPropertyChangeListener((PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
final Node[] nodes = outlineViewPanel.getExplorerManager().getSelectedNodes();
contactPane.setNode(nodes);
contactPane.setNode(nodes != null && nodes.length > 0 ? nodes[0] : null);
}
});
outlineViewPanel.getExplorerManager().setRootContext(new TableFilterNode(new DataResultFilterNode(new AbstractNode(Children.create(nodeFactory, true)), outlineViewPanel.getExplorerManager()), true));
// When a new set of nodes are added to the OutlineView the childrenAdded
// seems to be fired before the childrenRemoved.
outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter(){
outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter() {
@Override
public void childrenAdded(NodeMemberEvent nme) {
updateOutlineViewPanel();
@ -123,9 +107,9 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
@Override
public void childrenRemoved(NodeMemberEvent nme) {
updateOutlineViewPanel();
}
}
});
splitPane.setResizeWeight(0.5);
splitPane.setDividerLocation(0.5);
}
@ -144,7 +128,7 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
public void setSelectionInfo(SelectionInfo info) {
nodeFactory.refresh(info);
}
@Override
public Lookup getLookup() {
return proxyLookup;
@ -153,21 +137,53 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
@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, 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
public void removeNotify() {
super.removeNotify();
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", focusPropertyListener); //NON-NLS
if (focusPropertyListener != null) {
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", focusPropertyListener); //NON-NLS
}
}
private void updateOutlineViewPanel() {
int nodeCount = outlineViewPanel.getExplorerManager().getRootContext().getChildren().getNodesCount();
if(nodeCount == 0) {
if (nodeCount == 0) {
outlineViewPanel.hideOutlineView(Bundle.ContactsViewer_noContacts_message());
} else {
outlineViewPanel.showOutlineView();
@ -185,13 +201,11 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
java.awt.GridBagConstraints gridBagConstraints;
splitPane = new javax.swing.JSplitPane();
contactPane = new org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane();
outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
setLayout(new java.awt.GridBagLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setRightComponent(contactPane);
splitPane.setLeftComponent(outlineViewPanel);
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
private org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane contactPane;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;
private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables

View File

@ -46,21 +46,6 @@
</Constraint>
</Constraints>
</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>
</Container>
</SubComponents>

View File

@ -33,8 +33,6 @@ import org.openide.nodes.AbstractNode;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
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.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -42,56 +40,42 @@ import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import org.sleuthkit.datamodel.AbstractContent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* A Panel that shows the media (thumbnails) for the selected account.
* A Panel that shows the media (thumbnails) for the selected account.
*/
final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerManager.Provider, Lookup.Provider {
private static final Logger logger = Logger.getLogger(MediaViewer.class.getName());
private static final long serialVersionUID = 1L;
private final ExplorerManager tableEM = new ExplorerManager();
private final PropertyChangeListener focusPropertyListener;
private PropertyChangeListener focusPropertyListener;
private final ModifiableProxyLookup proxyLookup;
private final MessageDataContent contentViewer;
@Messages({
"MediaViewer_Name=Media Attachments"
})
/**
* Creates new form ThumbnailViewer
*/
public MediaViewer() {
MediaViewer() {
initComponents();
splitPane.setResizeWeight(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()));
// 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) -> {
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
handleNodeSelectionChange();
@ -124,7 +108,7 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
});
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to update selection." , ex);
logger.log(Level.WARNING, "Unable to update selection.", ex);
}
thumbnailViewer.resetComponent();
@ -145,16 +129,49 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
@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);
}
/**
* 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
public void removeNotify() {
super.removeNotify();
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", focusPropertyListener);
if (focusPropertyListener != null) {
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", focusPropertyListener);
}
}
/**
@ -192,7 +209,6 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
splitPane = new javax.swing.JSplitPane();
thumbnailViewer = new org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail(tableEM);
contentViewer = new MessageDataContent();
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));
splitPane.setLeftComponent(thumbnailViewer);
contentViewer.setPreferredSize(new java.awt.Dimension(450, 400));
splitPane.setRightComponent(contentViewer);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
@ -217,7 +230,6 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer contentViewer;
private javax.swing.JSplitPane splitPane;
private org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail thumbnailViewer;
// End of variables declaration//GEN-END:variables

View File

@ -19,9 +19,21 @@
package org.sleuthkit.autopsy.communications.relationships;
import java.beans.PropertyChangeEvent;
import java.util.List;
import java.util.logging.Level;
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.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
@ -32,7 +44,9 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
* 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;
final private ExplorerManager explorerManager = new ExplorerManager();
@ -46,4 +60,53 @@ final class MessageDataContent extends MessageContentViewer implements DataConte
public ExplorerManager getExplorerManager() {
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

@ -48,22 +48,22 @@ import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
class MessageNode extends BlackboardArtifactNode {
public static final String UNTHREADED_ID = "<UNTHREADED>";
private static final Logger logger = Logger.getLogger(MessageNode.class.getName());
private final String threadID;
private final Action preferredAction;
MessageNode(BlackboardArtifact artifact, String threadID, Action preferredAction) {
MessageNode(BlackboardArtifact artifact, String threadID, Action preferredAction) {
super(artifact);
this.preferredAction = preferredAction;
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); // NON-NLS
String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); // NON-NLS
setDisplayName(removeEndIgnoreCase.isEmpty() ? stripEnd : removeEndIgnoreCase);
this.threadID = threadID;
}
@ -73,12 +73,12 @@ class MessageNode extends BlackboardArtifactNode {
"MessageNode_Node_Property_To=To",
"MessageNode_Node_Property_Date=Date",
"MessageNode_Node_Property_Subject=Subject",
"MessageNode_Node_Property_Attms=Attachments"
"MessageNode_Node_Property_Attms=Attachment Count"
})
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet sheet = Sheet.createDefault();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
@ -89,42 +89,45 @@ class MessageNode extends BlackboardArtifactNode {
final BlackboardArtifact artifact = getArtifact();
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
if(fromID == null ||
(fromID != TSK_EMAIL_MSG &&
fromID != TSK_MESSAGE)) {
if (fromID == null
|| (fromID != TSK_EMAIL_MSG
&& fromID != TSK_MESSAGE)) {
return sheet;
}
sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",threadID == null ? UNTHREADED_ID : threadID)); //NON-NLS
if (threadID != null) {
sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID", "", threadID)); //NON-NLS
}
sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "",
getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS
getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS
try {
sheetSet.put(new NodeProperty<>("Attms", Bundle.MessageNode_Node_Property_Attms(), "", getAttachmentsCount())); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS
}
switch (fromID) {
case TSK_EMAIL_MSG:
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "",
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"))); //NON-NLS
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "",
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;"))); //NON-NLS
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;
String msg_from = getAttributeDisplayString(artifact, TSK_EMAIL_FROM);
String msg_to = getAttributeDisplayString(artifact, TSK_EMAIL_TO);
String date = getAttributeDisplayString(artifact, TSK_DATETIME_SENT);
if (msg_from.isEmpty()) {
msg_from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
}
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;
}
@ -138,16 +141,16 @@ class MessageNode extends BlackboardArtifactNode {
public String getSourceName() {
return getDisplayName();
}
String getThreadID() {
return threadID;
}
@Override
public Action getPreferredAction() {
return preferredAction;
}
private int getAttachmentsCount() throws TskCoreException {
final BlackboardArtifact artifact = getArtifact();
int attachmentsCount;
@ -158,8 +161,7 @@ class MessageNode extends BlackboardArtifactNode {
try {
MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class);
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);
return 0;
}

View File

@ -57,18 +57,19 @@ import org.sleuthkit.autopsy.coreutils.Logger;
*
*/
@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 long serialVersionUID = 1L;
private final ModifiableProxyLookup proxyLookup;
private final PropertyChangeListener focusPropertyListener;
private PropertyChangeListener focusPropertyListener;
private final ThreadChildNodeFactory rootMessageFactory;
private final MessagesChildNodeFactory threadMessageNodeFactory;
private SelectionInfo currentSelectionInfo = null;
OutlineViewPanel currentPanel;
private OutlineViewPanel currentPanel;
@Messages({
"MessageViewer_tabTitle=Messages",
@ -87,7 +88,7 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
/**
* Creates new form MessageViewer
*/
public MessageViewer() {
MessageViewer() {
initComponents();
currentPanel = rootTablePane;
@ -95,26 +96,9 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
rootMessageFactory = new ThreadChildNodeFactory(new ShowThreadMessagesAction());
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(
new AbstractNode(Children.create(rootMessageFactory, true)));
rootTablePane.getOutlineView().setPopupAllowed(false);
Outline outline = rootTablePane.getOutlineView().getOutline();
@ -135,9 +119,9 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
threadMessagesPanel.setChildFactory(threadMessageNodeFactory);
rootTablePane.setTableColumnsWidth(10, 20, 70);
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
@ -170,11 +154,38 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
@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);
}
/**
* 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
public void removeNotify() {
super.removeNotify();
@ -230,18 +241,18 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
} else {
threadNameLabel.setText(Bundle.MessageViewer_viewMessage_unthreaded());
}
showMessagesPane();
showMessagesPane();
}
}
/**
* Make the threads pane visible.
*/
private void showThreadsPane() {
switchCard("threads");
}
/**
* Make the message pane visible.
*/
@ -250,32 +261,32 @@ public class MessageViewer extends JPanel implements RelationshipsViewer {
Outline outline = rootTablePane.getOutlineView().getOutline();
outline.clearSelection();
}
/**
* Changes the visible panel (card).
*
*
* @param cardName Name of card to show
*/
private void switchCard(String cardName) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
CardLayout layout = (CardLayout)getLayout();
CardLayout layout = (CardLayout) getLayout();
layout.show(MessageViewer.this, cardName);
}
});
}
/**
* Scales the given image to the given width and height.
*
*
* @param srcImg Image to scale
* @param w Image width
* @param h Image height
*
* @param w Image width
* @param h Image height
*
* @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);
Graphics2D g2 = resizedImg.createGraphics();

View File

@ -35,16 +35,6 @@
</Constraint>
</Constraints>
</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>
</Container>
</SubComponents>

View File

@ -38,43 +38,30 @@ import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
/**
*
* General Purpose class for panels that need OutlineView of message nodes at
* the top with a MessageContentViewer at the bottom.
* General Purpose class for panels that need OutlineView of message nodes at
* 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 ModifiableProxyLookup proxyLookup;
private final PropertyChangeListener focusPropertyListener;
private PropertyChangeListener focusPropertyListener;
private final MessageDataContent messageContentViewer;
/**
* Creates new form MessagesPanel
*/
public MessagesPanel() {
MessagesPanel() {
initComponents();
messageContentViewer = new MessageDataContent();
splitPane.setRightComponent(messageContentViewer);
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();
outlineViewPanel.getOutlineView().setPropertyColumns(
"From", Bundle.MessageViewer_columnHeader_From(),
@ -92,23 +79,22 @@ public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider
if (nodes != null && nodes.length == 1) {
messageContentViewer.setNode(nodes[0]);
}
else {
} else {
messageContentViewer.setNode(null);
}
}
});
splitPane.setResizeWeight(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) {
this();
setChildFactory(nodeFactory);
}
@Override
public Lookup getLookup() {
return proxyLookup;
@ -117,11 +103,38 @@ public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider
@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);
}
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
public void removeNotify() {
super.removeNotify();
@ -135,9 +148,9 @@ public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider
new DataResultFilterNode(
new AbstractNode(
Children.create(nodeFactory, true)),
outlineViewPanel.getExplorerManager()),true));
outlineViewPanel.getExplorerManager()), true));
}
/**
* 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
@ -149,20 +162,17 @@ public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider
splitPane = new javax.swing.JSplitPane();
outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
messageContentViewer = new MessageDataContent();
setLayout(new java.awt.BorderLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setLeftComponent(outlineViewPanel);
splitPane.setRightComponent(messageContentViewer);
add(splitPane, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer messageContentViewer;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;
private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables

View File

@ -63,22 +63,7 @@ Metadata.toolTip=Displays metadata about the file.
Metadata.nodeText.nonFilePassedIn=Non-file passed in
Metadata.nodeText.text=From The Sleuth Kit istat Tool:
Metadata.nodeText.exceptionNotice.text=Error getting file metadata:
MessageContentViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageContentViewer.headersScrollPane.TabConstraints.tabTitle=Headers
MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle=Text
MessageContentViewer.htmlPane.TabConstraints.tabTitle=HTML
MessageContentViewer.fromText.text=from address goes here
MessageContentViewer.fromLabel.text=From:
MessageContentViewer.datetimeText.text=date goes here
MessageContentViewer.toText.text=to list goes here
MessageContentViewer.toLabel.text=To:
MessageContentViewer.ccText.text=cc list goes here
MessageContentViewer.subjectLabel.text=Subject:
MessageContentViewer.subjectText.text=subject goes here
MessageContentViewer.directionText.text=direction
MessageContentViewer.ccLabel.text=CC:
MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageContentViewer.viewInNewWindowButton.text=View in New Window
MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text
JPEGViewerDummy.jLabel1.text=You are looking at a JPEG file:
JPEGViewerDummy.jTextField1.text=jTextField1
SQLiteViewer.nextPageButton.text=
@ -954,25 +939,4 @@ manager.properties.lafError =\
manager.properties.brokenProperty = Broken default property {0} value: {1}
manager.properties.missingProperty = Missing default property {0} value: {1}
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
ContactArtifactViewer.contactNameLabel.text=Joanna Doe
ContactArtifactViewer.phonesLabel.text=Phone
ContactArtifactViewer.emailsLabel.text=Email
ContactArtifactViewer.othersLabel.text=Other
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
CallLogArtifactViewer.dataSourceNameLabel.text=data source name
CallLogArtifactViewer.jLabel2.text=Device Id
CallLogArtifactViewer.deviceIdLabel.text=device id
CallLogArtifactViewer.localAccountIdLabel.text=local account
CallLogArtifactViewer.localAccountLabel.text=Local Account
CallLogArtifactViewer.jLabel4.text=Data Source Name
CallLogArtifactViewer.otherInfoLabel.text=Other Information
CallLogArtifactViewer.sourceSectionLabel.text=Source
CallLogArtifactViewer.callDetailsLabel.text=Call Details
CallLogArtifactViewer.durationLabel.text=Duration....
CallLogArtifactViewer.dateTimeLabel.text=Date/Time.....
CallLogArtifactViewer.directionLabel.text=Direction
CallLogArtifactViewer.onLabel.text=On
CallLogArtifactViewer.callLabel.text=call

View File

@ -85,9 +85,6 @@ MediaViewVideoPanel.progressLabel.text=00:00
MediaViewVideoPanel.infoLabel.text=info
MediaViewImagePanel.imgFileTooLarge.msg=Could not load image file (too large): {0}
MessageContentViewer.AtrachmentsPanel.title=Attachments
MessageContentViewer.title=Message
MessageContentViewer.toolTip=Displays messages.
Metadata.nodeText.none=None
Metadata.nodeText.truncated=(results truncated)
Metadata.nodeText.unknown=Unknown
@ -119,22 +116,7 @@ Metadata.toolTip=Displays metadata about the file.
Metadata.nodeText.nonFilePassedIn=Non-file passed in
Metadata.nodeText.text=From The Sleuth Kit istat Tool:
Metadata.nodeText.exceptionNotice.text=Error getting file metadata:
MessageContentViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageContentViewer.headersScrollPane.TabConstraints.tabTitle=Headers
MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle=Text
MessageContentViewer.htmlPane.TabConstraints.tabTitle=HTML
MessageContentViewer.fromText.text=from address goes here
MessageContentViewer.fromLabel.text=From:
MessageContentViewer.datetimeText.text=date goes here
MessageContentViewer.toText.text=to list goes here
MessageContentViewer.toLabel.text=To:
MessageContentViewer.ccText.text=cc list goes here
MessageContentViewer.subjectLabel.text=Subject:
MessageContentViewer.subjectText.text=subject goes here
MessageContentViewer.directionText.text=direction
MessageContentViewer.ccLabel.text=CC:
MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageContentViewer.viewInNewWindowButton.text=View in New Window
MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text
JPEGViewerDummy.jLabel1.text=You are looking at a JPEG file:
JPEGViewerDummy.jTextField1.text=jTextField1
PDFViewer.encryptedDialog=This document is password protected.
@ -1045,3 +1027,4 @@ manager.properties.lafError =\
manager.properties.brokenProperty = Broken default property {0} value: {1}
manager.properties.missingProperty = Missing default property {0} value: {1}

View File

@ -92,22 +92,7 @@ Metadata.toolTip=\u30d5\u30a1\u30a4\u30eb\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u3
Metadata.nodeText.nonFilePassedIn=\u30d5\u30a1\u30a4\u30eb\u4ee5\u5916\u306e\u3082\u306e\u304c\u901a\u904e\u3057\u307e\u3057\u305f
Metadata.nodeText.text=\u9001\u4fe1\u5143\u306eSleuth Kit\u306eistat\u30c4\u30fc\u30eb:
Metadata.nodeText.exceptionNotice.text=\u30d5\u30a1\u30a4\u30eb\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f:
MessageContentViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageContentViewer.headersScrollPane.TabConstraints.tabTitle=\u30d8\u30c3\u30c0\u30fc
MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle=\u30c6\u30ad\u30b9\u30c8
MessageContentViewer.htmlPane.TabConstraints.tabTitle=HTML
MessageContentViewer.fromText.text=\u9001\u4fe1\u5143\u30a2\u30c9\u30ec\u30b9\u3092\u3053\u3053\u306b\u8868\u793a
MessageContentViewer.fromLabel.text=\u5dee\u51fa\u4eba:
MessageContentViewer.datetimeText.text=\u65e5\u4ed8\u3092\u3053\u3053\u306b\u8868\u793a
MessageContentViewer.toText.text=\u5b9b\u5148\u306eTO\u30ea\u30b9\u30c8\u3092\u3053\u3053\u306b\u8868\u793a
MessageContentViewer.toLabel.text=\u5b9b\u5148:
MessageContentViewer.ccText.text=\u5b9b\u5148\u306eCC\u30ea\u30b9\u30c8\u3092\u3053\u3053\u306b\u8868\u793a
MessageContentViewer.subjectLabel.text=\u4ef6\u540d:
MessageContentViewer.subjectText.text=\u4ef6\u540d\u3092\u3053\u3053\u306b\u8868\u793a
MessageContentViewer.directionText.text=\u9001\u53d7\u4fe1\u306e\u7a2e\u5225
MessageContentViewer.ccLabel.text=CC:
MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle=\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb
MessageContentViewer.viewInNewWindowButton.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a
JPEGViewerDummy.jLabel1.text=JPEG\u30d5\u30a1\u30a4\u30eb\u304c\u8868\u793a\u3055\u308c\u3066\u3044\u307e\u3059:
JPEGViewerDummy.jTextField1.text=jTextField1
PListNode.KeyCol=\u30ad\u30fc
@ -164,5 +149,3 @@ MediaViewImagePanel.tagsMenu.text_1=\u30bf\u30b0\u30e1\u30cb\u30e5\u30fc
SQLiteViewer.readTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
# {0} - tableName
SQLiteViewer.selectTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u30ab\u30a6\u30f3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
DefaultArtifactContentViewer.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
DefaultArtifactContentViewer.copyMenuItem.text=\u30b3\u30d4\u30fc

View File

@ -1,364 +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"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="topPanel" min="-2" pref="860" max="-2" attributes="0"/>
<Component id="bottomPanel" min="-2" max="-2" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="topPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bottomPanel" min="-2" pref="297" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="topPanel">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="callDetailsPanel">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 150]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="First"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="25" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="callDetailsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="25" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="directionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="callLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="onLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="dateTimeLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="durationLabel" min="-2" pref="88" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<Component id="participantsListPanel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="485" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="callDetailsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="participantsListPanel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="directionLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="callLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="onLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="dateTimeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="durationLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="directionLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font component="directionLabel" property="font" relativeSize="true" size="0"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.directionLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="dateTimeLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.dateTimeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="durationLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.durationLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="callDetailsLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="callDetailsLabel" property="font" relativeSize="true" size="0"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.callDetailsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="onLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.onLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="callLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.callLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="participantsListPanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="350" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="58" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="otherAttributesPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Last"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="otherAttributesListPanel" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="25" max="-2" attributes="0"/>
<Component id="otherInfoLabel" min="-2" pref="372" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="otherInfoLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="otherAttributesListPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="otherInfoLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="otherInfoLabel" property="font" relativeSize="true" size="0"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.otherInfoLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="otherAttributesListPanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="413" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="88" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="bottomPanel">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="localAccountInfoPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="North"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="localAccountLabel" min="-2" max="-2" attributes="0"/>
<Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="jLabel4" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="localAccountIdLabel" min="-2" max="-2" attributes="0"/>
<Component id="dataSourceNameLabel" min="-2" pref="130" max="-2" attributes="0"/>
<Component id="deviceIdLabel" alignment="0" pref="437" max="32767" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="25" max="-2" attributes="0"/>
<Component id="sourceSectionLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace pref="243" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="sourceSectionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="localAccountLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="localAccountIdLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabel4" alignment="3" min="-2" pref="16" max="-2" attributes="0"/>
<Component id="dataSourceNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="deviceIdLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="22" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jLabel4">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.jLabel4.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="dataSourceNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.dataSourceNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jLabel2">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.jLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="deviceIdLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.deviceIdLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="localAccountLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.localAccountLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="localAccountIdLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.localAccountIdLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="sourceSectionLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="sourceSectionLabel" property="font" relativeSize="true" size="0"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="CallLogArtifactViewer.sourceSectionLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -1,723 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.JScrollPane;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle;
/**
* This is a viewer for TSK_CALLLOG artifacts.
*
*
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class CallLogArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer {
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
private final static Logger logger = Logger.getLogger(CallLogArtifactViewer.class.getName());
private static final long serialVersionUID = 1L;
private static final Set<Integer> HANDLED_ATTRIBUTE_TYPES = new HashSet<Integer>(Arrays.asList(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID()
));
/**
* Creates new form CalllogArtifactViewer
*/
public CallLogArtifactViewer() {
initComponents();
customizeComponents();
}
/**
* 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() {
topPanel = new javax.swing.JPanel();
callDetailsPanel = new javax.swing.JPanel();
directionLabel = new javax.swing.JLabel();
dateTimeLabel = new javax.swing.JLabel();
durationLabel = new javax.swing.JLabel();
callDetailsLabel = new javax.swing.JLabel();
onLabel = new javax.swing.JLabel();
callLabel = new javax.swing.JLabel();
participantsListPanel = new javax.swing.JPanel();
otherAttributesPanel = new javax.swing.JPanel();
otherInfoLabel = new javax.swing.JLabel();
otherAttributesListPanel = new javax.swing.JPanel();
bottomPanel = new javax.swing.JPanel();
localAccountInfoPanel = new javax.swing.JPanel();
jLabel4 = new javax.swing.JLabel();
dataSourceNameLabel = new javax.swing.JLabel();
jLabel2 = new javax.swing.JLabel();
deviceIdLabel = new javax.swing.JLabel();
localAccountLabel = new javax.swing.JLabel();
localAccountIdLabel = new javax.swing.JLabel();
sourceSectionLabel = new javax.swing.JLabel();
topPanel.setLayout(new java.awt.BorderLayout());
callDetailsPanel.setPreferredSize(new java.awt.Dimension(400, 150));
directionLabel.setFont(directionLabel.getFont());
org.openide.awt.Mnemonics.setLocalizedText(directionLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.directionLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(dateTimeLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.dateTimeLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(durationLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.durationLabel.text")); // NOI18N
callDetailsLabel.setFont(callDetailsLabel.getFont().deriveFont(callDetailsLabel.getFont().getStyle() | java.awt.Font.BOLD));
org.openide.awt.Mnemonics.setLocalizedText(callDetailsLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.callDetailsLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(onLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.onLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(callLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.callLabel.text")); // NOI18N
javax.swing.GroupLayout participantsListPanelLayout = new javax.swing.GroupLayout(participantsListPanel);
participantsListPanel.setLayout(participantsListPanelLayout);
participantsListPanelLayout.setHorizontalGroup(
participantsListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 350, Short.MAX_VALUE)
);
participantsListPanelLayout.setVerticalGroup(
participantsListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 58, Short.MAX_VALUE)
);
javax.swing.GroupLayout callDetailsPanelLayout = new javax.swing.GroupLayout(callDetailsPanel);
callDetailsPanel.setLayout(callDetailsPanelLayout);
callDetailsPanelLayout.setHorizontalGroup(
callDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(callDetailsPanelLayout.createSequentialGroup()
.addGap(25, 25, 25)
.addGroup(callDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(callDetailsLabel)
.addGroup(callDetailsPanelLayout.createSequentialGroup()
.addGap(25, 25, 25)
.addGroup(callDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(callDetailsPanelLayout.createSequentialGroup()
.addComponent(directionLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(callLabel))
.addGroup(callDetailsPanelLayout.createSequentialGroup()
.addComponent(onLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(dateTimeLabel)
.addGap(18, 18, 18)
.addComponent(durationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addComponent(participantsListPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(485, Short.MAX_VALUE))
);
callDetailsPanelLayout.setVerticalGroup(
callDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(callDetailsPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(callDetailsLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(participantsListPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(callDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(directionLabel)
.addComponent(callLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(callDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(onLabel)
.addComponent(dateTimeLabel)
.addComponent(durationLabel))
.addGap(20, 20, 20))
);
topPanel.add(callDetailsPanel, java.awt.BorderLayout.PAGE_START);
otherInfoLabel.setFont(otherInfoLabel.getFont().deriveFont(otherInfoLabel.getFont().getStyle() | java.awt.Font.BOLD));
org.openide.awt.Mnemonics.setLocalizedText(otherInfoLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.otherInfoLabel.text")); // NOI18N
javax.swing.GroupLayout otherAttributesListPanelLayout = new javax.swing.GroupLayout(otherAttributesListPanel);
otherAttributesListPanel.setLayout(otherAttributesListPanelLayout);
otherAttributesListPanelLayout.setHorizontalGroup(
otherAttributesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 413, Short.MAX_VALUE)
);
otherAttributesListPanelLayout.setVerticalGroup(
otherAttributesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 88, Short.MAX_VALUE)
);
javax.swing.GroupLayout otherAttributesPanelLayout = new javax.swing.GroupLayout(otherAttributesPanel);
otherAttributesPanel.setLayout(otherAttributesPanelLayout);
otherAttributesPanelLayout.setHorizontalGroup(
otherAttributesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(otherAttributesPanelLayout.createSequentialGroup()
.addGroup(otherAttributesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(otherAttributesListPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(otherAttributesPanelLayout.createSequentialGroup()
.addGap(25, 25, 25)
.addComponent(otherInfoLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 372, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGap(24, 24, 24))
);
otherAttributesPanelLayout.setVerticalGroup(
otherAttributesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(otherAttributesPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(otherInfoLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(otherAttributesListPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
topPanel.add(otherAttributesPanel, java.awt.BorderLayout.PAGE_END);
bottomPanel.setLayout(new java.awt.BorderLayout());
org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.jLabel4.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(dataSourceNameLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.dataSourceNameLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.jLabel2.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deviceIdLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.deviceIdLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(localAccountLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.localAccountLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(localAccountIdLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.localAccountIdLabel.text")); // NOI18N
sourceSectionLabel.setFont(sourceSectionLabel.getFont().deriveFont(sourceSectionLabel.getFont().getStyle() | java.awt.Font.BOLD));
org.openide.awt.Mnemonics.setLocalizedText(sourceSectionLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.sourceSectionLabel.text")); // NOI18N
javax.swing.GroupLayout localAccountInfoPanelLayout = new javax.swing.GroupLayout(localAccountInfoPanel);
localAccountInfoPanel.setLayout(localAccountInfoPanelLayout);
localAccountInfoPanelLayout.setHorizontalGroup(
localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(localAccountInfoPanelLayout.createSequentialGroup()
.addGroup(localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(localAccountInfoPanelLayout.createSequentialGroup()
.addGap(50, 50, 50)
.addGroup(localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(localAccountLabel)
.addComponent(jLabel2)
.addComponent(jLabel4))
.addGap(24, 24, 24)
.addGroup(localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(localAccountIdLabel)
.addComponent(dataSourceNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(deviceIdLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 437, Short.MAX_VALUE)))
.addGroup(localAccountInfoPanelLayout.createSequentialGroup()
.addGap(25, 25, 25)
.addComponent(sourceSectionLabel)))
.addContainerGap(243, Short.MAX_VALUE))
);
localAccountInfoPanelLayout.setVerticalGroup(
localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(localAccountInfoPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(sourceSectionLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(localAccountLabel)
.addComponent(localAccountIdLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel4, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(dataSourceNameLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel2)
.addComponent(deviceIdLabel))
.addContainerGap(22, Short.MAX_VALUE))
);
bottomPanel.add(localAccountInfoPanel, java.awt.BorderLayout.NORTH);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(topPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 860, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(bottomPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(topPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(bottomPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 297, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void customizeComponents() {
// disable the name label for now.
// this.toOrFromNameLabel.setVisible(false);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel bottomPanel;
private javax.swing.JLabel callDetailsLabel;
private javax.swing.JPanel callDetailsPanel;
private javax.swing.JLabel callLabel;
private javax.swing.JLabel dataSourceNameLabel;
private javax.swing.JLabel dateTimeLabel;
private javax.swing.JLabel deviceIdLabel;
private javax.swing.JLabel directionLabel;
private javax.swing.JLabel durationLabel;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel localAccountIdLabel;
private javax.swing.JPanel localAccountInfoPanel;
private javax.swing.JLabel localAccountLabel;
private javax.swing.JLabel onLabel;
private javax.swing.JPanel otherAttributesListPanel;
private javax.swing.JPanel otherAttributesPanel;
private javax.swing.JLabel otherInfoLabel;
private javax.swing.JPanel participantsListPanel;
private javax.swing.JLabel sourceSectionLabel;
private javax.swing.JPanel topPanel;
// End of variables declaration//GEN-END:variables
@Override
public void setArtifact(BlackboardArtifact artifact) {
this.removeAll();
this.initComponents();
this.customizeComponents();
CallLogViewData callLogViewData = null;
try {
callLogViewData = getCallLogViewData(artifact);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting attributes for Calllog artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
}
// update the view with the call log data
if (callLogViewData != null) {
updateView(callLogViewData);
}
// repaint
this.revalidate();
}
/**
* Updates the call log viewer UI components from the given data.
*
* @param callLogViewData Call log data to display.
*/
private void updateView(CallLogViewData callLogViewData) {
// populate call partcipants list.
updateParticipantsPanel(callLogViewData);
if (callLogViewData.getDirection() != null) {
this.directionLabel.setText(callLogViewData.getDirection());
} else {
this.directionLabel.setVisible(false);
this.callLabel.setVisible(false);
}
if (callLogViewData.getDateTimeStr() != null) {
this.dateTimeLabel.setText(callLogViewData.getDateTimeStr());
if (callLogViewData.getDuration() != null) {
this.durationLabel.setText(callLogViewData.getDuration());
} else {
this.durationLabel.setVisible(false);
}
} else {
this.dateTimeLabel.setVisible(false);
this.durationLabel.setVisible(false);
}
// Populate other attributs panel
updateOtherAttributesPanel(callLogViewData.getOtherAttributes());
// populate local account and data source
if (callLogViewData.getLocalAccountId() != null) {
// Vik-6383 find and display the persona for this account, and a button
this.localAccountIdLabel.setText(callLogViewData.getLocalAccountId());
} else {
this.localAccountLabel.setVisible(false);
this.localAccountIdLabel.setVisible(false);
}
if (callLogViewData.getDataSourceName() != null) {
this.dataSourceNameLabel.setText(callLogViewData.getDataSourceName());
}
if (callLogViewData.getDataSourceDeviceId() != null) {
this.deviceIdLabel.setText(callLogViewData.getDataSourceDeviceId());
}
}
/**
* Updates the Call participants panel.
*
* @param callLogViewData Call log data to display.
*/
private void updateParticipantsPanel(CallLogViewData callLogViewData) {
// create a gridbag layout to show each participant on one line
GridBagLayout gridBagLayout = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
constraints.gridy = 0;
constraints.insets = new java.awt.Insets(4, 0, 0, 0);
// show primary sender/receiver
showParticipant(callLogViewData.getNumberDesignator(), callLogViewData.getNumber(), gridBagLayout, constraints );
constraints.gridy++;
for (String recipient : callLogViewData.getOtherRecipients()) {
showParticipant(Bundle.CallLogArtifactViewer_number_to(), recipient, gridBagLayout, constraints );
constraints.gridy++;
}
participantsListPanel.setLayout(gridBagLayout);
participantsListPanel.revalidate();
}
/**
* Display a call participant in the view.
*
* @param participantDesignator Label to show - To/From.
* @param accountIdentifier account identifier for the participant.
* @param gridBagLayout
* @param constraints
*/
private void showParticipant(String participantDesignator, String accountIdentifier, GridBagLayout gridBagLayout, GridBagConstraints constraints) {
constraints.fill = GridBagConstraints.NONE;
constraints.weightx = 0;
constraints.gridx = 0;
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new Dimension(25, 0), new Dimension(25, 0), new Dimension(25, 0));
participantsListPanel.add(filler1, constraints);
constraints.gridx++;
javax.swing.JLabel toLabel = new javax.swing.JLabel();
toLabel.setText(participantDesignator);
gridBagLayout.setConstraints(toLabel, constraints);
participantsListPanel.add(toLabel);
constraints.gridx++;
javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new Dimension(5, 0), new Dimension(5, 0), new Dimension(5, 0));
participantsListPanel.add(filler2, constraints);
// Add attribute name label
constraints.gridx++;
// Add recipients number/Id
javax.swing.JLabel participantAccountLabel = new javax.swing.JLabel();
participantAccountLabel.setText(accountIdentifier);
gridBagLayout.setConstraints(participantAccountLabel, constraints);
participantsListPanel.add(participantAccountLabel);
// TBD Vik-6383 find and display the persona for this account, and a button
// constraints.gridx += 2;
// javax.swing.JButton personaButton = new javax.swing.JButton();
// personaButton.setText("Persona");
// gridBagLayout.setConstraints(personaButton, constraints);
// otherParticipantsListPanel.add(personaButton);
// add a filler to take up rest of the space
constraints.gridx++;
constraints.weightx = 1.0;
constraints.fill = GridBagConstraints.HORIZONTAL;
participantsListPanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)));
}
/**
* Updates the Other Attributes panel with the given list of attributes.
* This panel displays any uncommon attributes that might not be shown already.
*
* @param otherAttributes List of attributes to display.
*/
private void updateOtherAttributesPanel(Map<String, String> otherAttributes) {
if (otherAttributes == null || otherAttributes.isEmpty()) {
this.otherAttributesPanel.setVisible(false);
return;
}
// create a gridbag layout to show attribute on one line
GridBagLayout gridBagLayout = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
constraints.gridy = 0;
constraints.insets = new java.awt.Insets(4, 12, 0, 0);
for (Map.Entry<String, String> attribute : otherAttributes.entrySet()) {
constraints.fill = GridBagConstraints.NONE;
constraints.weightx = 0;
constraints.gridx = 0;
// Add a small horizontal filler at the beginning
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new Dimension(25, 0), new Dimension(25, 0), new Dimension(25, 0));
otherAttributesListPanel.add(filler1, constraints);
// Add attribute name label
constraints.gridx++;
javax.swing.JLabel attrNameLabel = new javax.swing.JLabel();
attrNameLabel.setText(attribute.getKey());
gridBagLayout.setConstraints(attrNameLabel, constraints);
this.otherAttributesListPanel.add(attrNameLabel);
// Add value
constraints.gridx += 2;
javax.swing.JLabel attrValueLabel = new javax.swing.JLabel();
attrValueLabel.setText(attribute.getValue());
gridBagLayout.setConstraints(attrValueLabel, constraints);
this.otherAttributesListPanel.add(attrValueLabel);
// add a filler to take up rest of the horizontal space
constraints.gridx++;
constraints.weightx = 1.0;
constraints.fill = GridBagConstraints.HORIZONTAL;
otherAttributesListPanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)));
constraints.gridy++;
}
otherAttributesListPanel.setLayout(gridBagLayout);
otherAttributesListPanel.revalidate();
}
@Override
public Component getComponent() {
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
@Override
public boolean isSupported(BlackboardArtifact artifact) {
return artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID();
}
/**
* Extracts data from the call log artifact for display in the view.
*
* @param artifact Artifact to extract data from.
* @return CallLogViewData Extracted data to be displayed.
* @throws TskCoreException
*/
@NbBundle.Messages({
"CallLogArtifactViewer_number_from=From",
"CallLogArtifactViewer_number_to=To"})
private CallLogViewData getCallLogViewData(BlackboardArtifact artifact) throws TskCoreException {
BlackboardAttribute directionAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION));
BlackboardAttribute recipientsAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
BlackboardAttribute numberAttr = null;
BlackboardAttribute localAccountAttr = null;
CallLogViewData callLogViewData = null;
String direction = null;
String numberDesignator = null;
if (directionAttr != null) {
// if direction is known, depending on the direction,
// the TO or the FROM attribute is the primary number of interest.
// and the other, if available, is possibly the local account of device owner.
direction = directionAttr.getValueString();
if (direction.equalsIgnoreCase("Incoming")) {
numberAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
numberDesignator = Bundle.CallLogArtifactViewer_number_from();
} else if (direction.equalsIgnoreCase("Outgoing")) {
numberAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
numberDesignator = Bundle.CallLogArtifactViewer_number_to();
}
}
// if direction isn't known, check all the usual attributes that may have the number/address
if (numberAttr == null) {
numberAttr = ObjectUtils.firstNonNull(
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
);
}
if (numberAttr != null) {
// check if it's a list of numbers and if so,
// split it, take the first one.
String[] numbers = numberAttr.getValueString().split(",");
// create a CallLogViewData with the primary number/id.
callLogViewData = new CallLogViewData(StringUtils.trim(numbers[0]));
// set direction
callLogViewData.setDirection(direction);
callLogViewData.setNumberDesignator(numberDesignator);
// get other recipients
extractOtherRecipeints(recipientsAttr, directionAttr, callLogViewData);
// get date, duration,
extractTimeAndDuration(artifact, callLogViewData);
// get the data source name and device id
Content dataSource = artifact.getDataSource();
callLogViewData.setDataSourceName(dataSource.getName());
String deviceId = ((DataSource) dataSource).getDeviceId();
callLogViewData.setDataSourceDeviceId(deviceId);
// set the local account, if it can be deduced
if (localAccountAttr != null) {
String attrValue = localAccountAttr.getValueString();
if (attrValue.equalsIgnoreCase(deviceId) == false && attrValue.contains(",") == false) {
callLogViewData.setLocalAccountId(attrValue);
}
}
// set any other additional attriubutes.
callLogViewData.setOtherAttributes(extractOtherAttributes(artifact));
}
return callLogViewData;
}
/**
* Extracts the other recipients numbers from the given attribute.
* Updates the given CallLogViewData with the other recipients list.
*
* @param recipientsAttr TO attribute, must not be null.
* @param directionAttr Direction attribute.
* @param callLogViewData CallLogViewData object to update.
*
*/
private void extractOtherRecipeints(BlackboardAttribute recipientsAttr, BlackboardAttribute directionAttr, CallLogViewData callLogViewData) {
if (recipientsAttr == null) {
return;
}
// check if it's a list of numbers and and if so,
// split it,
String[] numbers = recipientsAttr.getValueString().split(",");
List<String> otherNumbers = null;
int startIdx = 0;
// for an outgoing call, the first number has already been plucked as the primary recipient.
// for an incoming call with multiple recipeints, we can't distinguish the local account number, so everyone is "other"
if (directionAttr != null && directionAttr.getValueString().equalsIgnoreCase("Outgoing")) {
startIdx = 1;
}
if (numbers.length > 1) {
otherNumbers = new ArrayList<>();
for (int i = startIdx; i < numbers.length; i++) {
otherNumbers.add(StringUtils.trim(numbers[i]));
}
}
callLogViewData.setOtherRecipients(otherNumbers);
}
/**
* Returns the attributes from the given artifact that are not already
* displayed by the artifact viewer.
*
* @param artifact Call log artifact.
* @return Attribute names/values.
* @throws TskCoreException
*/
private Map<String, String> extractOtherAttributes(BlackboardArtifact artifact) throws TskCoreException {
List<BlackboardAttribute> attributes = artifact.getAttributes();
Map<String, String> otherAttributes = new HashMap<>();
for (BlackboardAttribute attr : attributes) {
if (HANDLED_ATTRIBUTE_TYPES.contains(attr.getAttributeType().getTypeID()) == false) {
otherAttributes.put(attr.getAttributeType().getDisplayName(), attr.getDisplayString());
}
}
return otherAttributes;
}
/**
* Extract the call time and duration from the artifact and saves in the
* CallLogViewData.
*
* @param artifact Call log artifact.
* @param callLogViewData CallLogViewData object to save the time & duration
* in.
*
* @throws TskCoreException
*/
private void extractTimeAndDuration(BlackboardArtifact artifact, CallLogViewData callLogViewData ) throws TskCoreException {
BlackboardAttribute startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START));
if (startTimeAttr == null) {
startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME));
}
if (startTimeAttr != null) {
long startTime = startTimeAttr.getValueLong();
Content content = artifact.getDataSource();
if (null != content && 0 != startTime) {
dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
callLogViewData.setDateTimeStr(dateFormatter.format(new java.util.Date(startTime * 1000)));
}
BlackboardAttribute endTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END));
if (endTimeAttr != null) {
long endTime = endTimeAttr.getValueLong();
if (endTime > 0 && (endTime - startTime) > 0) {
callLogViewData.setDuration(String.format("%d seconds", (endTime - startTime)));
}
}
}
}
}

View File

@ -1,170 +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,0,-74,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="namePanel">
<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="4" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="contactNameLabel" min="-2" pref="240" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="contactNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="contactNameLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="contactNameLabel" italic="true" property="font" relativeSize="true" size="6"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContactArtifactViewer.contactNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="phonesLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="phonesLabel" property="font" relativeSize="true" size="2"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContactArtifactViewer.phonesLabel.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="1" gridWidth="3" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JPanel" name="phoneNumbersPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="4" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
</Container>
<Component class="javax.swing.JLabel" name="emailsLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="emailsLabel" property="font" relativeSize="true" size="2"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContactArtifactViewer.emailsLabel.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="3" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JPanel" name="emailsPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="4" gridWidth="4" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
</Container>
<Component class="javax.swing.JLabel" name="othersLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="othersLabel" property="font" relativeSize="true" size="2"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContactArtifactViewer.othersLabel.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="5" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JPanel" name="otherAttrsPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="6" gridWidth="4" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler1">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 32767]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalGlue"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="7" gridWidth="4" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.Box$Filler" name="filler2">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.HorizontalGlue"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="4" gridY="0" gridWidth="1" gridHeight="8" fill="1" ipadX="2" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -1,320 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* This class displays TSK_CONTACT artifact.
*/
public class ContactArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer {
private final static Logger logger = Logger.getLogger(ContactArtifactViewer.class.getName());
private static final long serialVersionUID = 1L;
/**
* Creates new form for ContactArtifactViewer
*/
public ContactArtifactViewer() {
initComponents();
}
/**
* 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;
namePanel = new javax.swing.JPanel();
contactNameLabel = new javax.swing.JLabel();
phonesLabel = new javax.swing.JLabel();
phoneNumbersPanel = new javax.swing.JPanel();
emailsLabel = new javax.swing.JLabel();
emailsPanel = new javax.swing.JPanel();
othersLabel = new javax.swing.JLabel();
otherAttrsPanel = new javax.swing.JPanel();
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
setLayout(new java.awt.GridBagLayout());
contactNameLabel.setFont(contactNameLabel.getFont().deriveFont((contactNameLabel.getFont().getStyle() | java.awt.Font.ITALIC) | java.awt.Font.BOLD, contactNameLabel.getFont().getSize()+6));
org.openide.awt.Mnemonics.setLocalizedText(contactNameLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.contactNameLabel.text")); // NOI18N
javax.swing.GroupLayout namePanelLayout = new javax.swing.GroupLayout(namePanel);
namePanel.setLayout(namePanelLayout);
namePanelLayout.setHorizontalGroup(
namePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(namePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(contactNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 240, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
namePanelLayout.setVerticalGroup(
namePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(namePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(contactNameLabel)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
add(namePanel, gridBagConstraints);
phonesLabel.setFont(phonesLabel.getFont().deriveFont(phonesLabel.getFont().getStyle() | java.awt.Font.BOLD, phonesLabel.getFont().getSize()+2));
org.openide.awt.Mnemonics.setLocalizedText(phonesLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.phonesLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(phonesLabel, gridBagConstraints);
phoneNumbersPanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(phoneNumbersPanel, gridBagConstraints);
emailsLabel.setFont(emailsLabel.getFont().deriveFont(emailsLabel.getFont().getStyle() | java.awt.Font.BOLD, emailsLabel.getFont().getSize()+2));
org.openide.awt.Mnemonics.setLocalizedText(emailsLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.emailsLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(emailsLabel, gridBagConstraints);
emailsPanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 4;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(emailsPanel, gridBagConstraints);
othersLabel.setFont(othersLabel.getFont().deriveFont(othersLabel.getFont().getStyle() | java.awt.Font.BOLD, othersLabel.getFont().getSize()+2));
org.openide.awt.Mnemonics.setLocalizedText(othersLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.othersLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 5;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(othersLabel, gridBagConstraints);
otherAttrsPanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 6;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(otherAttrsPanel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 7;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weighty = 1.0;
add(filler1, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 4;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridheight = 8;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.ipadx = 2;
gridBagConstraints.weightx = 1.0;
add(filler2, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
@Override
public void setArtifact(BlackboardArtifact artifact) {
// wipe the panel clean
this.removeAll();
initComponents();
List<BlackboardAttribute> phoneNumList = new ArrayList<>();
List<BlackboardAttribute> emailList = new ArrayList<>();
List<BlackboardAttribute> nameList = new ArrayList<>();
List<BlackboardAttribute> otherList = new ArrayList<>();
try {
// Get all the attributes and group them by the section panels they go in
for (BlackboardAttribute bba : artifact.getAttributes()) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_PHONE")) {
phoneNumList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) {
emailList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) {
nameList.add(bba);
} else {
otherList.add(bba);
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting attributes for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
}
// update name section
updateNamePanel(nameList);
// update contact attributes sections
updateSection(phoneNumList, this.phonesLabel, this.phoneNumbersPanel);
updateSection(emailList, this.emailsLabel, this.emailsPanel);
updateSection(otherList, this.othersLabel, this.otherAttrsPanel);
// repaint
this.revalidate();
}
@Override
public Component getComponent() {
// Slap a vertical scrollbar on the panel.
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
/**
* Checks if the given artifact is supported by this viewer.
* This viewer supports TSK_CONTACT artifacts.
*
* @param artifact artifact to check.
* @return True if the artifact is supported, false otherwise.
*/
@Override
public boolean isSupported(BlackboardArtifact artifact) {
return artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID();
}
/**
* Updates the contact name in the view.
*
* @param attributesList
*/
private void updateNamePanel(List<BlackboardAttribute> attributesList) {
for (BlackboardAttribute bba : attributesList) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) {
contactNameLabel.setText(bba.getDisplayString());
break;
}
}
contactNameLabel.revalidate();
}
/**
* Updates the view by displaying the given list of attributes in the given section panel.
*
* @param sectionAttributesList list of attributes to display.
* @param sectionLabel section name label.
* @param sectionPanel section panel to display the attributes in.
*/
private void updateSection(List<BlackboardAttribute> sectionAttributesList, JLabel sectionLabel, JPanel sectionPanel) {
// If there are no attributes for tis section, hide the section panel and the section label
if (sectionAttributesList.isEmpty()) {
sectionLabel.setVisible(false);
sectionPanel.setVisible(false);
return;
}
// create a gridbag layout to show each attribute on one line
GridBagLayout gridBagLayout = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
constraints.gridy = 0;
constraints.insets = new java.awt.Insets(4, 12, 0, 0);
for (BlackboardAttribute bba : sectionAttributesList) {
constraints.fill = GridBagConstraints.NONE;
constraints.weightx = 0;
constraints.gridx = 0;
// Add a label for attribute type
javax.swing.JLabel attrTypeLabel = new javax.swing.JLabel();
String attrLabel = bba.getAttributeType().getDisplayName();
attrTypeLabel.setText(attrLabel);
// make type label bold - uncomment if needed.
//attrTypeLabel.setFont(attrTypeLabel.getFont().deriveFont(Font.BOLD, attrTypeLabel.getFont().getSize() ));
gridBagLayout.setConstraints(attrTypeLabel, constraints);
sectionPanel.add(attrTypeLabel);
// Add the attribute value
constraints.gridx++;
javax.swing.JLabel attrValueLabel = new javax.swing.JLabel();
attrValueLabel.setText(bba.getValueString());
gridBagLayout.setConstraints(attrValueLabel, constraints);
sectionPanel.add(attrValueLabel);
// add a filler to take up rest of the space
constraints.gridx++;
constraints.weightx = 1.0;
constraints.fill = GridBagConstraints.HORIZONTAL;
sectionPanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)));
constraints.gridy++;
}
sectionPanel.setLayout(gridBagLayout);
sectionPanel.revalidate();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel contactNameLabel;
private javax.swing.JLabel emailsLabel;
private javax.swing.JPanel emailsPanel;
private javax.swing.Box.Filler filler1;
private javax.swing.Box.Filler filler2;
private javax.swing.JPanel namePanel;
private javax.swing.JPanel otherAttrsPanel;
private javax.swing.JLabel othersLabel;
private javax.swing.JPanel phoneNumbersPanel;
private javax.swing.JLabel phonesLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -42,7 +42,7 @@ import org.w3c.dom.events.EventTarget;
* A file content viewer for HTML files.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class HtmlPanel extends javax.swing.JPanel {
public final class HtmlPanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(HtmlPanel.class.getName());
private static final long serialVersionUID = 1L;
@ -54,7 +54,7 @@ final class HtmlPanel extends javax.swing.JPanel {
/**
* Creates new form HtmlViewerPanel
*/
HtmlPanel() {
public HtmlPanel() {
initComponents();
Platform.runLater(() -> {
webView = new WebView();
@ -83,7 +83,7 @@ final class HtmlPanel extends javax.swing.JPanel {
*
* @param htmlText The HTML text to be applied to the text pane.
*/
void setHtmlText(String htmlText) {
public void setHtmlText(String htmlText) {
this.htmlText = htmlText;
refresh();
}
@ -91,7 +91,7 @@ final class HtmlPanel extends javax.swing.JPanel {
/**
* Clear the HTML in the text pane and disable the show/hide button.
*/
void reset() {
public void reset() {
Platform.runLater(() -> {
webView.getEngine().loadContent("", TEXT_TYPE);
});

View File

@ -131,6 +131,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
private double zoomRatio;
private double rotation; // Can be 0, 90, 180, and 270.
private boolean autoResize = true; // Auto resize when the user changes the size
// of the content viewer unless the user has used the zoom buttons.
private static final double[] ZOOM_STEPS = {
0.0625, 0.125, 0.25, 0.375, 0.5, 0.75,
1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10};
@ -197,7 +199,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
imageTaggingOptions.add(exportTagsMenuItem);
imageTaggingOptions.setPopupSize(300, 150);
//Disable image tagging for non-windows users or upon failure to load OpenCV.
if (!PlatformUtil.isWindowsOS() || !OpenCvLoader.openCvIsLoaded()) {
tagsMenu.setEnabled(false);
@ -266,8 +268,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
/**
* Handle tags menu item enabling and disabling given the state of the
* content viewer. For example, when the tags group is empty (no tags on image),
* disable delete menu item, hide menu item, and export menu item.
* content viewer. For example, when the tags group is empty (no tags on
* image), disable delete menu item, hide menu item, and export menu item.
*/
private void subscribeTagMenuItemsToStateChanges() {
pcs.addPropertyChangeListener((event) -> {
@ -394,6 +396,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}
try {
autoResize = true;
Image fxImage = readImageTask.get();
masterGroup.getChildren().clear();
tagsGroup.getChildren().clear();
@ -464,7 +467,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* current file.
*
* @param contentTags
*
* @return
*
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
@ -488,7 +493,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* appropriate type.
*
* @param contentTags
*
* @return
*
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
@ -685,16 +692,21 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}// </editor-fold>//GEN-END:initComponents
private void rotateLeftButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateLeftButtonActionPerformed
autoResize = false;
rotation = (rotation + 270) % 360;
updateView();
}//GEN-LAST:event_rotateLeftButtonActionPerformed
private void rotateRightButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateRightButtonActionPerformed
autoResize = false;
rotation = (rotation + 90) % 360;
updateView();
}//GEN-LAST:event_rotateRightButtonActionPerformed
private void zoomInButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
autoResize = false;
// Find the next zoom step.
for (int i = 0; i < ZOOM_STEPS.length; i++) {
if (zoomRatio < ZOOM_STEPS[i]) {
@ -706,6 +718,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}//GEN-LAST:event_zoomInButtonActionPerformed
private void zoomOutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
autoResize = false;
// Find the next zoom step.
for (int i = ZOOM_STEPS.length - 1; i >= 0; i--) {
if (zoomRatio > ZOOM_STEPS[i]) {
@ -717,11 +730,16 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}//GEN-LAST:event_zoomOutButtonActionPerformed
private void zoomResetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomResetButtonActionPerformed
autoResize = true;
resetView();
}//GEN-LAST:event_zoomResetButtonActionPerformed
private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
updateView();
if (autoResize) {
resetView();
} else {
updateView();
}
}//GEN-LAST:event_formComponentResized
/**
@ -801,6 +819,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* Creates an ImageTag instance from the ContentViewerTag.
*
* @param contentViewerTag
*
* @return
*/
private ImageTag buildImageTag(ContentViewerTag<ImageTagRegion> contentViewerTag) {
@ -883,17 +902,17 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
List<ContentTag> tags = Case.getCurrentCase().getServices()
.getTagsManager().getContentTagsByContent(file);
List<ContentViewerTag<ImageTagRegion>> contentViewerTags = getContentViewerTags(tags);
//Pull out image tag regions
Collection<ImageTagRegion> regions = contentViewerTags.stream()
.map(cvTag -> cvTag.getDetails()).collect(Collectors.toList());
//Apply tags to image and write to file
BufferedImage taggedImage = ImageTagsUtil.getImageWithTags(file, regions);
Path output = Paths.get(exportChooser.getSelectedFile().getPath(),
FilenameUtils.getBaseName(file.getName()) + "-with_tags.png"); //NON-NLS
ImageIO.write(taggedImage, "png", output.toFile());
JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport());
} catch (Exception ex) { //Runtime exceptions may spill out of ImageTagsUtil from JavaFX.
//This ensures we (devs and users) have something when it doesn't work.

View File

@ -37,14 +37,14 @@ import org.sleuthkit.autopsy.texttranslation.ui.TranslateTextTask;
/**
* This is a panel for translation with a subcomponent that allows for translation.
*/
class TranslatablePanel extends JPanel {
public class TranslatablePanel extends JPanel {
/**
* This is an exception that can occur during the normal operation of the translatable
* panel. For instance, this exception can be thrown if it is not possible to set the child
* content to the provided content string.
*/
class TranslatablePanelException extends Exception {
public class TranslatablePanelException extends Exception {
public static final long serialVersionUID = 1L;
TranslatablePanelException(String message) {
@ -61,7 +61,7 @@ class TranslatablePanel extends JPanel {
* This describes a child component to be placed as a child of this panel. The child received
* from {@link #getRootComponent() getRootComponent() } will listen for content updates from setContent().
*/
interface ContentComponent {
public interface ContentComponent {
/**
* This method gets root component of the translation panel.
* @return the root component to insert into the translatable panel
@ -191,7 +191,7 @@ class TranslatablePanel extends JPanel {
@Messages({"TranslatablePanel.comboBoxOption.originalText=Original Text",
"TranslatablePanel.comboBoxOption.translatedText=Translated Text"})
TranslatablePanel(ContentComponent contentComponent) {
public TranslatablePanel(ContentComponent contentComponent) {
this(
contentComponent,
Bundle.TranslatablePanel_comboBoxOption_originalText(),
@ -263,7 +263,7 @@ class TranslatablePanel extends JPanel {
* This resets the component to an empty state and sets the translation bar visibility
* based on whether there is a provider.
*/
final void reset() {
public final void reset() {
setContent(null, null);
}
@ -272,7 +272,7 @@ class TranslatablePanel extends JPanel {
* @param content the content for the panel
* @param contentDescriptor the content descriptor to be used in error messages
*/
void setContent(String content, String contentDescriptor) {
public void setContent(String content, String contentDescriptor) {
cancelPendingTranslation();
setTranslationEnabled();
this.translateComboBox.setSelectedIndex(0);

View File

@ -0,0 +1,76 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import javax.swing.JButton;
import javax.swing.JLabel;
/**
* A data bag for the persona searching thread. It wraps the account id to
* search for, and the UI label and button to update once the search completes.
*/
class AccountPersonaSearcherData {
// Account identifier to search personas for.
private final String accountIdentifer;
// Persona name label to be updated when the search is complete.
private final JLabel personaNameLabel;
// Persona action button to be updated when the search is complete
private final JButton personaActionButton;
/**
* Constructor.
*
* @param accountIdentifer Account identifier.
* @param personaNameLabel Persona name label.
* @param personaActionButton Persona button.
*/
AccountPersonaSearcherData(String accountIdentifer, JLabel personaNameLabel, JButton personaActionButton) {
this.accountIdentifer = accountIdentifer;
this.personaNameLabel = personaNameLabel;
this.personaActionButton = personaActionButton;
}
/**
* Gets the account identifier.
*
* @return Account identifier.
*/
public String getAccountIdentifer() {
return accountIdentifer;
}
/**
* Gets the persona name label.
*
* @return Persona name label.
*/
public JLabel getPersonaNameLabel() {
return personaNameLabel;
}
/**
* Gets the persona button.
*
* @return Persona button.
*/
public JButton getPersonaActionButton() {
return personaActionButton;
}
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component;
import org.sleuthkit.datamodel.BlackboardArtifact;

View File

@ -0,0 +1,34 @@
# Copyright 2020 Basis Technology Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS
# IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language
# governing permissions and limitations under the License.
#
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
MessageArtifactViewer.ccLabel.text=CC:
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageArtifactViewer.toText.text=to list goes here
MessageArtifactViewer.toLabel.text=To:
MessageArtifactViewer.htmlPane.TabConstraints.tabTitle=HTML
MessageArtifactViewer.fromText.text=from address goes here
MessageArtifactViewer.datetimeText.text=date goes here
MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle=Headers
MessageArtifactViewer.fromLabel.text=From:
MessageArtifactViewer.accountsTab.TabConstraints.tabTitle=Accounts
MessageArtifactViewer.directionText.text=direction
MessageArtifactViewer.subjectText.text=subject goes here
MessageArtifactViewer.viewInNewWindowButton.text=View in New Window
MessageArtifactViewer.subjectLabel.text=Subject:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageArtifactViewer.ccText.text=cc list goes here
MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text

View File

@ -0,0 +1,85 @@
# Copyright 2020 Basis Technology Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS
# IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language
# governing permissions and limitations under the License.
#
CalllogArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas.
CallLogArtifactViewer_heading_metadata=Metadata
CallLogArtifactViewer_heading_others=Other Attributes
CallLogArtifactViewer_heading_parties=Parties
CallLogArtifactViewer_heading_Source=Source
CallLogArtifactViewer_label_datasource=Data Source
CallLogArtifactViewer_label_date=Date
CallLogArtifactViewer_label_direction=Direction
CallLogArtifactViewer_label_duration=Duration
CallLogArtifactViewer_label_from=From
CallLogArtifactViewer_label_to=To
CallLogArtifactViewer_suffix_local=(Local)
CallLogArtifactViewer_value_unknown=Unknown
CommunicationArtifactViewerHelper_menuitem_copy=Copy
CommunicationArtifactViewerHelper_persona_button_create=Create
CommunicationArtifactViewerHelper_persona_button_view=View
CommunicationArtifactViewerHelper_persona_label=Persona:
CommunicationArtifactViewerHelper_persona_searching=Searching...
CommunicationArtifactViewerHelper_persona_unknown=Unknown
ContactArtifactViewer.contactImage.text=
ContactArtifactViewer_contactname_unknown=Unknown
ContactArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas.
ContactArtifactViewer_emails_header=Email
ContactArtifactViewer_found_all_accounts_label=All accounts found.
ContactArtifactViewer_heading_Source=Source
ContactArtifactViewer_label_datasource=Data Source
ContactArtifactViewer_missing_account_label=Missing contact account
ContactArtifactViewer_others_header=Other
ContactArtifactViewer_persona_account_justification=Account found in Contact artifact
ContactArtifactViewer_persona_button_new=Create
ContactArtifactViewer_persona_button_view=View
ContactArtifactViewer_persona_header=Persona
ContactArtifactViewer_persona_label=Persona
ContactArtifactViewer_persona_match_num=Match
ContactArtifactViewer_persona_no_match=No matches found
ContactArtifactViewer_persona_searching=Searching...
ContactArtifactViewer_persona_unknown=Unknown
ContactArtifactViewer_phones_header=Phone
ContactArtifactViewer_plural_suffix=s
DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database
DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database
DefaultArtifactContentViewer.attrsTableHeader.sources=Source(s)
DefaultArtifactContentViewer.attrsTableHeader.type=Type
DefaultArtifactContentViewer.attrsTableHeader.value=Value
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
MessageAccountPanel_button_create_label=Create
MessageAccountPanel_button_view_label=View
MessageAccountPanel_persona_label=Persona:
MessageAccountPanel_unknown_label=Unknown
MessageArtifactViewer.AttachmentPanel.title=Attachments
MessageArtifactViewer.ccLabel.text=CC:
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageArtifactViewer.toText.text=to list goes here
MessageArtifactViewer.toLabel.text=To:
MessageArtifactViewer.htmlPane.TabConstraints.tabTitle=HTML
MessageArtifactViewer.fromText.text=from address goes here
MessageArtifactViewer.datetimeText.text=date goes here
MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle=Headers
MessageArtifactViewer.fromLabel.text=From:
MessageArtifactViewer.accountsTab.TabConstraints.tabTitle=Accounts
MessageArtifactViewer.directionText.text=direction
MessageArtifactViewer.subjectText.text=subject goes here
MessageArtifactViewer.viewInNewWindowButton.text=View in New Window
MessageArtifactViewer.subjectLabel.text=Subject:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageArtifactViewer.ccText.text=cc list goes here
MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text
# {0} - Persona count
PersonaDisplayTask_persona_count_suffix=(1 of {0})

View File

@ -0,0 +1,19 @@
DefaultArtifactContentViewer.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
DefaultArtifactContentViewer.copyMenuItem.text=\u30b3\u30d4\u30fc
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageArtifactViewer.toText.text=\u5b9b\u5148\u306eTO\u30ea\u30b9\u30c8\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.toLabel.text=\u5b9b\u5148:
MessageArtifactViewer.htmlPane.TabConstraints.tabTitle=HTML
MessageArtifactViewer.fromText.text=\u9001\u4fe1\u5143\u30a2\u30c9\u30ec\u30b9\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.datetimeText.text=\u65e5\u4ed8\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle=\u30d8\u30c3\u30c0\u30fc
MessageArtifactViewer.fromLabel.text=\u5dee\u51fa\u4eba:
MessageArtifactViewer.directionText.text=\u9001\u53d7\u4fe1\u306e\u7a2e\u5225
MessageArtifactViewer.subjectText.text=\u4ef6\u540d\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.viewInNewWindowButton.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a
MessageArtifactViewer.subjectLabel.text=\u4ef6\u540d:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb
MessageArtifactViewer.ccText.text=\u5b9b\u5148\u306eCC\u30ea\u30b9\u30c8\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.ccLabel.text=CC:

View File

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

View File

@ -0,0 +1,502 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.JScrollPane;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Artifact viewer for Call log artifacts.
*
* Displays the To/From and other parties, and metadata for a call.
*/
@ServiceProvider(service = ArtifactContentViewer.class)
public class CallLogArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer {
private final static Logger logger = Logger.getLogger(CallLogArtifactViewer.class.getName());
private static final long serialVersionUID = 1L;
private static final Set<Integer> HANDLED_ATTRIBUTE_TYPES = new HashSet<Integer>(Arrays.asList(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID()
));
private GridBagLayout m_gridBagLayout = new GridBagLayout();
private GridBagConstraints m_constraints = new GridBagConstraints();
private PersonaAccountFetcher currentAccountFetcher = null;
/**
* Creates new form CallLogArtifactViewer.
*/
public CallLogArtifactViewer() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
setLayout(new java.awt.GridBagLayout());
}// </editor-fold>//GEN-END:initComponents
@Override
public void setArtifact(BlackboardArtifact artifact) {
resetComponent();
if (artifact == null) {
return;
}
CallLogViewData callLogViewData = null;
try {
callLogViewData = getCallLogViewData(artifact);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting attributes for Calllog artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
}
// update the view with the call log data
if (callLogViewData != null) {
List<AccountPersonaSearcherData> personaSearchDataList = updateView(callLogViewData);
if(!personaSearchDataList.isEmpty()) {
currentAccountFetcher = new PersonaAccountFetcher(artifact, personaSearchDataList, this);
currentAccountFetcher.execute();
} else {
currentAccountFetcher = null;
}
}
// repaint
this.revalidate();
}
/**
* Extracts data from the call log artifact for display in the view.
*
* @param artifact Artifact to extract data from.
*
* @return CallLogViewData Extracted data to be displayed.
*
* @throws TskCoreException
*/
private CallLogViewData getCallLogViewData(BlackboardArtifact artifact) throws TskCoreException {
if (artifact == null) {
return null;
}
BlackboardAttribute directionAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION));
BlackboardAttribute toAccountAttr = null;
BlackboardAttribute fromAccountAttr = null;
BlackboardAttribute localAccountAttr = null;
CallLogViewData callLogViewData = null;
String direction = null;
String fromAccountIdentifier = null;
String toAccountIdentifier = null;
List<String> otherParties = null;
Content dataSource = artifact.getDataSource();
String deviceId = ((DataSource) dataSource).getDeviceId();
if (directionAttr != null) {
direction = directionAttr.getValueString();
if (direction.equalsIgnoreCase("Incoming")) {
fromAccountAttr = ObjectUtils.firstNonNull(
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
);
toAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
} else if (direction.equalsIgnoreCase("Outgoing")) {
toAccountAttr = ObjectUtils.firstNonNull(
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
);
fromAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
}
}
// if direction isn't known, check all the usual attributes that may have the number/address
// in the absence of sufficent data, any number available will be displayed as a From address.
if (fromAccountAttr == null) {
fromAccountAttr = ObjectUtils.firstNonNull(
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
);
}
// get the from account address
if (fromAccountAttr != null) {
String fromAccountAttrValue = fromAccountAttr.getValueString();
if (fromAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
fromAccountIdentifier = fromAccountAttrValue;
}
}
if (toAccountAttr != null) {
// TO may be a list of comma separated values.
String[] numbers = toAccountAttr.getValueString().split(",");
String toAccountAttrValue = StringUtils.trim(numbers[0]);
if (toAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
toAccountIdentifier = toAccountAttrValue;
}
// if more than one To address, then stick the rest of them in the
// "Other parties" list.
if (numbers.length > 1) {
otherParties = new ArrayList<>();
for (int i = 1; i < numbers.length; i++) {
otherParties.add(StringUtils.trim(numbers[i]));
}
}
}
// if we have at least one address attribute
if (null != fromAccountAttr || null != toAccountAttr) {
callLogViewData = new CallLogViewData(fromAccountIdentifier, toAccountIdentifier);
callLogViewData.setDirection(direction);
callLogViewData.setOtherParties(otherParties);
extractTimeAndDuration(artifact, callLogViewData);
callLogViewData.setDataSourceName(dataSource.getName());
// set local account, if it can be deduced.
if (localAccountAttr != null) {
String attrValue = localAccountAttr.getValueString();
// value must be a singular address and not a deviceId to be the local account id
if (attrValue.equalsIgnoreCase(deviceId) == false && attrValue.contains(",") == false) {
callLogViewData.setLocalAccountId(attrValue);
}
}
callLogViewData.setOtherAttributes(extractOtherAttributes(artifact));
}
return callLogViewData;
}
/**
* Extract the call time and duration from the artifact and saves in the
* CallLogViewData.
*
* @param artifact Call log artifact.
* @param callLogViewData CallLogViewData object to save the time & duration
* in.
*
* @throws TskCoreException
*/
private void extractTimeAndDuration(BlackboardArtifact artifact, CallLogViewData callLogViewData) throws TskCoreException {
BlackboardAttribute startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START));
if (startTimeAttr == null) {
startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME));
}
if (startTimeAttr != null) {
long startTime = startTimeAttr.getValueLong();
callLogViewData.setDateTimeStr(startTimeAttr.getDisplayString());
BlackboardAttribute endTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END));
if (endTimeAttr != null) {
long endTime = endTimeAttr.getValueLong();
if (endTime > 0 && (endTime - startTime) > 0) {
callLogViewData.setDuration(String.format("%d seconds", (endTime - startTime)));
}
}
}
}
/**
* Returns the attributes from the given artifact that are not already
* displayed by the artifact viewer.
*
* @param artifact Call log artifact.
*
* @return Attribute names/values.
*
* @throws TskCoreException
*/
private Map<String, String> extractOtherAttributes(BlackboardArtifact artifact) throws TskCoreException {
List<BlackboardAttribute> attributes = artifact.getAttributes();
Map<String, String> otherAttributes = new HashMap<>();
for (BlackboardAttribute attr : attributes) {
if (HANDLED_ATTRIBUTE_TYPES.contains(attr.getAttributeType().getTypeID()) == false) {
otherAttributes.put(attr.getAttributeType().getDisplayName(), attr.getDisplayString());
}
}
return otherAttributes;
}
/**
* Update the viewer with the call log data.
*
* @param callLogViewData Call log data to update the view with.
*
* @return List of AccountPersonaSearcherData objects.
*/
@NbBundle.Messages({
"CallLogArtifactViewer_heading_parties=Parties",
"CallLogArtifactViewer_value_unknown=Unknown",
"CallLogArtifactViewer_label_from=From",
"CallLogArtifactViewer_label_to=To"
})
private List<AccountPersonaSearcherData> updateView(CallLogViewData callLogViewData) {
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_parties());
List<AccountPersonaSearcherData> dataList = new ArrayList<>();
// Display From address
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_from());
if (callLogViewData.getFromAccount() != null) {
// check if this is local account
String accountDisplayString = getAccountDisplayString(callLogViewData.getFromAccount(), callLogViewData);
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
// show persona
dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()));
} else {
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown());
}
// Display To:
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to());
if (callLogViewData.getToAccount() != null) {
String accountDisplayString = getAccountDisplayString(callLogViewData.getToAccount(), callLogViewData);
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()));
} else {
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown());
}
// Display other parties
for (String otherParty : callLogViewData.getOtherParties()) {
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, otherParty);
dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty));
}
updateMetadataView(callLogViewData);
updateOtherAttributesView(callLogViewData);
updateSourceView(callLogViewData);
if (CentralRepository.isEnabled() == false) {
showCRDisabledMessage();
}
CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
this.setLayout(m_gridBagLayout);
this.revalidate();
this.repaint();
return dataList;
}
/**
* Update the call log meta data section.
*
* @param callLogViewData Call log data.
*/
@NbBundle.Messages({
"CallLogArtifactViewer_heading_metadata=Metadata",
"CallLogArtifactViewer_label_direction=Direction",
"CallLogArtifactViewer_label_date=Date",
"CallLogArtifactViewer_label_duration=Duration"
})
private void updateMetadataView(CallLogViewData callLogViewData) {
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_metadata());
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_direction());
if (callLogViewData.getDirection() != null) {
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDirection());
} else {
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown());
}
if (callLogViewData.getDateTimeStr() != null) {
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_date());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDateTimeStr());
}
if (callLogViewData.getDuration() != null) {
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_duration());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDuration());
}
}
/**
* Update the call log source section.
*
* @param callLogViewData
*/
@NbBundle.Messages({
"CallLogArtifactViewer_heading_Source=Source",
"CallLogArtifactViewer_label_datasource=Data Source",})
private void updateSourceView(CallLogViewData callLogViewData) {
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_Source());
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_datasource());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDataSourceName());
}
/**
* Update the other attributes section.
*
* @param callLogViewData Call log data.
*/
@NbBundle.Messages({
"CallLogArtifactViewer_heading_others=Other Attributes"
})
private void updateOtherAttributesView(CallLogViewData callLogViewData) {
if (callLogViewData.getOtherAttributes().isEmpty()) {
return;
}
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_others());
for (Map.Entry<String, String> entry : callLogViewData.getOtherAttributes().entrySet()) {
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, entry.getKey());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, entry.getValue());
}
}
@NbBundle.Messages({
"CalllogArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas."
})
private void showCRDisabledMessage() {
CommunicationArtifactViewerHelper.addBlankLine(this, m_gridBagLayout, m_constraints);
m_constraints.gridy++;
CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
m_constraints.gridy++;
}
/**
* Returns display string for a account. Checks if the given account is the
* local account, if it is known. If it is, it appends a "(Local)" suffix to
* account display string.
*
* @param accountIdentifier Account identifier to check.
* @param callLogViewDataNew Call log data which may have the lock account.
*
* @return Account string to display.
*/
@NbBundle.Messages({
"CallLogArtifactViewer_suffix_local=(Local)",})
private String getAccountDisplayString(String accountIdentifier, CallLogViewData callLogViewDataNew) {
String accountDisplayValue = accountIdentifier;
if (callLogViewDataNew.getLocalAccountId() != null && callLogViewDataNew.getLocalAccountId().equalsIgnoreCase(accountIdentifier)) {
accountDisplayValue += " " + Bundle.CallLogArtifactViewer_suffix_local();
}
return accountDisplayValue;
}
@Override
public Component getComponent() {
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
@Override
public boolean isSupported(BlackboardArtifact artifact) {
return (artifact != null)
&& (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID());
}
/**
* Resets all artifact specific state.
*/
private void resetComponent() {
// cancel any outstanding persona searching threads.
if(currentAccountFetcher != null && !currentAccountFetcher.isDone()) {
currentAccountFetcher.cancel(true);
currentAccountFetcher = null;
}
// clear the panel
this.removeAll();
this.setLayout(null);
m_gridBagLayout = new GridBagLayout();
m_constraints = new GridBagConstraints();
m_constraints.anchor = GridBagConstraints.FIRST_LINE_START;
m_constraints.gridy = 0;
m_constraints.gridx = 0;
m_constraints.weighty = 0.0;
m_constraints.weightx = 0.0; // keep components fixed horizontally.
m_constraints.insets = new java.awt.Insets(0, CommunicationArtifactViewerHelper.LEFT_INSET, 0, 0);
m_constraints.fill = GridBagConstraints.NONE;
}
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.util.ArrayList;
import java.util.Collection;
@ -29,50 +29,61 @@ import java.util.Map;
*/
final class CallLogViewData {
// primary to/from number/adddress/accountId
private String number;
private String numberTypeDesignator; // to for from designator
private String name = null;
private String fromAccount = null;
private String toAccount = null;
// account identifier of the device owner, if known.
// will be one of the to or from account.
private String localAccountId = null;
private String direction;
private String dateTimeStr = null;
private String duration = null;
private Collection<String> otherRecipients = new ArrayList<>();
private String dataSourceName = null;
private String dataSourceDeviceId = null;
private String localAccountId = null; // number/accountId of device owner, may not be always known
// Account identifers of other parties in the call.
private Collection<String> otherParties = new ArrayList<>();
private Map<String, String> otherAttributes = new HashMap<>();
CallLogViewData(String number) {
this(number, null);
private String dataSourceName = null;
/**
* Constructor.
*
* @param fromAccount From account identifier, may be null;
* @param toAccount To account identifier, may be null;
*/
CallLogViewData(String fromAccount, String toAccount) {
this(fromAccount, toAccount, null);
}
CallLogViewData(String number, String direction) {
this.number = number;
/**
* Constructor.
*
* @param fromAccount From account identifier, may be null;
* @param toAccount To account identifier, may be null;
* @param direction Direction, may be null.
*/
CallLogViewData(String fromAccount, String toAccount, String direction) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.direction = direction;
}
String getNumber() {
return number;
String getFromAccount() {
return fromAccount;
}
void setNumber(String number) {
this.number = number;
void setFromAccount(String fromAccount) {
this.fromAccount = fromAccount;
}
public String getNumberDesignator() {
return numberTypeDesignator;
String getToAccount() {
return toAccount;
}
public void setNumberDesignator(String numberDesignator) {
this.numberTypeDesignator = numberDesignator;
}
String getName() {
return name;
}
void setName(String name) {
this.name = name;
void setToAccount(String toAccount) {
this.toAccount = toAccount;
}
String getDirection() {
@ -91,14 +102,6 @@ final class CallLogViewData {
this.dataSourceName = dataSourceName;
}
String getDataSourceDeviceId() {
return dataSourceDeviceId;
}
void setDataSourceDeviceId(String dataSourceDeviceId) {
this.dataSourceDeviceId = dataSourceDeviceId;
}
String getDateTimeStr() {
return dateTimeStr;
}
@ -115,13 +118,13 @@ final class CallLogViewData {
this.duration = duration;
}
Collection<String> getOtherRecipients() {
return Collections.unmodifiableCollection(otherRecipients);
Collection<String> getOtherParties() {
return Collections.unmodifiableCollection(otherParties);
}
void setOtherRecipients(Collection<String> otherParticipants) {
void setOtherParties(Collection<String> otherParticipants) {
if (otherParticipants != null) {
this.otherRecipients = new ArrayList<>(otherParticipants);
this.otherParties = new ArrayList<>(otherParticipants);
}
}
@ -130,7 +133,7 @@ final class CallLogViewData {
}
public void setOtherAttributes(Map<String, String> otherAttributes) {
if (otherRecipients != null) {
if (otherAttributes != null) {
this.otherAttributes = new HashMap<>(otherAttributes);
}
}

View File

@ -0,0 +1,459 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
/**
*
* A class to help display a communication artifact in a panel using a
* gridbaglayout.
*/
final class CommunicationArtifactViewerHelper {
// Number of columns in the gridbag layout.
private final static int MAX_COLS = 4;
final static int LEFT_INSET = 12;
/**
* Empty private constructor
*/
private CommunicationArtifactViewerHelper() {
}
/**
* Adds a new heading to the panel.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param headerString Heading string to display.
*
* @return JLabel Heading label added.
*/
static JLabel addHeader(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String headerString) {
Insets savedInsets = constraints.insets;
// create label for heading
javax.swing.JLabel headingLabel = new javax.swing.JLabel();
// add a blank line before the start of new section, unless it's
// the first section
if (constraints.gridy != 0) {
addBlankLine(panel, gridbagLayout, constraints);
}
constraints.gridy++;
constraints.gridx = 0;
// let the header span all of the row
constraints.gridwidth = MAX_COLS;
constraints.insets = new Insets(0, 0, 0, 0); // No inset for header
// set text
headingLabel.setText(headerString);
// make it large and bold
headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2));
// add to panel
gridbagLayout.setConstraints(headingLabel, constraints);
panel.add(headingLabel);
// reset constraints to normal
constraints.gridwidth = 1;
// add line end glue
addLineEndGlue(panel, gridbagLayout, constraints);
//restore insets
constraints.insets = savedInsets;
return headingLabel;
}
/**
* Adds the given component to the panel.
*
* Caller must know what it's doing and set up all the constraints properly.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param component Component to add.
*/
static void addComponent(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, JComponent component) {
// add to panel
gridbagLayout.setConstraints(component, constraints);
panel.add(component);
}
/**
* Adds a filler/glue at the end of the line to keep the other columns
* aligned, in case the panel is resized.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
*/
static void addLineEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
// Place the filler just past the last column.
constraints.gridx = MAX_COLS;
double savedWeightX = constraints.weightx;
int savedFill = constraints.fill;
constraints.weightx = 1.0; // take up all the horizontal space
constraints.fill = GridBagConstraints.BOTH;
javax.swing.Box.Filler horizontalFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0));
gridbagLayout.setConstraints(horizontalFiller, constraints);
panel.add(horizontalFiller);
// restore fill & weight
constraints.fill = savedFill;
constraints.weightx = savedWeightX;
}
/**
* Adds a filler/glue at the bottom of the panel to keep the data rows
* aligned, in case the panel is resized.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
*/
static void addPageEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
constraints.gridx = 0;
double savedWeighty = constraints.weighty;
int savedFill = constraints.fill;
constraints.weighty = 1.0; // take up all the vertical space
constraints.fill = GridBagConstraints.VERTICAL;
javax.swing.Box.Filler vertFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(0, 32767));
gridbagLayout.setConstraints(vertFiller, constraints);
panel.add(vertFiller, constraints);
//Resore weight & fill
constraints.weighty = savedWeighty;
constraints.fill = savedFill;
}
/**
* Adds a blank line to the panel.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
*/
static void addBlankLine(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
constraints.gridy++;
constraints.gridx = 0;
javax.swing.JLabel filler = new javax.swing.JLabel(" ");
gridbagLayout.setConstraints(filler, constraints);
panel.add(filler);
addLineEndGlue(panel, gridbagLayout, constraints);
}
/**
* Adds a label/key to the panel at col 0.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Key name to display.
*
* @return Label added.
*/
static JLabel addKey(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String keyString) {
return addKeyAtCol(panel, gridbagLayout, constraints, keyString, 0);
}
/**
* Adds a label/key to the panel at specified column.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Key name to display.
* @param gridx column index, must be less than MAX_COLS - 1.
*
* @return Label added.
*/
static JLabel addKeyAtCol(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String keyString, int gridx) {
// create label
javax.swing.JLabel keyLabel = new javax.swing.JLabel();
constraints.gridy++;
constraints.gridx = gridx < MAX_COLS - 1 ? gridx : MAX_COLS - 2;
// set text
keyLabel.setText(keyString + ": ");
// add to panel
gridbagLayout.setConstraints(keyLabel, constraints);
panel.add(keyLabel);
return keyLabel;
}
/**
* Adds a value string to the panel at col 1.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Value string to display.
*
* @return Label added.
*/
static JLabel addValue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String valueString) {
return addValueAtCol(panel, gridbagLayout, constraints, valueString, 1);
}
/**
* Adds a value string to the panel at specified column.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Value string to display.
* @param gridx Column index, must be less than MAX_COLS;
*
* @return Label added.
*/
static JLabel addValueAtCol(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String valueString, int gridx) {
// create label,
javax.swing.JLabel valueField = new javax.swing.JLabel();
constraints.gridx = gridx < MAX_COLS ? gridx : MAX_COLS - 1;
int savedGridwidth = constraints.gridwidth;
// let the value span 2 cols
constraints.gridwidth = 2;
// set text
valueField.setText(valueString);
// attach a right click menu with Copy option
valueField.addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mouseClicked(java.awt.event.MouseEvent evt) {
valueLabelMouseClicked(evt, valueField);
}
});
// add label to panel
gridbagLayout.setConstraints(valueField, constraints);
panel.add(valueField);
// restore constraints
constraints.gridwidth = savedGridwidth;
// end the line
addLineEndGlue(panel, gridbagLayout, constraints);
return valueField;
}
/**
* Displays a message string, starting at column 0, and spanning the entire
* row.
*
* @param panel Panel to show.
* @param gridbagLayout Layout to use.
* @param constraints Constraints to use.
*
* @param messageString Message to display.
*
* @return Label for message added.
*/
static JLabel addMessageRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String messageString) {
return addMessageRow(panel, gridbagLayout, constraints, messageString, 0);
}
/**
* Displays a message string, starting at specified column, and spanning the
* entire row.
*
* @param panel Panel to show.
* @param gridbagLayout Layout to use.
* @param constraints Constraints to use.
*
* @param messageString Message to display.
*
* @return Label for message added.
*/
static JLabel addMessageRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String messageString, int gridx) {
// create label
javax.swing.JLabel messageLabel = new javax.swing.JLabel();
constraints.gridy++;
constraints.gridx = gridx < MAX_COLS - 1 ? gridx : MAX_COLS - 2;
int savedGridwidth = constraints.gridwidth;
constraints.gridwidth = 3;
// set text
messageLabel.setText(messageString);
// add to panel
gridbagLayout.setConstraints(messageLabel, constraints);
panel.add(messageLabel);
addLineEndGlue(panel, gridbagLayout, constraints);
// restore constraints
constraints.gridwidth = savedGridwidth;
return messageLabel;
}
/**
* Adds a Persona row to the panel.
*
* Adds a persona name label and a button to the panel. Kicks off a
* background task to search for persona for the given account. Updates the
* persona name and button when the task is done.
*
* If CentralRepostory is disabled, just displays 'Unknown' persona name.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param accountIdentifier Account identifier to search the persona.
*
* @return List of AccountPersonaSearcherData objects.
*/
@NbBundle.Messages({
"CommunicationArtifactViewerHelper_persona_label=Persona: ",
"CommunicationArtifactViewerHelper_persona_searching=Searching...",
"CommunicationArtifactViewerHelper_persona_unknown=Unknown",
"CommunicationArtifactViewerHelper_persona_button_view=View",
"CommunicationArtifactViewerHelper_persona_button_create=Create"
})
static List<AccountPersonaSearcherData> addPersonaRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String accountIdentifier) {
List<AccountPersonaSearcherData> dataList = new ArrayList<>();
constraints.gridy++;
constraints.gridx = 1;
Insets savedInsets = constraints.insets;
// extra Indent in
constraints.insets = new java.awt.Insets(0, 2 * LEFT_INSET, 0, 0);
// create label
javax.swing.JLabel personaLabel = new javax.swing.JLabel();
String personaLabelText = Bundle.CommunicationArtifactViewerHelper_persona_label();
personaLabelText = personaLabelText.concat(CentralRepository.isEnabled()
? Bundle.CommunicationArtifactViewerHelper_persona_searching()
: Bundle.CommunicationArtifactViewerHelper_persona_unknown());
personaLabel.setText(personaLabelText);
// add to panel
gridbagLayout.setConstraints(personaLabel, constraints);
panel.add(personaLabel);
// restore constraint
constraints.insets = savedInsets;
constraints.gridx++;
// Place a button as place holder. It will be enabled when persona is available.
javax.swing.JButton personaButton = new javax.swing.JButton();
personaButton.setText(Bundle.CommunicationArtifactViewerHelper_persona_button_view());
personaButton.setMargin(new Insets(0, 5, 0, 5));
personaButton.setEnabled(false);
gridbagLayout.setConstraints(personaButton, constraints);
panel.add(personaButton);
if (CentralRepository.isEnabled()) {
// kick off a task to find the persona for this account
dataList.add(new AccountPersonaSearcherData(accountIdentifier, personaLabel, personaButton));
} else {
personaLabel.setEnabled(false);
}
addLineEndGlue(panel, gridbagLayout, constraints);
return dataList;
}
/**
* Event handler for mouse click event. Attaches a 'Copy' menu item to right
* click.
*
* @param evt Event to check.
* @param valueLabel Label to attach the menu item to.
*/
@NbBundle.Messages({
"CommunicationArtifactViewerHelper_menuitem_copy=Copy"
})
private static void valueLabelMouseClicked(java.awt.event.MouseEvent evt, JLabel valueLabel) {
if (SwingUtilities.isRightMouseButton(evt)) {
JPopupMenu popup = new JPopupMenu();
JMenuItem copyMenu = new JMenuItem(Bundle.CommunicationArtifactViewerHelper_menuitem_copy()); // NON-NLS
copyMenu.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(valueLabel.getText()), null);
}
});
popup.add(copyMenu);
popup.show(valueLabel, evt.getX(), evt.getY());
}
}
}

View File

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

View File

@ -0,0 +1,867 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.SwingWorker;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
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.PersonaAccount;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* This class displays the TSK_CONTACT artifact.
*/
@ServiceProvider(service = ArtifactContentViewer.class)
public class ContactArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer {
private final static Logger logger = Logger.getLogger(ContactArtifactViewer.class.getName());
private static final long serialVersionUID = 1L;
private GridBagLayout m_gridBagLayout = new GridBagLayout();
private GridBagConstraints m_constraints = new GridBagConstraints();
private JLabel personaSearchStatusLabel;
private BlackboardArtifact contactArtifact;
private String contactName;
private String datasourceName;
private List<BlackboardAttribute> phoneNumList = new ArrayList<>();
private List<BlackboardAttribute> emailList = new ArrayList<>();
private List<BlackboardAttribute> nameList = new ArrayList<>();
private List<BlackboardAttribute> otherList = new ArrayList<>();
private List<BlackboardAttribute> accountAttributesList = new ArrayList<>();
private final static String DEFAULT_IMAGE_PATH = "/org/sleuthkit/autopsy/images/defaultContact.png";
private final ImageIcon defaultImage;
// A list of unique accounts matching the attributes of the contact artifact.
private final List<CentralRepoAccount> contactUniqueAccountsList = new ArrayList<>();
// A list of all unique personas and their account, found by searching on the
// account identifier attributes of the Contact artifact.
private final Map<Persona, ArrayList<CentralRepoAccount>> contactUniquePersonasMap = new HashMap<>();
private ContactPersonaSearcherTask personaSearchTask;
/**
* Creates new form ContactArtifactViewer
*/
public ContactArtifactViewer() {
initComponents();
defaultImage = new ImageIcon(ContactArtifactViewer.class.getResource(DEFAULT_IMAGE_PATH));
}
/**
* 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() {
setToolTipText(""); // NOI18N
setLayout(new java.awt.GridBagLayout());
}// </editor-fold>//GEN-END:initComponents
@Override
public void setArtifact(BlackboardArtifact artifact) {
// Reset the panel.
resetComponent();
if (artifact == null) {
return;
}
try {
extractArtifactData(artifact);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting attributes for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
return;
}
updateView();
this.setLayout(this.m_gridBagLayout);
this.revalidate();
this.repaint();
}
@Override
public Component getComponent() {
// Slap a vertical scrollbar on the panel.
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
@Override
public boolean isSupported(BlackboardArtifact artifact) {
return (artifact != null)
&& (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID());
}
/**
* Extracts data from the artifact to be displayed in the panel.
*
* @param artifact Artifact to show.
* @throws TskCoreException
*/
private void extractArtifactData(BlackboardArtifact artifact) throws TskCoreException {
this.contactArtifact = artifact;
phoneNumList = new ArrayList<>();
emailList = new ArrayList<>();
nameList = new ArrayList<>();
otherList = new ArrayList<>();
accountAttributesList = new ArrayList<>();
// Get all the attributes and group them by the section panels they go in
for (BlackboardAttribute bba : contactArtifact.getAttributes()) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_PHONE")) {
phoneNumList.add(bba);
accountAttributesList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) {
emailList.add(bba);
accountAttributesList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) {
nameList.add(bba);
} else {
otherList.add(bba);
if (bba.getAttributeType().getTypeName().equalsIgnoreCase("TSK_ID")) {
accountAttributesList.add(bba);
}
}
}
datasourceName = contactArtifact.getDataSource().getName();
}
/**
* Updates the view with the data extracted from the artifact.
*/
private void updateView() {
// Update contact name, image, phone numbers
updateContactDetails();
// update artifact source panel
updateSource();
// show a empty Personas panel and kick off a serch for personas
initiatePersonasSearch();
}
/**
* Updates the view with contact's details.
*/
@NbBundle.Messages({
"ContactArtifactViewer_phones_header=Phone",
"ContactArtifactViewer_emails_header=Email",
"ContactArtifactViewer_others_header=Other",})
private void updateContactDetails() {
// update image and name.
updateContactImage(m_gridBagLayout, m_constraints);
updateContactName(m_gridBagLayout, m_constraints);
// update contact attributes sections
updateContactMethodSection(phoneNumList, Bundle.ContactArtifactViewer_phones_header(), m_gridBagLayout, m_constraints);
updateContactMethodSection(emailList, Bundle.ContactArtifactViewer_emails_header(), m_gridBagLayout, m_constraints);
updateContactMethodSection(otherList, Bundle.ContactArtifactViewer_others_header(), m_gridBagLayout, m_constraints);
}
/**
* Updates the contact image in the view.
*
* @param contactPanelLayout Panel layout.
* @param contactPanelConstraints Layout constraints.
*
*/
@NbBundle.Messages({
"ContactArtifactViewer.contactImage.text=",})
private void updateContactImage(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
// place the image on the top right corner
Insets savedInsets = contactPanelConstraints.insets;
contactPanelConstraints.gridy = 0;
contactPanelConstraints.gridx = 0;
contactPanelConstraints.insets = new Insets(0, 0, 0, 0);
javax.swing.JLabel contactImage = new javax.swing.JLabel();
contactImage.setIcon(getImageFromArtifact(contactArtifact));
contactImage.setText(Bundle.ContactArtifactViewer_contactImage_text());
// add image to top left corner of the page.
CommunicationArtifactViewerHelper.addComponent(this, contactPanelLayout, contactPanelConstraints, contactImage);
CommunicationArtifactViewerHelper.addLineEndGlue(this, contactPanelLayout, contactPanelConstraints);
contactPanelConstraints.gridy++;
contactPanelConstraints.insets = savedInsets;
}
/**
* Updates the contact name in the view.
*
* @param contactPanelLayout Panel layout.
* @param contactPanelConstraints Layout constraints.
*
*/
@NbBundle.Messages({
"ContactArtifactViewer_contactname_unknown=Unknown",})
private void updateContactName(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
boolean foundName = false;
for (BlackboardAttribute bba : this.nameList) {
if (StringUtils.isEmpty(bba.getValueString()) == false) {
contactName = bba.getDisplayString();
CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, contactName);
foundName = true;
break;
}
}
if (foundName == false) {
CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, Bundle.ContactArtifactViewer_contactname_unknown());
}
}
/**
* Updates the view by displaying the given list of attributes in the given
* section panel.
*
* @param sectionAttributesList List of attributes to display.
* @param sectionHeader Section name label.
* @param contactPanelLayout Panel layout.
* @param contactPanelConstraints Layout constraints.
*
*/
@NbBundle.Messages({
"ContactArtifactViewer_plural_suffix=s",})
private void updateContactMethodSection(List<BlackboardAttribute> sectionAttributesList, String sectionHeader, GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
// If there are no attributes for this section, do nothing
if (sectionAttributesList.isEmpty()) {
return;
}
String sectionHeaderString = sectionHeader;
if (sectionAttributesList.size() > 1) {
sectionHeaderString = sectionHeaderString.concat(Bundle.ContactArtifactViewer_plural_suffix());
}
CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, sectionHeaderString);
for (BlackboardAttribute bba : sectionAttributesList) {
CommunicationArtifactViewerHelper.addKey(this, contactPanelLayout, contactPanelConstraints, bba.getAttributeType().getDisplayName());
CommunicationArtifactViewerHelper.addValue(this, contactPanelLayout, contactPanelConstraints, bba.getDisplayString());
}
}
/**
* Updates the source section.
*/
@NbBundle.Messages({
"ContactArtifactViewer_heading_Source=Source",
"ContactArtifactViewer_label_datasource=Data Source",})
private void updateSource() {
CommunicationArtifactViewerHelper.addHeader(this, this.m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_heading_Source());
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_label_datasource());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, m_constraints, datasourceName);
}
/**
* Initiates a search for Personas for the accounts associated with the
* Contact.
*
*/
@NbBundle.Messages({
"ContactArtifactViewer_persona_header=Persona",
"ContactArtifactViewer_persona_searching=Searching...",
"ContactArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas.",
"ContactArtifactViewer_persona_unknown=Unknown"
})
private void initiatePersonasSearch() {
// add a section header
JLabel personaHeader = CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_persona_header());
m_constraints.gridy++;
// add a status label
String personaStatusLabelText = CentralRepository.isEnabled()
? Bundle.ContactArtifactViewer_persona_searching()
: Bundle.ContactArtifactViewer_persona_unknown();
this.personaSearchStatusLabel = new javax.swing.JLabel();
personaSearchStatusLabel.setText(personaStatusLabelText);
m_constraints.gridx = 0;
CommunicationArtifactViewerHelper.addComponent(this, m_gridBagLayout, m_constraints, personaSearchStatusLabel);
if (CentralRepository.isEnabled()) {
// Kick off a background task to serach for personas for the contact
personaSearchTask = new ContactPersonaSearcherTask(contactArtifact);
personaSearchTask.execute();
} else {
personaHeader.setEnabled(false);
personaSearchStatusLabel.setEnabled(false);
CommunicationArtifactViewerHelper.addBlankLine(this, m_gridBagLayout, m_constraints);
m_constraints.gridy++;
CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
m_constraints.gridy++;
CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
}
}
/**
* Updates the Persona panel with the gathered persona information.
*/
private void updatePersonas() {
// Remove the "Searching....." label
this.remove(personaSearchStatusLabel);
m_constraints.gridx = 0;
if (contactUniquePersonasMap.isEmpty()) {
// No persona found - show a button to create one.
showPersona(null, 0, Collections.emptyList(), this.m_gridBagLayout, this.m_constraints);
} else {
int matchCounter = 0;
for (Map.Entry<Persona, ArrayList<CentralRepoAccount>> entry : contactUniquePersonasMap.entrySet()) {
List<CentralRepoAccount> missingAccounts = new ArrayList<>();
ArrayList<CentralRepoAccount> personaAccounts = entry.getValue();
matchCounter++;
// create a list of accounts missing from this persona
for (CentralRepoAccount account : contactUniqueAccountsList) {
if (personaAccounts.contains(account) == false) {
missingAccounts.add(account);
}
}
showPersona(entry.getKey(), matchCounter, missingAccounts, m_gridBagLayout, m_constraints);
m_constraints.gridy += 2;
}
}
// add veritcal glue at the end
CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
// redraw the panel
this.setLayout(this.m_gridBagLayout);
this.revalidate();
this.repaint();
}
/**
* Displays the given persona in the persona panel.
*
* @param persona Persona to display.
* @param matchNumber Number of matches.
* @param missingAccountsList List of contact accounts this persona may be
* missing.
* @param gridBagLayout Layout to use.
* @param constraints layout constraints.
*
* @throws CentralRepoException
*/
@NbBundle.Messages({
"ContactArtifactViewer_persona_label=Persona ",
"ContactArtifactViewer_persona_no_match=No matches found",
"ContactArtifactViewer_persona_button_view=View",
"ContactArtifactViewer_persona_button_new=Create",
"ContactArtifactViewer_persona_match_num=Match ",
"ContactArtifactViewer_missing_account_label=Missing contact account",
"ContactArtifactViewer_found_all_accounts_label=All accounts found."
})
private void showPersona(Persona persona, int matchNumber, List<CentralRepoAccount> missingAccountsList, GridBagLayout gridBagLayout, GridBagConstraints constraints) {
// save the original insets
Insets savedInsets = constraints.insets;
// some label are indented 2x to appear indented w.r.t column above
Insets extraIndentInsets = new java.awt.Insets(0, 2 * CommunicationArtifactViewerHelper.LEFT_INSET, 0, 0);
// Add a Match X label in col 0.
constraints.gridx = 0;
javax.swing.JLabel matchNumberLabel = CommunicationArtifactViewerHelper.addKey(this, gridBagLayout, constraints, String.format("%s %d", Bundle.ContactArtifactViewer_persona_match_num(), matchNumber));
javax.swing.JLabel personaNameLabel = new javax.swing.JLabel();
javax.swing.JButton personaButton = new javax.swing.JButton();
String personaName;
String personaButtonText;
ActionListener personaButtonListener;
if (persona != null) {
personaName = persona.getName();
personaButtonText = Bundle.ContactArtifactViewer_persona_button_view();
personaButtonListener = new ViewPersonaButtonListener(this, persona);
} else {
matchNumberLabel.setVisible(false);
personaName = Bundle.ContactArtifactViewer_persona_no_match();
personaButtonText = Bundle.ContactArtifactViewer_persona_button_new();
personaButtonListener = new CreatePersonaButtonListener(this, new PersonaUIComponents(personaNameLabel, personaButton));
}
//constraints.gridwidth = 1; // TBD: this may not be needed if we use single panel
constraints.gridx++;
personaNameLabel.setText(personaName);
gridBagLayout.setConstraints(personaNameLabel, constraints);
CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, personaNameLabel);
//personasPanel.add(personaNameLabel);
// Add a Persona action button
constraints.gridx++;
//constraints.gridwidth = 1;
personaButton.setText(personaButtonText);
personaButton.addActionListener(personaButtonListener);
// Shirnk the button height.
personaButton.setMargin(new Insets(0, 5, 0, 5));
gridBagLayout.setConstraints(personaButton, constraints);
CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, personaButton);
CommunicationArtifactViewerHelper.addLineEndGlue(this, gridBagLayout, constraints);
constraints.insets = savedInsets;
// if we have a persona, indicate if any of the contact's accounts are missing from it.
if (persona != null) {
if (missingAccountsList.isEmpty()) {
constraints.gridy++;
constraints.gridx = 1;
//constraints.insets = labelInsets;
javax.swing.JLabel accountsStatus = new javax.swing.JLabel(Bundle.ContactArtifactViewer_found_all_accounts_label());
constraints.insets = extraIndentInsets;
CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, accountsStatus);
constraints.insets = savedInsets;
CommunicationArtifactViewerHelper.addLineEndGlue(this, gridBagLayout, constraints);
} else {
// show missing accounts.
for (CentralRepoAccount missingAccount : missingAccountsList) {
//constraints.weightx = 0;
constraints.gridx = 0;
constraints.gridy++;
// this needs an extra indent
constraints.insets = extraIndentInsets;
CommunicationArtifactViewerHelper.addKeyAtCol(this, gridBagLayout, constraints, Bundle.ContactArtifactViewer_missing_account_label(), 1);
constraints.insets = savedInsets;
CommunicationArtifactViewerHelper.addValueAtCol(this, gridBagLayout, constraints, missingAccount.getIdentifier(), 2);
}
}
}
// restore insets
constraints.insets = savedInsets;
}
/**
* Resets all artifact specific state.
*/
private void resetComponent() {
contactArtifact = null;
contactName = null;
datasourceName = null;
contactUniqueAccountsList.clear();
contactUniquePersonasMap.clear();
phoneNumList.clear();
emailList.clear();
nameList.clear();
otherList.clear();
accountAttributesList.clear();
if (personaSearchTask != null) {
personaSearchTask.cancel(Boolean.TRUE);
personaSearchTask = null;
}
// clear the panel
this.removeAll();
this.setLayout(null);
m_gridBagLayout = new GridBagLayout();
m_constraints = new GridBagConstraints();
m_constraints.anchor = GridBagConstraints.FIRST_LINE_START;
m_constraints.gridy = 0;
m_constraints.gridx = 0;
m_constraints.weighty = 0.0;
m_constraints.weightx = 0.0; // keep components fixed horizontally.
m_constraints.insets = new java.awt.Insets(0, CommunicationArtifactViewerHelper.LEFT_INSET, 0, 0);
m_constraints.fill = GridBagConstraints.NONE;
}
/**
* Gets an image from a TSK_CONTACT artifact.
*
* @param artifact
*
* @return Image from a TSK_CONTACT artifact or default image if none was
* found or the artifact is not a TSK_CONTACT
*/
private 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 throw 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;
}
/**
* Thread to search for a personas for all account identifier attributes for
* a contact.
*/
private class ContactPersonaSearcherTask extends SwingWorker<Map<Persona, ArrayList<CentralRepoAccount>>, Void> {
private final BlackboardArtifact artifact;
private final List<CentralRepoAccount> uniqueAccountsList = new ArrayList<>();
/**
* Creates a persona searcher task.
*
* @param accountAttributesList List of attributes that may map to
* accounts.
*/
ContactPersonaSearcherTask(BlackboardArtifact artifact) {
this.artifact = artifact;
}
@Override
protected Map<Persona, ArrayList<CentralRepoAccount>> doInBackground() throws Exception {
Map<Persona, ArrayList<CentralRepoAccount>> uniquePersonas = new HashMap<>();
CommunicationsManager commManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
List<Account> contactAccountsList = commManager.getAccountsRelatedToArtifact(artifact);
for (Account account : contactAccountsList) {
if (isCancelled()) {
return new HashMap<>();
}
Collection<PersonaAccount> personaAccounts = PersonaAccount.getPersonaAccountsForAccount(account);
if (personaAccounts != null && !personaAccounts.isEmpty()) {
// look for unique accounts
Collection<CentralRepoAccount> accountCandidates
= personaAccounts
.stream()
.map(PersonaAccount::getAccount)
.collect(Collectors.toList());
for (CentralRepoAccount crAccount : accountCandidates) {
if (uniqueAccountsList.contains(crAccount) == false) {
uniqueAccountsList.add(crAccount);
}
}
// get personas for the account
Collection<Persona> personas
= personaAccounts
.stream()
.map(PersonaAccount::getPersona)
.collect(Collectors.toList());
// make a list of unique personas, along with all their accounts
for (Persona persona : personas) {
if (uniquePersonas.containsKey(persona) == false) {
Collection<CentralRepoAccount> accounts = persona.getPersonaAccounts()
.stream()
.map(PersonaAccount::getAccount)
.collect(Collectors.toList());
ArrayList<CentralRepoAccount> personaAccountsList = new ArrayList<>(accounts);
uniquePersonas.put(persona, personaAccountsList);
}
}
}
}
return uniquePersonas;
}
@Override
protected void done() {
Map<Persona, ArrayList<CentralRepoAccount>> personasMap;
try {
personasMap = super.get();
if (this.isCancelled()) {
return;
}
contactUniquePersonasMap.clear();
contactUniquePersonasMap.putAll(personasMap);
contactUniqueAccountsList.clear();
contactUniqueAccountsList.addAll(uniqueAccountsList);
updatePersonas();
} catch (CancellationException ex) {
logger.log(Level.INFO, "Persona searching was canceled."); //NON-NLS
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Persona searching was interrupted."); //NON-NLS
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during Persona search.", ex); //NON-NLS
}
}
}
/**
* A wrapper class that bags the UI components that need to be updated when
* a persona search task or a create dialog returns.
*/
private class PersonaUIComponents {
private final JLabel personaNameLabel;
private final JButton personaActionButton;
/**
* Constructor.
*
* @param personaNameLabel Persona name label.
* @param personaActionButton Persona action button.
*/
PersonaUIComponents(JLabel personaNameLabel, JButton personaActionButton) {
this.personaNameLabel = personaNameLabel;
this.personaActionButton = personaActionButton;
}
/**
* Returns persona name label.
*
* @return Persona name label.
*/
public JLabel getPersonaNameLabel() {
return personaNameLabel;
}
/**
* Returns persona action button.
*
* @return Persona action button.
*/
public JButton getPersonaActionButton() {
return personaActionButton;
}
}
/**
* Action listener for Create persona button.
*/
private class CreatePersonaButtonListener implements ActionListener {
private final Component parentComponent;
private final PersonaUIComponents personaUIComponents;
/**
* Constructs a listener for Create persona button..
*
* @param personaUIComponents UI components.
*/
CreatePersonaButtonListener(Component parentComponent, PersonaUIComponents personaUIComponents) {
this.personaUIComponents = personaUIComponents;
this.parentComponent = parentComponent;
}
@NbBundle.Messages({
"ContactArtifactViewer_persona_account_justification=Account found in Contact artifact"
})
@Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
// Launch the Persona Create dialog - do not display immediately
PersonaDetailsDialog createPersonaDialog = new PersonaDetailsDialog(parentComponent,
PersonaDetailsMode.CREATE, null, new PersonaCreateCallbackImpl(parentComponent, personaUIComponents), false);
// Pre populate the persona name and accounts if we have them.
PersonaDetailsPanel personaPanel = createPersonaDialog.getDetailsPanel();
if (contactName != null) {
personaPanel.setPersonaName(contactName);
}
// pass the list of accounts to the dialog
for (CentralRepoAccount account : contactUniqueAccountsList) {
personaPanel.addAccount(account, Bundle.ContactArtifactViewer_persona_account_justification(), Persona.Confidence.HIGH);
}
// display the dialog now
createPersonaDialog.display();
}
}
/**
* Action listener for View persona button.
*/
private class ViewPersonaButtonListener implements ActionListener {
private final Persona persona;
private final Component parentComponent;
/**
* Creates listener for View persona button.
*
* @param persona
*/
ViewPersonaButtonListener(Component parentComponent, Persona persona) {
this.persona = persona;
this.parentComponent = parentComponent;
}
@Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
new PersonaDetailsDialog(parentComponent,
PersonaDetailsMode.VIEW, persona, new PersonaViewCallbackImpl());
}
}
/**
* Callback method for the create mode of the PersonaDetailsDialog
*/
class PersonaCreateCallbackImpl implements PersonaDetailsDialogCallback {
private final Component parentComponent;
private final PersonaUIComponents personaUIComponents;
/**
* Creates a callback to handle new persona creation.
*
* @param personaUIComponents UI Components.
*/
PersonaCreateCallbackImpl(Component parentComponent, PersonaUIComponents personaUIComponents) {
this.parentComponent = parentComponent;
this.personaUIComponents = personaUIComponents;
}
@Override
public void callback(Persona persona) {
JButton personaButton = personaUIComponents.getPersonaActionButton();
if (persona != null) {
// update the persona name label with newly created persona,
// and change the button to a "View" button
personaUIComponents.getPersonaNameLabel().setText(persona.getName());
personaUIComponents.getPersonaActionButton().setText(Bundle.ContactArtifactViewer_persona_button_view());
// replace action listener with a View button listener
for (ActionListener act : personaButton.getActionListeners()) {
personaButton.removeActionListener(act);
}
personaButton.addActionListener(new ViewPersonaButtonListener(parentComponent, persona));
}
personaButton.getParent().revalidate();
personaButton.getParent().repaint();
}
}
/**
* Callback method for the view mode of the PersonaDetailsDialog
*/
class PersonaViewCallbackImpl implements PersonaDetailsDialogCallback {
@Override
public void callback(Persona persona) {
// nothing to do
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -11,14 +11,14 @@
<MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="DefaultArtifactContentViewer.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="DefaultArtifactContentViewer.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="DefaultArtifactContentViewer.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="DefaultArtifactContentViewer.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>

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