mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge remote-tracking branch 'upstream/develop' into 7357_imageGalleryDeadlock
This commit is contained in:
commit
a962ab34c5
@ -39,6 +39,7 @@ import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -81,8 +82,14 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.HostsAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.HostsRemovedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.OsAccountAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsRemovedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
|
||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils;
|
||||
@ -130,11 +137,19 @@ import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.FileSystem;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.HostManager.HostsCreationEvent;
|
||||
import org.sleuthkit.datamodel.HostManager.HostsUpdateEvent;
|
||||
import org.sleuthkit.datamodel.HostManager.HostsDeletionEvent;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.OsAccountManager;
|
||||
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsCreationEvent;
|
||||
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsUpdateEvent;
|
||||
import org.sleuthkit.datamodel.Person;
|
||||
import org.sleuthkit.datamodel.PersonManager.PersonsCreationEvent;
|
||||
import org.sleuthkit.datamodel.PersonManager.PersonsUpdateEvent;
|
||||
import org.sleuthkit.datamodel.PersonManager.PersonsDeletionEvent;
|
||||
import org.sleuthkit.datamodel.Report;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TimelineManager;
|
||||
@ -426,8 +441,38 @@ public class Case {
|
||||
* OSAccount associated with the current case has changed.
|
||||
* Call getOsAccount to get the changed account;
|
||||
*/
|
||||
OS_ACCOUNT_CHANGED;
|
||||
OS_ACCOUNT_CHANGED,
|
||||
|
||||
/**
|
||||
* Hosts associated with the current case added.
|
||||
*/
|
||||
HOSTS_ADDED,
|
||||
|
||||
/**
|
||||
* Hosts associated with the current case has changed.
|
||||
*/
|
||||
HOSTS_CHANGED,
|
||||
|
||||
/**
|
||||
* Hosts associated with the current case has been deleted.
|
||||
*/
|
||||
HOSTS_DELETED,
|
||||
|
||||
/**
|
||||
* Persons associated with the current case added.
|
||||
*/
|
||||
PERSONS_ADDED,
|
||||
|
||||
/**
|
||||
* Persons associated with the current case has changed.
|
||||
*/
|
||||
PERSONS_CHANGED,
|
||||
|
||||
/**
|
||||
* Persons associated with the current case has been deleted.
|
||||
*/
|
||||
PERSONS_DELETED
|
||||
;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -474,6 +519,78 @@ public class Case {
|
||||
eventPublisher.publish(new OsAccountChangedEvent(account));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit HostCreationEvent
|
||||
* indicating that hosts have been created.
|
||||
*
|
||||
* @param event The sleuthkit event for the creation of hosts.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishHostsAddedEvent(HostsCreationEvent event) {
|
||||
eventPublisher.publish(new HostsAddedEvent(
|
||||
event == null ? Collections.emptyList() : event.getHosts()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit HostUpdateEvent
|
||||
* indicating that hosts have been updated.
|
||||
*
|
||||
* @param event The sleuthkit event for the updating of hosts.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishHostsChangedEvent(HostsUpdateEvent event) {
|
||||
eventPublisher.publish(new HostsChangedEvent(
|
||||
event == null ? Collections.emptyList() : event.getHosts()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit HostDeletedEvent
|
||||
* indicating that hosts have been deleted.
|
||||
*
|
||||
* @param event The sleuthkit event for the deleting of hosts.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishHostsDeletedEvent(HostsDeletionEvent event) {
|
||||
eventPublisher.publish(new HostsRemovedEvent(
|
||||
event == null ? Collections.emptyList() : event.getHosts()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit PersonCreationEvent
|
||||
* indicating that persons have been created.
|
||||
*
|
||||
* @param event The sleuthkit event for the creation of persons.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishPersonsAddedEvent(PersonsCreationEvent event) {
|
||||
eventPublisher.publish(new PersonsAddedEvent(
|
||||
event == null ? Collections.emptyList() : event.getPersons()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit PersonUpdateEvent
|
||||
* indicating that persons have been updated.
|
||||
*
|
||||
* @param event The sleuthkit event for the updating of persons.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishPersonsChangedEvent(PersonsUpdateEvent event) {
|
||||
eventPublisher.publish(new PersonsChangedEvent(
|
||||
event == null ? Collections.emptyList() : event.getPersons()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit PersonDeletedEvent
|
||||
* indicating that persons have been deleted.
|
||||
*
|
||||
* @param event The sleuthkit event for the deleting of persons.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishPersonsDeletedEvent(PersonsDeletionEvent event) {
|
||||
eventPublisher.publish(new PersonsRemovedEvent(
|
||||
event == null ? Collections.emptyList() : event.getPersons()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1680,7 +1797,55 @@ public class Case {
|
||||
public void notifyOsAccountChanged(OsAccount account) {
|
||||
eventPublisher.publish(new OsAccountChangedEvent(account));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a host has been added.
|
||||
* @param host The host that has been added.
|
||||
*/
|
||||
public void notifyHostAdded(Host host) {
|
||||
eventPublisher.publish(new HostsAddedEvent(Collections.singletonList(host)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a host has been changed.
|
||||
* @param newValue The host that has been updated.
|
||||
*/
|
||||
public void notifyHostChanged(Host newValue) {
|
||||
eventPublisher.publish(new HostsChangedEvent(Collections.singletonList(newValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a host has been deleted.
|
||||
* @param host The host that has been deleted.
|
||||
*/
|
||||
public void notifyHostDeleted(Host host) {
|
||||
eventPublisher.publish(new HostsRemovedEvent(Collections.singletonList(host)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a person has been added.
|
||||
* @param person The person that has been added.
|
||||
*/
|
||||
public void notifyPersonAdded(Person person) {
|
||||
eventPublisher.publish(new PersonsAddedEvent(Collections.singletonList(person)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a person has been changed.
|
||||
* @param newValue The person that has been updated.
|
||||
*/
|
||||
public void notifyPersonChanged(Person newValue) {
|
||||
eventPublisher.publish(new PersonsChangedEvent(Collections.singletonList(newValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a person has been deleted.
|
||||
* @param person The person that has been deleted.
|
||||
*/
|
||||
public void notifyPersonDeleted(Person person) {
|
||||
eventPublisher.publish(new PersonsRemovedEvent(Collections.singletonList(person)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a report to the case.
|
||||
*
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Copyright 2013-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -51,8 +51,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* independently of the wizard.
|
||||
*/
|
||||
@ServiceProviders(value = {
|
||||
@ServiceProvider(service = DataSourceProcessor.class)
|
||||
,
|
||||
@ServiceProvider(service = DataSourceProcessor.class),
|
||||
@ServiceProvider(service = AutoIngestDataSourceProcessor.class)}
|
||||
)
|
||||
public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSourceProcessor {
|
||||
@ -83,7 +82,6 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
private String sha1;
|
||||
private String sha256;
|
||||
private Host host = null;
|
||||
private boolean setDataSourceOptionsCalled;
|
||||
|
||||
static {
|
||||
filtersList.add(allFilter);
|
||||
@ -184,8 +182,8 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
@Override
|
||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
run(null, 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
|
||||
@ -208,71 +206,71 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
this.host = host;
|
||||
try {
|
||||
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
||||
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.host);
|
||||
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.host);
|
||||
} 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());
|
||||
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.
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* @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,
|
||||
public void runWithIngestStream(IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
|
||||
DataSourceProcessorCallback callBack) {
|
||||
runWithIngestStream(null, settings, progress, 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.
|
||||
* 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
|
||||
* This method should not be called unless isPanelValid returns true, and
|
||||
* should only be called for DSPs that support ingest streams.
|
||||
*
|
||||
* @param host The host for this data source.
|
||||
* @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.
|
||||
*
|
||||
* @param host The host for this data source.
|
||||
* @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(Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
|
||||
public void runWithIngestStream(Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
|
||||
DataSourceProcessorCallback callBack) {
|
||||
|
||||
|
||||
// Read the settings from the wizard
|
||||
readConfigSettings();
|
||||
this.host = host;
|
||||
|
||||
|
||||
// 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, this.host);
|
||||
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.host);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
|
||||
final List<String> errors = new ArrayList<>();
|
||||
@ -291,46 +289,43 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progress, callBack);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store the options from the config panel.
|
||||
*/
|
||||
private void readConfigSettings() {
|
||||
if (!setDataSourceOptionsCalled) {
|
||||
configPanel.storeSettings();
|
||||
deviceId = UUID.randomUUID().toString();
|
||||
imagePath = configPanel.getContentPaths();
|
||||
sectorSize = configPanel.getSectorSize();
|
||||
timeZone = configPanel.getTimeZone();
|
||||
ignoreFatOrphanFiles = configPanel.getNoFatOrphans();
|
||||
md5 = configPanel.getMd5();
|
||||
if (md5.isEmpty()) {
|
||||
md5 = null;
|
||||
}
|
||||
sha1 = configPanel.getSha1();
|
||||
if (sha1.isEmpty()) {
|
||||
sha1 = null;
|
||||
}
|
||||
sha256 = configPanel.getSha256();
|
||||
if (sha256.isEmpty()) {
|
||||
sha256 = null;
|
||||
}
|
||||
configPanel.storeSettings();
|
||||
deviceId = UUID.randomUUID().toString();
|
||||
imagePath = configPanel.getContentPaths();
|
||||
sectorSize = configPanel.getSectorSize();
|
||||
timeZone = configPanel.getTimeZone();
|
||||
ignoreFatOrphanFiles = configPanel.getNoFatOrphans();
|
||||
md5 = configPanel.getMd5();
|
||||
if (md5.isEmpty()) {
|
||||
md5 = null;
|
||||
}
|
||||
sha1 = configPanel.getSha1();
|
||||
if (sha1.isEmpty()) {
|
||||
sha1 = null;
|
||||
}
|
||||
sha256 = configPanel.getSha256();
|
||||
if (sha256.isEmpty()) {
|
||||
sha256 = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -357,11 +352,11 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
ingestStream = new DefaultIngestStream();
|
||||
try {
|
||||
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
||||
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId);
|
||||
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());
|
||||
errors.add(ex.getMessage());
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
|
||||
return;
|
||||
}
|
||||
@ -375,10 +370,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".
|
||||
*
|
||||
* 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
|
||||
@ -400,28 +395,28 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
*/
|
||||
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;
|
||||
}
|
||||
// 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),
|
||||
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();
|
||||
}
|
||||
@ -455,7 +450,6 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
ignoreFatOrphanFiles = false;
|
||||
host = null;
|
||||
configPanel.reset();
|
||||
setDataSourceOptionsCalled = false;
|
||||
}
|
||||
|
||||
private static boolean isAcceptedByFiler(File file, List<FileFilter> filters) {
|
||||
@ -488,56 +482,56 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
// able to process the data source
|
||||
return 100;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
|
||||
process(deviceId, dataSourcePath, null, progressMonitor, callBack);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void process(String deviceId, Path dataSourcePath, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
|
||||
// this method does not use the config panel
|
||||
this.deviceId = deviceId;
|
||||
this.imagePath = dataSourcePath.toString();
|
||||
this.sectorSize = 0;
|
||||
this.timeZone = Calendar.getInstance().getTimeZone().getID();
|
||||
this.host = host;
|
||||
this.ignoreFatOrphanFiles = false;
|
||||
setDataSourceOptionsCalled = true;
|
||||
|
||||
|
||||
ingestStream = new DefaultIngestStream();
|
||||
try {
|
||||
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
||||
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId, host);
|
||||
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId, host);
|
||||
} 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());
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IngestStream processWithIngestStream(String deviceId, Path dataSourcePath, IngestJobSettings settings, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
|
||||
return processWithIngestStream(deviceId, dataSourcePath, null, settings, progressMonitor, callBack);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IngestStream processWithIngestStream(String deviceId, Path dataSourcePath, Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
|
||||
// this method does not use the config panel
|
||||
this.deviceId = deviceId;
|
||||
this.imagePath = dataSourcePath.toString();
|
||||
this.sectorSize = 0;
|
||||
this.timeZone = Calendar.getInstance().getTimeZone().getID();
|
||||
this.host = host;
|
||||
this.ignoreFatOrphanFiles = false;
|
||||
setDataSourceOptionsCalled = true;
|
||||
|
||||
|
||||
// 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, host);
|
||||
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, host);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
|
||||
final List<String> errors = new ArrayList<>();
|
||||
@ -555,34 +549,10 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
errors.add(ex.getMessage());
|
||||
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callBack);
|
||||
|
||||
return ingestStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configuration of the data source processor without using the
|
||||
* selection and configuration panel.
|
||||
*
|
||||
* @param imagePath Path to the image file.
|
||||
* @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.
|
||||
*
|
||||
* @deprecated Use the provided overload of the run method instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setDataSourceOptions(String imagePath, String timeZone, boolean ignoreFatOrphanFiles) {
|
||||
this.deviceId = UUID.randomUUID().toString();
|
||||
this.imagePath = imagePath;
|
||||
this.sectorSize = 0;
|
||||
this.timeZone = Calendar.getInstance().getTimeZone().getID();
|
||||
this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
|
||||
this.host = null;
|
||||
setDataSourceOptionsCalled = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Copyright 2013-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -19,7 +19,6 @@
|
||||
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;
|
||||
@ -60,7 +59,6 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
private Host host;
|
||||
private ImageWriterSettings imageWriterSettings;
|
||||
private boolean ignoreFatOrphanFiles;
|
||||
private boolean setDataSourceOptionsCalled;
|
||||
|
||||
/**
|
||||
* Constructs a local drive data source processor that implements the
|
||||
@ -139,7 +137,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
run(null, 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
|
||||
@ -157,38 +155,36 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
*/
|
||||
@Override
|
||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
if (!setDataSourceOptionsCalled) {
|
||||
deviceId = UUID.randomUUID().toString();
|
||||
drivePath = configPanel.getContentPath();
|
||||
sectorSize = configPanel.getSectorSize();
|
||||
timeZone = configPanel.getTimeZone();
|
||||
ignoreFatOrphanFiles = configPanel.getNoFatOrphans();
|
||||
if (configPanel.getImageWriterEnabled()) {
|
||||
imageWriterSettings = configPanel.getImageWriterSettings();
|
||||
} else {
|
||||
imageWriterSettings = null;
|
||||
}
|
||||
deviceId = UUID.randomUUID().toString();
|
||||
drivePath = configPanel.getContentPath();
|
||||
sectorSize = configPanel.getSectorSize();
|
||||
timeZone = configPanel.getTimeZone();
|
||||
ignoreFatOrphanFiles = configPanel.getNoFatOrphans();
|
||||
if (configPanel.getImageWriterEnabled()) {
|
||||
imageWriterSettings = configPanel.getImageWriterSettings();
|
||||
} else {
|
||||
imageWriterSettings = null;
|
||||
}
|
||||
|
||||
|
||||
this.host = host;
|
||||
|
||||
Image image;
|
||||
try {
|
||||
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
||||
new String[]{drivePath}, sectorSize,
|
||||
timeZone, null, null, null, deviceId, this.host);
|
||||
new String[]{drivePath}, sectorSize,
|
||||
timeZone, null, null, null, deviceId, this.host);
|
||||
} 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),
|
||||
new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, imageWriterSettings),
|
||||
progressMonitor,
|
||||
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
|
||||
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
|
||||
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));
|
||||
new Thread(addDiskTask).start();
|
||||
}
|
||||
@ -244,19 +240,19 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
Image image;
|
||||
try {
|
||||
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
||||
new String[]{drivePath}, sectorSize,
|
||||
timeZone, null, null, null, deviceId);
|
||||
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()),
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
@ -285,30 +281,5 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
drivePath = null;
|
||||
timeZone = null;
|
||||
ignoreFatOrphanFiles = false;
|
||||
setDataSourceOptionsCalled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configuration of the data source processor without using the
|
||||
* configuration panel.
|
||||
*
|
||||
* @param drivePath Path to the local drive.
|
||||
* @param timeZone The time zone to use when processing dates
|
||||
* and times for the local drive, obtained from
|
||||
* java.util.TimeZone.getID.
|
||||
* @param ignoreFatOrphanFiles Whether to parse orphans if the image has a
|
||||
* FAT filesystem.
|
||||
*
|
||||
* @deprecated Use the provided overload of the run method instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setDataSourceOptions(String drivePath, String timeZone, boolean ignoreFatOrphanFiles) {
|
||||
this.deviceId = UUID.randomUUID().toString();
|
||||
this.drivePath = drivePath;
|
||||
this.sectorSize = 0;
|
||||
this.timeZone = Calendar.getInstance().getTimeZone().getID();
|
||||
this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
|
||||
setDataSourceOptionsCalled = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -78,7 +78,6 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
* when the deprecated method setDataSourceOptions is removed.
|
||||
*/
|
||||
private List<String> localFilePaths;
|
||||
private boolean setDataSourceOptionsCalled;
|
||||
|
||||
/**
|
||||
* Constructs a local/logical files and/or directories data source processor
|
||||
@ -139,7 +138,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
return configPanel.validatePanel();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 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. Returns as soon as the background task is started.
|
||||
@ -156,8 +155,8 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
@Override
|
||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
run(null, 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
|
||||
@ -175,23 +174,21 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
*/
|
||||
@Override
|
||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
if (!setDataSourceOptionsCalled) {
|
||||
|
||||
localFilePaths = configPanel.getContentPaths();
|
||||
if (configPanel.subTypeIsLogicalEvidencePanel()) {
|
||||
try {
|
||||
//if the L01 option was chosen
|
||||
localFilePaths = extractLogicalEvidenceFileContents(localFilePaths);
|
||||
} catch (L01Exception ex) {
|
||||
//contents of l01 could not be extracted don't add data source or run ingest
|
||||
final List<String> errors = new ArrayList<>();
|
||||
errors.add(ex.getMessage());
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
|
||||
return;
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Exception while getting open case.", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
localFilePaths = configPanel.getContentPaths();
|
||||
if (configPanel.subTypeIsLogicalEvidencePanel()) {
|
||||
try {
|
||||
//if the L01 option was chosen
|
||||
localFilePaths = extractLogicalEvidenceFileContents(localFilePaths);
|
||||
} catch (L01Exception ex) {
|
||||
//contents of l01 could not be extracted don't add data source or run ingest
|
||||
final List<String> errors = new ArrayList<>();
|
||||
errors.add(ex.getMessage());
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
|
||||
return;
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Exception while getting open case.", ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
run(UUID.randomUUID().toString(), configPanel.getFileSetName(), localFilePaths, host, progressMonitor, callback);
|
||||
@ -220,7 +217,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
command.add("-f");
|
||||
command.add("files");
|
||||
command.add("-t");
|
||||
File l01Dir = new File(Case.getCurrentCaseThrows().getModuleDirectory(), L01_EXTRACTION_DIR);
|
||||
File l01Dir = new File(Case.getCurrentCaseThrows().getModuleDirectory(), L01_EXTRACTION_DIR);
|
||||
if (!l01Dir.exists()) {
|
||||
l01Dir.mkdirs();
|
||||
}
|
||||
@ -307,7 +304,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
|
||||
return executablePath;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the given settings instead of those provided by the
|
||||
@ -385,7 +382,6 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
public void reset() {
|
||||
configPanel.select();
|
||||
localFilePaths = null;
|
||||
setDataSourceOptionsCalled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -421,34 +417,13 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
|
||||
process(deviceId, dataSourcePath, null, progressMonitor, callBack);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void process(String deviceId, Path dataSourcePath, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
|
||||
List<String> filePaths = Arrays.asList(new String[]{dataSourcePath.toString()});
|
||||
run(deviceId, "", filePaths, host, progressMonitor, callBack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configuration of the data source processor without using the
|
||||
* configuration panel. The data source processor will assign a UUID to the
|
||||
* data source and will use the time zone of the machine executing this code
|
||||
* when when processing dates and times for the image.
|
||||
*
|
||||
* @param paths A list of local/logical file and/or directory
|
||||
* localFilePaths.
|
||||
*
|
||||
* @deprecated Use the provided overload of the run method instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setDataSourceOptions(String paths) {
|
||||
// The LocalFilesPanel used to separate file paths with a comma and pass
|
||||
// them as a string, but because file names are allowed to contain
|
||||
// commas, this approach was buggy and replaced. We now pass a list of
|
||||
// String paths.
|
||||
this.localFilePaths = Arrays.asList(paths.split(","));
|
||||
setDataSourceOptionsCalled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom exception for the L01 processing.
|
||||
*/
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.events;
|
||||
|
||||
import java.util.List;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
|
||||
/**
|
||||
* Event fired when new hosts are added.
|
||||
*/
|
||||
public class HostsAddedEvent extends HostsEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* @param dataModelObjects The hosts that have been added.
|
||||
*/
|
||||
public HostsAddedEvent(List<Host> dataModelObjects) {
|
||||
super(Case.Events.HOSTS_ADDED.name(), dataModelObjects);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.events;
|
||||
|
||||
import java.util.List;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
|
||||
/**
|
||||
* Event fired when hosts are changed.
|
||||
*/
|
||||
public class HostsChangedEvent extends HostsEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param dataModelObjects The new values for the hosts that have been
|
||||
* changed.
|
||||
*/
|
||||
public HostsChangedEvent(List<Host> dataModelObjects) {
|
||||
super(Case.Events.HOSTS_CHANGED.name(), dataModelObjects);
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.events;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.HostManager;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Base event class for when something pertaining to hosts changes.
|
||||
*/
|
||||
public class HostsEvent extends TskDataModelChangeEvent<Host> {
|
||||
|
||||
/**
|
||||
* Retrieves a list of ids from a list of hosts.
|
||||
*
|
||||
* @param hosts The hosts.
|
||||
* @return The list of ids.
|
||||
*/
|
||||
private static List<Long> getIds(List<Host> hosts) {
|
||||
return getSafeList(hosts).stream()
|
||||
.filter(h -> h != null)
|
||||
.map(h -> h.getId()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hosts or an empty list.
|
||||
*
|
||||
* @param hosts The host list.
|
||||
* @return The host list or an empty list if the parameter is null.
|
||||
*/
|
||||
private static List<Host> getSafeList(List<Host> hosts) {
|
||||
return hosts == null ? Collections.emptyList() : hosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param eventName The name of the Case.Events enum value for the event
|
||||
* type.
|
||||
* @param dataModelObjects The list of hosts for the event.
|
||||
*/
|
||||
protected HostsEvent(String eventName, List<Host> dataModelObjects) {
|
||||
super(eventName, getIds(dataModelObjects), new ArrayList<>(getSafeList(dataModelObjects)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Host> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
|
||||
HostManager hostManager = caseDb.getHostManager();
|
||||
List<Host> toRet = new ArrayList<>();
|
||||
if (ids != null) {
|
||||
for (Long id : ids) {
|
||||
if (id == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<Host> thisHostOpt = hostManager.getHost(id);
|
||||
thisHostOpt.ifPresent((h) -> toRet.add(h));
|
||||
}
|
||||
}
|
||||
|
||||
return toRet;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.events;
|
||||
|
||||
import java.util.List;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
|
||||
/**
|
||||
* Event fired when hosts are removed.
|
||||
*/
|
||||
public class HostsRemovedEvent extends HostsEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* @param dataModelObjects The list of hosts that have been deleted.
|
||||
*/
|
||||
public HostsRemovedEvent(List<Host> dataModelObjects) {
|
||||
super(Case.Events.HOSTS_DELETED.name(), dataModelObjects);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.events;
|
||||
|
||||
import java.util.List;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.Person;
|
||||
|
||||
/**
|
||||
* Event fired when new persons are added.
|
||||
*/
|
||||
public class PersonsAddedEvent extends PersonsEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* @param dataModelObjects The persons that have been added.
|
||||
*/
|
||||
public PersonsAddedEvent(List<Person> dataModelObjects) {
|
||||
super(Case.Events.PERSONS_ADDED.name(), dataModelObjects);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.events;
|
||||
|
||||
import java.util.List;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.Person;
|
||||
|
||||
/**
|
||||
* Event fired when persons are changed.
|
||||
*/
|
||||
public class PersonsChangedEvent extends PersonsEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param dataModelObjects The new values for the persons that have been
|
||||
* changed.
|
||||
*/
|
||||
public PersonsChangedEvent(List<Person> dataModelObjects) {
|
||||
super(Case.Events.PERSONS_CHANGED.name(), dataModelObjects);
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.events;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.sleuthkit.datamodel.Person;
|
||||
import org.sleuthkit.datamodel.PersonManager;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Base event class for when something pertaining to persons changes.
|
||||
*/
|
||||
public class PersonsEvent extends TskDataModelChangeEvent<Person> {
|
||||
|
||||
/**
|
||||
* Retrieves a list of ids from a list of persons.
|
||||
*
|
||||
* @param persons The persons.
|
||||
* @return The list of ids.
|
||||
*/
|
||||
private static List<Long> getIds(List<Person> persons) {
|
||||
return getSafeList(persons).stream()
|
||||
.filter(h -> h != null)
|
||||
.map(h -> h.getId()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the persons or an empty list.
|
||||
*
|
||||
* @param persons The person list.
|
||||
* @return The person list or an empty list if the parameter is null.
|
||||
*/
|
||||
private static List<Person> getSafeList(List<Person> persons) {
|
||||
return persons == null ? Collections.emptyList() : persons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param eventName The name of the Case.Events enum value for the event
|
||||
* type.
|
||||
* @param dataModelObjects The list of persons for the event.
|
||||
*/
|
||||
protected PersonsEvent(String eventName, List<Person> dataModelObjects) {
|
||||
super(eventName, getIds(dataModelObjects), new ArrayList<>(getSafeList(dataModelObjects)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Person> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
|
||||
PersonManager personManager = caseDb.getPersonManager();
|
||||
List<Person> toRet = new ArrayList<>();
|
||||
if (ids != null) {
|
||||
for (Long id : ids) {
|
||||
if (id == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<Person> thisPersonOpt = personManager.getPerson(id);
|
||||
thisPersonOpt.ifPresent((h) -> toRet.add(h));
|
||||
}
|
||||
}
|
||||
|
||||
return toRet;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.events;
|
||||
|
||||
import java.util.List;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.Person;
|
||||
|
||||
/**
|
||||
* Event fired when persons are removed.
|
||||
*/
|
||||
public class PersonsRemovedEvent extends PersonsEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* @param dataModelObjects The list of persons that have been deleted.
|
||||
*/
|
||||
public PersonsRemovedEvent(List<Person> dataModelObjects) {
|
||||
super(Case.Events.PERSONS_DELETED.name(), dataModelObjects);
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
DataContentViewerOtherCases.showCaseDetailsMenuItem.text=Show Case Details
|
||||
DataContentViewerOtherCases.table.toolTip.text=Click column name to sort. Right-click on the table for more options.
|
||||
DataContentViewerOtherCases.exportToCSVMenuItem.text=Export all Other Occurrences to CSV
|
||||
DataContentViewerOtherCases.showCommonalityMenuItem.text=Show Frequency
|
||||
DataContentViewerOtherCases.earliestCaseDate.text=Earliest Case Date
|
||||
DataContentViewerOtherCases.earliestCaseLabel.toolTipText=
|
||||
DataContentViewerOtherCases.earliestCaseLabel.text=Central Repository Starting Date:
|
||||
DataContentViewerOtherCases.foundInLabel.text=
|
||||
OtherOccurrencesPanel.caseDatasourceFileSplitPane.toolTipText=
|
||||
OtherOccurrencesPanel.earliestCaseLabel.toolTipText=
|
||||
OtherOccurrencesPanel.earliestCaseLabel.text=Central Repository Starting Date:
|
||||
OtherOccurrencesPanel.earliestCaseDate.text=Earliest Case Date
|
||||
OtherOccurrencesPanel.foundInLabel.text=
|
||||
OtherOccurrencesPanel.filesTable.toolTipText=Click column name to sort. Right-click on the table for more options.
|
||||
OtherOccurrencesPanel.exportToCSVMenuItem.text=Export all Other Occurrences to CSV
|
||||
OtherOccurrencesPanel.showCommonalityMenuItem.text=Show Frequency
|
||||
OtherOccurrencesPanel.showCaseDetailsMenuItem.text=Show Case Details
|
||||
|
@ -1,38 +1,6 @@
|
||||
DataContentViewerOtherCases.caseDetailsDialog.noCaseNameError=Error
|
||||
DataContentViewerOtherCases.caseDetailsDialog.noDetails=No details for this case.
|
||||
DataContentViewerOtherCases.caseDetailsDialog.noDetailsReference=No case details for Global reference properties.
|
||||
DataContentViewerOtherCases.caseDetailsDialog.notSelected=No Row Selected
|
||||
# {0} - commonality percentage
|
||||
# {1} - correlation type
|
||||
# {2} - correlation value
|
||||
DataContentViewerOtherCases.correlatedArtifacts.byType={0}% of data sources have {2} (type: {1})\n
|
||||
DataContentViewerOtherCases.correlatedArtifacts.failed=Failed to get frequency details.
|
||||
DataContentViewerOtherCases.correlatedArtifacts.isEmpty=There are no files or artifacts to correlate.
|
||||
DataContentViewerOtherCases.correlatedArtifacts.title=Attribute Frequency
|
||||
DataContentViewerOtherCases.dataSources.header.text=Data Source Name
|
||||
DataContentViewerOtherCases.earliestCaseNotAvailable=\ Not Enabled.
|
||||
DataContentViewerOtherCases.foundIn.text=Found %d instances in %d cases and %d data sources.
|
||||
DataContentViewerOtherCases.noOpenCase.errMsg=No open case available.
|
||||
DataContentViewerOtherCases.showCaseDetailsMenuItem.text=Show Case Details
|
||||
DataContentViewerOtherCases.table.noArtifacts=Item has no attributes with which to search.
|
||||
DataContentViewerOtherCases.table.noResultsFound=No results found.
|
||||
DataContentViewerOtherCases.table.toolTip.text=Click column name to sort. Right-click on the table for more options.
|
||||
DataContentViewerOtherCases.exportToCSVMenuItem.text=Export all Other Occurrences to CSV
|
||||
DataContentViewerOtherCases.showCommonalityMenuItem.text=Show Frequency
|
||||
DataContentViewerOtherCases.earliestCaseDate.text=Earliest Case Date
|
||||
DataContentViewerOtherCases.earliestCaseLabel.toolTipText=
|
||||
DataContentViewerOtherCases.earliestCaseLabel.text=Central Repository Starting Date:
|
||||
DataContentViewerOtherCases.foundInLabel.text=
|
||||
DataContentViewerOtherCases.title=Other Occurrences
|
||||
DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other occurrences.
|
||||
DataContentViewerOtherCasesModel.csvHeader.attribute=Matched Attribute
|
||||
DataContentViewerOtherCasesModel.csvHeader.case=Case
|
||||
DataContentViewerOtherCasesModel.csvHeader.comment=Comment
|
||||
DataContentViewerOtherCasesModel.csvHeader.dataSource=Data Source
|
||||
DataContentViewerOtherCasesModel.csvHeader.device=Device
|
||||
DataContentViewerOtherCasesModel.csvHeader.known=Known
|
||||
DataContentViewerOtherCasesModel.csvHeader.path=Path
|
||||
DataContentViewerOtherCasesModel.csvHeader.value=Attribute Value
|
||||
OccurrencePanel.caseCreatedDateLabel.text=Created Date:
|
||||
OccurrencePanel.caseDetails.text=Case Details
|
||||
OccurrencePanel.caseNameLabel.text=Name:
|
||||
@ -51,3 +19,36 @@ OtherOccurrencesDataSourcesTableModel.dataSourceName=Data Source Name
|
||||
OtherOccurrencesDataSourcesTableModel.noData=No Data.
|
||||
OtherOccurrencesFilesTableModel.fileName=File Name
|
||||
OtherOccurrencesFilesTableModel.noData=No Data.
|
||||
OtherOccurrencesPanel.caseDatasourceFileSplitPane.toolTipText=
|
||||
OtherOccurrencesPanel.caseDetailsDialog.noCaseNameError=Error
|
||||
OtherOccurrencesPanel.caseDetailsDialog.noDetails=No details for this case.
|
||||
OtherOccurrencesPanel.caseDetailsDialog.noDetailsReference=No case details for Global reference properties.
|
||||
OtherOccurrencesPanel.caseDetailsDialog.notSelected=No Row Selected
|
||||
# {0} - commonality percentage
|
||||
# {1} - correlation type
|
||||
# {2} - correlation value
|
||||
OtherOccurrencesPanel.correlatedArtifacts.byType={0}% of data sources have {2} (type: {1})\n
|
||||
OtherOccurrencesPanel.correlatedArtifacts.failed=Failed to get frequency details.
|
||||
OtherOccurrencesPanel.correlatedArtifacts.isEmpty=There are no files or artifacts to correlate.
|
||||
OtherOccurrencesPanel.correlatedArtifacts.title=Attribute Frequency
|
||||
OtherOccurrencesPanel.csvHeader.attribute=Matched Attribute
|
||||
OtherOccurrencesPanel.csvHeader.case=Case
|
||||
OtherOccurrencesPanel.csvHeader.comment=Comment
|
||||
OtherOccurrencesPanel.csvHeader.dataSource=Data Source
|
||||
OtherOccurrencesPanel.csvHeader.device=Device
|
||||
OtherOccurrencesPanel.csvHeader.known=Known
|
||||
OtherOccurrencesPanel.csvHeader.path=Path
|
||||
OtherOccurrencesPanel.csvHeader.value=Attribute Value
|
||||
OtherOccurrencesPanel.earliestCaseLabel.toolTipText=
|
||||
OtherOccurrencesPanel.earliestCaseLabel.text=Central Repository Starting Date:
|
||||
OtherOccurrencesPanel.earliestCaseDate.text=Earliest Case Date
|
||||
OtherOccurrencesPanel.earliestCaseNotAvailable=\ Not Enabled.
|
||||
OtherOccurrencesPanel.foundIn.text=Found %d instances in %d cases and %d data sources.
|
||||
OtherOccurrencesPanel.foundInLabel.text=
|
||||
OtherOccurrencesPanel.filesTable.toolTipText=Click column name to sort. Right-click on the table for more options.
|
||||
OtherOccurrencesPanel.exportToCSVMenuItem.text=Export all Other Occurrences to CSV
|
||||
OtherOccurrencesPanel.noOpenCase.errMsg=No open case available.
|
||||
OtherOccurrencesPanel.showCommonalityMenuItem.text=Show Frequency
|
||||
OtherOccurrencesPanel.showCaseDetailsMenuItem.text=Show Case Details
|
||||
OtherOccurrencesPanel.table.noArtifacts=Item has no attributes with which to search.
|
||||
OtherOccurrencesPanel.table.noResultsFound=No results found.
|
||||
|
@ -13,16 +13,9 @@ DataContentViewerOtherCases.dataSources.header.text=\u30c7\u30fc\u30bf\u30bd\u30
|
||||
DataContentViewerOtherCases.earliestCaseNotAvailable=\ \u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u305b\u3093\u3002
|
||||
DataContentViewerOtherCases.foundIn.text=%d \u306e\u30b1\u30fc\u30b9\u3068 %d \u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5185\u306b %d \u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002
|
||||
DataContentViewerOtherCases.noOpenCase.errMsg=\u958b\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002
|
||||
DataContentViewerOtherCases.showCaseDetailsMenuItem.text=\u30b1\u30fc\u30b9\u8a73\u7d30\u3092\u8868\u793a
|
||||
DataContentViewerOtherCases.table.noArtifacts=\u9805\u76ee\u306b\u691c\u7d22\u306b\u5229\u7528\u3067\u304d\u308b\u5c5e\u6027\u306f\u3042\u308a\u307e\u305b\u3093\u3002
|
||||
DataContentViewerOtherCases.table.noResultsFound=\u8a72\u5f53\u3059\u308b\u7d50\u679c\u304c\u3042\u308a\u307e\u305b\u3093\u3002
|
||||
DataContentViewerOtherCases.table.toolTip.text=\u5217\u540d\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30bd\u30fc\u30c8\u3057\u307e\u3059\u3002\u30c6\u30fc\u30d6\u30eb\u3092\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3066\u3055\u3089\u306a\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8868\u793a\u3057\u307e\u3059\u3002
|
||||
DataContentViewerOtherCases.exportToCSVMenuItem.text=\u305d\u306e\u4ed6\u3059\u3079\u3066\u306e\u767a\u751f\u3092CSV\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
|
||||
DataContentViewerOtherCases.showCommonalityMenuItem.text=\u983b\u5ea6\u3092\u8868\u793a
|
||||
DataContentViewerOtherCases.earliestCaseDate.text=\u6700\u3082\u53e4\u3044\u30b1\u30fc\u30b9\u65e5\u4ed8
|
||||
DataContentViewerOtherCases.earliestCaseLabel.toolTipText=
|
||||
DataContentViewerOtherCases.earliestCaseLabel.text=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u958b\u59cb\u65e5:
|
||||
DataContentViewerOtherCases.foundInLabel.text=
|
||||
DataContentViewerOtherCases.title=\u305d\u306e\u4ed6\u306e\u767a\u751f
|
||||
DataContentViewerOtherCases.toolTip=\u305d\u306e\u4ed6\u306e\u767a\u751f\u304b\u3089\u9078\u629e\u3057\u305f\u30d5\u30a1\u30a4\u30eb/\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u8868\u793a\u3057\u307e\u3059\u3002
|
||||
DataContentViewerOtherCasesModel.csvHeader.attribute=\u4e00\u81f4\u3057\u305f\u5c5e\u6027
|
||||
@ -51,3 +44,11 @@ OtherOccurrencesDataSourcesTableModel.dataSourceName=\u30c7\u30fc\u30bf\u30bd\u3
|
||||
OtherOccurrencesDataSourcesTableModel.noData=\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093\u3002
|
||||
OtherOccurrencesFilesTableModel.fileName=\u30d5\u30a1\u30a4\u30eb\u540d
|
||||
OtherOccurrencesFilesTableModel.noData=\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093\u3002
|
||||
OtherOccurrencesPanel.filesTable.toolTipText=\u5217\u540d\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30bd\u30fc\u30c8\u3057\u307e\u3059\u3002\u30c6\u30fc\u30d6\u30eb\u3092\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3066\u3055\u3089\u306a\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8868\u793a\u3057\u307e\u3059\u3002
|
||||
OtherOccurrencesPanel.earliestCaseLabel.toolTipText=
|
||||
OtherOccurrencesPanel.earliestCaseLabel.text=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u958b\u59cb\u65e5:
|
||||
OtherOccurrencesPanel.earliestCaseDate.text=\u6700\u3082\u53e4\u3044\u30b1\u30fc\u30b9\u65e5\u4ed8
|
||||
OtherOccurrencesPanel.foundInLabel.text=
|
||||
OtherOccurrencesPanel.showCommonalityMenuItem.text=\u983b\u5ea6\u3092\u8868\u793a
|
||||
OtherOccurrencesPanel.showCaseDetailsMenuItem.text=\u30b1\u30fc\u30b9\u8a73\u7d30\u3092\u8868\u793a
|
||||
OtherOccurrencesPanel.exportToCSVMenuItem.text=\u305d\u306e\u4ed6\u3059\u3079\u3066\u306e\u767a\u751f\u3092CSV\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
|
||||
|
@ -1,42 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Container class="javax.swing.JPopupMenu" name="rightClickPopupMenu">
|
||||
<Events>
|
||||
<EventHandler event="popupMenuWillBecomeVisible" listener="javax.swing.event.PopupMenuListener" parameters="javax.swing.event.PopupMenuEvent" handler="rightClickPopupMenuPopupMenuWillBecomeVisible"/>
|
||||
</Events>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
|
||||
<Property name="useNullLayout" type="boolean" value="true"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="exportToCSVMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.exportToCSVMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="showCaseDetailsMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.showCaseDetailsMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="showCommonalityMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.showCommonalityMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JFileChooser" name="CSVFileChooser">
|
||||
</Component>
|
||||
</NonVisualComponents>
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[1000, 10]"/>
|
||||
@ -58,280 +22,5 @@
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="tableContainerPanel" alignment="0" pref="1000" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="tableContainerPanel" pref="78" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="tableContainerPanel">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[600, 63]"/>
|
||||
</Property>
|
||||
<Property name="requestFocusEnabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
|
||||
<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="tablesViewerSplitPane" max="32767" attributes="0"/>
|
||||
<Component id="jPanel1" alignment="1" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="tablesViewerSplitPane" pref="37" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jPanel1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="tablesViewerSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="450"/>
|
||||
<Property name="resizeWeight" type="double" value="0.75"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="caseDatasourceFileSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="300"/>
|
||||
<Property name="resizeWeight" type="double" value="0.66"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value=""/>
|
||||
</Properties>
|
||||
<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>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="caseDatasourceSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="150"/>
|
||||
<Property name="resizeWeight" type="double" value="0.5"/>
|
||||
</Properties>
|
||||
<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>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="caseScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[150, 30]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
<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>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="casesTable">
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="casesTableModel" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="dataSourceScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[150, 30]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</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>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="dataSourcesTable">
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="dataSourcesTableModel" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="filesTableScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[150, 30]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<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"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="filesTable">
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="filesTableModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.table.toolTip.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
|
||||
<TableColumnModel selectionModel="0"/>
|
||||
</Property>
|
||||
<Property name="componentPopupMenu" type="javax.swing.JPopupMenu" editor="org.netbeans.modules.form.ComponentChooserEditor">
|
||||
<ComponentRef name="rightClickPopupMenu"/>
|
||||
</Property>
|
||||
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
|
||||
<JTableSelectionModel selectionMode="0"/>
|
||||
</Property>
|
||||
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
|
||||
<TableHeader reorderingAllowed="true" resizingAllowed="true"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="detailsPanelScrollPane">
|
||||
<Properties>
|
||||
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[300, 100]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<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>
|
||||
<Container class="javax.swing.JPanel" name="jPanel1">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[576, 22]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="foundInLabel">
|
||||
<Properties>
|
||||
<Property name="horizontalAlignment" type="int" value="11"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.foundInLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[400, 16]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="3" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="earliestCaseDate">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseDate.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[200, 16]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="7" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="earliestCaseLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[260, 16]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.Box$Filler" name="filler1">
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[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="2" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.1" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
</Form>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -151,7 +151,7 @@ final class OtherOccurrencesDataSourcesTableModel extends AbstractTableModel {
|
||||
//place holder value will be used since correlation attribute was unavailble
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get current case", ex);
|
||||
caseUUID = DataContentViewerOtherCases.getPlaceholderUUID();
|
||||
caseUUID = OtherOccurrencesPanel.getPlaceholderUUID();
|
||||
}
|
||||
}
|
||||
dataSourceSet.add(new DataSourceColumnItem(nodeData.getCaseName(), nodeData.getDeviceID(), nodeData.getDataSourceName(), caseUUID));
|
||||
|
@ -130,7 +130,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel {
|
||||
//place holder value will be used since correlation attribute was unavailble
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get current case", ex);
|
||||
caseUUID = DataContentViewerOtherCases.getPlaceholderUUID();
|
||||
caseUUID = OtherOccurrencesPanel.getPlaceholderUUID();
|
||||
}
|
||||
}
|
||||
return nodeData.getCaseName() + nodeData.getDataSourceName() + nodeData.getDeviceID() + nodeData.getFilePath() + caseUUID;
|
||||
|
@ -0,0 +1,321 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Container class="javax.swing.JPopupMenu" name="rightClickPopupMenu">
|
||||
<Events>
|
||||
<EventHandler event="popupMenuWillBecomeVisible" listener="javax.swing.event.PopupMenuListener" parameters="javax.swing.event.PopupMenuEvent" handler="rightClickPopupMenuPopupMenuWillBecomeVisible"/>
|
||||
</Events>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
|
||||
<Property name="useNullLayout" type="boolean" value="true"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="exportToCSVMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="OtherOccurrencesPanel.exportToCSVMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="showCaseDetailsMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="OtherOccurrencesPanel.showCaseDetailsMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="showCommonalityMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="OtherOccurrencesPanel.showCommonalityMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JFileChooser" name="CSVFileChooser">
|
||||
</Component>
|
||||
</NonVisualComponents>
|
||||
<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="tableContainerPanel" alignment="0" pref="528" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="tableContainerPanel" alignment="0" pref="143" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="tableContainerPanel">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[600, 63]"/>
|
||||
</Property>
|
||||
<Property name="requestFocusEnabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="tablesViewerSplitPane" alignment="0" pref="0" max="32767" attributes="0"/>
|
||||
<Component id="jPanel1" alignment="1" pref="516" max="32767" attributes="0"/>
|
||||
</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 min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="tablesViewerSplitPane" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jPanel1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="tablesViewerSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="450"/>
|
||||
<Property name="resizeWeight" type="double" value="0.75"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="caseDatasourceFileSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="300"/>
|
||||
<Property name="resizeWeight" type="double" value="0.66"/>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="OtherOccurrencesPanel.caseDatasourceFileSplitPane.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<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>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="caseDatasourceSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="150"/>
|
||||
<Property name="resizeWeight" type="double" value="0.5"/>
|
||||
</Properties>
|
||||
<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>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="caseScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[150, 30]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
<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>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="casesTable">
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="casesTableModel" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="dataSourceScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[150, 30]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</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>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="dataSourcesTable">
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="dataSourcesTableModel" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="filesTableScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[150, 30]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<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"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="filesTable">
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="filesTableModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="OtherOccurrencesPanel.filesTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
|
||||
<JTableSelectionModel selectionMode="0"/>
|
||||
</Property>
|
||||
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
|
||||
<TableHeader reorderingAllowed="true" resizingAllowed="true"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="detailsPanelScrollPane">
|
||||
<Properties>
|
||||
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[300, 100]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<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>
|
||||
<Container class="javax.swing.JPanel" name="jPanel1">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[576, 22]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="foundInLabel">
|
||||
<Properties>
|
||||
<Property name="horizontalAlignment" type="int" value="11"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="OtherOccurrencesPanel.foundInLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[400, 16]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="3" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="earliestCaseDate">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="OtherOccurrencesPanel.earliestCaseDate.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[200, 16]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="7" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="earliestCaseLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="OtherOccurrencesPanel.earliestCaseLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="OtherOccurrencesPanel.earliestCaseLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[260, 16]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.Box$Filler" name="filler1">
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[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="2" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.1" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2017-2021 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.contentviewer;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Used as a key to ensure we eliminate duplicates from the result set by not
|
||||
* overwriting CR correlation instances.
|
||||
*/
|
||||
final class UniquePathKey {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(UniquePathKey.class.getName());
|
||||
private final String dataSourceID;
|
||||
private final String filePath;
|
||||
private final String type;
|
||||
private final String caseUUID;
|
||||
|
||||
UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) {
|
||||
super();
|
||||
dataSourceID = nodeData.getDeviceID();
|
||||
if (nodeData.getFilePath() != null) {
|
||||
filePath = nodeData.getFilePath().toLowerCase();
|
||||
} else {
|
||||
filePath = null;
|
||||
}
|
||||
type = nodeData.getType();
|
||||
String tempCaseUUID;
|
||||
try {
|
||||
tempCaseUUID = nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID();
|
||||
} catch (CentralRepoException ignored) {
|
||||
//non central repo nodeData won't have a correlation case
|
||||
try {
|
||||
tempCaseUUID = Case.getCurrentCaseThrows().getName();
|
||||
//place holder value will be used since correlation attribute was unavailble
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get current case", ex);
|
||||
tempCaseUUID = OtherOccurrencesPanel.getPlaceholderUUID();
|
||||
}
|
||||
}
|
||||
caseUUID = tempCaseUUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof UniquePathKey) {
|
||||
UniquePathKey otherKey = (UniquePathKey) (other);
|
||||
return (Objects.equals(otherKey.getDataSourceID(), this.getDataSourceID())
|
||||
&& Objects.equals(otherKey.getFilePath(), this.getFilePath())
|
||||
&& Objects.equals(otherKey.getType(), this.getType())
|
||||
&& Objects.equals(otherKey.getCaseUUID(), this.getCaseUUID()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getDataSourceID(), getFilePath(), getType(), getCaseUUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of this UniquePathKey.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file path for the UniquePathKey.
|
||||
*
|
||||
* @return the filePath
|
||||
*/
|
||||
String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data source id for the UniquePathKey.
|
||||
*
|
||||
* @return the dataSourceID
|
||||
*/
|
||||
String getDataSourceID() {
|
||||
return dataSourceID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the case uuid for the UniquePathKey
|
||||
*
|
||||
* @return the case UUID
|
||||
*/
|
||||
String getCaseUUID() {
|
||||
return caseUUID;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
TableDataPanel.titleLabel.text=Title
|
@ -0,0 +1,15 @@
|
||||
OsAccountDataPanel_basic_address=Address
|
||||
OsAccountDataPanel_basic_admin=Administrator
|
||||
OsAccountDataPanel_basic_creationDate=Creation Date
|
||||
OsAccountDataPanel_basic_fullname=Full Name
|
||||
OsAccountDataPanel_basic_login=Login
|
||||
OsAccountDataPanel_basic_title=Basic Properties
|
||||
OsAccountDataPanel_basic_type=Type
|
||||
OsAccountDataPanel_realm_address=Address
|
||||
OsAccountDataPanel_realm_confidence=Confidence
|
||||
OsAccountDataPanel_realm_name=Name
|
||||
OsAccountDataPanel_realm_title=Realm Properties
|
||||
OsAccountDataPanel_realm_unknown=Unknown
|
||||
OsAccountViewer_title=Os Account
|
||||
OsAccountViewer_tooltip=Viewer for OS accounts related to the selected node.
|
||||
TableDataPanel.titleLabel.text=Title
|
294
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountDataPanel.java
Executable file
294
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountDataPanel.java
Executable file
@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.osaccount;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import static java.util.Locale.US;
|
||||
import java.util.Optional;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.contentviewers.osaccount.SectionData.RowData;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.OsAccountRealm;
|
||||
|
||||
/**
|
||||
* Panel for displaying the properties of an OsAccount.
|
||||
*/
|
||||
public class OsAccountDataPanel extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final int KEY_COLUMN = 0;
|
||||
private static final int VALUE_COLUMN = 1;
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMM dd yyyy", US);
|
||||
|
||||
// Panel constructor.
|
||||
OsAccountDataPanel() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the panel layout.
|
||||
*/
|
||||
private void initialize() {
|
||||
this.setLayout(new GridBagLayout());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OsAccount to display in this panel.
|
||||
*
|
||||
* @param account OsAccount to display, if null is passed the panel will
|
||||
* appear blank.
|
||||
*/
|
||||
void setOsAccount(OsAccount account) {
|
||||
removeAll();
|
||||
|
||||
if (account != null) {
|
||||
List<SectionData> data = new ArrayList<>();
|
||||
data.add(buildBasicProperties(account));
|
||||
|
||||
OsAccountRealm realm = account.getRealm();
|
||||
if (realm != null) {
|
||||
data.add(buildRealmProperties(realm));
|
||||
}
|
||||
|
||||
addDataComponents(data);
|
||||
}
|
||||
revalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Give all of the data to display, create the swing components and add to
|
||||
* the panel.
|
||||
*
|
||||
* @param panelData Data to be displayed.
|
||||
*/
|
||||
private void addDataComponents(List<SectionData> panelData) {
|
||||
int rowCnt = 0;
|
||||
for (SectionData section : panelData) {
|
||||
addTitle(section.getTitle(), rowCnt++);
|
||||
|
||||
for (RowData<String, String> rowData : section) {
|
||||
String key = rowData.getKey();
|
||||
String value = rowData.getValue();
|
||||
|
||||
addPropertyName(key, rowCnt);
|
||||
addPropertyValue(value, rowCnt++);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the constraints for a Vertical Glue to fill the space, if
|
||||
// any at the bottom of the panel.
|
||||
GridBagConstraints constraints = new GridBagConstraints();
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = rowCnt;
|
||||
constraints.gridwidth = 2;
|
||||
constraints.fill = GridBagConstraints.BOTH;
|
||||
constraints.weightx = 1;
|
||||
constraints.weighty = 1;
|
||||
add(Box.createVerticalGlue(), constraints);
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OsAccountDataPanel_basic_title=Basic Properties",
|
||||
"OsAccountDataPanel_basic_login=Login",
|
||||
"OsAccountDataPanel_basic_fullname=Full Name",
|
||||
"OsAccountDataPanel_basic_address=Address",
|
||||
"OsAccountDataPanel_basic_admin=Administrator",
|
||||
"OsAccountDataPanel_basic_type=Type",
|
||||
"OsAccountDataPanel_basic_creationDate=Creation Date",
|
||||
})
|
||||
|
||||
/**
|
||||
* Returns the data for the Basic Properties section of the panel.
|
||||
*
|
||||
* @param account Selected account
|
||||
*
|
||||
* @return The basic properties data for the given account.
|
||||
*/
|
||||
private SectionData buildBasicProperties(OsAccount account) {
|
||||
SectionData data = new SectionData(Bundle.OsAccountDataPanel_basic_title());
|
||||
|
||||
Optional<String> optional = account.getLoginName();
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_login(),
|
||||
optional.isPresent() ? optional.get() : "");
|
||||
|
||||
optional = account.getFullName();
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_fullname(),
|
||||
optional.isPresent() ? optional.get() : "");
|
||||
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_address(),
|
||||
account.getName() == null || account.getName().isEmpty() ? "" : account.getName());
|
||||
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_type(), account.getOsAccountType().getName());
|
||||
|
||||
Optional<Long> crTime = account.getCreationTime();
|
||||
if(crTime.isPresent()) {
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_creationDate(), DATE_FORMAT.format(new Date(crTime.get() * 1000)));
|
||||
}
|
||||
else {
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_creationDate(), "");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OsAccountDataPanel_realm_title=Realm Properties",
|
||||
"OsAccountDataPanel_realm_name=Name",
|
||||
"OsAccountDataPanel_realm_address=Address",
|
||||
"OsAccountDataPanel_realm_confidence=Confidence",
|
||||
"OsAccountDataPanel_realm_unknown=Unknown",
|
||||
"OsAccountDataPanel_realm_scope=Scope",})
|
||||
|
||||
/**
|
||||
* Builds the Realm Properties.
|
||||
*
|
||||
* @param realm A valid OsAccountRealm.
|
||||
*
|
||||
* @return Data to be displayed for the given realm.
|
||||
*/
|
||||
private SectionData buildRealmProperties(OsAccountRealm realm) {
|
||||
SectionData data = new SectionData(Bundle.OsAccountDataPanel_realm_title());
|
||||
|
||||
Optional<String> optional = realm.getRealmName();
|
||||
data.addData(Bundle.OsAccountDataPanel_realm_name(),
|
||||
optional.isPresent() ? optional.get() : Bundle.OsAccountDataPanel_realm_unknown());
|
||||
|
||||
optional = realm.getRealmAddr();
|
||||
data.addData(Bundle.OsAccountDataPanel_realm_address(),
|
||||
optional.isPresent() ? optional.get() : "");
|
||||
|
||||
data.addData(Bundle.OsAccountDataPanel_realm_scope(),
|
||||
realm.getScope().getName());
|
||||
|
||||
data.addData(Bundle.OsAccountDataPanel_realm_confidence(),
|
||||
realm.getScopeConfidence().getName());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a section title to the panel with the given title and location.
|
||||
*
|
||||
* @param title Section title.
|
||||
* @param row Row in the layout the title will appear.
|
||||
*/
|
||||
private void addTitle(String title, int row) {
|
||||
JLabel label = new JLabel(title);
|
||||
// Make the title bold.
|
||||
label.setFont(label.getFont().deriveFont(Font.BOLD));
|
||||
add(label, getTitleContraints(row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the property name at the given row in the layout.
|
||||
*
|
||||
* @param key The property name.
|
||||
* @param row The row in the layout.
|
||||
*/
|
||||
private void addPropertyName(String key, int row) {
|
||||
JLabel label = new JLabel(key);
|
||||
add(label, getPropertyNameContraints(row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the property value at the given row in the layout.
|
||||
*
|
||||
* @param value The value to display.
|
||||
* @param row The row in the layout.
|
||||
*/
|
||||
private void addPropertyValue(String value, int row) {
|
||||
JLabel label = new JLabel(value);
|
||||
add(label, getPropertyValueContraints(row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the constraints for a title at the given row.
|
||||
*
|
||||
* @param row The row to generate the title constraints for.
|
||||
*
|
||||
* @return Constraints for a title row.
|
||||
*/
|
||||
private GridBagConstraints getTitleContraints(int row) {
|
||||
GridBagConstraints constraints = new GridBagConstraints();
|
||||
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = row;
|
||||
constraints.gridwidth = 2; // The title goes across the other columns
|
||||
constraints.gridheight = 1;
|
||||
constraints.anchor = GridBagConstraints.NORTHWEST;
|
||||
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
constraints.weightx = 1;
|
||||
constraints.insets = new Insets(5, 5, 5, 9);
|
||||
|
||||
return constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the constraints for a property name at the given row.
|
||||
*
|
||||
* @param row
|
||||
*
|
||||
* @return Constraints for the property name label.
|
||||
*/
|
||||
private GridBagConstraints getPropertyNameContraints(int row) {
|
||||
GridBagConstraints constraints = new GridBagConstraints();
|
||||
|
||||
constraints.gridx = KEY_COLUMN;
|
||||
constraints.gridy = row;
|
||||
constraints.gridwidth = 1; // The title goes across the other columns
|
||||
constraints.gridheight = 1;
|
||||
constraints.anchor = GridBagConstraints.WEST;
|
||||
constraints.insets = new Insets(0, 13, 5, 5);
|
||||
|
||||
return constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the constraints for a property value at the given row.
|
||||
*
|
||||
* @param row Row in the layout.
|
||||
*
|
||||
* @return The constraints for the property label.
|
||||
*/
|
||||
private GridBagConstraints getPropertyValueContraints(int row) {
|
||||
GridBagConstraints constraints = new GridBagConstraints();
|
||||
|
||||
constraints.gridx = VALUE_COLUMN;
|
||||
constraints.gridy = row;
|
||||
constraints.gridwidth = 1; // The title goes across the other columns
|
||||
constraints.gridheight = 1;
|
||||
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
constraints.weightx = 1;
|
||||
constraints.insets = new Insets(0, 5, 5, 5);
|
||||
|
||||
return constraints;
|
||||
}
|
||||
}
|
28
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountViewer.form
Executable file
28
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountViewer.form
Executable file
@ -0,0 +1,28 @@
|
||||
<?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 class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
162
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountViewer.java
Executable file
162
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountViewer.java
Executable file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.osaccount;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.DataArtifact;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* DataContentViewer for OsAccounts.
|
||||
*/
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 12)
|
||||
public class OsAccountViewer extends javax.swing.JPanel implements DataContentViewer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger logger = Logger.getLogger(OsAccountViewer.class.getName());
|
||||
|
||||
private final OsAccountDataPanel dataPanel = new OsAccountDataPanel();
|
||||
|
||||
/**
|
||||
* Creates new form OsAccountViewer
|
||||
*/
|
||||
public OsAccountViewer() {
|
||||
initComponents();
|
||||
|
||||
mainScrollPane.setViewportView(dataPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNode(Node node) {
|
||||
OsAccount osAccount = null;
|
||||
try {
|
||||
osAccount = node.getLookup().lookup(OsAccount.class);
|
||||
if (osAccount == null) {
|
||||
Optional<OsAccount> optional;
|
||||
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
|
||||
if (file != null) {
|
||||
optional = file.getOsAccount();
|
||||
if (optional.isPresent()) {
|
||||
osAccount = optional.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (osAccount == null) {
|
||||
DataArtifact dataArtifact = node.getLookup().lookup(DataArtifact.class);
|
||||
if (dataArtifact != null) {
|
||||
Optional<OsAccount> optional = dataArtifact.getOsAccount();
|
||||
if (optional.isPresent()) {
|
||||
osAccount = optional.get();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to get OsAccount for node %s", node.getDisplayName()), ex);
|
||||
}
|
||||
|
||||
if (osAccount != null) {
|
||||
dataPanel.setOsAccount(osAccount);
|
||||
}
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OsAccountViewer_title=Os Account"
|
||||
})
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return Bundle.OsAccountViewer_title();
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OsAccountViewer_tooltip=Viewer for OS accounts related to the selected node."
|
||||
})
|
||||
@Override
|
||||
public String getToolTip() {
|
||||
return Bundle.OsAccountViewer_tooltip();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataContentViewer createInstance() {
|
||||
return new OsAccountViewer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetComponent() {
|
||||
dataPanel.setOsAccount(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported(Node node) {
|
||||
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
|
||||
OsAccount osAccount = node.getLookup().lookup(OsAccount.class);
|
||||
DataArtifact dataArtifact = node.getLookup().lookup(DataArtifact.class);
|
||||
|
||||
try {
|
||||
return osAccount != null
|
||||
|| (file != null && file.getOsAccount().isPresent())
|
||||
|| (dataArtifact != null && dataArtifact.getOsAccount().isPresent());
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to determine if node %s is Supported for OsAccountViewer", node.getDisplayName()), ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int isPreferred(Node node) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
mainScrollPane = new javax.swing.JScrollPane();
|
||||
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
add(mainScrollPane, java.awt.BorderLayout.CENTER);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JScrollPane mainScrollPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
108
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/SectionData.java
Executable file
108
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/SectionData.java
Executable file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.osaccount;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Data model for OsAccount panels, but could be reused in other places. The
|
||||
* model represents a titled section of key\value pairs.
|
||||
*/
|
||||
final class SectionData implements Iterable<SectionData.RowData<String, String>> {
|
||||
|
||||
private final String title;
|
||||
private final List<RowData<String, String>> data;
|
||||
|
||||
/**
|
||||
* Construct a new SectionData object.
|
||||
*
|
||||
* @param title
|
||||
*/
|
||||
SectionData(String title) {
|
||||
this.title = title;
|
||||
this.data = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title for this section.
|
||||
*
|
||||
* @return The section title.
|
||||
*/
|
||||
String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new property name\property value pair.
|
||||
*
|
||||
* @param key The property display name.
|
||||
* @param value The property value.
|
||||
*/
|
||||
void addData(String properytName, String propertyValue) {
|
||||
data.add(new RowData<>(properytName, propertyValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<RowData<String, String>> iterator() {
|
||||
return data.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a row of data. In this case it is just a key value pair.
|
||||
*
|
||||
* @param <K> Property Name.
|
||||
* @param <V> Property Value.
|
||||
*/
|
||||
static class RowData<K, V> {
|
||||
|
||||
private final K key;
|
||||
private final V value;
|
||||
|
||||
/**
|
||||
* Construct a new row of data for the model.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
RowData(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key.
|
||||
*
|
||||
* @return The key value.
|
||||
*/
|
||||
K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
V getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,11 +23,11 @@ import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Node;
|
||||
@ -47,6 +47,19 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
*/
|
||||
public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Object> {
|
||||
|
||||
private static final Set<Case.Events> LISTENING_EVENTS = EnumSet.of(
|
||||
Case.Events.DATA_SOURCE_ADDED,
|
||||
Case.Events.HOSTS_ADDED,
|
||||
Case.Events.HOSTS_DELETED,
|
||||
Case.Events.PERSONS_ADDED,
|
||||
Case.Events.PERSONS_DELETED,
|
||||
Case.Events.PERSONS_CHANGED
|
||||
);
|
||||
|
||||
private static final Set<String> LISTENING_EVENT_NAMES = LISTENING_EVENTS.stream()
|
||||
.map(evt -> evt.name())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AutopsyTreeChildFactory.class.getName());
|
||||
|
||||
/**
|
||||
@ -56,7 +69,7 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())
|
||||
if (LISTENING_EVENT_NAMES.contains(eventType)
|
||||
&& Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
|
||||
refreshChildren();
|
||||
}
|
||||
@ -66,13 +79,13 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
super.addNotify();
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||
Case.addEventTypeSubscriber(LISTENING_EVENTS, pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeNotify() {
|
||||
super.removeNotify();
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||
Case.removeEventTypeSubscriber(LISTENING_EVENTS, pcl);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,11 +107,11 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
|
||||
.map(PersonGrouping::new)
|
||||
.sorted()
|
||||
.forEach(list::add);
|
||||
|
||||
|
||||
if (CollectionUtils.isNotEmpty(personManager.getHostsForPerson(null))) {
|
||||
list.add(new PersonGrouping(null));
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// otherwise, just show host level
|
||||
|
@ -50,7 +50,7 @@ public class ContentNodeSelectionInfo implements NodeSelectionInfo {
|
||||
@Override
|
||||
public boolean matches(Node candidateNode) {
|
||||
Content content = candidateNode.getLookup().lookup(Content.class);
|
||||
return content.getId() == contentId;
|
||||
return (content != null && content.getId() == contentId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Host.HostStatus;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -95,6 +94,13 @@ public class DataSourcesByTypeNode extends DisplayableItemNode {
|
||||
|
||||
private static final String NAME = Bundle.DataSourcesHostsNode_name();
|
||||
|
||||
/**
|
||||
* @return The name used to identify the node of this type with a lookup.
|
||||
*/
|
||||
public static String getNameIdentifier() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*/
|
||||
|
@ -43,7 +43,15 @@ import org.sleuthkit.datamodel.TskDataException;
|
||||
*/
|
||||
public class DataSourcesNode extends DisplayableItemNode {
|
||||
|
||||
public static final String NAME = NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.name");
|
||||
private static final String NAME = NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.name");
|
||||
|
||||
/**
|
||||
* @return The name used to identify the node of this type with a lookup.
|
||||
*/
|
||||
public static String getNameIdentifier() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
private final String displayName;
|
||||
|
||||
// NOTE: The images passed in via argument will be ignored.
|
||||
|
@ -33,9 +33,11 @@ import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.WeakListeners;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.hosts.AssociatePersonsMenuAction;
|
||||
import org.sleuthkit.autopsy.datamodel.hosts.RemoveParentPersonAction;
|
||||
@ -76,7 +78,7 @@ public class HostNode extends DisplayableItemNode {
|
||||
/**
|
||||
* Listener for handling DATA_SOURCE_ADDED events.
|
||||
*/
|
||||
private final PropertyChangeListener pcl = new PropertyChangeListener() {
|
||||
private final PropertyChangeListener dataSourceAddedPcl = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
@ -88,12 +90,12 @@ public class HostNode extends DisplayableItemNode {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), dataSourceAddedPcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeNotify() {
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), dataSourceAddedPcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -163,7 +165,39 @@ public class HostNode extends DisplayableItemNode {
|
||||
return new DataSourceGroupingNode(key.getDataSource());
|
||||
};
|
||||
|
||||
/**
|
||||
* Listener for handling host change events.
|
||||
*/
|
||||
private final PropertyChangeListener hostChangePcl = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (hostId != null && eventType.equals(Case.Events.HOSTS_CHANGED.toString()) && evt instanceof HostsChangedEvent) {
|
||||
((HostsChangedEvent) evt).getNewValue().stream()
|
||||
.filter(h -> h != null && h.getId() == hostId)
|
||||
.findFirst()
|
||||
.ifPresent((newHost) -> {
|
||||
setName(newHost.getName());
|
||||
setDisplayName(newHost.getName());
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the host name or 'unknown host' if null.
|
||||
*
|
||||
* @param host The host.
|
||||
* @return The display name.
|
||||
*/
|
||||
private static String getHostName(Host host) {
|
||||
return (host == null || host.getName() == null)
|
||||
? Bundle.HostGroupingNode_unknownHostNode_title()
|
||||
: host.getName();
|
||||
}
|
||||
|
||||
private final Host host;
|
||||
private final Long hostId;
|
||||
|
||||
/**
|
||||
* Main constructor for HostDataSources key where data source children
|
||||
@ -192,14 +226,25 @@ public class HostNode extends DisplayableItemNode {
|
||||
* @param host The host.
|
||||
*/
|
||||
private HostNode(Children children, Host host) {
|
||||
super(children, host == null ? null : Lookups.singleton(host));
|
||||
this(children, host, getHostName(host));
|
||||
}
|
||||
|
||||
String safeName = (host == null || host.getName() == null)
|
||||
? Bundle.HostGroupingNode_unknownHostNode_title()
|
||||
: host.getName();
|
||||
|
||||
super.setName(safeName);
|
||||
super.setDisplayName(safeName);
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param children The children for this host node.
|
||||
* @param host The host.
|
||||
* @param displayName The displayName.
|
||||
*/
|
||||
private HostNode(Children children, Host host, String displayName) {
|
||||
super(children,
|
||||
host == null ? Lookups.fixed(displayName) : Lookups.fixed(host, displayName));
|
||||
|
||||
hostId = host == null ? null : host.getId();
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.HOSTS_CHANGED),
|
||||
WeakListeners.propertyChange(hostChangePcl, this));
|
||||
super.setName(displayName);
|
||||
super.setDisplayName(displayName);
|
||||
this.setIconBaseWithExtension(ICON_PATH);
|
||||
this.host = host;
|
||||
}
|
||||
|
@ -23,16 +23,20 @@ import java.beans.PropertyChangeListener;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.Action;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
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.WeakListeners;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.persons.DeletePersonAction;
|
||||
import org.sleuthkit.autopsy.datamodel.persons.EditPersonAction;
|
||||
@ -48,6 +52,14 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
public class PersonGroupingNode extends DisplayableItemNode {
|
||||
|
||||
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/person.png";
|
||||
|
||||
/**
|
||||
* Returns the id of an unknown persons node. This can be used with a node lookup.
|
||||
* @return The id of an unknown persons node.
|
||||
*/
|
||||
public static String getUnknownPersonId() {
|
||||
return Bundle.PersonNode_unknownPersonNode_title();
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for creating the host children of this person.
|
||||
@ -56,6 +68,15 @@ public class PersonGroupingNode extends DisplayableItemNode {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PersonChildren.class.getName());
|
||||
|
||||
private static final Set<Case.Events> CHILD_EVENTS = EnumSet.of(
|
||||
Case.Events.HOSTS_ADDED,
|
||||
Case.Events.HOSTS_DELETED,
|
||||
Case.Events.PERSONS_CHANGED);
|
||||
|
||||
private static final Set<String> CHILD_EVENTS_STR = CHILD_EVENTS.stream()
|
||||
.map(ev -> ev.name())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
private final Person person;
|
||||
|
||||
/**
|
||||
@ -68,15 +89,13 @@ public class PersonGroupingNode extends DisplayableItemNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for handling DATA_SOURCE_ADDED and DATA_SOURCE_DELETED
|
||||
* events.
|
||||
* Listener for handling adding and removing host events.
|
||||
*/
|
||||
private final PropertyChangeListener pcl = new PropertyChangeListener() {
|
||||
private final PropertyChangeListener hostAddedDeletedPcl = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())
|
||||
|| eventType.equals(Case.Events.DATA_SOURCE_DELETED.toString())) {
|
||||
if (eventType != null && CHILD_EVENTS_STR.contains(eventType)) {
|
||||
refresh(true);
|
||||
}
|
||||
}
|
||||
@ -84,12 +103,12 @@ public class PersonGroupingNode extends DisplayableItemNode {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||
Case.addEventTypeSubscriber(CHILD_EVENTS, hostAddedDeletedPcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeNotify() {
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||
Case.removeEventTypeSubscriber(CHILD_EVENTS, hostAddedDeletedPcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -117,6 +136,38 @@ public class PersonGroupingNode extends DisplayableItemNode {
|
||||
}
|
||||
|
||||
private final Person person;
|
||||
private final Long personId;
|
||||
|
||||
/**
|
||||
* Listener for handling person change events.
|
||||
*/
|
||||
private final PropertyChangeListener personChangePcl = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (personId != null && eventType.equals(Case.Events.PERSONS_CHANGED.toString()) && evt instanceof PersonsChangedEvent) {
|
||||
((PersonsChangedEvent) evt).getNewValue().stream()
|
||||
.filter(p -> p != null && p.getId() == personId)
|
||||
.findFirst()
|
||||
.ifPresent((newPerson) -> {
|
||||
setName(newPerson.getName());
|
||||
setDisplayName(newPerson.getName());
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the display name for this person or "Unknown Persons".
|
||||
*
|
||||
* @param person The person.
|
||||
* @return The non-empty string for the display name.
|
||||
*/
|
||||
private static String getDisplayName(Person person) {
|
||||
return (person == null || person.getName() == null)
|
||||
? getUnknownPersonId()
|
||||
: person.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
@ -124,16 +175,25 @@ public class PersonGroupingNode extends DisplayableItemNode {
|
||||
* @param person The person record to be represented.
|
||||
*/
|
||||
PersonGroupingNode(Person person) {
|
||||
super(Children.create(new PersonChildren(person), false), person == null ? null : Lookups.singleton(person));
|
||||
this(person, getDisplayName(person));
|
||||
}
|
||||
|
||||
String safeName = (person == null || person.getName() == null)
|
||||
? Bundle.PersonNode_unknownPersonNode_title()
|
||||
: person.getName();
|
||||
|
||||
super.setName(safeName);
|
||||
super.setDisplayName(safeName);
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param person The person.
|
||||
* @param displayName The display name for the person.
|
||||
*/
|
||||
private PersonGroupingNode(Person person, String displayName) {
|
||||
super(Children.create(new PersonChildren(person), false),
|
||||
person == null ? Lookups.fixed(displayName) : Lookups.fixed(person, displayName));
|
||||
super.setName(displayName);
|
||||
super.setDisplayName(displayName);
|
||||
this.setIconBaseWithExtension(ICON_PATH);
|
||||
this.person = person;
|
||||
this.personId = person == null ? null : person.getId();
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.PERSONS_CHANGED),
|
||||
WeakListeners.propertyChange(personChangePcl, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,8 +31,12 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
public class ResultsNode extends DisplayableItemNode {
|
||||
|
||||
@NbBundle.Messages("ResultsNode.name.text=Results")
|
||||
public static final String NAME = Bundle.ResultsNode_name_text();
|
||||
private static final String NAME = Bundle.ResultsNode_name_text();
|
||||
|
||||
public static String getNameIdentifier() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
public ResultsNode(SleuthkitCase sleuthkitCase) {
|
||||
this(sleuthkitCase, 0);
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ import javax.swing.filechooser.FileFilter;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.openide.util.lookup.ServiceProviders;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
|
@ -46,13 +46,7 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor {
|
||||
|
||||
private static final BlackboardAttribute.Type TYPE_SET_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME);
|
||||
|
||||
private static final Set<String> EXCLUDED_KEYWORD_SEARCH_ITEMS = new HashSet<>(Arrays.asList(
|
||||
"PHONE NUMBERS",
|
||||
"IP ADDRESSES",
|
||||
"EMAIL ADDRESSES",
|
||||
"URLS",
|
||||
"CREDIT CARD NUMBERS"
|
||||
));
|
||||
private static final Set<String> EXCLUDED_KEYWORD_SEARCH_ITEMS = new HashSet<>();
|
||||
|
||||
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
|
||||
ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
|
||||
|
@ -353,7 +353,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
*
|
||||
* @return Seconds from java epoch.
|
||||
*/
|
||||
Long getDateAsLong() {
|
||||
public Long getDateAsLong() {
|
||||
return date;
|
||||
}
|
||||
|
||||
|
@ -21,14 +21,18 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
@ -38,30 +42,35 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
@Messages({
|
||||
"AnalysisPanel_keyColumn_title=Name",
|
||||
"AnalysisPanel_countColumn_title=Count",
|
||||
"AnalysisPanel_keywordSearchModuleName=Keyword Search"
|
||||
})
|
||||
"AnalysisPanel_keywordSearchModuleName=Keyword Search",
|
||||
"AnalysisPanel_hashsetHits_tabName=Hashset Hits",
|
||||
"AnalysisPanel_keywordHits_tabName=Keyword Hits",
|
||||
"AnalysisPanel_interestingItemHits_tabName=Interesting Item Hits",})
|
||||
public class AnalysisPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Default Column definitions for each table
|
||||
*/
|
||||
private static final List<ColumnModel<Pair<String, Long>>> DEFAULT_COLUMNS = Arrays.asList(
|
||||
// Default Column definitions for each table
|
||||
private static final List<ColumnModel<Pair<String, Long>, DefaultCellModel<?>>> DEFAULT_COLUMNS = Arrays.asList(
|
||||
new ColumnModel<>(
|
||||
Bundle.AnalysisPanel_keyColumn_title(),
|
||||
(pair) -> new DefaultCellModel(pair.getKey()),
|
||||
(pair) -> new DefaultCellModel<>(pair.getKey()),
|
||||
300
|
||||
),
|
||||
new ColumnModel<>(
|
||||
Bundle.AnalysisPanel_countColumn_title(),
|
||||
(pair) -> new DefaultCellModel(String.valueOf(pair.getValue())),
|
||||
(pair) -> new DefaultCellModel<>(pair.getValue()),
|
||||
100
|
||||
)
|
||||
);
|
||||
|
||||
// Identifies the key in the records for the tables.
|
||||
private static final Function<Pair<String, Long>, String> DEFAULT_KEY_PROVIDER = (pair) -> pair.getKey();
|
||||
|
||||
private final DataFetcher<DataSource, List<Pair<String, Long>>> hashsetsFetcher;
|
||||
private final DataFetcher<DataSource, List<Pair<String, Long>>> keywordsFetcher;
|
||||
private final DataFetcher<DataSource, List<Pair<String, Long>>> interestingItemsFetcher;
|
||||
|
||||
private final JTablePanel<Pair<String, Long>> hashsetHitsTable
|
||||
= JTablePanel.getJTablePanel(DEFAULT_COLUMNS)
|
||||
.setKeyFunction(DEFAULT_KEY_PROVIDER);
|
||||
@ -98,19 +107,23 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
|
||||
public AnalysisPanel(AnalysisSummary analysisData) {
|
||||
super(analysisData);
|
||||
|
||||
hashsetsFetcher = (dataSource) -> analysisData.getHashsetCounts(dataSource);
|
||||
keywordsFetcher = (dataSource) -> analysisData.getKeywordCounts(dataSource);
|
||||
interestingItemsFetcher = (dataSource) -> analysisData.getInterestingItemCounts(dataSource);
|
||||
|
||||
// set up data acquisition methods
|
||||
dataFetchComponents = Arrays.asList(
|
||||
// hashset hits loading components
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> analysisData.getHashsetCounts(dataSource),
|
||||
hashsetsFetcher,
|
||||
(result) -> hashsetHitsTable.showDataFetchResult(result)),
|
||||
// keyword hits loading components
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> analysisData.getKeywordCounts(dataSource),
|
||||
keywordsFetcher,
|
||||
(result) -> keywordHitsTable.showDataFetchResult(result)),
|
||||
// interesting item hits loading components
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> analysisData.getInterestingItemCounts(dataSource),
|
||||
interestingItemsFetcher,
|
||||
(result) -> interestingItemsTable.showDataFetchResult(result))
|
||||
);
|
||||
|
||||
@ -216,6 +229,16 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@Override
|
||||
List<ExcelSheetExport> getExports(DataSource dataSource) {
|
||||
return Stream.of(
|
||||
getTableExport(hashsetsFetcher, DEFAULT_COLUMNS, Bundle.AnalysisPanel_hashsetHits_tabName(), dataSource),
|
||||
getTableExport(keywordsFetcher, DEFAULT_COLUMNS, Bundle.AnalysisPanel_keywordHits_tabName(), dataSource),
|
||||
getTableExport(interestingItemsFetcher, DEFAULT_COLUMNS, Bundle.AnalysisPanel_interestingItemHits_tabName(), dataSource))
|
||||
.filter(sheet -> sheet != null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
@ -38,11 +38,18 @@ import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.DefaultMenuItem;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialExecutor;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor;
|
||||
@ -244,12 +251,12 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
|
||||
* @param artifact The artifact.
|
||||
* @return The menu item for a go to artifact menu item.
|
||||
*/
|
||||
protected CellModelTableCellRenderer.MenuItem getArtifactNavigateItem(BlackboardArtifact artifact) {
|
||||
protected MenuItem getArtifactNavigateItem(BlackboardArtifact artifact) {
|
||||
if (artifact == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CellModelTableCellRenderer.DefaultMenuItem(
|
||||
return new DefaultMenuItem(
|
||||
Bundle.BaseDataSourceSummaryPanel_goToArtifact(),
|
||||
() -> {
|
||||
final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
|
||||
@ -292,7 +299,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
|
||||
* @param path The path to the file.
|
||||
* @return The menu item or null if file cannot be found in data source.
|
||||
*/
|
||||
protected CellModelTableCellRenderer.MenuItem getFileNavigateItem(String path) {
|
||||
protected MenuItem getFileNavigateItem(String path) {
|
||||
if (StringUtils.isNotBlank(path)) {
|
||||
Path p = Paths.get(path);
|
||||
String fileName = normalizePath(p.getFileName().toString());
|
||||
@ -320,12 +327,12 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
|
||||
* @param file The file.
|
||||
* @return The menu item list for a go to artifact menu item.
|
||||
*/
|
||||
protected CellModelTableCellRenderer.MenuItem getFileNavigateItem(AbstractFile file) {
|
||||
protected MenuItem getFileNavigateItem(AbstractFile file) {
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CellModelTableCellRenderer.DefaultMenuItem(
|
||||
return new DefaultMenuItem(
|
||||
Bundle.BaseDataSourceSummaryPanel_goToFile(),
|
||||
() -> {
|
||||
new ViewContextAction(Bundle.BaseDataSourceSummaryPanel_goToFile(), file)
|
||||
@ -440,6 +447,132 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
|
||||
*/
|
||||
protected abstract void onNewDataSource(DataSource dataSource);
|
||||
|
||||
/**
|
||||
* Returns all the excel exportable items associated with the tab.
|
||||
*
|
||||
* @param dataSource The data source that results should be filtered.
|
||||
* @return The excel exportable objects.
|
||||
*/
|
||||
abstract List<ExcelSheetExport> getExports(DataSource dataSource);
|
||||
|
||||
/**
|
||||
* Runs a data fetcher and returns the result handling any possible errors
|
||||
* with a log message.
|
||||
*
|
||||
* @param dataFetcher The means of fetching the data.
|
||||
* @param sheetName The name of the sheet.
|
||||
* @param ds The data source.
|
||||
* @return The fetched data.
|
||||
*/
|
||||
protected static <T> T getFetchResult(
|
||||
DataFetcher<DataSource, T> dataFetcher,
|
||||
String sheetName, DataSource ds) {
|
||||
|
||||
try {
|
||||
return dataFetcher.runQuery(ds);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING,
|
||||
String.format("There was an error while acquiring data for exporting worksheet(s): '%s' for dataSource: %s",
|
||||
sheetName == null ? "<null>" : sheetName,
|
||||
ds == null || ds.getName() == null ? "<null>" : ds.getName()), ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that converts data into a excel sheet data.
|
||||
*/
|
||||
protected interface ExcelExportFunction<T> {
|
||||
|
||||
/**
|
||||
* Function that converts data into an excel sheet.
|
||||
*
|
||||
* @param data The data.
|
||||
* @return The excel sheet export.
|
||||
* @throws ExcelExportException
|
||||
*/
|
||||
ExcelSheetExport convert(T data) throws ExcelExportException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that converts data into an excel sheet export handling
|
||||
* possible excel exceptions.
|
||||
*
|
||||
* @param excelConverter Function to convert data to an excel sheet export.
|
||||
* @param data The data. If data is null, null will be returned.
|
||||
* @param sheetName The name(s) of the sheet (to be used in the error
|
||||
* message).
|
||||
* @return The excel sheet export.
|
||||
*/
|
||||
protected static <T> ExcelSheetExport convertToExcel(ExcelExportFunction<T> excelConverter, T data, String sheetName) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return excelConverter.convert(data);
|
||||
} catch (ExcelExportException ex) {
|
||||
logger.log(Level.WARNING,
|
||||
String.format("There was an error while preparing export of worksheet(s): '%s'",
|
||||
sheetName == null ? "<null>" : sheetName), ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an excel sheet export given the fetching of data or null if no
|
||||
* export created.
|
||||
*
|
||||
* @param dataFetcher The means of fetching data.
|
||||
* @param excelConverter The means of converting data to excel.
|
||||
* @param sheetName The name of the sheet (for error handling reporting).
|
||||
* @param ds The data source to use for fetching data.
|
||||
* @return The excel sheet export or null if no export could be generated.
|
||||
*/
|
||||
protected static <T> ExcelSheetExport getExport(
|
||||
DataFetcher<DataSource, T> dataFetcher, ExcelExportFunction<T> excelConverter,
|
||||
String sheetName, DataSource ds) {
|
||||
|
||||
T data = getFetchResult(dataFetcher, sheetName, ds);
|
||||
return convertToExcel(excelConverter, data, sheetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an excel table export of the data or null if no export created.
|
||||
*
|
||||
* @param columnsModel The model for the columns.
|
||||
* @param sheetName The name for the sheet.
|
||||
* @param data The data to be exported.
|
||||
* @return The excel table export or null if no export could be generated.
|
||||
*/
|
||||
protected static <T, C extends ExcelCellModel> ExcelSheetExport getTableExport(List<ColumnModel<T, C>> columnsModel,
|
||||
String sheetName, List<T> data) {
|
||||
|
||||
return convertToExcel((dataList) -> new ExcelTableExport<T, C>(sheetName, columnsModel, dataList),
|
||||
data,
|
||||
sheetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an excel table export of the data or null if no export created.
|
||||
*
|
||||
* @param dataFetcher The means of fetching data for the data source and the
|
||||
* export.
|
||||
* @param columnsModel The model for the columns.
|
||||
* @param sheetName The name for the sheet.
|
||||
* @param ds The data source.
|
||||
* @return The excel export or null if no export created.
|
||||
*/
|
||||
protected static <T, C extends ExcelCellModel> ExcelSheetExport getTableExport(
|
||||
DataFetcher<DataSource, List<T>> dataFetcher, List<ColumnModel<T, C>> columnsModel,
|
||||
String sheetName, DataSource ds) {
|
||||
|
||||
return getExport(dataFetcher,
|
||||
(dataList) -> new ExcelTableExport<T, C>(sheetName, columnsModel, dataList),
|
||||
sheetName,
|
||||
ds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that shows a loading screen with loadable components,
|
||||
* create swing workers from the datafetch components and data source
|
||||
|
@ -58,3 +58,7 @@ UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more opt
|
||||
UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more options
|
||||
UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options
|
||||
TimelinePanel.viewInTimelineBtn.text=View in Timeline
|
||||
ExportPanel.xlsxExportMessage.text=Export Data from Data Source Summary to an Excel Document.
|
||||
ExportPanel.xlsxExportButton.text=Export Summary Data
|
||||
ExcelExportDialog.titleLabel.text=Data Source Summary has been exported to:
|
||||
ExcelExportDialog.okButton.text=OK
|
||||
|
@ -1,5 +1,8 @@
|
||||
AnalysisPanel_countColumn_title=Count
|
||||
AnalysisPanel_hashsetHits_tabName=Hashset Hits
|
||||
AnalysisPanel_interestingItemHits_tabName=Interesting Item Hits
|
||||
AnalysisPanel_keyColumn_title=Name
|
||||
AnalysisPanel_keywordHits_tabName=Keyword Hits
|
||||
AnalysisPanel_keywordSearchModuleName=Keyword Search
|
||||
BaseDataSourceSummaryPanel_goToArtifact=View Source Result
|
||||
BaseDataSourceSummaryPanel_goToFile=View Source File in Directory
|
||||
@ -45,6 +48,7 @@ DataSourceSummaryNode.column.type.header=Type
|
||||
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source
|
||||
DataSourceSummaryTabbedPane_analysisTab_title=Analysis
|
||||
DataSourceSummaryTabbedPane_detailsTab_title=Container
|
||||
DataSourceSummaryTabbedPane_exportTab_title=Export
|
||||
DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation
|
||||
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
|
||||
DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases
|
||||
@ -52,19 +56,37 @@ DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files
|
||||
DataSourceSummaryTabbedPane_timelineTab_title=Timeline
|
||||
DataSourceSummaryTabbedPane_typesTab_title=Types
|
||||
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
|
||||
ExcelExportAction_exportToXLSX_beginExport=Beginning Export...
|
||||
# {0} - tabName
|
||||
ExcelExportAction_exportToXLSX_gatheringTabData=Fetching Data for {0} Tab...
|
||||
ExcelExportAction_exportToXLSX_writingToFile=Writing to File...
|
||||
ExcelExportAction_getXLSXPath_directory=DataSourceSummary
|
||||
ExcelExportAction_moduleName=Data Source Summary
|
||||
ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling...
|
||||
ExcelExportAction_runXLSXExport_progressCancelTitle=Cancel
|
||||
# {0} - dataSource
|
||||
ExcelExportAction_runXLSXExport_progressTitle=Exporting {0} to XLSX
|
||||
ExcelExportDialog_title=Data Source Summary Exported
|
||||
GeolocationPanel_cityColumn_title=Closest City
|
||||
GeolocationPanel_countColumn_title=Count
|
||||
GeolocationPanel_mostCommon_tabName=Most Common Cities
|
||||
GeolocationPanel_mostRecent_tabName=Most Recent Cities
|
||||
GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX Parser was not run.
|
||||
GeolocationPanel_unknownRow_title=Unknown
|
||||
PastCasesPanel_caseColumn_title=Case
|
||||
PastCasesPanel_countColumn_title=Count
|
||||
PastCasesPanel_notableFileTable_tabName=Cases with Common Notable
|
||||
PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run.
|
||||
PastCasesPanel_sameIdsTable_tabName=Past Cases with the Same Devices
|
||||
RecentFilePanel_col_header_domain=Domain
|
||||
RecentFilePanel_col_header_path=Path
|
||||
RecentFilePanel_col_header_sender=Sender
|
||||
RecentFilePanel_emailParserModuleName=Email Parser
|
||||
RecentFilePanel_no_open_documents=No recently open documents found.
|
||||
RecentFilesPanel_attachmentsTable_tabName=Recent Attachments
|
||||
RecentFilesPanel_col_head_date=Date
|
||||
RecentFilesPanel_docsTable_tabName=Recently Opened Documents
|
||||
RecentFilesPanel_downloadsTable_tabName=Recently Downloads
|
||||
SizeRepresentationUtil_units_bytes=\ bytes
|
||||
SizeRepresentationUtil_units_gigabytes=\ GB
|
||||
SizeRepresentationUtil_units_kilobytes=\ kB
|
||||
@ -123,21 +145,30 @@ UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more opt
|
||||
UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more options
|
||||
UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options
|
||||
TimelinePanel.viewInTimelineBtn.text=View in Timeline
|
||||
ExportPanel.xlsxExportMessage.text=Export Data from Data Source Summary to an Excel Document.
|
||||
ExportPanel.xlsxExportButton.text=Export Summary Data
|
||||
ExcelExportDialog.titleLabel.text=Data Source Summary has been exported to:
|
||||
ExcelExportDialog.okButton.text=OK
|
||||
UserActivityPanel_noDataExists=No communication data exists
|
||||
UserActivityPanel_tab_title=User Activity
|
||||
UserActivityPanel_TopAccountTableModel_accountType_header=Account Type
|
||||
UserActivityPanel_TopAccountTableModel_lastAccess_header=Last Accessed
|
||||
UserActivityPanel_TopAccountTableModel_tabName=Recent Account Types Used
|
||||
UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed
|
||||
UserActivityPanel_TopDeviceAttachedTableModel_deviceId_header=Device Id
|
||||
UserActivityPanel_TopDeviceAttachedTableModel_makeModel_header=Make and Model
|
||||
UserActivityPanel_TopDeviceAttachedTableModel_tabName=Recent Devices Attached
|
||||
UserActivityPanel_TopDomainsTableModel_count_header=Visits
|
||||
UserActivityPanel_TopDomainsTableModel_domain_header=Domain
|
||||
UserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Accessed
|
||||
UserActivityPanel_TopDomainsTableModel_tabName=Recent Domains
|
||||
UserActivityPanel_TopProgramsTableModel_count_header=Run Times
|
||||
UserActivityPanel_TopProgramsTableModel_folder_header=Folder
|
||||
UserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run
|
||||
UserActivityPanel_TopProgramsTableModel_name_header=Program
|
||||
UserActivityPanel_TopProgramsTableModel_tabName=Recent Programs
|
||||
UserActivityPanel_TopWebSearchTableModel_dateAccessed_header=Date Accessed
|
||||
UserActivityPanel_TopWebSearchTableModel_searchString_header=Search String
|
||||
UserActivityPanel_TopWebSearchTableModel_tabName=Recent Web Searches
|
||||
UserActivityPanel_TopWebSearchTableModel_translatedResult_header=Translated
|
||||
ViewSummaryInformationAction.name.text=View Summary Information
|
||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -32,6 +33,7 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
@ -275,6 +277,11 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
((DefaultTableModel) filePathsTable.getModel()).setRowCount(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource ds) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -16,15 +16,6 @@
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JTabbedPane" name="tabbedPane">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
|
||||
<CardConstraints cardName="tabbedPane"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="noDataSourcePane">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
@ -57,5 +48,29 @@
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="tabContentPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
|
||||
<CardConstraints cardName="tabbedPane"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JTabbedPane" name="tabbedPane">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -25,9 +25,12 @@ import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.ui.ExcelExportAction.ExportableTab;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
@ -43,7 +46,8 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
"DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases",
|
||||
"DataSourceSummaryTabbedPane_analysisTab_title=Analysis",
|
||||
"DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation",
|
||||
"DataSourceSummaryTabbedPane_timelineTab_title=Timeline"
|
||||
"DataSourceSummaryTabbedPane_timelineTab_title=Timeline",
|
||||
"DataSourceSummaryTabbedPane_exportTab_title=Export"
|
||||
})
|
||||
public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
|
||||
@ -51,28 +55,14 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
* Records of tab information (i.e. title, component, function to call on
|
||||
* new data source).
|
||||
*/
|
||||
private class DataSourceTab {
|
||||
private class DataSourceTab implements ExportableTab {
|
||||
|
||||
private final String tabTitle;
|
||||
private final Component component;
|
||||
private final Consumer<DataSource> onDataSource;
|
||||
private final Function<DataSource, List<ExcelSheetExport>> excelExporter;
|
||||
private final Runnable onClose;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param tabTitle The title of the tab.
|
||||
* @param component The component to be displayed.
|
||||
* @param onDataSource The function to be called on a new data source.
|
||||
* @param onClose Called to cleanup resources when closing tabs.
|
||||
*/
|
||||
DataSourceTab(String tabTitle, Component component, Consumer<DataSource> onDataSource, Runnable onClose) {
|
||||
this.tabTitle = tabTitle;
|
||||
this.component = component;
|
||||
this.onDataSource = onDataSource;
|
||||
this.onClose = onClose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
@ -81,19 +71,35 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
* @param notifyParentClose Notifies parent to trigger a close.
|
||||
*/
|
||||
DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) {
|
||||
|
||||
this(tabTitle, panel, panel::setDataSource, panel::getExports, panel::close);
|
||||
panel.setParentCloseListener(() -> notifyParentClose());
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param tabTitle The title of the tab.
|
||||
* @param component The component to be displayed.
|
||||
* @param onDataSource The function to be called on a new data source.
|
||||
* @param excelExporter The function that creates excel exports for a
|
||||
* particular data source for this tab. Can be null for no exports.
|
||||
* @param onClose Called to cleanup resources when closing tabs. Can be
|
||||
* null for no-op.
|
||||
*/
|
||||
DataSourceTab(String tabTitle, Component component, Consumer<DataSource> onDataSource,
|
||||
Function<DataSource, List<ExcelSheetExport>> excelExporter, Runnable onClose) {
|
||||
this.tabTitle = tabTitle;
|
||||
this.component = panel;
|
||||
this.onDataSource = panel::setDataSource;
|
||||
this.onClose = panel::close;
|
||||
this.component = component;
|
||||
this.onDataSource = onDataSource;
|
||||
this.excelExporter = excelExporter;
|
||||
this.onClose = onClose;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The title for the tab.
|
||||
*/
|
||||
String getTabTitle() {
|
||||
@Override
|
||||
public String getTabTitle() {
|
||||
return tabTitle;
|
||||
}
|
||||
|
||||
@ -111,6 +117,11 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
return onDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ExcelSheetExport> getExcelExports(DataSource dataSource) {
|
||||
return excelExporter == null ? null : excelExporter.apply(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The action for closing resources in the tab.
|
||||
*/
|
||||
@ -120,13 +131,17 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// needs to match value provided for card layout in designed
|
||||
private static final String TABBED_PANE = "tabbedPane";
|
||||
private static final String NO_DATASOURCE_PANE = "noDataSourcePane";
|
||||
|
||||
private Runnable notifyParentClose = null;
|
||||
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
|
||||
|
||||
|
||||
// create an export panel whose button triggers the export to XLSX action
|
||||
private final ExportPanel exportPanel = new ExportPanel();
|
||||
|
||||
private final List<DataSourceTab> tabs = Arrays.asList(
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()),
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()),
|
||||
@ -136,11 +151,24 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_geolocationTab_title(), new GeolocationPanel()),
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_timelineTab_title(), new TimelinePanel()),
|
||||
// do nothing on closing
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> {
|
||||
}),
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel())
|
||||
new DataSourceTab(
|
||||
Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(),
|
||||
ingestHistoryPanel,
|
||||
ingestHistoryPanel::setDataSource,
|
||||
null,
|
||||
null),
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()),
|
||||
new DataSourceTab(
|
||||
Bundle.DataSourceSummaryTabbedPane_exportTab_title(),
|
||||
exportPanel,
|
||||
null,
|
||||
null,
|
||||
null)
|
||||
);
|
||||
|
||||
|
||||
// the action that does the export
|
||||
private final ExcelExportAction exportAction = new ExcelExportAction(tabs);
|
||||
|
||||
private DataSource dataSource = null;
|
||||
private CardLayout cardLayout;
|
||||
|
||||
@ -194,6 +222,9 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
|
||||
// set this to no datasource initially
|
||||
cardLayout.show(this, NO_DATASOURCE_PANE);
|
||||
|
||||
// set action for when user requests xlsx export
|
||||
exportPanel.setXlsxExportAction(() -> exportAction.accept(getDataSource()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,11 +243,13 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
|
||||
|
||||
for (DataSourceTab tab : tabs) {
|
||||
tab.getOnDataSource().accept(dataSource);
|
||||
if (tab.getOnDataSource() != null) {
|
||||
tab.getOnDataSource().accept(dataSource);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this.dataSource == null) {
|
||||
cardLayout.show(this, NO_DATASOURCE_PANE);
|
||||
} else {
|
||||
@ -229,12 +262,15 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
*/
|
||||
public void close() {
|
||||
for (DataSourceTab tab : tabs) {
|
||||
tab.getOnClose().run();
|
||||
if (tab.getOnClose() != null) {
|
||||
tab.getOnClose().run();
|
||||
}
|
||||
}
|
||||
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), caseEventsListener);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -244,12 +280,12 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
tabbedPane = new javax.swing.JTabbedPane();
|
||||
javax.swing.JPanel noDataSourcePane = new javax.swing.JPanel();
|
||||
javax.swing.JLabel noDataSourceLabel = new javax.swing.JLabel();
|
||||
javax.swing.JPanel tabContentPane = new javax.swing.JPanel();
|
||||
tabbedPane = new javax.swing.JTabbedPane();
|
||||
|
||||
setLayout(new java.awt.CardLayout());
|
||||
add(tabbedPane, "tabbedPane");
|
||||
|
||||
noDataSourcePane.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
@ -258,6 +294,11 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
noDataSourcePane.add(noDataSourceLabel, java.awt.BorderLayout.CENTER);
|
||||
|
||||
add(noDataSourcePane, "noDataSourcePane");
|
||||
|
||||
tabContentPane.setLayout(new java.awt.BorderLayout());
|
||||
tabContentPane.add(tabbedPane, java.awt.BorderLayout.CENTER);
|
||||
|
||||
add(tabContentPane, "tabbedPane");
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
|
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
|
||||
import org.sleuthkit.autopsy.progress.ProgressIndicator;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Action that exports tab data to an excel workbook.
|
||||
*/
|
||||
@Messages({
|
||||
"ExcelExportAction_moduleName=Data Source Summary",})
|
||||
class ExcelExportAction implements Consumer<DataSource> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ExcelExportAction.class.getName());
|
||||
|
||||
/**
|
||||
* A tab that can be exported.
|
||||
*/
|
||||
interface ExportableTab {
|
||||
|
||||
/**
|
||||
* Returns the name of the tab.
|
||||
*
|
||||
* @return The tab name.
|
||||
*/
|
||||
String getTabTitle();
|
||||
|
||||
/**
|
||||
* Given the data source, provides the excel exports for this tab.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @return The excel exports or null.
|
||||
*/
|
||||
List<ExcelSheetExport> getExcelExports(DataSource dataSource);
|
||||
}
|
||||
|
||||
private final ExcelExport excelExport = ExcelExport.getInstance();
|
||||
private final List<? extends ExportableTab> tabExports;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param tabExports The different tabs that may have excel exports.
|
||||
*/
|
||||
ExcelExportAction(List<? extends ExportableTab> tabExports) {
|
||||
this.tabExports = Collections.unmodifiableList(new ArrayList<>(tabExports));
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts the data source for which this export pertains, prompts user for
|
||||
* output location, and exports the data.
|
||||
*
|
||||
* @param ds The data source.
|
||||
*/
|
||||
@Override
|
||||
public void accept(DataSource ds) {
|
||||
if (ds == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
File outputLoc = getXLSXPath(ds.getName());
|
||||
if (outputLoc == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
runXLSXExport(ds, outputLoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an xlsx path for the data source summary export.
|
||||
*
|
||||
* @param dataSourceName The name of the data source.
|
||||
* @return The file to which the excel document should be written or null if
|
||||
* file already exists or cancellation.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"ExcelExportAction_getXLSXPath_directory=DataSourceSummary",})
|
||||
private File getXLSXPath(String dataSourceName) {
|
||||
// set initial path to reports directory with filename that is
|
||||
// a combination of the data source name and time stamp
|
||||
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss");
|
||||
String fileName = String.format("%s-%s.xlsx", dataSourceName == null ? "" : FileUtil.escapeFileName(dataSourceName), dateFormat.format(new Date()));
|
||||
try {
|
||||
String reportsDir = Case.getCurrentCaseThrows().getReportDirectory();
|
||||
File reportsDirFile = Paths.get(reportsDir, Bundle.ExcelExportAction_getXLSXPath_directory()).toFile();
|
||||
if (!reportsDirFile.exists()) {
|
||||
reportsDirFile.mkdirs();
|
||||
}
|
||||
|
||||
return Paths.get(reportsDirFile.getAbsolutePath(), fileName).toFile();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to find reports directory.", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An action listener that handles cancellation of the export process.
|
||||
*/
|
||||
private class CancelExportListener implements ActionListener {
|
||||
|
||||
private SwingWorker<Boolean, Void> worker = null;
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (worker != null && !worker.isCancelled() && !worker.isDone()) {
|
||||
worker.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the swing worker that could be cancelled.
|
||||
*
|
||||
* @return The swing worker that could be cancelled.
|
||||
*/
|
||||
SwingWorker<Boolean, Void> getWorker() {
|
||||
return worker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the swing worker that could be cancelled.
|
||||
*
|
||||
* @param worker The swing worker that could be cancelled.
|
||||
*/
|
||||
void setWorker(SwingWorker<Boolean, Void> worker) {
|
||||
this.worker = worker;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles managing the gui and exporting data from the tabs into an excel
|
||||
* document.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param path The output path.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"# {0} - dataSource",
|
||||
"ExcelExportAction_runXLSXExport_progressTitle=Exporting {0} to XLSX",
|
||||
"ExcelExportAction_runXLSXExport_progressCancelTitle=Cancel",
|
||||
"ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling..."
|
||||
})
|
||||
private void runXLSXExport(DataSource dataSource, File path) {
|
||||
|
||||
CancelExportListener cancelButtonListener = new CancelExportListener();
|
||||
|
||||
ProgressIndicator progressIndicator = new ModalDialogProgressIndicator(
|
||||
WindowManager.getDefault().getMainWindow(),
|
||||
Bundle.ExcelExportAction_runXLSXExport_progressTitle(dataSource.getName()),
|
||||
new String[]{Bundle.ExcelExportAction_runXLSXExport_progressCancelTitle()},
|
||||
Bundle.ExcelExportAction_runXLSXExport_progressCancelTitle(),
|
||||
cancelButtonListener
|
||||
);
|
||||
|
||||
SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
|
||||
@Override
|
||||
protected Boolean doInBackground() throws Exception {
|
||||
exportToXLSX(progressIndicator, dataSource, path);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
get();
|
||||
} catch (ExecutionException ex) {
|
||||
logger.log(Level.WARNING, "Error while trying to export data source summary to xlsx.", ex);
|
||||
} catch (InterruptedException | CancellationException ex) {
|
||||
// no op on cancellation
|
||||
} finally {
|
||||
progressIndicator.finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cancelButtonListener.setWorker(worker);
|
||||
worker.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Action that handles updating progress and exporting data from the tabs.
|
||||
*
|
||||
* @param progressIndicator The progress indicator.
|
||||
* @param dataSource The data source to be exported.
|
||||
* @param path The path of the excel export.
|
||||
* @throws InterruptedException
|
||||
* @throws IOException
|
||||
* @throws ExcelExportException
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"ExcelExportAction_exportToXLSX_beginExport=Beginning Export...",
|
||||
"# {0} - tabName",
|
||||
"ExcelExportAction_exportToXLSX_gatheringTabData=Fetching Data for {0} Tab...",
|
||||
"ExcelExportAction_exportToXLSX_writingToFile=Writing to File...",})
|
||||
|
||||
private void exportToXLSX(ProgressIndicator progressIndicator, DataSource dataSource, File path)
|
||||
throws InterruptedException, IOException, ExcelExport.ExcelExportException {
|
||||
|
||||
int exportWeight = 3;
|
||||
int totalWeight = tabExports.size() + exportWeight;
|
||||
progressIndicator.start(Bundle.ExcelExportAction_exportToXLSX_beginExport(), totalWeight);
|
||||
List<ExcelExport.ExcelSheetExport> sheetExports = new ArrayList<>();
|
||||
for (int i = 0; i < tabExports.size(); i++) {
|
||||
if (Thread.interrupted()) {
|
||||
throw new InterruptedException("Export has been cancelled.");
|
||||
}
|
||||
|
||||
ExportableTab tab = tabExports.get(i);
|
||||
progressIndicator.progress(Bundle.ExcelExportAction_exportToXLSX_gatheringTabData(tab == null ? "" : tab.getTabTitle()), i);
|
||||
|
||||
List<ExcelExport.ExcelSheetExport> exports = tab.getExcelExports(dataSource);
|
||||
if (exports != null) {
|
||||
sheetExports.addAll(exports);
|
||||
}
|
||||
}
|
||||
|
||||
if (Thread.interrupted()) {
|
||||
throw new InterruptedException("Export has been cancelled.");
|
||||
}
|
||||
|
||||
progressIndicator.progress(Bundle.ExcelExportAction_exportToXLSX_writingToFile(), tabExports.size());
|
||||
excelExport.writeExcel(sheetExports, path);
|
||||
|
||||
progressIndicator.finish();
|
||||
|
||||
try {
|
||||
// add to reports
|
||||
Case curCase = Case.getCurrentCaseThrows();
|
||||
curCase.addReport(path.getParent(),
|
||||
Bundle.ExcelExportAction_moduleName(),
|
||||
path.getName(),
|
||||
dataSource);
|
||||
|
||||
// and show finished dialog
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
ExcelExportDialog dialog = new ExcelExportDialog(WindowManager.getDefault().getMainWindow(), path);
|
||||
dialog.setResizable(false);
|
||||
dialog.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
||||
dialog.setVisible(true);
|
||||
dialog.toFront();
|
||||
});
|
||||
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "There was an error attaching report to case.", ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
<?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"/>
|
||||
</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" alignment="1" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="linkTextScrollPane" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
<Component id="okButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="titleLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="116" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="titleLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="linkTextScrollPane" min="-2" pref="39" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="okButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="titleLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExcelExportDialog.titleLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="okButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExcelExportDialog.okButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="linkTextScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextArea" name="linkText">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color type="null"/>
|
||||
</Property>
|
||||
<Property name="columns" type="int" value="20"/>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="java.awt.Color.BLUE" type="code"/>
|
||||
</Property>
|
||||
<Property name="lineWrap" type="boolean" value="true"/>
|
||||
<Property name="rows" type="int" value="1"/>
|
||||
<Property name="wrapStyleWord" type="boolean" value="true"/>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="null"/>
|
||||
</Property>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Dialog showing where the data source summary excel export can be located.
|
||||
*/
|
||||
@Messages({
|
||||
"ExcelExportDialog_title=Data Source Summary Exported"
|
||||
})
|
||||
public class ExcelExportDialog extends javax.swing.JDialog {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ExcelExportDialog.class.getName());
|
||||
|
||||
/**
|
||||
* Creates new form ExcelExportDialog
|
||||
*/
|
||||
public ExcelExportDialog(java.awt.Frame parent, File filePath) {
|
||||
super(parent, true);
|
||||
|
||||
initComponents();
|
||||
setTitle(Bundle.ExcelExportDialog_title());
|
||||
|
||||
this.linkText.setText(filePath.getAbsolutePath());
|
||||
this.linkText.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
Desktop.getDesktop().open(filePath);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Unable to open: " + filePath.getAbsolutePath(), ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
this.linkText.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
javax.swing.JLabel titleLabel = new javax.swing.JLabel();
|
||||
javax.swing.JButton okButton = new javax.swing.JButton();
|
||||
javax.swing.JScrollPane linkTextScrollPane = new javax.swing.JScrollPane();
|
||||
linkText = new javax.swing.JTextArea();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(titleLabel, org.openide.util.NbBundle.getMessage(ExcelExportDialog.class, "ExcelExportDialog.titleLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(ExcelExportDialog.class, "ExcelExportDialog.okButton.text")); // NOI18N
|
||||
okButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
okButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
linkText.setEditable(false);
|
||||
linkText.setBackground(null);
|
||||
linkText.setColumns(20);
|
||||
linkText.setForeground(java.awt.Color.BLUE);
|
||||
linkText.setLineWrap(true);
|
||||
linkText.setRows(1);
|
||||
linkText.setWrapStyleWord(true);
|
||||
linkText.setBorder(null);
|
||||
linkText.setOpaque(false);
|
||||
linkTextScrollPane.setViewportView(linkText);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(linkTextScrollPane)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(0, 0, Short.MAX_VALUE)
|
||||
.addComponent(okButton))
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
|
||||
.addComponent(titleLabel)
|
||||
.addGap(0, 116, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(titleLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(linkTextScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(okButton)
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
|
||||
dispose();
|
||||
}//GEN-LAST:event_okButtonActionPerformed
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JTextArea linkText;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
<?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">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="xlsxExportMessage" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="xlsxExportButton" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="62" 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="xlsxExportMessage" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="xlsxExportButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="250" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="xlsxExportButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExportPanel.xlsxExportButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="xlsxExportButtonActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="xlsxExportMessage">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExportPanel.xlsxExportMessage.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
/**
|
||||
* The panel that provides options for exporting data source summary data.
|
||||
*/
|
||||
public class ExportPanel extends javax.swing.JPanel {
|
||||
|
||||
private Runnable xlsxExportAction;
|
||||
|
||||
/**
|
||||
* Creates new form ExportPanel
|
||||
*/
|
||||
public ExportPanel() {
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action that handles exporting to excel.
|
||||
*
|
||||
* @return The action that handles exporting to excel.
|
||||
*/
|
||||
public Runnable getXlsxExportAction() {
|
||||
return xlsxExportAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action that handles exporting to excel.
|
||||
*
|
||||
* @param onXlsxExport The action that handles exporting to excel.
|
||||
*/
|
||||
public void setXlsxExportAction(Runnable onXlsxExport) {
|
||||
this.xlsxExportAction = onXlsxExport;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
javax.swing.JButton xlsxExportButton = new javax.swing.JButton();
|
||||
javax.swing.JLabel xlsxExportMessage = new javax.swing.JLabel();
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(xlsxExportButton, org.openide.util.NbBundle.getMessage(ExportPanel.class, "ExportPanel.xlsxExportButton.text")); // NOI18N
|
||||
xlsxExportButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
xlsxExportButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(xlsxExportMessage, org.openide.util.NbBundle.getMessage(ExportPanel.class, "ExportPanel.xlsxExportMessage.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(xlsxExportMessage)
|
||||
.addComponent(xlsxExportButton))
|
||||
.addContainerGap(62, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(xlsxExportMessage)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(xlsxExportButton)
|
||||
.addContainerGap(250, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void xlsxExportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_xlsxExportButtonActionPerformed
|
||||
if (this.xlsxExportAction != null) {
|
||||
xlsxExportAction.run();
|
||||
}
|
||||
}//GEN-LAST:event_xlsxExportButtonActionPerformed
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@ -38,13 +39,15 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.City
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityData;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityRecordCount;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.CityRecord;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
|
||||
import org.sleuthkit.autopsy.geolocation.GeoFilter;
|
||||
import org.sleuthkit.autopsy.geolocation.GeoLocationUIException;
|
||||
import org.sleuthkit.autopsy.geolocation.GeolocationTopComponent;
|
||||
@ -59,32 +62,79 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
"GeolocationPanel_cityColumn_title=Closest City",
|
||||
"GeolocationPanel_countColumn_title=Count",
|
||||
"GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX Parser was not run.",
|
||||
"GeolocationPanel_unknownRow_title=Unknown",})
|
||||
"GeolocationPanel_unknownRow_title=Unknown",
|
||||
"GeolocationPanel_mostCommon_tabName=Most Common Cities",
|
||||
"GeolocationPanel_mostRecent_tabName=Most Recent Cities",})
|
||||
public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
/**
|
||||
* Object encapsulating the view model of this panel.
|
||||
*/
|
||||
private static class GeolocationViewModel {
|
||||
|
||||
private final List<Pair<String, Integer>> mostRecentData;
|
||||
private final List<Pair<String, Integer>> mostCommonData;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param mostRecentData The data to be displayed in the most recent
|
||||
* table.
|
||||
* @param mostCommonData The data to be displayed in the most common
|
||||
* table.
|
||||
*/
|
||||
GeolocationViewModel(List<Pair<String, Integer>> mostRecentData, List<Pair<String, Integer>> mostCommonData) {
|
||||
this.mostRecentData = mostRecentData;
|
||||
this.mostCommonData = mostCommonData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data to be displayed in the most recent table.
|
||||
*
|
||||
* @return The data to be displayed in the most recent table.
|
||||
*/
|
||||
List<Pair<String, Integer>> getMostRecentData() {
|
||||
return mostRecentData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data to be displayed in the most common table.
|
||||
*
|
||||
* @return The data to be displayed in the most common table.
|
||||
*/
|
||||
List<Pair<String, Integer>> getMostCommonData() {
|
||||
return mostCommonData;
|
||||
}
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final int DAYS_COUNT = 30;
|
||||
private static final int MAX_COUNT = 10;
|
||||
|
||||
// The column indicating the city
|
||||
private static final ColumnModel<Pair<String, Integer>> CITY_COL = new ColumnModel<>(
|
||||
private static final ColumnModel<Pair<String, Integer>, DefaultCellModel<?>> CITY_COL = new ColumnModel<>(
|
||||
Bundle.GeolocationPanel_cityColumn_title(),
|
||||
(pair) -> new DefaultCellModel(pair.getLeft()),
|
||||
(pair) -> new DefaultCellModel<>(pair.getLeft()),
|
||||
300
|
||||
);
|
||||
|
||||
// The column indicating the count of points seen close to that city
|
||||
private static final ColumnModel<Pair<String, Integer>> COUNT_COL = new ColumnModel<>(
|
||||
private static final ColumnModel<Pair<String, Integer>, DefaultCellModel<?>> COUNT_COL = new ColumnModel<>(
|
||||
Bundle.GeolocationPanel_countColumn_title(),
|
||||
(pair) -> new DefaultCellModel(Integer.toString(pair.getRight())),
|
||||
(pair) -> new DefaultCellModel<>(pair.getRight()),
|
||||
100
|
||||
);
|
||||
|
||||
private static final List<ColumnModel<Pair<String, Integer>, DefaultCellModel<?>>> DEFAULT_TEMPLATE = Arrays.asList(
|
||||
CITY_COL,
|
||||
COUNT_COL
|
||||
);
|
||||
|
||||
// tables displaying city and number of hits for that city
|
||||
private final JTablePanel<Pair<String, Integer>> mostCommonTable = JTablePanel.getJTablePanel(Arrays.asList(CITY_COL, COUNT_COL))
|
||||
private final JTablePanel<Pair<String, Integer>> mostCommonTable = JTablePanel.getJTablePanel(DEFAULT_TEMPLATE)
|
||||
.setKeyFunction((pair) -> pair.getLeft());
|
||||
|
||||
private final JTablePanel<Pair<String, Integer>> mostRecentTable = JTablePanel.getJTablePanel(Arrays.asList(CITY_COL, COUNT_COL))
|
||||
private final JTablePanel<Pair<String, Integer>> mostRecentTable = JTablePanel.getJTablePanel(DEFAULT_TEMPLATE)
|
||||
.setKeyFunction((pair) -> pair.getLeft());
|
||||
|
||||
// loadable components on this tab
|
||||
@ -99,6 +149,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
private final GeolocationSummary whereUsedData;
|
||||
|
||||
private final DataFetcher<DataSource, GeolocationViewModel> geolocationFetcher;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*/
|
||||
@ -115,10 +167,13 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
super(whereUsedData);
|
||||
|
||||
this.whereUsedData = whereUsedData;
|
||||
|
||||
this.geolocationFetcher = (dataSource) -> convertToViewModel(whereUsedData.getCityCounts(dataSource, DAYS_COUNT, MAX_COUNT));
|
||||
|
||||
// set up data acquisition methods
|
||||
dataFetchComponents = Arrays.asList(
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> whereUsedData.getCityCounts(dataSource, DAYS_COUNT, MAX_COUNT),
|
||||
geolocationFetcher,
|
||||
(result) -> handleData(result)));
|
||||
|
||||
initComponents();
|
||||
@ -127,11 +182,12 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
/**
|
||||
* Means of rendering data to be shown in the tables.
|
||||
*
|
||||
* @param result The result of fetching data for a data source.
|
||||
* @param result The result of fetching data for a data source and
|
||||
* processing into view model data.
|
||||
*/
|
||||
private void handleData(DataFetchResult<CityData> result) {
|
||||
showCityContent(DataFetchResult.getSubResult(result, (dr) -> dr.getMostCommon()), mostCommonTable, commonViewInGeolocationBtn);
|
||||
showCityContent(DataFetchResult.getSubResult(result, (dr) -> dr.getMostRecent()), mostRecentTable, recentViewInGeolocationBtn);
|
||||
private void handleData(DataFetchResult<GeolocationViewModel> result) {
|
||||
showCityContent(DataFetchResult.getSubResult(result, (dr) -> dr.getMostCommonData()), mostCommonTable, commonViewInGeolocationBtn);
|
||||
showCityContent(DataFetchResult.getSubResult(result, (dr) -> dr.getMostRecentData()), mostRecentTable, recentViewInGeolocationBtn);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,7 +212,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
} else if (cityIdentifiers.size() >= 3) {
|
||||
return String.format("%s, %s; %s", cityIdentifiers.get(0), cityIdentifiers.get(1), cityIdentifiers.get(2));
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -188,7 +244,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
*/
|
||||
private List<Pair<String, Integer>> formatList(CityCountsList countsList) {
|
||||
if (countsList == null) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Stream<CityRecordCount> countsStream = ((countsList.getCounts() == null)
|
||||
@ -206,6 +262,21 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts CityData from GeolocationSummary into data that can be directly
|
||||
* put into table in this panel.
|
||||
*
|
||||
* @param cityData The city data.
|
||||
* @return The view model data.
|
||||
*/
|
||||
private GeolocationViewModel convertToViewModel(CityData cityData) {
|
||||
if (cityData == null) {
|
||||
return new GeolocationViewModel(Collections.emptyList(), Collections.emptyList());
|
||||
} else {
|
||||
return new GeolocationViewModel(formatList(cityData.getMostRecent()), formatList(cityData.getMostCommon()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows data in a particular table.
|
||||
*
|
||||
@ -213,13 +284,12 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
* @param table The table where the data will be displayed.
|
||||
* @param goToGeolocation The corresponding geolocation navigation button.
|
||||
*/
|
||||
private void showCityContent(DataFetchResult<CityCountsList> result, JTablePanel<Pair<String, Integer>> table, JButton goToGeolocation) {
|
||||
DataFetchResult<List<Pair<String, Integer>>> convertedData = DataFetchResult.getSubResult(result, (countsList) -> formatList(countsList));
|
||||
if (convertedData != null && convertedData.getResultType() == DataFetchResult.ResultType.SUCCESS && CollectionUtils.isNotEmpty(convertedData.getData())) {
|
||||
private void showCityContent(DataFetchResult<List<Pair<String, Integer>>> result, JTablePanel<Pair<String, Integer>> table, JButton goToGeolocation) {
|
||||
if (result != null && result.getResultType() == DataFetchResult.ResultType.SUCCESS && CollectionUtils.isNotEmpty(result.getData())) {
|
||||
goToGeolocation.setEnabled(true);
|
||||
}
|
||||
|
||||
table.showDataFetchResult(convertedData);
|
||||
table.showDataFetchResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -279,6 +349,19 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
onNewDataSource(dataFetchComponents, tables, dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
|
||||
GeolocationViewModel model = getFetchResult(geolocationFetcher, "Geolocation sheets", dataSource);
|
||||
if (model == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.asList(
|
||||
getTableExport(DEFAULT_TEMPLATE, Bundle.GeolocationPanel_mostRecent_tabName(), model.getMostRecentData()),
|
||||
getTableExport(DEFAULT_TEMPLATE, Bundle.GeolocationPanel_mostCommon_tabName(), model.getMostCommonData())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
ingestRunningLabel.unregister();
|
||||
|
@ -19,18 +19,23 @@
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getFetchResult;
|
||||
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getTableExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
@ -40,29 +45,34 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
@Messages({
|
||||
"PastCasesPanel_caseColumn_title=Case",
|
||||
"PastCasesPanel_countColumn_title=Count",
|
||||
"PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run."
|
||||
})
|
||||
"PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run.",
|
||||
"PastCasesPanel_notableFileTable_tabName=Cases with Common Notable",
|
||||
"PastCasesPanel_sameIdsTable_tabName=Past Cases with the Same Devices",})
|
||||
public class PastCasesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final ColumnModel<Pair<String, Long>> CASE_COL = new ColumnModel<>(
|
||||
// model for column indicating the case
|
||||
private static final ColumnModel<Pair<String, Long>, DefaultCellModel<?>> CASE_COL = new ColumnModel<>(
|
||||
Bundle.PastCasesPanel_caseColumn_title(),
|
||||
(pair) -> new DefaultCellModel(pair.getKey()),
|
||||
(pair) -> new DefaultCellModel<>(pair.getKey()),
|
||||
300
|
||||
);
|
||||
|
||||
private static final ColumnModel<Pair<String, Long>> COUNT_COL = new ColumnModel<>(
|
||||
// model for column indicating the count
|
||||
private static final ColumnModel<Pair<String, Long>, DefaultCellModel<?>> COUNT_COL = new ColumnModel<>(
|
||||
Bundle.PastCasesPanel_countColumn_title(),
|
||||
(pair) -> new DefaultCellModel(String.valueOf(pair.getValue())),
|
||||
(pair) -> new DefaultCellModel<>(pair.getValue()),
|
||||
100
|
||||
);
|
||||
|
||||
private static final List<ColumnModel<Pair<String, Long>>> DEFAULT_COLUMNS = Arrays.asList(CASE_COL, COUNT_COL);
|
||||
// the template for columns in both tables in this tab
|
||||
private static List<ColumnModel<Pair<String, Long>, DefaultCellModel<?>>> DEFAULT_TEMPLATE
|
||||
= Arrays.asList(CASE_COL, COUNT_COL);
|
||||
|
||||
private final JTablePanel<Pair<String, Long>> notableFileTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS);
|
||||
private final JTablePanel<Pair<String, Long>> notableFileTable = JTablePanel.getJTablePanel(DEFAULT_TEMPLATE);
|
||||
|
||||
private final JTablePanel<Pair<String, Long>> sameIdTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS);
|
||||
private final JTablePanel<Pair<String, Long>> sameIdTable = JTablePanel.getJTablePanel(DEFAULT_TEMPLATE);
|
||||
|
||||
private final List<JTablePanel<?>> tables = Arrays.asList(
|
||||
notableFileTable,
|
||||
@ -73,6 +83,8 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
|
||||
|
||||
private final DataFetcher<DataSource, PastCasesResult> pastCasesFetcher;
|
||||
|
||||
public PastCasesPanel() {
|
||||
this(new PastCasesSummary());
|
||||
}
|
||||
@ -83,10 +95,12 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
|
||||
public PastCasesPanel(PastCasesSummary pastCaseData) {
|
||||
super(pastCaseData);
|
||||
|
||||
this.pastCasesFetcher = (dataSource) -> pastCaseData.getPastCasesData(dataSource);
|
||||
|
||||
// set up data acquisition methods
|
||||
dataFetchComponents = Arrays.asList(
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> pastCaseData.getPastCasesData(dataSource),
|
||||
pastCasesFetcher,
|
||||
(result) -> handleResult(result))
|
||||
);
|
||||
|
||||
@ -114,6 +128,19 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
|
||||
onNewDataSource(dataFetchComponents, tables, dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
|
||||
PastCasesResult result = getFetchResult(pastCasesFetcher, "Past cases sheets", dataSource);
|
||||
if (result == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.asList(
|
||||
getTableExport(DEFAULT_TEMPLATE, Bundle.PastCasesPanel_notableFileTable_tabName(), result.getTaggedNotable()),
|
||||
getTableExport(DEFAULT_TEMPLATE, Bundle.PastCasesPanel_sameIdsTable_tabName(), result.getSameIdsResults())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
ingestRunningLabel.unregister();
|
||||
|
@ -18,39 +18,100 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails;
|
||||
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getTableExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ListTableModel;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
* Data Source Summary recent files panel.
|
||||
*/
|
||||
@Messages({
|
||||
"RecentFilesPanel_docsTable_tabName=Recently Opened Documents",
|
||||
"RecentFilesPanel_downloadsTable_tabName=Recently Downloads",
|
||||
"RecentFilesPanel_attachmentsTable_tabName=Recent Attachments",})
|
||||
public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String DATETIME_FORMAT_STR = "yyyy/MM/dd HH:mm:ss";
|
||||
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat(DATETIME_FORMAT_STR, Locale.getDefault());
|
||||
|
||||
private final List<JTablePanel<?>> tablePanelList = new ArrayList<>();
|
||||
private final List<DataFetchWorker.DataFetchComponents<DataSource, ?>> dataFetchComponents = new ArrayList<>();
|
||||
|
||||
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
|
||||
|
||||
private final RecentFilesSummary dataHandler;
|
||||
private final DataFetcher<DataSource, List<RecentFileDetails>> docsFetcher;
|
||||
private final DataFetcher<DataSource, List<RecentDownloadDetails>> downloadsFetcher;
|
||||
private final DataFetcher<DataSource, List<RecentAttachmentDetails>> attachmentsFetcher;
|
||||
|
||||
private final List<ColumnModel<RecentFileDetails, DefaultCellModel<?>>> docsTemplate = Arrays.asList(
|
||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel<>(prog.getPath())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 250),
|
||||
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
||||
getDateFunct(),
|
||||
80));
|
||||
|
||||
private final List<ColumnModel<RecentDownloadDetails, DefaultCellModel<?>>> downloadsTemplate = Arrays.asList(
|
||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_domain(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel<>(prog.getWebDomain())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 100),
|
||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel<>(prog.getPath())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 250),
|
||||
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
||||
getDateFunct(),
|
||||
80));
|
||||
|
||||
private final List<ColumnModel<RecentAttachmentDetails, DefaultCellModel<?>>> attachmentsTemplate = Arrays.asList(
|
||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel<>(prog.getPath())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 250),
|
||||
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
||||
getDateFunct(),
|
||||
80),
|
||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_sender(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel<>(prog.getSender())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 150));
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
@Messages({
|
||||
"RecentFilesPanel_col_head_date=Date",
|
||||
"RecentFilePanel_col_header_domain=Domain",
|
||||
@ -58,10 +119,6 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
||||
"RecentFilePanel_col_header_sender=Sender",
|
||||
"RecentFilePanel_emailParserModuleName=Email Parser"
|
||||
})
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public RecentFilesPanel() {
|
||||
this(new RecentFilesSummary());
|
||||
}
|
||||
@ -71,12 +128,28 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
||||
*/
|
||||
public RecentFilesPanel(RecentFilesSummary dataHandler) {
|
||||
super(dataHandler);
|
||||
this.dataHandler = dataHandler;
|
||||
docsFetcher = (dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10);
|
||||
downloadsFetcher = (dataSource) -> dataHandler.getRecentDownloads(dataSource, 10);
|
||||
attachmentsFetcher = (dataSource) -> dataHandler.getRecentAttachments(dataSource, 10);
|
||||
|
||||
initComponents();
|
||||
initalizeTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that gets the date from the RecentFileDetails object and
|
||||
* converts into a DefaultCellModel to be displayed in a table.
|
||||
*
|
||||
* @return The function that determines the date cell from a RecentFileDetails object.
|
||||
*/
|
||||
private <T extends RecentFileDetails> Function<T, DefaultCellModel<?>> getDateFunct() {
|
||||
return (T lastAccessed) -> {
|
||||
Function<Date, String> dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt);
|
||||
return new DefaultCellModel<>(new Date(lastAccessed.getDateAsLong() * 1000), dateParser, DATETIME_FORMAT_STR)
|
||||
.setPopupMenuRetriever(getPopupFunct(lastAccessed));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a base class of RecentFileDetails and provides the pertinent menu
|
||||
* items.
|
||||
@ -117,6 +190,16 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
||||
onNewDataSource(dataFetchComponents, tablePanelList, dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
|
||||
return Stream.of(
|
||||
getTableExport(docsFetcher, docsTemplate, Bundle.RecentFilesPanel_docsTable_tabName(), dataSource),
|
||||
getTableExport(downloadsFetcher, downloadsTemplate, Bundle.RecentFilesPanel_downloadsTable_tabName(), dataSource),
|
||||
getTableExport(attachmentsFetcher, attachmentsTemplate, Bundle.RecentFilesPanel_attachmentsTable_tabName(), dataSource))
|
||||
.filter(sheet -> sheet != null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
ingestRunningLabel.unregister();
|
||||
@ -140,30 +223,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void initalizeOpenDocsTable() {
|
||||
List<ColumnModel<RecentFileDetails>> list = Arrays.asList(
|
||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(prog.getPath())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 250),
|
||||
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(prog.getDateAsString())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 80));
|
||||
|
||||
ListTableModel<RecentFileDetails> tableModel = JTablePanel.getTableModel(list);
|
||||
ListTableModel<RecentFileDetails> tableModel = JTablePanel.getTableModel(docsTemplate);
|
||||
|
||||
JTablePanel<RecentFileDetails> pane = (JTablePanel<RecentFileDetails>) openedDocPane;
|
||||
pane.setModel(tableModel);
|
||||
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
|
||||
pane.setColumnModel(JTablePanel.getTableColumnModel(docsTemplate));
|
||||
pane.setKeyFunction((recentFile) -> recentFile.getPath());
|
||||
pane.setCellListener(CellModelTableCellRenderer.getMouseListener());
|
||||
tablePanelList.add(pane);
|
||||
|
||||
DataFetchWorker.DataFetchComponents<DataSource, List<RecentFileDetails>> worker
|
||||
= new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10),
|
||||
docsFetcher,
|
||||
(result) -> pane.showDataFetchResult(result));
|
||||
|
||||
dataFetchComponents.add(worker);
|
||||
@ -174,35 +245,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void initalizeDownloadTable() {
|
||||
List<ColumnModel<RecentDownloadDetails>> list = Arrays.asList(
|
||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_domain(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(prog.getWebDomain())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 100),
|
||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(prog.getPath())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 250),
|
||||
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(prog.getDateAsString())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 80));
|
||||
|
||||
ListTableModel<RecentDownloadDetails> tableModel = JTablePanel.getTableModel(list);
|
||||
ListTableModel<RecentDownloadDetails> tableModel = JTablePanel.getTableModel(downloadsTemplate);
|
||||
|
||||
JTablePanel<RecentDownloadDetails> pane = (JTablePanel<RecentDownloadDetails>) downloadsPane;
|
||||
pane.setModel(tableModel);
|
||||
pane.setKeyFunction((download) -> download.getPath());
|
||||
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
|
||||
pane.setColumnModel(JTablePanel.getTableColumnModel(downloadsTemplate));
|
||||
pane.setCellListener(CellModelTableCellRenderer.getMouseListener());
|
||||
tablePanelList.add(pane);
|
||||
|
||||
DataFetchWorker.DataFetchComponents<DataSource, List<RecentDownloadDetails>> worker
|
||||
= new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> dataHandler.getRecentDownloads(dataSource, 10),
|
||||
downloadsFetcher,
|
||||
(result) -> pane.showDataFetchResult(result));
|
||||
|
||||
dataFetchComponents.add(worker);
|
||||
@ -213,35 +267,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void initalizeAttchementsTable() {
|
||||
List<ColumnModel<RecentAttachmentDetails>> list = Arrays.asList(
|
||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(prog.getPath())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 250),
|
||||
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(prog.getDateAsString())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 80),
|
||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_sender(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(prog.getSender())
|
||||
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||
}, 150));
|
||||
|
||||
ListTableModel<RecentAttachmentDetails> tableModel = JTablePanel.getTableModel(list);
|
||||
ListTableModel<RecentAttachmentDetails> tableModel = JTablePanel.getTableModel(attachmentsTemplate);
|
||||
|
||||
JTablePanel<RecentAttachmentDetails> pane = (JTablePanel<RecentAttachmentDetails>) attachmentsPane;
|
||||
pane.setModel(tableModel);
|
||||
pane.setKeyFunction((attachment) -> attachment.getPath());
|
||||
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
|
||||
pane.setColumnModel(JTablePanel.getTableColumnModel(attachmentsTemplate));
|
||||
pane.setCellListener(CellModelTableCellRenderer.getMouseListener());
|
||||
tablePanelList.add(pane);
|
||||
|
||||
DataFetchWorker.DataFetchComponents<DataSource, List<RecentAttachmentDetails>> worker
|
||||
= new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> dataHandler.getRecentAttachments(dataSource, 10),
|
||||
attachmentsFetcher,
|
||||
(result) -> pane.showDataFetchResult(result)
|
||||
);
|
||||
|
||||
|
@ -23,6 +23,7 @@ import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -45,6 +46,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.OrderedKey;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
|
||||
@ -65,7 +67,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
"TimlinePanel_last30DaysChart_fileEvts_title=File Events",
|
||||
"TimlinePanel_last30DaysChart_artifactEvts_title=Result Events",})
|
||||
public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TimelinePanel.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy");
|
||||
@ -94,7 +96,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
// actions to load data for this tab
|
||||
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
|
||||
|
||||
|
||||
public TimelinePanel() {
|
||||
this(new TimelineSummary());
|
||||
}
|
||||
@ -104,14 +106,14 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
*/
|
||||
public TimelinePanel(TimelineSummary timelineData) {
|
||||
super(timelineData);
|
||||
|
||||
|
||||
// set up data acquisition methods
|
||||
dataFetchComponents = Arrays.asList(
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT),
|
||||
(result) -> handleResult(result))
|
||||
);
|
||||
|
||||
|
||||
initComponents();
|
||||
}
|
||||
|
||||
@ -127,7 +129,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
private static String formatDate(Date date, DateFormat formatter) {
|
||||
return date == null ? null : formatter.format(date);
|
||||
}
|
||||
|
||||
|
||||
private static final Color FILE_EVT_COLOR = new Color(228, 22, 28);
|
||||
private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100);
|
||||
|
||||
@ -147,25 +149,25 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
// Create a bar chart item for each recent days activity item
|
||||
List<BarChartItem> fileEvtCounts = new ArrayList<>();
|
||||
List<BarChartItem> artifactEvtCounts = new ArrayList<>();
|
||||
|
||||
|
||||
for (int i = 0; i < recentDaysActivity.size(); i++) {
|
||||
DailyActivityAmount curItem = recentDaysActivity.get(i);
|
||||
|
||||
|
||||
long fileAmt = curItem.getFileActivityCount();
|
||||
long artifactAmt = curItem.getArtifactActivityCount() * 100;
|
||||
String formattedDate = (i == 0 || i == recentDaysActivity.size() - 1)
|
||||
? formatDate(curItem.getDay(), CHART_FORMAT) : "";
|
||||
|
||||
|
||||
OrderedKey thisKey = new OrderedKey(formattedDate, i);
|
||||
fileEvtCounts.add(new BarChartItem(thisKey, fileAmt));
|
||||
artifactEvtCounts.add(new BarChartItem(thisKey, artifactAmt));
|
||||
}
|
||||
|
||||
|
||||
return Arrays.asList(
|
||||
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_fileEvts_title(), FILE_EVT_COLOR, fileEvtCounts),
|
||||
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts));
|
||||
}
|
||||
|
||||
|
||||
private final Object timelineBtnLock = new Object();
|
||||
private TimelineSummaryData curTimelineData = null;
|
||||
|
||||
@ -181,11 +183,11 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
|
||||
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
|
||||
last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity())));
|
||||
|
||||
|
||||
if (result != null
|
||||
&& result.getResultType() == DataFetchResult.ResultType.SUCCESS
|
||||
&& result.getData() != null) {
|
||||
|
||||
|
||||
synchronized (this.timelineBtnLock) {
|
||||
this.curTimelineData = result.getData();
|
||||
this.viewInTimelineBtn.setEnabled(true);
|
||||
@ -210,7 +212,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
if (curTimelineData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
dataSource = curTimelineData.getDataSource();
|
||||
if (CollectionUtils.isNotEmpty(curTimelineData.getMostRecentDaysActivity())) {
|
||||
minDate = curTimelineData.getMostRecentDaysActivity().get(0).getDay();
|
||||
@ -221,7 +223,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
openFilteredChart(dataSource, minDate, maxDate);
|
||||
}
|
||||
|
||||
@ -240,46 +242,51 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
// notify dialog (if in dialog) should close.
|
||||
TimelinePanel.this.notifyParentClose();
|
||||
|
||||
|
||||
Interval timeSpan = null;
|
||||
|
||||
|
||||
try {
|
||||
final TimeLineController controller = TimeLineModule.getController();
|
||||
|
||||
|
||||
if (dataSource != null) {
|
||||
controller.pushFilters(timelineUtils.getDataSourceFilterState(dataSource));
|
||||
}
|
||||
|
||||
|
||||
if (minDate != null && maxDate != null) {
|
||||
timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate));
|
||||
}
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to view time range in Timeline view", ex);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
openTimelineAction.showTimeline(timeSpan);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "An unexpected exception occurred while opening the timeline.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void fetchInformation(DataSource dataSource) {
|
||||
fetchInformation(dataFetchComponents, dataSource);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onNewDataSource(DataSource dataSource) {
|
||||
onNewDataSource(dataFetchComponents, loadableComponents, dataSource);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
ingestRunningLabel.unregister();
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -373,4 +380,5 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton viewInTimelineBtn;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import java.sql.SQLException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -39,6 +40,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
|
||||
@ -404,6 +406,11 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
return longVal == null ? "0" : COMMA_FORMATTER.format(longVal);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -25,6 +25,9 @@ import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
|
||||
@ -34,12 +37,15 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.Top
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem;
|
||||
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getTableExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
@ -47,6 +53,11 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
*/
|
||||
@Messages({
|
||||
"UserActivityPanel_tab_title=User Activity",
|
||||
"UserActivityPanel_TopProgramsTableModel_tabName=Recent Programs",
|
||||
"UserActivityPanel_TopDomainsTableModel_tabName=Recent Domains",
|
||||
"UserActivityPanel_TopWebSearchTableModel_tabName=Recent Web Searches",
|
||||
"UserActivityPanel_TopDeviceAttachedTableModel_tabName=Recent Devices Attached",
|
||||
"UserActivityPanel_TopAccountTableModel_tabName=Recent Account Types Used",
|
||||
"UserActivityPanel_TopProgramsTableModel_name_header=Program",
|
||||
"UserActivityPanel_TopProgramsTableModel_folder_header=Folder",
|
||||
"UserActivityPanel_TopProgramsTableModel_count_header=Run Times",
|
||||
@ -66,33 +77,21 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
|
||||
private static final String DATETIME_FORMAT_STR = "yyyy/MM/dd HH:mm:ss";
|
||||
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat(DATETIME_FORMAT_STR, Locale.getDefault());
|
||||
private static final int TOP_PROGS_COUNT = 10;
|
||||
private static final int TOP_DOMAINS_COUNT = 10;
|
||||
private static final int TOP_SEARCHES_COUNT = 10;
|
||||
private static final int TOP_ACCOUNTS_COUNT = 5;
|
||||
private static final int TOP_DEVICES_COUNT = 10;
|
||||
private static final String ANDROID_FACTORY = "org.python.proxies.module$AndroidModuleFactory";
|
||||
private static final String ANDROID_MODULE_NAME = "Android Analyzer";
|
||||
|
||||
/**
|
||||
* Gets a string formatted date or returns empty string if the date is null.
|
||||
*
|
||||
* @param date The date.
|
||||
*
|
||||
* @return The formatted date string or empty string if the date is null.
|
||||
*/
|
||||
private static String getFormatted(Date date) {
|
||||
return date == null ? "" : DATETIME_FORMAT.format(date);
|
||||
}
|
||||
|
||||
// set up recent programs table
|
||||
private final JTablePanel<TopProgramsResult> topProgramsTable = JTablePanel.getJTablePanel(Arrays.asList(
|
||||
private final List<ColumnModel<TopProgramsResult, DefaultCellModel<?>>> topProgramsTemplate = Arrays.asList(
|
||||
// program name column
|
||||
new ColumnModel<TopProgramsResult>(
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopProgramsTableModel_name_header(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(prog.getProgramName())
|
||||
return new DefaultCellModel<>(prog.getProgramName())
|
||||
.setTooltip(prog.getProgramPath())
|
||||
.setPopupMenu(getPopup(prog));
|
||||
},
|
||||
@ -101,7 +100,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopProgramsTableModel_folder_header(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(
|
||||
return new DefaultCellModel<>(
|
||||
getShortFolderName(
|
||||
prog.getProgramPath(),
|
||||
prog.getProgramName()))
|
||||
@ -113,29 +112,24 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopProgramsTableModel_count_header(),
|
||||
(prog) -> {
|
||||
String runTimes = prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes());
|
||||
return new DefaultCellModel(runTimes)
|
||||
return new DefaultCellModel<>(prog.getRunTimes(), (num) -> num == null ? "" : num.toString())
|
||||
.setPopupMenu(getPopup(prog));
|
||||
},
|
||||
80),
|
||||
// last run date column
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopProgramsTableModel_lastrun_header(),
|
||||
(prog) -> {
|
||||
return new DefaultCellModel(getFormatted(prog.getLastAccessed()))
|
||||
.setPopupMenu(getPopup(prog));
|
||||
},
|
||||
getDateFunct(),
|
||||
150)
|
||||
))
|
||||
.setKeyFunction((prog) -> prog.getProgramPath() + ":" + prog.getProgramName());
|
||||
);
|
||||
|
||||
// set up recent domains table
|
||||
private final JTablePanel<TopDomainsResult> recentDomainsTable = JTablePanel.getJTablePanel(Arrays.asList(
|
||||
private final List<ColumnModel<TopDomainsResult, DefaultCellModel<?>>> topDomainsTemplate = Arrays.asList(
|
||||
// domain column
|
||||
new ColumnModel<TopDomainsResult>(
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopDomainsTableModel_domain_header(),
|
||||
(recentDomain) -> {
|
||||
return new DefaultCellModel(recentDomain.getDomain())
|
||||
return new DefaultCellModel<>(recentDomain.getDomain())
|
||||
.setPopupMenu(getPopup(recentDomain));
|
||||
},
|
||||
250),
|
||||
@ -143,29 +137,24 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopDomainsTableModel_count_header(),
|
||||
(recentDomain) -> {
|
||||
String visitTimes = recentDomain.getVisitTimes() == null ? "" : Long.toString(recentDomain.getVisitTimes());
|
||||
return new DefaultCellModel(visitTimes)
|
||||
return new DefaultCellModel<>(recentDomain.getVisitTimes(), (num) -> num == null ? "" : num.toString())
|
||||
.setPopupMenu(getPopup(recentDomain));
|
||||
},
|
||||
100),
|
||||
// last accessed column
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopDomainsTableModel_lastAccess_header(),
|
||||
(recentDomain) -> {
|
||||
return new DefaultCellModel(getFormatted(recentDomain.getLastAccessed()))
|
||||
.setPopupMenu(getPopup(recentDomain));
|
||||
},
|
||||
getDateFunct(),
|
||||
150)
|
||||
))
|
||||
.setKeyFunction((domain) -> domain.getDomain());
|
||||
);
|
||||
|
||||
// top web searches table
|
||||
private final JTablePanel<TopWebSearchResult> topWebSearchesTable = JTablePanel.getJTablePanel(Arrays.asList(
|
||||
private final List<ColumnModel<TopWebSearchResult, DefaultCellModel<?>>> topWebSearchesTemplate = Arrays.asList(
|
||||
// search string column
|
||||
new ColumnModel<TopWebSearchResult>(
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopWebSearchTableModel_searchString_header(),
|
||||
(webSearch) -> {
|
||||
return new DefaultCellModel(webSearch.getSearchString())
|
||||
return new DefaultCellModel<>(webSearch.getSearchString())
|
||||
.setPopupMenu(getPopup(webSearch));
|
||||
},
|
||||
250
|
||||
@ -173,31 +162,27 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
// last accessed
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopWebSearchTableModel_dateAccessed_header(),
|
||||
(webSearch) -> {
|
||||
return new DefaultCellModel(getFormatted(webSearch.getLastAccessed()))
|
||||
.setPopupMenu(getPopup(webSearch));
|
||||
},
|
||||
getDateFunct(),
|
||||
150
|
||||
),
|
||||
// translated value
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopWebSearchTableModel_translatedResult_header(),
|
||||
(webSearch) -> {
|
||||
return new DefaultCellModel(webSearch.getTranslatedResult())
|
||||
return new DefaultCellModel<>(webSearch.getTranslatedResult())
|
||||
.setPopupMenu(getPopup(webSearch));
|
||||
},
|
||||
250
|
||||
)
|
||||
))
|
||||
.setKeyFunction((query) -> query.getSearchString());
|
||||
);
|
||||
|
||||
// top devices attached table
|
||||
private final JTablePanel<TopDeviceAttachedResult> topDevicesAttachedTable = JTablePanel.getJTablePanel(Arrays.asList(
|
||||
private final List<ColumnModel<TopDeviceAttachedResult, DefaultCellModel<?>>> topDevicesTemplate = Arrays.asList(
|
||||
// device id column
|
||||
new ColumnModel<TopDeviceAttachedResult>(
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopDeviceAttachedTableModel_deviceId_header(),
|
||||
(device) -> {
|
||||
return new DefaultCellModel(device.getDeviceId())
|
||||
return new DefaultCellModel<>(device.getDeviceId())
|
||||
.setPopupMenu(getPopup(device));
|
||||
},
|
||||
250
|
||||
@ -205,10 +190,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
// last accessed
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header(),
|
||||
(device) -> {
|
||||
return new DefaultCellModel(getFormatted(device.getLastAccessed()))
|
||||
.setPopupMenu(getPopup(device));
|
||||
},
|
||||
getDateFunct(),
|
||||
150
|
||||
),
|
||||
// make and model
|
||||
@ -220,21 +202,20 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
String makeModelString = (make.isEmpty() || model.isEmpty())
|
||||
? make + model
|
||||
: String.format("%s - %s", make, model);
|
||||
return new DefaultCellModel(makeModelString)
|
||||
return new DefaultCellModel<>(makeModelString)
|
||||
.setPopupMenu(getPopup(device));
|
||||
},
|
||||
250
|
||||
)
|
||||
))
|
||||
.setKeyFunction((topDevice) -> topDevice.getDeviceId());
|
||||
);
|
||||
|
||||
// top accounts table
|
||||
private final JTablePanel<TopAccountResult> topAccountsTable = JTablePanel.getJTablePanel(Arrays.asList(
|
||||
private final List<ColumnModel<TopAccountResult, DefaultCellModel<?>>> topAccountsTemplate = Arrays.asList(
|
||||
// account type column
|
||||
new ColumnModel<TopAccountResult>(
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopAccountTableModel_accountType_header(),
|
||||
(account) -> {
|
||||
return new DefaultCellModel(account.getAccountType())
|
||||
return new DefaultCellModel<>(account.getAccountType())
|
||||
.setPopupMenu(getPopup(account));
|
||||
},
|
||||
250
|
||||
@ -242,15 +223,37 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
// last accessed
|
||||
new ColumnModel<>(
|
||||
Bundle.UserActivityPanel_TopAccountTableModel_lastAccess_header(),
|
||||
(account) -> {
|
||||
return new DefaultCellModel(getFormatted(account.getLastAccessed()))
|
||||
.setPopupMenu(getPopup(account));
|
||||
},
|
||||
getDateFunct(),
|
||||
150
|
||||
)
|
||||
))
|
||||
);
|
||||
|
||||
// set up recent programs table
|
||||
private final JTablePanel<TopProgramsResult> topProgramsTable = JTablePanel.getJTablePanel(topProgramsTemplate)
|
||||
.setKeyFunction((prog) -> prog.getProgramPath() + ":" + prog.getProgramName());
|
||||
|
||||
// set up recent domains table
|
||||
private final JTablePanel<TopDomainsResult> recentDomainsTable = JTablePanel.getJTablePanel(topDomainsTemplate)
|
||||
.setKeyFunction((domain) -> domain.getDomain());
|
||||
|
||||
// top web searches table
|
||||
private final JTablePanel<TopWebSearchResult> topWebSearchesTable = JTablePanel.getJTablePanel(topWebSearchesTemplate)
|
||||
.setKeyFunction((query) -> query.getSearchString());
|
||||
|
||||
// top devices attached table
|
||||
private final JTablePanel<TopDeviceAttachedResult> topDevicesAttachedTable = JTablePanel.getJTablePanel(topDevicesTemplate)
|
||||
.setKeyFunction((topDevice) -> topDevice.getDeviceId());
|
||||
|
||||
// top accounts table
|
||||
private final JTablePanel<TopAccountResult> topAccountsTable = JTablePanel.getJTablePanel(topAccountsTemplate)
|
||||
.setKeyFunction((topAccount) -> topAccount.getAccountType());
|
||||
|
||||
private final DataFetcher<DataSource, List<TopProgramsResult>> topProgramsFetcher;
|
||||
private final DataFetcher<DataSource, List<TopDomainsResult>> topDomainsFetcher;
|
||||
private final DataFetcher<DataSource, List<TopWebSearchResult>> topWebSearchesFetcher;
|
||||
private final DataFetcher<DataSource, List<TopDeviceAttachedResult>> topDevicesAttachedFetcher;
|
||||
private final DataFetcher<DataSource, List<TopAccountResult>> topAccountsFetcher;
|
||||
|
||||
private final List<JTablePanel<?>> tables = Arrays.asList(
|
||||
topProgramsTable,
|
||||
recentDomainsTable,
|
||||
@ -281,33 +284,47 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
super(userActivityData);
|
||||
this.userActivityData = userActivityData;
|
||||
|
||||
this.topProgramsFetcher = (dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT);
|
||||
this.topDomainsFetcher = (dataSource) -> userActivityData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT);
|
||||
this.topWebSearchesFetcher = (dataSource) -> userActivityData.getMostRecentWebSearches(dataSource, TOP_SEARCHES_COUNT);
|
||||
this.topDevicesAttachedFetcher = (dataSource) -> userActivityData.getRecentDevices(dataSource, TOP_DEVICES_COUNT);
|
||||
this.topAccountsFetcher = (dataSource) -> userActivityData.getRecentAccounts(dataSource, TOP_ACCOUNTS_COUNT);
|
||||
|
||||
// set up data acquisition methods
|
||||
this.dataFetchComponents = Arrays.asList(
|
||||
// top programs query
|
||||
new DataFetchComponents<DataSource, List<TopProgramsResult>>(
|
||||
(dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
|
||||
topProgramsFetcher,
|
||||
(result) -> topProgramsTable.showDataFetchResult(result)),
|
||||
// top domains query
|
||||
new DataFetchComponents<DataSource, List<TopDomainsResult>>(
|
||||
(dataSource) -> userActivityData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT),
|
||||
topDomainsFetcher,
|
||||
(result) -> recentDomainsTable.showDataFetchResult(result)),
|
||||
// top web searches query
|
||||
new DataFetchComponents<DataSource, List<TopWebSearchResult>>(
|
||||
(dataSource) -> userActivityData.getMostRecentWebSearches(dataSource, TOP_SEARCHES_COUNT),
|
||||
topWebSearchesFetcher,
|
||||
(result) -> topWebSearchesTable.showDataFetchResult(result)),
|
||||
// top devices query
|
||||
new DataFetchComponents<DataSource, List<TopDeviceAttachedResult>>(
|
||||
(dataSource) -> userActivityData.getRecentDevices(dataSource, TOP_DEVICES_COUNT),
|
||||
topDevicesAttachedFetcher,
|
||||
(result) -> topDevicesAttachedTable.showDataFetchResult(result)),
|
||||
// top accounts query
|
||||
new DataFetchComponents<DataSource, List<TopAccountResult>>(
|
||||
(dataSource) -> userActivityData.getRecentAccounts(dataSource, TOP_ACCOUNTS_COUNT),
|
||||
topAccountsFetcher,
|
||||
(result) -> topAccountsTable.showDataFetchResult(result))
|
||||
);
|
||||
|
||||
initComponents();
|
||||
}
|
||||
|
||||
private <T extends LastAccessedArtifact> Function<T, DefaultCellModel<?>> getDateFunct() {
|
||||
return (T lastAccessed) -> {
|
||||
Function<Date, String> dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt);
|
||||
return new DefaultCellModel<>(lastAccessed.getLastAccessed(), dateParser, DATETIME_FORMAT_STR)
|
||||
.setPopupMenu(getPopup(lastAccessed));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a base class of LastAccessedArtifact and provides the pertinent
|
||||
* menu items. going to artifact.
|
||||
@ -349,6 +366,18 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
|
||||
return Stream.of(
|
||||
getTableExport(topProgramsFetcher, topProgramsTemplate, Bundle.UserActivityPanel_TopProgramsTableModel_tabName(), dataSource),
|
||||
getTableExport(topDomainsFetcher, topDomainsTemplate, Bundle.UserActivityPanel_TopDomainsTableModel_tabName(), dataSource),
|
||||
getTableExport(topWebSearchesFetcher, topWebSearchesTemplate, Bundle.UserActivityPanel_TopWebSearchTableModel_tabName(), dataSource),
|
||||
getTableExport(topDevicesAttachedFetcher, topDevicesTemplate, Bundle.UserActivityPanel_TopDeviceAttachedTableModel_tabName(), dataSource),
|
||||
getTableExport(topAccountsFetcher, topAccountsTemplate, Bundle.UserActivityPanel_TopAccountTableModel_tabName(), dataSource))
|
||||
.filter(sheet -> sheet != null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -1,5 +1,7 @@
|
||||
AbstractLoadableComponent_errorMessage_defaultText=There was an error loading results.
|
||||
AbstractLoadableComponent_loadingMessage_defaultText=Loading results...
|
||||
AbstractLoadableComponent_noDataExists_defaultText=No data exists.
|
||||
# {0} - sheetNumber
|
||||
ExcelExport_writeExcel_noSheetName=Sheet {0}
|
||||
IngestRunningLabel_defaultMessage=Ingest is currently running.
|
||||
PieChartPanel_noDataLabel=No Data
|
||||
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
|
||||
/**
|
||||
* Basic interface for a cell model.
|
||||
*/
|
||||
public interface CellModel {
|
||||
|
||||
/**
|
||||
* Describes the horizontal alignment.
|
||||
*/
|
||||
public enum HorizontalAlign {
|
||||
LEFT(JLabel.LEFT),
|
||||
CENTER(JLabel.CENTER),
|
||||
RIGHT(JLabel.RIGHT);
|
||||
|
||||
private final int jlabelAlignment;
|
||||
|
||||
/**
|
||||
* Constructor for a HorizontalAlign enum.
|
||||
*
|
||||
* @param jlabelAlignment The corresponding JLabel horizontal alignment
|
||||
* number.
|
||||
*/
|
||||
HorizontalAlign(int jlabelAlignment) {
|
||||
this.jlabelAlignment = jlabelAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The corresponding JLabel horizontal alignment (i.e.
|
||||
* JLabel.LEFT).
|
||||
*/
|
||||
int getJLabelAlignment() {
|
||||
return this.jlabelAlignment;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The root data object.
|
||||
*/
|
||||
Object getData();
|
||||
|
||||
/**
|
||||
* @return The text to be shown in the cell.
|
||||
*/
|
||||
default String getText() {
|
||||
Object data = getData();
|
||||
return (data == null) ? null : data.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tooltip (if any) to be displayed in the cell.
|
||||
*/
|
||||
String getTooltip();
|
||||
|
||||
/**
|
||||
* @return The horizontal alignment for the text in the cell.
|
||||
*/
|
||||
HorizontalAlign getHorizontalAlignment();
|
||||
}
|
@ -21,10 +21,7 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
import java.awt.Component;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
@ -34,6 +31,7 @@ import javax.swing.border.Border;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.CellMouseEvent;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.CellMouseListener;
|
||||
|
||||
@ -44,234 +42,6 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.CellMouseList
|
||||
public class CellModelTableCellRenderer extends DefaultTableCellRenderer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Describes the horizontal alignment.
|
||||
*/
|
||||
public enum HorizontalAlign {
|
||||
LEFT(JLabel.LEFT),
|
||||
CENTER(JLabel.CENTER),
|
||||
RIGHT(JLabel.RIGHT);
|
||||
|
||||
private final int jlabelAlignment;
|
||||
|
||||
/**
|
||||
* Constructor for a HorizontalAlign enum.
|
||||
*
|
||||
* @param jlabelAlignment The corresponding JLabel horizontal alignment
|
||||
* number.
|
||||
*/
|
||||
HorizontalAlign(int jlabelAlignment) {
|
||||
this.jlabelAlignment = jlabelAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The corresponding JLabel horizontal alignment (i.e.
|
||||
* JLabel.LEFT).
|
||||
*/
|
||||
int getJLabelAlignment() {
|
||||
return this.jlabelAlignment;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A menu item to be used within a popup menu.
|
||||
*/
|
||||
public interface MenuItem {
|
||||
|
||||
/**
|
||||
* @return The title for that popup menu item.
|
||||
*/
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* @return The action if that popup menu item is clicked.
|
||||
*/
|
||||
Runnable getAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of a menu item.
|
||||
*/
|
||||
public static class DefaultMenuItem implements MenuItem {
|
||||
|
||||
private final String title;
|
||||
private final Runnable action;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param title The title for the menu item.
|
||||
* @param action The action should the menu item be clicked.
|
||||
*/
|
||||
public DefaultMenuItem(String title, Runnable action) {
|
||||
this.title = title;
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic interface for a cell model.
|
||||
*/
|
||||
public interface CellModel {
|
||||
|
||||
/**
|
||||
* @return The text to be shown in the cell.
|
||||
*/
|
||||
String getText();
|
||||
|
||||
/**
|
||||
* @return The tooltip (if any) to be displayed in the cell.
|
||||
*/
|
||||
String getTooltip();
|
||||
|
||||
/**
|
||||
* @return The horizontal alignment for the text in the cell.
|
||||
*/
|
||||
HorizontalAlign getHorizontalAlignment();
|
||||
|
||||
/**
|
||||
* @return The insets for the cell text.
|
||||
*/
|
||||
Insets getInsets();
|
||||
|
||||
/**
|
||||
* @return The popup menu associated with this cell or null if no popup
|
||||
* menu should be shown for this cell.
|
||||
*/
|
||||
List<MenuItem> getPopupMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* The default cell model.
|
||||
*/
|
||||
public static class DefaultCellModel implements CellModel {
|
||||
|
||||
private final String text;
|
||||
private String tooltip;
|
||||
private HorizontalAlign horizontalAlignment;
|
||||
private Insets insets;
|
||||
private List<MenuItem> popupMenu;
|
||||
private Supplier<List<MenuItem>> menuItemSupplier;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param text The text to be displayed in the cell.
|
||||
*/
|
||||
public DefaultCellModel(String text) {
|
||||
this.text = text;
|
||||
this.tooltip = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTooltip() {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tooltip for this cell model.
|
||||
*
|
||||
* @param tooltip The tooltip for the cell model.
|
||||
*
|
||||
* @return As a utility, returns this.
|
||||
*/
|
||||
public DefaultCellModel setTooltip(String tooltip) {
|
||||
this.tooltip = tooltip;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HorizontalAlign getHorizontalAlignment() {
|
||||
return horizontalAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the horizontal alignment for this cell model.
|
||||
*
|
||||
* @param alignment The horizontal alignment for the cell model.
|
||||
*
|
||||
* @return As a utility, returns this.
|
||||
*/
|
||||
public DefaultCellModel setHorizontalAlignment(HorizontalAlign alignment) {
|
||||
this.horizontalAlignment = alignment;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getInsets() {
|
||||
return insets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the insets for the text within the cell
|
||||
*
|
||||
* @param insets The insets.
|
||||
*
|
||||
* @return As a utility, returns this.
|
||||
*/
|
||||
public DefaultCellModel setInsets(Insets insets) {
|
||||
this.insets = insets;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MenuItem> getPopupMenu() {
|
||||
if (popupMenu != null) {
|
||||
return Collections.unmodifiableList(popupMenu);
|
||||
}
|
||||
|
||||
if (menuItemSupplier != null) {
|
||||
return this.menuItemSupplier.get();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a function to lazy load the popup menu items.
|
||||
*
|
||||
* @param menuItemSupplier The lazy load function for popup items.
|
||||
* @return
|
||||
*/
|
||||
public DefaultCellModel setPopupMenuRetriever(Supplier<List<MenuItem>> menuItemSupplier) {
|
||||
this.menuItemSupplier = menuItemSupplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of items for a popup menu
|
||||
*
|
||||
* @param popupMenu
|
||||
* @return As a utility, returns this.
|
||||
*/
|
||||
public DefaultCellModel setPopupMenu(List<MenuItem> popupMenu) {
|
||||
this.popupMenu = popupMenu == null ? null : new ArrayList<>(popupMenu);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getText();
|
||||
}
|
||||
}
|
||||
|
||||
private static final int DEFAULT_ALIGNMENT = JLabel.LEFT;
|
||||
private static final Border DEFAULT_BORDER = BorderFactory.createEmptyBorder(1, 5, 1, 5);
|
||||
|
||||
@ -280,8 +50,8 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer {
|
||||
boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
|
||||
JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
if (value instanceof CellModel) {
|
||||
return getTableCellRendererComponent(c, (CellModel) value);
|
||||
if (value instanceof GuiCellModel) {
|
||||
return getTableCellRendererComponent(c, (GuiCellModel) value);
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
@ -297,7 +67,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer {
|
||||
*
|
||||
* @return The provided defaultCell.
|
||||
*/
|
||||
protected Component getTableCellRendererComponent(JLabel defaultCell, CellModel cellModel) {
|
||||
protected Component getTableCellRendererComponent(JLabel defaultCell, GuiCellModel cellModel) {
|
||||
// sets the text for the cell or null if not present.
|
||||
String text = cellModel.getText();
|
||||
if (StringUtils.isNotBlank(text)) {
|
||||
@ -340,9 +110,9 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer {
|
||||
|
||||
@Override
|
||||
public void mouseClicked(CellMouseEvent cellEvent) {
|
||||
if (cellEvent.getCellValue() instanceof CellModel && cellEvent.getMouseEvent().getButton() != MouseEvent.BUTTON1) {
|
||||
if (cellEvent.getCellValue() instanceof GuiCellModel && cellEvent.getMouseEvent().getButton() != MouseEvent.BUTTON1) {
|
||||
cellEvent.getTable().setRowSelectionInterval(cellEvent.getRow(), cellEvent.getRow());
|
||||
CellModel cellModel = (CellModel) cellEvent.getCellValue();
|
||||
GuiCellModel cellModel = (GuiCellModel) cellEvent.getCellValue();
|
||||
List<MenuItem> menuItems = cellModel.getPopupMenu();
|
||||
|
||||
// if there are menu items, show a popup menu for
|
||||
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Describes aspects of a column which can be used with getTableModel or
|
||||
* getJTablePanel. 'T' represents the object that will represent rows in the
|
||||
* table.
|
||||
*/
|
||||
public class ColumnModel<T, C extends CellModel> {
|
||||
|
||||
private final String headerTitle;
|
||||
private final Function<T, ? extends C> cellRenderer;
|
||||
private final Integer width;
|
||||
|
||||
/**
|
||||
* Constructor for a DataResultColumnModel.
|
||||
*
|
||||
* @param headerTitle The title for the column.
|
||||
* @param cellRenderer The method that generates a CellModel for the column
|
||||
* based on the data.
|
||||
*/
|
||||
public ColumnModel(String headerTitle, Function<T, ? extends C> cellRenderer) {
|
||||
this(headerTitle, cellRenderer, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a DataResultColumnModel.
|
||||
*
|
||||
* @param headerTitle The title for the column.
|
||||
* @param cellRenderer The method that generates a CellModel for the column
|
||||
* based on the data.
|
||||
* @param width The preferred width of the column.
|
||||
*/
|
||||
public ColumnModel(String headerTitle, Function<T, ? extends C> cellRenderer, Integer width) {
|
||||
this.headerTitle = headerTitle;
|
||||
this.cellRenderer = cellRenderer;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The title for the column.
|
||||
*/
|
||||
public String getHeaderTitle() {
|
||||
return headerTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The method that generates a CellModel for the column based on the
|
||||
* data.
|
||||
*/
|
||||
public Function<T, ? extends C> getCellRenderer() {
|
||||
return cellRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The preferred width of the column (can be null).
|
||||
*/
|
||||
public Integer getWidth() {
|
||||
return width;
|
||||
}
|
||||
}
|
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
import java.awt.Insets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
|
||||
|
||||
/**
|
||||
* The default cell model.
|
||||
*/
|
||||
public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
|
||||
|
||||
private final T data;
|
||||
private final Function<T, String> stringConverter;
|
||||
String tooltip;
|
||||
CellModel.HorizontalAlign horizontalAlignment;
|
||||
Insets insets;
|
||||
List<MenuItem> popupMenu;
|
||||
Supplier<List<MenuItem>> menuItemSupplier;
|
||||
private final String excelFormatString;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param data The data to be displayed in the cell.
|
||||
*/
|
||||
public DefaultCellModel(T data) {
|
||||
this(data, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param data The data to be displayed in the cell.
|
||||
* @param stringConverter The means of converting that data to a string or
|
||||
* null to use .toString method on object.
|
||||
*/
|
||||
public DefaultCellModel(T data, Function<T, String> stringConverter) {
|
||||
this(data, stringConverter, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param data The data to be displayed in the cell.
|
||||
* @param stringConverter The means of converting that data to a string or
|
||||
* null to use .toString method on object.
|
||||
* @param excelFormatString The apache poi excel format string to use with
|
||||
* the data.
|
||||
*
|
||||
* NOTE: Only certain data types can be exported. See
|
||||
* ExcelTableExport.createCell() for types.
|
||||
*/
|
||||
public DefaultCellModel(T data, Function<T, String> stringConverter, String excelFormatString) {
|
||||
this.data = data;
|
||||
this.stringConverter = stringConverter;
|
||||
this.excelFormatString = excelFormatString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExcelFormatString() {
|
||||
return this.excelFormatString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
if (this.stringConverter == null) {
|
||||
return this.data == null ? "" : this.data.toString();
|
||||
} else {
|
||||
return this.stringConverter.apply(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTooltip() {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tooltip for this cell model.
|
||||
*
|
||||
* @param tooltip The tooltip for the cell model.
|
||||
*
|
||||
* @return As a utility, returns this.
|
||||
*/
|
||||
public DefaultCellModel<T> setTooltip(String tooltip) {
|
||||
this.tooltip = tooltip;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HorizontalAlign getHorizontalAlignment() {
|
||||
return horizontalAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the horizontal alignment for this cell model.
|
||||
*
|
||||
* @param alignment The horizontal alignment for the cell model.
|
||||
*
|
||||
* @return As a utility, returns this.
|
||||
*/
|
||||
public DefaultCellModel<T> setHorizontalAlignment(CellModel.HorizontalAlign alignment) {
|
||||
this.horizontalAlignment = alignment;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getInsets() {
|
||||
return insets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the insets for the text within the cell
|
||||
*
|
||||
* @param insets The insets.
|
||||
*
|
||||
* @return As a utility, returns this.
|
||||
*/
|
||||
public DefaultCellModel<T> setInsets(Insets insets) {
|
||||
this.insets = insets;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MenuItem> getPopupMenu() {
|
||||
if (popupMenu != null) {
|
||||
return Collections.unmodifiableList(popupMenu);
|
||||
}
|
||||
|
||||
if (menuItemSupplier != null) {
|
||||
return this.menuItemSupplier.get();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a function to lazy load the popup menu items.
|
||||
*
|
||||
* @param menuItemSupplier The lazy load function for popup items.
|
||||
* @return
|
||||
*/
|
||||
public DefaultCellModel<T> setPopupMenuRetriever(Supplier<List<MenuItem>> menuItemSupplier) {
|
||||
this.menuItemSupplier = menuItemSupplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of items for a popup menu
|
||||
*
|
||||
* @param popupMenu
|
||||
* @return As a utility, returns this.
|
||||
*/
|
||||
public DefaultCellModel<T> setPopupMenu(List<MenuItem> popupMenu) {
|
||||
this.popupMenu = popupMenu == null ? null : new ArrayList<>(popupMenu);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getText();
|
||||
}
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
|
||||
/**
|
||||
* Class for handling Excel exporting.
|
||||
*/
|
||||
public class ExcelExport {
|
||||
|
||||
/**
|
||||
* Exception thrown in the event of an excel export issue.
|
||||
*/
|
||||
public static class ExcelExportException extends Exception {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string The message.
|
||||
*/
|
||||
public ExcelExportException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string The message.
|
||||
* @param thrwbl The inner exception.
|
||||
*/
|
||||
public ExcelExportException(String string, Throwable thrwbl) {
|
||||
super(string, thrwbl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class detailing aspects of the worksheet.
|
||||
*/
|
||||
public static class WorksheetEnv {
|
||||
|
||||
private final CellStyle headerStyle;
|
||||
private final Workbook parentWorkbook;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param headerStyle The cell style to use for headers.
|
||||
* @param parentWorkbook The parent workbook.
|
||||
*/
|
||||
WorksheetEnv(CellStyle headerStyle, Workbook parentWorkbook) {
|
||||
this.headerStyle = headerStyle;
|
||||
this.parentWorkbook = parentWorkbook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cell style to use for headers.
|
||||
*
|
||||
* @return The cell style to use for headers.
|
||||
*/
|
||||
public CellStyle getHeaderStyle() {
|
||||
return headerStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent workbook.
|
||||
*
|
||||
* @return The parent workbook.
|
||||
*/
|
||||
public Workbook getParentWorkbook() {
|
||||
return parentWorkbook;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An item to be exported as a sheet during export.
|
||||
*/
|
||||
public static interface ExcelSheetExport {
|
||||
|
||||
/**
|
||||
* Returns the name of the sheet to use with this item.
|
||||
*
|
||||
* NOTE: there can be no duplicates in a workbook.
|
||||
*
|
||||
* @return The name of the sheet to use with this item.
|
||||
*/
|
||||
String getSheetName();
|
||||
|
||||
/**
|
||||
* Renders this item to an excel worksheet.
|
||||
*
|
||||
* @param sheet The worksheet.
|
||||
* @param env The environment and preferences to use while exporting.
|
||||
* @throws ExcelExportException
|
||||
*/
|
||||
void renderSheet(Sheet sheet, WorksheetEnv env) throws ExcelExportException;
|
||||
}
|
||||
|
||||
private static ExcelExport instance = null;
|
||||
|
||||
/**
|
||||
* Retrieves a singleton instance of this class.
|
||||
* @return The instance.
|
||||
*/
|
||||
public static ExcelExport getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ExcelExport();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ExcelExport() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the exports to a workbook.
|
||||
* @param exports The sheets to export.
|
||||
* @param path The path to the output file.
|
||||
* @throws IOException
|
||||
* @throws ExcelExportException
|
||||
*/
|
||||
@Messages({
|
||||
"# {0} - sheetNumber",
|
||||
"ExcelExport_writeExcel_noSheetName=Sheet {0}"
|
||||
})
|
||||
public void writeExcel(List<ExcelSheetExport> exports, File path) throws IOException, ExcelExportException {
|
||||
// Create a Workbook
|
||||
Workbook workbook = new XSSFWorkbook(); // new HSSFWorkbook() for generating `.xls` file
|
||||
|
||||
// Create a Font for styling header cells
|
||||
Font headerFont = workbook.createFont();
|
||||
headerFont.setBold(true);
|
||||
//headerFont.setFontHeightInPoints((short) 14);
|
||||
|
||||
// Create a CellStyle with the font
|
||||
CellStyle headerCellStyle = workbook.createCellStyle();
|
||||
headerCellStyle.setFont(headerFont);
|
||||
|
||||
WorksheetEnv env = new WorksheetEnv(headerCellStyle, workbook);
|
||||
|
||||
if (exports != null) {
|
||||
for (int i = 0; i < exports.size(); i++) {
|
||||
ExcelSheetExport export = exports.get(i);
|
||||
if (export == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String sheetName = export.getSheetName();
|
||||
if (sheetName == null) {
|
||||
sheetName = Bundle.ExcelExport_writeExcel_noSheetName(i + 1);
|
||||
}
|
||||
|
||||
Sheet sheet = workbook.createSheet(sheetName);
|
||||
export.renderSheet(sheet, env);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the output to a file
|
||||
FileOutputStream fileOut = new FileOutputStream(path);
|
||||
workbook.write(fileOut);
|
||||
fileOut.close();
|
||||
|
||||
// Closing the workbook
|
||||
workbook.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
|
||||
|
||||
/**
|
||||
* An excel sheet export of table data.
|
||||
*/
|
||||
public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelExport.ExcelSheetExport {
|
||||
|
||||
/**
|
||||
* Basic interface for a cell model.
|
||||
*/
|
||||
public interface ExcelCellModel extends CellModel {
|
||||
|
||||
/**
|
||||
* @return The format string to be used with Apache POI during excel
|
||||
* export or null if none necessary.
|
||||
*/
|
||||
String getExcelFormatString();
|
||||
}
|
||||
|
||||
private final String sheetName;
|
||||
private final List<ColumnModel<T, C>> columns;
|
||||
private final List<T> data;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param sheetName The name of the sheet. NOTE: There can be no duplicates
|
||||
* in a workbook.
|
||||
* @param columns The columns of the table.
|
||||
* @param data The data to export.
|
||||
*/
|
||||
public ExcelTableExport(String sheetName, List<ColumnModel<T, C>> columns, List<T> data) {
|
||||
this.sheetName = sheetName;
|
||||
this.columns = columns;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSheetName() {
|
||||
return sheetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv style) throws ExcelExport.ExcelExportException {
|
||||
renderSheet(sheet, style, columns, data);
|
||||
// Resize all columns to fit the content size
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the data into the excel sheet.
|
||||
*
|
||||
* @param sheet The sheet.
|
||||
* @param worksheetEnv The worksheet environment and preferences.
|
||||
* @param columns The columns.
|
||||
* @param data The data.
|
||||
* @throws ExcelExportException
|
||||
*/
|
||||
private static <T, C extends ExcelCellModel> void renderSheet(
|
||||
Sheet sheet, ExcelExport.WorksheetEnv worksheetEnv, List<ColumnModel<T, C>> columns, List<T> data)
|
||||
throws ExcelExport.ExcelExportException {
|
||||
|
||||
List<T> safeData = data == null ? Collections.emptyList() : data;
|
||||
// Create a header row
|
||||
Row headerRow = sheet.createRow(0);
|
||||
// Create header cells
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(columns.get(i).getHeaderTitle());
|
||||
cell.setCellStyle(worksheetEnv.getHeaderStyle());
|
||||
}
|
||||
// freeze header row
|
||||
sheet.createFreezePane(0, 1);
|
||||
// Create Cell Style for each column (if one is needed)
|
||||
Map<String, CellStyle> cellStyles = new HashMap<>();
|
||||
for (int rowNum = 0; rowNum < safeData.size(); rowNum++) {
|
||||
T rowData = safeData.get(rowNum);
|
||||
Row row = sheet.createRow(rowNum + 1);
|
||||
for (int colNum = 0; colNum < columns.size(); colNum++) {
|
||||
ColumnModel<T, ? extends ExcelCellModel> colModel = columns.get(colNum);
|
||||
ExcelCellModel cellModel = colModel.getCellRenderer().apply(rowData);
|
||||
String formatString = cellModel.getExcelFormatString();
|
||||
Optional<CellStyle> cellStyle = (formatString == null)
|
||||
? Optional.empty()
|
||||
: Optional.of(cellStyles.computeIfAbsent(formatString, k -> createCellStyle(worksheetEnv.getParentWorkbook(), formatString)));
|
||||
createCell(row, colNum, cellModel, cellStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a cell style in the workbook with the given format string.
|
||||
*
|
||||
* @param workbook The workbook.
|
||||
* @param formatString The format string.
|
||||
* @return The cell style.
|
||||
*/
|
||||
private static <T> CellStyle createCellStyle(Workbook workbook, String formatString) {
|
||||
CellStyle cellStyle = workbook.createCellStyle();
|
||||
cellStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat(formatString));
|
||||
return cellStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an excel cell given the model.
|
||||
*
|
||||
* @param row The row in the excel document.
|
||||
* @param colNum The column number (not zero-indexed).
|
||||
* @param cellModel The model for the cell.
|
||||
* @param cellStyle The style to use.
|
||||
* @return The created cell.
|
||||
*/
|
||||
private static Cell createCell(Row row, int colNum, ExcelCellModel cellModel, Optional<CellStyle> cellStyle) {
|
||||
Object cellData = cellModel.getData();
|
||||
Cell cell = row.createCell(colNum);
|
||||
if (cellData instanceof Calendar) {
|
||||
cell.setCellValue((Calendar) cellData);
|
||||
} else if (cellData instanceof Date) {
|
||||
cell.setCellValue((Date) cellData);
|
||||
} else if (cellData instanceof Double) {
|
||||
cell.setCellValue((Double) cellData);
|
||||
} else if (cellData instanceof String) {
|
||||
cell.setCellValue((String) cellData);
|
||||
} else if (cellData instanceof Short) {
|
||||
cell.setCellValue((Short) cellData);
|
||||
} else if (cellData instanceof Integer) {
|
||||
cell.setCellValue((Integer) cellData);
|
||||
} else if (cellData instanceof Long) {
|
||||
cell.setCellValue((Long) cellData);
|
||||
} else if (cellData instanceof Float) {
|
||||
cell.setCellValue((Float) cellData);
|
||||
} else {
|
||||
cell.setCellValue(cellModel.getText());
|
||||
}
|
||||
cellStyle.ifPresent(cs -> cell.setCellStyle(cs));
|
||||
return cell;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
import java.awt.Insets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Basic interface for a cell model.
|
||||
*/
|
||||
public interface GuiCellModel extends CellModel {
|
||||
|
||||
/**
|
||||
* A menu item to be used within a popup menu.
|
||||
*/
|
||||
public interface MenuItem {
|
||||
|
||||
/**
|
||||
* @return The title for that popup menu item.
|
||||
*/
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* @return The action if that popup menu item is clicked.
|
||||
*/
|
||||
Runnable getAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of a menu item.
|
||||
*/
|
||||
public static class DefaultMenuItem implements MenuItem {
|
||||
|
||||
private final String title;
|
||||
private final Runnable action;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param title The title for the menu item.
|
||||
* @param action The action should the menu item be clicked.
|
||||
*/
|
||||
public DefaultMenuItem(String title, Runnable action) {
|
||||
this.title = title;
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable getAction() {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The insets for the cell text.
|
||||
*/
|
||||
Insets getInsets();
|
||||
|
||||
/**
|
||||
* @return The popup menu associated with this cell or null if no popup menu
|
||||
* should be shown for this cell.
|
||||
*/
|
||||
List<MenuItem> getPopupMenu();
|
||||
|
||||
}
|
@ -34,7 +34,6 @@ import javax.swing.plaf.LayerUI;
|
||||
import javax.swing.table.DefaultTableColumnModel;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.CellModel;
|
||||
|
||||
/**
|
||||
* A table that displays a list of items and also can display messages for
|
||||
@ -158,65 +157,6 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes aspects of a column which can be used with getTableModel or
|
||||
* getJTablePanel. 'T' represents the object that will represent rows in the
|
||||
* table.
|
||||
*/
|
||||
public static class ColumnModel<T> {
|
||||
|
||||
private final String headerTitle;
|
||||
private final Function<T, CellModelTableCellRenderer.CellModel> cellRenderer;
|
||||
private final Integer width;
|
||||
|
||||
/**
|
||||
* Constructor for a DataResultColumnModel.
|
||||
*
|
||||
* @param headerTitle The title for the column.
|
||||
* @param cellRenderer The method that generates a CellModel for the
|
||||
* column based on the data.
|
||||
*/
|
||||
public ColumnModel(String headerTitle, Function<T, CellModelTableCellRenderer.CellModel> cellRenderer) {
|
||||
this(headerTitle, cellRenderer, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a DataResultColumnModel.
|
||||
*
|
||||
* @param headerTitle The title for the column.
|
||||
* @param cellRenderer The method that generates a CellModel for the
|
||||
* column based on the data.
|
||||
* @param width The preferred width of the column.
|
||||
*/
|
||||
public ColumnModel(String headerTitle, Function<T, CellModelTableCellRenderer.CellModel> cellRenderer, Integer width) {
|
||||
this.headerTitle = headerTitle;
|
||||
this.cellRenderer = cellRenderer;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The title for the column.
|
||||
*/
|
||||
public String getHeaderTitle() {
|
||||
return headerTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The method that generates a CellModel for the column based on
|
||||
* the data.
|
||||
*/
|
||||
public Function<T, CellModel> getCellRenderer() {
|
||||
return cellRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The preferred width of the column (can be null).
|
||||
*/
|
||||
public Integer getWidth() {
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final CellModelTableCellRenderer DEFAULT_CELL_RENDERER = new CellModelTableCellRenderer();
|
||||
@ -228,12 +168,12 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
||||
*
|
||||
* @return The corresponding TableColumnModel to be used with a JTable.
|
||||
*/
|
||||
public static <T> TableColumnModel getTableColumnModel(List<ColumnModel<T>> columns) {
|
||||
public static <T, C extends GuiCellModel> TableColumnModel getTableColumnModel(List<ColumnModel<T, C>> columns) {
|
||||
TableColumnModel tableModel = new DefaultTableColumnModel();
|
||||
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
TableColumn col = new TableColumn(i);
|
||||
ColumnModel<T> model = columns.get(i);
|
||||
ColumnModel<T, C> model = columns.get(i);
|
||||
// if a preferred width is specified in the column definition,
|
||||
// set the underlying TableColumn preferred width.
|
||||
if (model.getWidth() != null && model.getWidth() >= 0) {
|
||||
@ -260,7 +200,7 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
||||
*
|
||||
* @return The corresponding ListTableModel.
|
||||
*/
|
||||
public static <T> ListTableModel<T> getTableModel(List<ColumnModel<T>> columns) {
|
||||
public static <T, C extends GuiCellModel> ListTableModel<T> getTableModel(List<ColumnModel<T, C>> columns) {
|
||||
List<Function<T, ? extends Object>> columnRenderers = columns.stream()
|
||||
.map((colModel) -> colModel.getCellRenderer())
|
||||
.collect(Collectors.toList());
|
||||
@ -276,7 +216,7 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
||||
*
|
||||
* @return The corresponding JTablePanel.
|
||||
*/
|
||||
public static <T> JTablePanel<T> getJTablePanel(List<ColumnModel<T>> columns) {
|
||||
public static <T, C extends GuiCellModel> JTablePanel<T> getJTablePanel(List<ColumnModel<T, C>> columns) {
|
||||
ListTableModel<T> tableModel = getTableModel(columns);
|
||||
JTablePanel<T> resultTable = new JTablePanel<>(tableModel)
|
||||
.setColumnModel(getTableColumnModel(columns))
|
||||
@ -285,6 +225,7 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
||||
return resultTable;
|
||||
}
|
||||
|
||||
|
||||
private JScrollPane tableScrollPane;
|
||||
private Overlay overlayLayer;
|
||||
private ListTableModel<T> tableModel;
|
||||
|
@ -81,6 +81,7 @@ import org.sleuthkit.autopsy.datamodel.InterestingHits;
|
||||
import org.sleuthkit.autopsy.datamodel.KeywordHits;
|
||||
import org.sleuthkit.autopsy.datamodel.ResultsNode;
|
||||
import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory;
|
||||
import org.sleuthkit.autopsy.datamodel.PersonGroupingNode;
|
||||
import org.sleuthkit.autopsy.datamodel.Tags;
|
||||
import org.sleuthkit.autopsy.datamodel.ViewsNode;
|
||||
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
|
||||
@ -89,6 +90,9 @@ import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.Person;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -193,7 +197,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
private void preExpandNodes(Children rootChildren) {
|
||||
BeanTreeView tree = getTree();
|
||||
|
||||
Node results = rootChildren.findChild(ResultsNode.NAME);
|
||||
Node results = rootChildren.findChild(ResultsNode.getNameIdentifier());
|
||||
if (!Objects.isNull(results)) {
|
||||
tree.expandNode(results);
|
||||
Children resultsChildren = results.getChildren();
|
||||
@ -265,7 +269,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
* Setter to determine if rejected results should be shown or not.
|
||||
*
|
||||
* @param showRejectedResults True if showing rejected results; otherwise
|
||||
* false.
|
||||
* false.
|
||||
*/
|
||||
public void setShowRejectedResults(boolean showRejectedResults) {
|
||||
this.showRejectedResults = showRejectedResults;
|
||||
@ -797,7 +801,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
} // change in node selection
|
||||
else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||
respondSelection((Node[]) event.getOldValue(), (Node[]) event.getNewValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1012,8 +1016,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
* Set the selected node using a path to a previously selected node.
|
||||
*
|
||||
* @param previouslySelectedNodePath Path to a previously selected node.
|
||||
* @param rootNodeName Name of the root node to match, may be
|
||||
* null.
|
||||
* @param rootNodeName Name of the root node to match, may be null.
|
||||
*/
|
||||
private void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName) {
|
||||
if (previouslySelectedNodePath == null) {
|
||||
@ -1070,12 +1073,97 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does dfs search of node while nodes are Host, Person, or
|
||||
* DataSourcesByType looking for the Results Node.
|
||||
*
|
||||
* @param node The node.
|
||||
* @return The child nodes that are at the data source level.
|
||||
*/
|
||||
private Node getResultsNodeSearch(Node node, long dataSourceId) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
} else if (node.getLookup().lookup(Host.class) != null
|
||||
|| node.getLookup().lookup(Person.class) != null
|
||||
|| PersonGroupingNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
|
||||
Children children = node.getChildren();
|
||||
Node[] childNodes = children == null ? null : children.getNodes();
|
||||
if (childNodes != null) {
|
||||
for (Node child : childNodes) {
|
||||
Node foundExtracted = getResultsNodeSearch(child, dataSourceId);
|
||||
if (foundExtracted != null) {
|
||||
return foundExtracted;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DataSource dataSource = node.getLookup().lookup(DataSource.class);
|
||||
if (dataSource != null && dataSource.getId() == dataSourceId) {
|
||||
Children dsChildren = node.getChildren();
|
||||
if (dsChildren != null) {
|
||||
return dsChildren.findChild(ResultsNode.getNameIdentifier());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the results node for the specific artifact.
|
||||
*
|
||||
* @param art The artifact to find the relevant Results Node.
|
||||
* @return THe Results Node or null.
|
||||
*/
|
||||
private Node getResultsNode(final BlackboardArtifact art) {
|
||||
Children rootChilds = em.getRootContext().getChildren();
|
||||
|
||||
Node resultsNode = rootChilds.findChild(ResultsNode.getNameIdentifier());
|
||||
if (resultsNode != null) {
|
||||
return resultsNode;
|
||||
}
|
||||
|
||||
long dataSourceId;
|
||||
try {
|
||||
dataSourceId = art.getDataSource().getId();
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "There was an error fetching the data source id for artifact.", ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
Node[] rootNodes = rootChilds.getNodes();
|
||||
if (rootNodes != null) {
|
||||
for (Node rootNode : rootNodes) {
|
||||
resultsNode = getResultsNodeSearch(rootNode, dataSourceId);
|
||||
if (resultsNode != null) {
|
||||
return resultsNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to artifact and shows in view.
|
||||
*
|
||||
* NOTE: This code will likely need updating in the event that the structure
|
||||
* of the nodes is changed (i.e. adding parent levels). Places to look when
|
||||
* changing node structure include:
|
||||
*
|
||||
* DirectoryTreeTopComponent.viewArtifact, ViewContextAction
|
||||
*
|
||||
* @param art The artifact.
|
||||
*/
|
||||
public void viewArtifact(final BlackboardArtifact art) {
|
||||
int typeID = art.getArtifactTypeID();
|
||||
String typeName = art.getArtifactTypeName();
|
||||
Children rootChilds = em.getRootContext().getChildren();
|
||||
Node treeNode = null;
|
||||
Node resultsNode = rootChilds.findChild(ResultsNode.NAME);
|
||||
|
||||
Node resultsNode = getResultsNode(art);
|
||||
if (resultsNode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Children resultsChilds = resultsNode.getChildren();
|
||||
if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
|
||||
Node hashsetRootNode = resultsChilds.findChild(typeName);
|
||||
@ -1160,11 +1248,16 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
if (setNode == null) {
|
||||
return;
|
||||
}
|
||||
Children interestingChildren = setNode.getChildren();
|
||||
if (interestingChildren == null) {
|
||||
|
||||
Children fileArtifactChildren = setNode.getChildren();
|
||||
Node[] fileArtifactNodes = fileArtifactChildren == null ? null : fileArtifactChildren.getNodes();
|
||||
if (fileArtifactNodes == null || fileArtifactNodes.length != 2) {
|
||||
return;
|
||||
}
|
||||
treeNode = interestingChildren.findChild(art.getDisplayName());
|
||||
|
||||
treeNode = (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())
|
||||
? fileArtifactNodes[0]
|
||||
: fileArtifactNodes[1];
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
|
||||
}
|
||||
|
@ -22,10 +22,13 @@ import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.beans.PropertyVetoException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
@ -43,8 +46,10 @@ import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
|
||||
import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentNodeSelectionInfo;
|
||||
import org.sleuthkit.autopsy.datamodel.DataSourcesByTypeNode;
|
||||
import org.sleuthkit.autopsy.datamodel.DataSourcesNode;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
||||
import org.sleuthkit.autopsy.datamodel.PersonGroupingNode;
|
||||
import org.sleuthkit.autopsy.datamodel.RootContentChildren;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -52,6 +57,8 @@ import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentVisitor;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.FileSystem;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.Person;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
@ -141,6 +148,12 @@ public class ViewContextAction extends AbstractAction {
|
||||
* branch of the tree view to the level of the parent of the content,
|
||||
* selecting the parent in the tree view, then selecting the content in the
|
||||
* results view.
|
||||
*
|
||||
* NOTE: This code will likely need updating in the event that the structure
|
||||
* of the nodes is changed (i.e. adding parent levels). Places to look when
|
||||
* changing node structure include:
|
||||
*
|
||||
* DirectoryTreeTopComponent.viewArtifact, ViewContextAction
|
||||
*
|
||||
* @param event The action event.
|
||||
*/
|
||||
@ -186,23 +199,26 @@ public class ViewContextAction extends AbstractAction {
|
||||
DataSource datasource = skCase.getDataSource(contentDSObjid);
|
||||
dsname = datasource.getName();
|
||||
Children rootChildren = treeViewExplorerMgr.getRootContext().getChildren();
|
||||
|
||||
|
||||
if (null != parentContent) {
|
||||
// the tree view needs to be searched to find the parent treeview node.
|
||||
/* NOTE: we can't do a lookup by data source name here, becase if there
|
||||
are multiple data sources with the same name, then "getChildren().findChild(dsname)"
|
||||
simply returns the first one that it finds. Instead we have to loop over all
|
||||
data sources with that name, and make sure we find the correct one.
|
||||
*/
|
||||
for (int i = 0; i < rootChildren.getNodesCount(); i++) {
|
||||
*/
|
||||
List<Node> dataSourceLevelNodes = Stream.of(rootChildren.getNodes())
|
||||
.flatMap(rootNode -> getDataSourceLevelNodes(rootNode).stream())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Node treeNode : dataSourceLevelNodes) {
|
||||
// in the root, look for a data source node with the name of interest
|
||||
Node treeNode = rootChildren.getNodeAt(i);
|
||||
if (!(treeNode.getName().equals(dsname))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// for this data source, get the "Data Sources" child node
|
||||
Node datasourceGroupingNode = treeNode.getChildren().findChild(DataSourcesNode.NAME);
|
||||
Node datasourceGroupingNode = treeNode.getChildren().findChild(DataSourcesNode.getNameIdentifier());
|
||||
|
||||
// check whether this is the data source we are looking for
|
||||
parentTreeViewNode = findParentNodeInTree(parentContent, datasourceGroupingNode);
|
||||
@ -218,7 +234,7 @@ public class ViewContextAction extends AbstractAction {
|
||||
Node datasourceGroupingNode = rootChildren.findChild(dsname);
|
||||
if (!Objects.isNull(datasourceGroupingNode)) {
|
||||
Children dsChildren = datasourceGroupingNode.getChildren();
|
||||
parentTreeViewNode = dsChildren.findChild(DataSourcesNode.NAME);
|
||||
parentTreeViewNode = dsChildren.findChild(DataSourcesNode.getNameIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,13 +250,19 @@ public class ViewContextAction extends AbstractAction {
|
||||
}
|
||||
} else { // Classic view
|
||||
// Start the search at the DataSourcesNode
|
||||
parentTreeViewNode = treeViewExplorerMgr.getRootContext().getChildren().findChild(DataSourcesNode.NAME);
|
||||
|
||||
if (null != parentContent) {
|
||||
// the tree view needs to be searched to find the parent treeview node.
|
||||
Node potentialParentTreeViewNode = findParentNodeInTree(parentContent, parentTreeViewNode);
|
||||
if (potentialParentTreeViewNode != null) {
|
||||
parentTreeViewNode = potentialParentTreeViewNode;
|
||||
Children rootChildren = treeViewExplorerMgr.getRootContext().getChildren();
|
||||
Node rootDsNode = rootChildren == null ? null : rootChildren.findChild(DataSourcesByTypeNode.getNameIdentifier());
|
||||
if (rootDsNode != null) {
|
||||
for (Node dataSourceLevelNode : getDataSourceLevelNodes(rootDsNode)) {
|
||||
DataSource dataSource = dataSourceLevelNode.getLookup().lookup(DataSource.class);
|
||||
if (dataSource != null) {
|
||||
// the tree view needs to be searched to find the parent treeview node.
|
||||
Node potentialParentTreeViewNode = findParentNodeInTree(parentContent, dataSourceLevelNode);
|
||||
if (potentialParentTreeViewNode != null) {
|
||||
parentTreeViewNode = potentialParentTreeViewNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -286,6 +308,34 @@ public class ViewContextAction extends AbstractAction {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If the node has lookup of host or person, returns children. If not, just
|
||||
* returns itself.
|
||||
*
|
||||
* @param node The node.
|
||||
* @return The child nodes that are at the data source level.
|
||||
*/
|
||||
private List<Node> getDataSourceLevelNodes(Node node) {
|
||||
if (node == null) {
|
||||
return Collections.emptyList();
|
||||
} else if (node.getLookup().lookup(Host.class) != null ||
|
||||
node.getLookup().lookup(Person.class) != null ||
|
||||
DataSourcesByTypeNode.getNameIdentifier().equals(node.getLookup().lookup(String.class)) ||
|
||||
PersonGroupingNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
|
||||
Children children = node.getChildren();
|
||||
Node[] childNodes = children == null ? null : children.getNodes();
|
||||
if (childNodes == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Stream.of(node.getChildren().getNodes())
|
||||
.flatMap(parent -> getDataSourceLevelNodes(parent).stream())
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
return Arrays.asList(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches tree for parent node by getting an ordered list of the ancestors
|
||||
* of the specified content.
|
||||
|
@ -20,12 +20,19 @@ package org.sleuthkit.autopsy.discovery.ui;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.awt.Component;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesPanel;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -38,6 +45,7 @@ import org.sleuthkit.autopsy.discovery.search.SearchData;
|
||||
final class DomainDetailsPanel extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(DomainDetailsPanel.class.getName());
|
||||
private ArtifactsWorker singleArtifactDomainWorker;
|
||||
private String domain;
|
||||
private String selectedTabName = null;
|
||||
@ -47,6 +55,7 @@ final class DomainDetailsPanel extends JPanel {
|
||||
*
|
||||
* @param selectedTabName The name of the tab to select initially.
|
||||
*/
|
||||
@NbBundle.Messages({"DomainDetailsPanel.otherOccurrencesTab.title=Other Occurrences"})
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
DomainDetailsPanel() {
|
||||
initComponents();
|
||||
@ -56,6 +65,9 @@ final class DomainDetailsPanel extends JPanel {
|
||||
for (BlackboardArtifact.ARTIFACT_TYPE type : SearchData.Type.DOMAIN.getArtifactTypes()) {
|
||||
jTabbedPane1.add(type.getDisplayName(), new DomainArtifactsTabPanel(type));
|
||||
}
|
||||
if (CentralRepository.isEnabled()) {
|
||||
jTabbedPane1.add(Bundle.DomainDetailsPanel_otherOccurrencesTab_title(), new OtherOccurrencesPanel());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,6 +194,17 @@ final class DomainDetailsPanel extends JPanel {
|
||||
runDomainWorker((DomainArtifactsTabPanel) selectedComponent, false);
|
||||
} else if (selectedComponent instanceof MiniTimelinePanel) {
|
||||
runMiniTimelineWorker((MiniTimelinePanel) selectedComponent, false);
|
||||
} else if (selectedComponent instanceof OtherOccurrencesPanel) {
|
||||
if (CentralRepository.isEnabled()) {
|
||||
try {
|
||||
((OtherOccurrencesPanel) selectedComponent).populateTableForOneType(CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID), domain);
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.INFO, "Central repository exception while trying to get instances by type and value for domain: " + domain, ex);
|
||||
((OtherOccurrencesPanel) selectedComponent).reset();
|
||||
}
|
||||
} else {
|
||||
((OtherOccurrencesPanel) selectedComponent).reset();
|
||||
}
|
||||
}
|
||||
//send fade in event
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true));
|
||||
@ -200,6 +223,8 @@ final class DomainDetailsPanel extends JPanel {
|
||||
((DomainArtifactsTabPanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED);
|
||||
} else if (comp instanceof MiniTimelinePanel) {
|
||||
((MiniTimelinePanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED, domain);
|
||||
} else if (comp instanceof OtherOccurrencesPanel) {
|
||||
((OtherOccurrencesPanel) comp).reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,16 +93,12 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
|
||||
* reflect the currently selected domain. Will populate the list with
|
||||
* nothing when a domain is not used.
|
||||
*
|
||||
* @param useDomain If the currently selected domain should be used to
|
||||
* retrieve a list.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void sendPopulateEvent(boolean useDomain) {
|
||||
void sendPopulateEvent() {
|
||||
String domain = "";
|
||||
if (useDomain) {
|
||||
if (domainList.getSelectedIndex() != -1) {
|
||||
domain = domainListModel.getElementAt(domainList.getSelectedIndex()).getResultDomain().getDomain();
|
||||
}
|
||||
if (domainList.getSelectedIndex() != -1) {
|
||||
domain = domainListModel.getElementAt(domainList.getSelectedIndex()).getResultDomain().getDomain();
|
||||
}
|
||||
//send populateMesage
|
||||
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.PopulateDomainTabsEvent(domain));
|
||||
|
@ -128,8 +128,8 @@ final class ResultsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
});
|
||||
domainSummaryViewer.addListSelectionListener((e) -> {
|
||||
if (resultType == SearchData.Type.DOMAIN) {
|
||||
domainSummaryViewer.sendPopulateEvent(!e.getValueIsAdjusting());
|
||||
if (resultType == SearchData.Type.DOMAIN && !e.getValueIsAdjusting()) {
|
||||
domainSummaryViewer.sendPopulateEvent();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -323,6 +323,11 @@ abstract class IngestTaskPipeline<T extends IngestTask> {
|
||||
* performing the task.
|
||||
*/
|
||||
abstract void performTask(IngestJobPipeline ingestJobPipeline, T task) throws IngestModuleException;
|
||||
|
||||
@Override
|
||||
public void shutDown() {
|
||||
module.shutDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -22,19 +22,20 @@ ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
|
||||
ILeappAnalyzerIngestModule.processing.file=Processing file {0}
|
||||
ILeappAnalyzerIngestModule.parsing.file=Parsing file {0}
|
||||
ILeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
|
||||
IleappAnalyzerIngestModule.not.64.bit.os=iLeapp will not run on 32bit operating system
|
||||
IleappAnalyzerIngestModule.not.64.bit.os=iLeapp will not run on a 32bit operating system
|
||||
ALeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
|
||||
ALeappAnalyzerIngestModule.processing.file=Processing file {0}
|
||||
ALeappAnalyzerIngestModule.parsing.file=Parsing file {0}
|
||||
ALeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
|
||||
AleappAnalyzerIngestModule.not.64.bit.os=aLeapp will not run on 32bit operating system
|
||||
AleappAnalyzerIngestModule.not.64.bit.os=aLeapp will not run on a 32bit operating system
|
||||
ILeappAnalyzerIngestModule.report.name=iLeapp Html Report
|
||||
ILeappAnalyzerIngestModule.requires.windows=iLeapp module requires windows.
|
||||
ILeappAnalyzerIngestModule.running.iLeapp=Running iLeapp
|
||||
ILeappAnalyzerIngestModule.starting.iLeapp=Starting iLeapp
|
||||
ILeappAnalyzerModuleFactory_moduleDesc=Uses iLEAPP to analyze logical acquisitions of iOS devices.
|
||||
ILeappAnalyzerModuleFactory_moduleName=iOS Analyzer (iLEAPP)
|
||||
LeappFileProcessor.cannot.load.artifact.xml=Cannor load xml artifact file.
|
||||
LeappFileProcessor.cannot.create.message.relationship=Cannot create TSK_MESSAGE Relationship.
|
||||
LeappFileProcessor.cannot.load.artifact.xml=Cannot load xml artifact file.
|
||||
LeappFileProcessor.cannotBuildXmlParser=Cannot buld an XML parser.
|
||||
LeappFileProcessor.completed=Leapp Processing Completed
|
||||
LeappFileProcessor.error.creating.new.artifacts=Error creating new artifacts.
|
||||
|
@ -23,7 +23,6 @@ import com.fasterxml.jackson.dataformat.csv.CsvMapper;
|
||||
import com.fasterxml.jackson.dataformat.csv.CsvParser;
|
||||
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@ -65,13 +64,21 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModule.ProcessResult;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.Blackboard;
|
||||
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskException;
|
||||
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
|
||||
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CallMediaType;
|
||||
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CommunicationDirection;
|
||||
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.MessageReadStatus;
|
||||
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
|
||||
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.FileAttachment;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.NodeList;
|
||||
@ -141,6 +148,26 @@ public final class LeappFileProcessor {
|
||||
.put("TSK_IP_DHCP", "DHCP Information")
|
||||
.build();
|
||||
|
||||
private static final Map<String, String> ACCOUNT_RELATIONSHIPS = ImmutableMap.<String, String>builder()
|
||||
.put("Zapya.tsv", "message")
|
||||
.put("sms messages.tsv", "message")
|
||||
.put("mms messages.tsv", "message")
|
||||
.put("Viber - Messages.tsv", "message")
|
||||
.put("Viber - Contacts.tsv", "contact")
|
||||
.put("Viber - Call Logs.tsv", "calllog")
|
||||
.put("Xender file transfer - Messages.tsv", "message")
|
||||
.put("Whatsapp - Contacts.tsv", "contact")
|
||||
.put("Whatsapp - Group Call Logs.tsv", "calllog")
|
||||
.put("Whatsapp - Single Call Logs.tsv", "calllog")
|
||||
.put("Whatsapp - Messages Logs.tsv", "message")
|
||||
.put("Shareit file transfer.tsv", "message")
|
||||
.put("tangomessages messages.tsv", "message")
|
||||
.put("Contacts.tsv", "contact")
|
||||
.put("IMO - AccountId.tsv", "contact")
|
||||
.put("IMO - messages.tsv", "message")
|
||||
|
||||
.build();
|
||||
|
||||
Blackboard blkBoard;
|
||||
|
||||
public LeappFileProcessor(String xmlFile, String moduleName) throws IOException, IngestModuleException, NoCurrentCaseException {
|
||||
@ -267,7 +294,7 @@ public final class LeappFileProcessor {
|
||||
|
||||
for (String LeappFileName : LeappFilesToProcess) {
|
||||
String fileName = FilenameUtils.getName(LeappFileName);
|
||||
File LeappFile = new File(LeappFileName);
|
||||
File LeappFile = new File(LeappFileName);
|
||||
if (tsvFileAttributes.containsKey(fileName)) {
|
||||
List<TsvColumn> attrList = tsvFileAttributes.get(fileName);
|
||||
BlackboardArtifact.Type artifactType = tsvFileArtifacts.get(fileName);
|
||||
@ -321,9 +348,22 @@ public final class LeappFileProcessor {
|
||||
Collection<BlackboardAttribute> bbattributes = processReadLine(columnItems, columnIndexes, attrList, fileName, lineNum);
|
||||
|
||||
if (!bbattributes.isEmpty()) {
|
||||
BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), dataSource, bbattributes);
|
||||
if (bbartifact != null) {
|
||||
bbartifacts.add(bbartifact);
|
||||
switch (ACCOUNT_RELATIONSHIPS.getOrDefault(fileName, "norelationship").toLowerCase()) {
|
||||
case "message":
|
||||
createMessageRelationship(bbattributes, dataSource, fileName);
|
||||
break;
|
||||
case "contact":
|
||||
createContactRelationship(bbattributes, dataSource, fileName);
|
||||
break;
|
||||
case "calllog":
|
||||
createCalllogRelationship(bbattributes, dataSource, fileName);
|
||||
break;
|
||||
default: // There is no relationship defined so just process the artifact normally
|
||||
BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), dataSource, bbattributes);
|
||||
if (bbartifact != null) {
|
||||
bbartifacts.add(bbartifact);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,6 +373,266 @@ public final class LeappFileProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"LeappFileProcessor.cannot.create.message.relationship=Cannot create TSK_MESSAGE Relationship.",
|
||||
})
|
||||
|
||||
private void createMessageRelationship(Collection<BlackboardAttribute> bbattributes, Content dataSource, String fileName) throws IngestModuleException {
|
||||
|
||||
String messageType = null;
|
||||
CommunicationDirection communicationDirection = CommunicationDirection.UNKNOWN;
|
||||
String senderId = null;
|
||||
String receipentId = null;
|
||||
String[] receipentIdList = null;
|
||||
Long dateTime = Long.valueOf(0);
|
||||
MessageReadStatus messageStatus = MessageReadStatus.UNKNOWN;
|
||||
String subject = null;
|
||||
String messageText = null;
|
||||
String threadId = null;
|
||||
List<BlackboardAttribute> otherAttributes = new ArrayList<>();
|
||||
List<FileAttachment> fileAttachments = new ArrayList<>();
|
||||
String sourceFile = null;
|
||||
MessageAttachments messageAttachments = null;
|
||||
|
||||
try {
|
||||
for (BlackboardAttribute bba : bbattributes) {
|
||||
switch (bba.getAttributeType().getTypeName()) {
|
||||
case "TSK_DIRECTION":
|
||||
if (bba.getValueString().toLowerCase().equals("outgoing")) {
|
||||
communicationDirection = CommunicationDirection.OUTGOING;
|
||||
} else if (bba.getValueString().toLowerCase().equals("incoming")) {
|
||||
communicationDirection = CommunicationDirection.INCOMING;
|
||||
}
|
||||
break;
|
||||
case "TSK_PHONE_NUMBER_FROM":
|
||||
if (!bba.getValueString().isEmpty()) {
|
||||
senderId = bba.getValueString();
|
||||
}
|
||||
break;
|
||||
case "TSK_PHONE_NUMBER_TO":
|
||||
if (!bba.getValueString().isEmpty()) {
|
||||
receipentIdList = bba.getValueString().split(",", 0);
|
||||
}
|
||||
break;
|
||||
case "TSK_DATETIME":
|
||||
dateTime = bba.getValueLong();
|
||||
break;
|
||||
case "TSK_COMMENT":
|
||||
messageType = bba.getValueString();
|
||||
break;
|
||||
case "TSK_ATTACHMENTS":
|
||||
if (!bba.getValueString().isEmpty()) {
|
||||
fileAttachments.add(new FileAttachment(Case.getCurrentCaseThrows().getSleuthkitCase(), dataSource, bba.getValueString()));
|
||||
}
|
||||
break;
|
||||
case "TSK_TEXT_FILE":
|
||||
sourceFile = bba.getValueString();
|
||||
break;
|
||||
case "TSK_READ_STATUS":
|
||||
if (bba.getValueInt() == 1 ) {
|
||||
messageStatus = MessageReadStatus.READ;
|
||||
} else {
|
||||
messageStatus = MessageReadStatus.UNREAD;
|
||||
}
|
||||
break;
|
||||
case "TSK_TEXT":
|
||||
messageText = bba.getValueString();
|
||||
break;
|
||||
case "TSK_SUBJECT":
|
||||
subject = bba.getValueString();
|
||||
break;
|
||||
default:
|
||||
otherAttributes.add(bba);
|
||||
break;
|
||||
}
|
||||
}
|
||||
AbstractFile absFile = findAbstractFile(dataSource, sourceFile);
|
||||
Account.Type accountType = getAccountType(fileName);
|
||||
if ((absFile != null) || (accountType != null)) {
|
||||
CommunicationArtifactsHelper accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(),
|
||||
moduleName, absFile, accountType);
|
||||
BlackboardArtifact messageArtifact = accountArtifact.addMessage(messageType, communicationDirection, senderId,
|
||||
receipentId, dateTime, messageStatus, subject,
|
||||
messageText, threadId, otherAttributes);
|
||||
if (!fileAttachments.isEmpty()) {
|
||||
messageAttachments = new MessageAttachments(fileAttachments, new ArrayList<>());
|
||||
accountArtifact.addAttachments(messageArtifact, messageAttachments);
|
||||
}
|
||||
}
|
||||
} catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) {
|
||||
throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void createContactRelationship(Collection<BlackboardAttribute> bbattributes, Content dataSource, String fileName) throws IngestModuleException {
|
||||
|
||||
String alternateId = null;
|
||||
String contactName = null;
|
||||
String phoneNumber = null;
|
||||
String homePhoneNumber = null;
|
||||
String mobilePhoneNumber = null;
|
||||
String emailAddr = null;
|
||||
List<BlackboardAttribute> otherAttributes = new ArrayList<>();
|
||||
String sourceFile = null;
|
||||
|
||||
try {
|
||||
for (BlackboardAttribute bba : bbattributes) {
|
||||
switch (bba.getAttributeType().getTypeName()) {
|
||||
case "TSK_PHONE_NUMBER":
|
||||
if (!bba.getValueString().isEmpty()) {
|
||||
phoneNumber = bba.getValueString();
|
||||
}
|
||||
break;
|
||||
case "TSK_NAME":
|
||||
if (!bba.getValueString().isEmpty()) {
|
||||
contactName = bba.getValueString();
|
||||
}
|
||||
break;
|
||||
case "TSK_TEXT_FILE":
|
||||
sourceFile = bba.getValueString();
|
||||
break;
|
||||
case "TSK_PHONE_NUMBER_HOME":
|
||||
homePhoneNumber = bba.getValueString();
|
||||
break;
|
||||
case "TSK_PHONE_NUMBER_MOBILE":
|
||||
mobilePhoneNumber = bba.getValueString();
|
||||
break;
|
||||
case "TSK_EMAIL":
|
||||
emailAddr = bba.getValueString();
|
||||
break;
|
||||
case "TSK_ID":
|
||||
alternateId = bba.getValueString();
|
||||
break;
|
||||
default:
|
||||
otherAttributes.add(bba);
|
||||
break;
|
||||
}
|
||||
}
|
||||
AbstractFile absFile = findAbstractFile(dataSource, sourceFile);
|
||||
Account.Type accountType = getAccountType(fileName);
|
||||
if ((absFile != null) || (accountType != null)) {
|
||||
|
||||
CommunicationArtifactsHelper accountArtifact;
|
||||
if (alternateId == null) {
|
||||
accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(),
|
||||
moduleName, absFile, accountType);
|
||||
} else {
|
||||
accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(),
|
||||
moduleName, absFile, accountType, accountType, alternateId);
|
||||
}
|
||||
BlackboardArtifact messageArtifact = accountArtifact.addContact(contactName, phoneNumber, homePhoneNumber, mobilePhoneNumber, emailAddr, otherAttributes);
|
||||
}
|
||||
} catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) {
|
||||
throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void createCalllogRelationship(Collection<BlackboardAttribute> bbattributes, Content dataSource, String fileName) throws IngestModuleException {
|
||||
|
||||
String callerId = null;
|
||||
List<String> calleeId = Arrays.asList();
|
||||
CommunicationDirection communicationDirection = CommunicationDirection.UNKNOWN;
|
||||
Long startDateTime = Long.valueOf(0);
|
||||
Long endDateTime = Long.valueOf(0);
|
||||
CallMediaType mediaType = CallMediaType.UNKNOWN;
|
||||
List<BlackboardAttribute> otherAttributes = new ArrayList<>();
|
||||
String sourceFile = null;
|
||||
|
||||
try {
|
||||
for (BlackboardAttribute bba : bbattributes) {
|
||||
switch (bba.getAttributeType().getTypeName()) {
|
||||
case "TSK_TEXT_FILE":
|
||||
sourceFile = bba.getValueString();
|
||||
break;
|
||||
case "TSK_DATETIME_START":
|
||||
startDateTime = bba.getValueLong();
|
||||
break;
|
||||
case "TSK_DATETIME_END":
|
||||
startDateTime = bba.getValueLong();
|
||||
break;
|
||||
case "TSK_DIRECTION":
|
||||
if (bba.getValueString().toLowerCase().equals("outgoing")) {
|
||||
communicationDirection = CommunicationDirection.OUTGOING;
|
||||
} else if (bba.getValueString().toLowerCase().equals("incoming")) {
|
||||
communicationDirection = CommunicationDirection.INCOMING;
|
||||
}
|
||||
break;
|
||||
case "TSK_PHONE_NUMBER_FROM":
|
||||
if (!bba.getValueString().isEmpty()) {
|
||||
callerId = bba.getValueString();
|
||||
}
|
||||
break;
|
||||
case "TSK_PHONE_NUMBER_TO":
|
||||
if (!bba.getValueString().isEmpty()) {
|
||||
String [] calleeTempList = bba.getValueString().split(",", 0);
|
||||
calleeId = Arrays.asList(calleeTempList);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
otherAttributes.add(bba);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (calleeId.isEmpty() && communicationDirection == CommunicationDirection.OUTGOING) {
|
||||
String [] calleeTempList = callerId.split(",", 0);
|
||||
calleeId = Arrays.asList(calleeTempList);
|
||||
callerId = null;
|
||||
}
|
||||
AbstractFile absFile = findAbstractFile(dataSource, sourceFile);
|
||||
Account.Type accountType = getAccountType(fileName);
|
||||
if ((absFile != null) || (accountType != null)) {
|
||||
CommunicationArtifactsHelper accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(),
|
||||
moduleName, absFile, accountType);
|
||||
BlackboardArtifact callLogArtifact = accountArtifact.addCalllog(communicationDirection, callerId, calleeId, startDateTime, endDateTime, mediaType, otherAttributes);
|
||||
}
|
||||
} catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) {
|
||||
throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Account.Type getAccountType(String AccountTypeName) {
|
||||
switch (AccountTypeName.toLowerCase()) {
|
||||
case "zapya.tsv":
|
||||
return Account.Type.ZAPYA;
|
||||
case "sms messages.tsv":
|
||||
return Account.Type.PHONE;
|
||||
case "contacts.tsv":
|
||||
return Account.Type.PHONE;
|
||||
case "imo - accountid.tsv":
|
||||
return Account.Type.IMO;
|
||||
case "imo - messages.tsv":
|
||||
return Account.Type.IMO;
|
||||
case "mms messages.tsv":
|
||||
return Account.Type.PHONE;
|
||||
case "viber - call logs.tsv":
|
||||
return Account.Type.VIBER;
|
||||
case "viber - contacts.tsv":
|
||||
return Account.Type.VIBER;
|
||||
case "viber - messages.tsv":
|
||||
return Account.Type.VIBER;
|
||||
case "xender file transfer - messages.tsv":
|
||||
return Account.Type.XENDER;
|
||||
case "whatsapp - single call logs.tsv":
|
||||
return Account.Type.WHATSAPP;
|
||||
case "whatsapp - messages logs.tsv":
|
||||
return Account.Type.WHATSAPP;
|
||||
case "whatsapp - group call logs.tsv":
|
||||
return Account.Type.WHATSAPP;
|
||||
case "whatsapp - contacts.tsv":
|
||||
return Account.Type.WHATSAPP;
|
||||
case "tangomessages messages.tsv":
|
||||
return Account.Type.TANGO;
|
||||
case "shareit file transfer.tsv":
|
||||
return Account.Type.SHAREIT;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the line read and create the necessary attributes for it.
|
||||
*
|
||||
@ -399,16 +699,17 @@ public final class LeappFileProcessor {
|
||||
|
||||
/**
|
||||
* Check type of attribute and possibly format string based on it.
|
||||
*
|
||||
*
|
||||
* @param colAttr Column Attribute information
|
||||
* @param value string to be formatted
|
||||
* @return formatted string based on attribute type if no attribute type found then return original string
|
||||
* @return formatted string based on attribute type if no attribute type
|
||||
* found then return original string
|
||||
*/
|
||||
private String formatValueBasedOnAttrType(TsvColumn colAttr, String value) {
|
||||
if (colAttr.getAttributeType().getTypeName().equals("TSK_DOMAIN")) {
|
||||
return NetworkUtils.extractDomain(value);
|
||||
}
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -460,6 +761,7 @@ public final class LeappFileProcessor {
|
||||
// Log this and continue on with processing
|
||||
logger.log(Level.WARNING, String.format("Attribute Type %s for file %s not defined.", attrType, fileName)); //NON-NLS
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -514,7 +816,7 @@ public final class LeappFileProcessor {
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"LeappFileProcessor.cannot.load.artifact.xml=Cannor load xml artifact file.",
|
||||
"LeappFileProcessor.cannot.load.artifact.xml=Cannot load xml artifact file.",
|
||||
"LeappFileProcessor.cannotBuildXmlParser=Cannot buld an XML parser.",
|
||||
"LeappFileProcessor_cannotParseXml=Cannot Parse XML file.",
|
||||
"LeappFileProcessor.postartifacts_error=Error posting Blackboard Artifact",
|
||||
@ -607,7 +909,7 @@ public final class LeappFileProcessor {
|
||||
for (int k = 0; k < attributeNlist.getLength(); k++) {
|
||||
NamedNodeMap nnm = attributeNlist.item(k).getAttributes();
|
||||
String attributeName = nnm.getNamedItem("attributename").getNodeValue();
|
||||
|
||||
|
||||
if (!attributeName.toLowerCase().matches("null")) {
|
||||
String columnName = nnm.getNamedItem("columnName").getNodeValue();
|
||||
String required = nnm.getNamedItem("required").getNodeValue();
|
||||
@ -704,7 +1006,7 @@ public final class LeappFileProcessor {
|
||||
*/
|
||||
private void configExtractor() throws IOException {
|
||||
PlatformUtil.extractResourceToUserConfigDir(LeappFileProcessor.class,
|
||||
xmlFile, true);
|
||||
xmlFile, true);
|
||||
}
|
||||
|
||||
private static final Set<String> ALLOWED_EXTENSIONS = new HashSet<>(Arrays.asList("zip", "tar", "tgz"));
|
||||
@ -761,4 +1063,35 @@ public final class LeappFileProcessor {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AbstractFile findAbstractFile(Content dataSource, String fileNamePath) {
|
||||
if (fileNamePath == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<AbstractFile> files;
|
||||
|
||||
String fileName = FilenameUtils.getName(fileNamePath);
|
||||
String filePath = FilenameUtils.normalize(FilenameUtils.getPath(fileNamePath), true);
|
||||
|
||||
FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
|
||||
|
||||
try {
|
||||
files = fileManager.findFiles(dataSource, fileName); //NON-NLS
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to find prefetch files.", ex); //NON-NLS
|
||||
return null; // No need to continue
|
||||
}
|
||||
|
||||
for (AbstractFile pFile : files) {
|
||||
|
||||
if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase())) {
|
||||
return pFile;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -344,17 +344,18 @@
|
||||
<AttributeName attributename="TSK_DATETIME" columnName="Date" required="yes"/>
|
||||
<AttributeName attributename="null" columnName="MSG ID" required="no"/>
|
||||
<AttributeName attributename="TSK_THREAD_ID" columnName="Thread ID" required="yes"/>
|
||||
<AttributeName attributename="TSK_DATETIME_SENT" columnName="Date sent" required="yes"/>
|
||||
<AttributeName attributename="null" columnName="Date sent" required="no"/>
|
||||
<AttributeName attributename="TSK_READ_STATUS" columnName="Read" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_FROM" columnName="From" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_TO" columnName="To" required="yes"/>
|
||||
<AttributeName attributename="null" columnName="Cc" required="no"/>
|
||||
<AttributeName attributename="null" columnName="Bcc" required="no"/>
|
||||
<AttributeName attributename="TSK_TEXT" columnName="Body" required="yes"/>
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
<!-- <FileName filename="partner settings.tsv" description="Partner Settings">
|
||||
<!-- <FileName filename="partner settings.tsv" description="Partner Settings">
|
||||
<ArtifactName artifactname="TSK_" comment="null">
|
||||
<AttributeName attributename="null" columnName="Name" required="no" />
|
||||
<AttributeName attributename="null" columnName="Value ) # Dont remove the comma" required="no" />
|
||||
@ -365,17 +366,139 @@
|
||||
|
||||
<FileName filename="sms messages.tsv" description="SMS messages">
|
||||
<ArtifactName artifactname="TSK_MESSAGE" comment="SMS messages">
|
||||
<AttributeName attributename="TSK_DATETIME" columnName="Date" required="yes"/>
|
||||
<AttributeName attributename="null" columnName="Date" required="no"/>
|
||||
<AttributeName attributename="null" columnName="MSG ID" required="no"/>
|
||||
<AttributeName attributename="TSK_THREAD_ID" columnName="Thread ID" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_FROM" columnName="Address" required="yes" />
|
||||
<AttributeName attributename="null" columnName="Contact ID" required="yes"/>
|
||||
<AttributeName attributename="TSK_DATETIME_SENT" columnName="Date sent" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_TO" columnName="Contact ID" required="yes"/>
|
||||
<AttributeName attributename="TSK_DATETIME" columnName="Date sent" required="yes"/>
|
||||
<AttributeName attributename="TSK_READ_STATUS" columnName="Read" required="yes"/>
|
||||
<AttributeName attributename="TSK_TEXT" columnName="Body" required="yes"/>
|
||||
<AttributeName attributename="null" columnName="Service Center" required="yes"/>
|
||||
<AttributeName attributename="null" columnName="Service Center" required="no"/>
|
||||
<AttributeName attributename="null" columnName="Error Code" required="no"/>
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
<FileName filename="Viber - Messages.tsv" description="Viber">
|
||||
<ArtifactName artifactname="TSK_MESSAGE" comment="Viber Message">
|
||||
<AttributeName attributename="TSK_DATETIME" columnName="Message Date" required="yes" />
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_FROM" columnName="From Phone Number" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_TO" columnName="Recipients" required="yes"/>
|
||||
<AttributeName attributename="TSK_THREAD_ID" columnName="Thread ID" required="yes" />
|
||||
<AttributeName attributename="TSK_TEXT" columnName="Message" required="yes" />
|
||||
<AttributeName attributename="TSK_DIRECTION" columnName="direction" required="yes"/>
|
||||
<AttributeName attributename="TSK_READ_STATUS" columnName="Read Status" required="yes"/>
|
||||
<AttributeName attributename="TSK_ATTACHMENTS" columnName="File Attachment" required="yes"/>
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
</aLeap_Files_To_Process>
|
||||
<FileName filename="Viber - Contacts.tsv" description="Viber">
|
||||
<ArtifactName artifactname="TSK_CONTACT" comment="Viber Contacts">
|
||||
<AttributeName attributename="TSK_NAME" columnName="display name" required="yes" />
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER" columnName="phone number" required="yes"/>
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
<FileName filename="Viber - Call Logs.tsv" description="Viber">
|
||||
<ArtifactName artifactname="TSK_CALLLOG" comment="Viber Contacts">
|
||||
<AttributeName attributename="TSK_DATETIME_START" columnName="Call Start Time" required="yes" />
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_FROM" columnName="phone number" required="yes"/>
|
||||
<AttributeName attributename="TSK_DIRECTION" columnName="Call Direction" required="yes"/>
|
||||
<AttributeName attributename="TSK_DATETIME_END" columnName="Call End Time" required="yes"/>
|
||||
<AttributeName attributename="null" columnName="Call Type" required="no"/>
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
<FileName filename="Zapya.tsv" description="Zapya">
|
||||
<ArtifactName artifactname="TSK_MESSAGE" comment="Zapya Message">
|
||||
<AttributeName attributename="null" columnName="Device" required="no"/>
|
||||
<AttributeName attributename="null" columnName="Name" required="no"/>
|
||||
<AttributeName attributename="TSK_DIRECTION" columnName="direction" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_FROM" columnName="fromid" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_TO" columnName="toid" required="yes"/>
|
||||
<AttributeName attributename="TSK_DATETIME" columnName="createtime" required="yes" />
|
||||
<AttributeName attributename="TSK_ATTACHMENTS" columnName="path" required="yes"/>
|
||||
<AttributeName attributename="null" columnName="title" required="no"/>
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
<FileName filename="Xender file transfer - Messages.tsv" description="Xender">
|
||||
<ArtifactName artifactname="TSK_MESSAGE" comment="Xender Message">
|
||||
<AttributeName attributename="TSK_ATTACHMENTS" columnName="file_path" required="yes"/>
|
||||
<AttributeName attributename="null" columnName="file_display_name" required="no"/>
|
||||
<AttributeName attributename="null" columnName="file_size" required="no"/>
|
||||
<AttributeName attributename="TSK_DATETIME" columnName="timestamp" required="yes" />
|
||||
<AttributeName attributename="TSK_DIRECTION" columnName="direction" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_TO" columnName="to_id" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_FROM" columnName="from_id" required="yes"/>
|
||||
<AttributeName attributename="TSK_THREAD_ID" columnName="session_id" required="yes" />
|
||||
<AttributeName attributename="null" columnName="sender_name" required="no"/>
|
||||
<AttributeName attributename="null" columnName="sender_device_id" required="no"/>
|
||||
<AttributeName attributename="null" columnName="recipient_name" required="no"/>
|
||||
<AttributeName attributename="null" columnName="recipient_device_id" required="no"/>
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
<FileName filename="Whatsapp - Single Call Logs.tsv" description="Whatsapp">
|
||||
<ArtifactName artifactname="TSK_CALLLOG" comment="Whatsapp Single Call Log">
|
||||
<AttributeName attributename="TSK_DATETIME_START" columnName="start_time" required="yes" />
|
||||
<AttributeName attributename="null" columnName="call_type" required="no"/>
|
||||
<AttributeName attributename="TSK_DATETIME_END" columnName="end_time" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_FROM" columnName="num" required="yes"/>
|
||||
<AttributeName attributename="TSK_DIRECTION" columnName="call_direction" required="yes"/>
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
<FileName filename="Whatsapp - Group Call Logs.tsv" description="Whatsapp">
|
||||
<ArtifactName artifactname="TSK_CALLLOG" comment="Whatsapp Group Call Log">
|
||||
<AttributeName attributename="null" columnName="call_type" required="no"/>
|
||||
<AttributeName attributename="TSK_DATETIME_START" columnName="start_time" required="yes" />
|
||||
<AttributeName attributename="TSK_DATETIME_END" columnName="end_time" required="yes"/>
|
||||
<AttributeName attributename="TSK_DIRECTION" columnName="call_direction" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_FROM" columnName="from_id" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_TO" columnName="group_members" required="yes"/>
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
<FileName filename="Whatsapp - Contacts.tsv" description="Whatsapp">
|
||||
<ArtifactName artifactname="TSK_CONTACT" comment="Whatsapp Contacts">
|
||||
<AttributeName attributename="TSK_EMAIL" columnName="number" required="yes"/>
|
||||
<AttributeName attributename="TSK_NAME" columnName="name" required="yes" />
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
<FileName filename="Whatsapp - Messages.tsv" description="Whatsapp">
|
||||
<ArtifactName artifactname="TSK_MESSAGE" comment="Whatsapp Messages">
|
||||
<AttributeName attributename="TSK_THREAD_ID" columnName="messages_id" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_TO" columnName="recipients" required="yes"/>
|
||||
<AttributeName attributename="TSK_DIRECTION" columnName="direction" required="yes"/>
|
||||
<AttributeName attributename="TSK_TEXT" columnName="content" required="yes"/>
|
||||
<AttributeName attributename="TSK_DATETIME_START" columnName="send_timestamp" required="yes" />
|
||||
<AttributeName attributename="TSK_DATETIME_END" columnName="received_timestamp" required="yes"/>
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER_FROM" columnName="number" required="yes"/>
|
||||
<AttributeName attributename="TSK_ATTACHMENTS" columnName="name" required="yes" />
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
<FileName filename="Contacts.tsv" description="Contacts">
|
||||
<ArtifactName artifactname="TSK_CONTACT" comment="Contacts">
|
||||
<AttributeName attributename="null" columnName="mimetype" required="no" />
|
||||
<AttributeName attributename="null" columnName="data1" required="no" />
|
||||
<AttributeName attributename="TSK_NAME" columnName="display_name" required="yes" />
|
||||
<AttributeName attributename="TSK_PHONE_NUMBER" columnName="phone_number" required="yes"/>
|
||||
<AttributeName attributename="TSK_EMAIL" columnName="email address" required="yes"/>
|
||||
<AttributeName attributename="TSK_TEXT_FILE" columnName="source file" required="yes"/>
|
||||
</ArtifactName>
|
||||
</FileName>
|
||||
|
||||
</aLeap_Files_To_Process>
|
||||
|
@ -1085,7 +1085,7 @@ public class PortableCaseReportModule implements ReportModule {
|
||||
Host newHost = null;
|
||||
if (content instanceof DataSource) {
|
||||
Host oldHost = ((DataSource)content).getHost();
|
||||
newHost = portableSkCase.getHostManager().getOrCreateHost(oldHost.getName());
|
||||
newHost = portableSkCase.getHostManager().createHost(oldHost.getName());
|
||||
}
|
||||
|
||||
CaseDbTransaction trans = portableSkCase.beginTransaction();
|
||||
|
@ -34,7 +34,7 @@ public class DomainSearchTestUtils {
|
||||
long totalVisits, long visits, long filesDownloaded, long dataSourceId) {
|
||||
Content dataSource = TskMockUtils.getDataSource(dataSourceId);
|
||||
return new ResultDomain(domain, start, end, totalVisits,
|
||||
visits, filesDownloaded, 0L, dataSource);
|
||||
visits, filesDownloaded, 0L, "", dataSource);
|
||||
}
|
||||
|
||||
public static ResultDomain mockDomainResult(String domain) {
|
||||
|
@ -662,7 +662,7 @@ public final class ImageGalleryController {
|
||||
|
||||
private static ListeningExecutorService getNewDBExecutor() {
|
||||
return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(
|
||||
new ThreadFactoryBuilder().setNameFormat("DB-Worker-Thread-%d").build()));
|
||||
new ThreadFactoryBuilder().setNameFormat("ImageGallery-DB-Worker-Thread-%d").build()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,7 +148,7 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of data source display name.
|
||||
* Get a list of data source display names.
|
||||
*
|
||||
* @return The list of data source name
|
||||
*/
|
||||
|
@ -9,8 +9,20 @@ DropdownListSearchPanel.selected=Ad Hoc Search data source filter is selected
|
||||
DropdownSingleTermSearchPanel.selected=Ad Hoc Search data source filter is selected
|
||||
DropdownSingleTermSearchPanel.warning.text=Boundary characters ^ and $ do not match word boundaries. Consider\nreplacing with an explicit list of boundary characters, such as [ \\.,]
|
||||
DropdownSingleTermSearchPanel.warning.title=Warning
|
||||
ExtractAllTermsReport.description.text=Extracts all unique words out of the current case. NOTE: The extracted words are lower-cased.
|
||||
ExtractAllTermsReport.error.noOpenCase=No currently open case.
|
||||
ExtractAllTermsReport.export.error=Error During Unique Word Extraction
|
||||
ExtractAllTermsReport.exportComplete=Unique Word Extraction Complete
|
||||
ExtractAllTermsReport.getName.text=Extract Unique Words
|
||||
# {0} - Number of extracted terms
|
||||
ExtractAllTermsReport.numberExtractedTerms=Extracted {0} terms...
|
||||
ExtractAllTermsReport.search.ingestInProgressBody=<html>Keyword Search Ingest is currently running.<br />Not all files have been indexed and unique word extraction might yield incomplete results.<br />Do you want to proceed with unique word extraction anyway?</html>
|
||||
# {0} - Keyword search commit frequency
|
||||
ExtractAllTermsReport.search.noFilesInIdxMsg=No files are in index yet. Try again later. Index is updated every {0} minutes.
|
||||
ExtractAllTermsReport.search.noFilesInIdxMsg2=No files are in index yet. Try again later
|
||||
ExtractAllTermsReport.search.searchIngestInProgressTitle=Keyword Search Ingest in Progress
|
||||
ExtractAllTermsReport.startExport=Starting Unique Word Extraction
|
||||
ExtractedContentPanel.setMarkup.panelTxt=<span style='font-style:italic'>Loading text... Please wait</span>
|
||||
# {0} - Content name
|
||||
ExtractedContentPanel.SetMarkup.progress.loading=Loading text for {0}
|
||||
GlobalEditListPanel.editKeyword.title=Edit Keyword
|
||||
GlobalEditListPanel.warning.text=Boundary characters ^ and $ do not match word boundaries. Consider\nreplacing with an explicit list of boundary characters, such as [ \\.,]
|
||||
@ -218,6 +230,7 @@ Server.deleteCore.exception.msg=Failed to delete Solr colelction {0}
|
||||
Server.exceptionMessage.unableToBackupCollection=Unable to backup Solr collection
|
||||
Server.exceptionMessage.unableToCreateCollection=Unable to create Solr collection
|
||||
Server.exceptionMessage.unableToRestoreCollection=Unable to restore Solr collection
|
||||
Server.getAllTerms.error=Extraction of all unique Solr terms failed:
|
||||
Server.start.exception.cantStartSolr.msg=Could not start Solr server process
|
||||
Server.start.exception.cantStartSolr.msg2=Could not start Solr server process
|
||||
Server.isRunning.exception.errCheckSolrRunning.msg=Error checking if Solr server is running
|
||||
|
134
KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractAllTermsReport.java
Executable file
134
KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractAllTermsReport.java
Executable file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 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.keywordsearch;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.report.GeneralReportModule;
|
||||
import org.sleuthkit.autopsy.report.GeneralReportSettings;
|
||||
import org.sleuthkit.autopsy.report.ReportProgressPanel;
|
||||
|
||||
/**
|
||||
* Instances of this class plug in to the reporting infrastructure to provide a
|
||||
* convenient way to extract all unique terms from Solr index.
|
||||
*/
|
||||
@ServiceProvider(service = GeneralReportModule.class)
|
||||
public class ExtractAllTermsReport implements GeneralReportModule {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ExtractAllTermsReport.class.getName());
|
||||
private static final String OUTPUT_FILE_NAME = "Unique Words.txt";
|
||||
|
||||
@NbBundle.Messages({
|
||||
"ExtractAllTermsReport.getName.text=Extract Unique Words"})
|
||||
@Override
|
||||
public String getName() {
|
||||
return Bundle.ExtractAllTermsReport_getName_text();
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"ExtractAllTermsReport.error.noOpenCase=No currently open case.",
|
||||
"# {0} - Keyword search commit frequency",
|
||||
"ExtractAllTermsReport.search.noFilesInIdxMsg=No files are in index yet. Try again later. Index is updated every {0} minutes.",
|
||||
"ExtractAllTermsReport.search.noFilesInIdxMsg2=No files are in index yet. Try again later",
|
||||
"ExtractAllTermsReport.search.searchIngestInProgressTitle=Keyword Search Ingest in Progress",
|
||||
"ExtractAllTermsReport.search.ingestInProgressBody=<html>Keyword Search Ingest is currently running.<br />Not all files have been indexed and unique word extraction might yield incomplete results.<br />Do you want to proceed with unique word extraction anyway?</html>",
|
||||
"ExtractAllTermsReport.startExport=Starting Unique Word Extraction",
|
||||
"ExtractAllTermsReport.export.error=Error During Unique Word Extraction",
|
||||
"ExtractAllTermsReport.exportComplete=Unique Word Extraction Complete"
|
||||
})
|
||||
@Override
|
||||
public void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel) {
|
||||
|
||||
if (!Case.isCaseOpen()) {
|
||||
logger.log(Level.SEVERE, "No open case when attempting to run {0} report", Bundle.ExtractAllTermsReport_getName_text()); //NON-NLS
|
||||
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, Bundle.ExtractAllTermsReport_error_noOpenCase());
|
||||
return;
|
||||
}
|
||||
|
||||
progressPanel.setIndeterminate(true);
|
||||
progressPanel.start();
|
||||
progressPanel.updateStatusLabel("Extracting unique words...");
|
||||
|
||||
boolean isIngestRunning = IngestManager.getInstance().isIngestRunning();
|
||||
|
||||
int filesIndexed = 0;
|
||||
try { // see if there are any indexed files
|
||||
filesIndexed = KeywordSearch.getServer().queryNumIndexedFiles();
|
||||
} catch (KeywordSearchModuleException | NoOpenCoreException ignored) {
|
||||
}
|
||||
|
||||
if (filesIndexed == 0) {
|
||||
if (isIngestRunning) {
|
||||
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, Bundle.ExtractAllTermsReport_search_noFilesInIdxMsg(KeywordSearchSettings.getUpdateFrequency().getTime()));
|
||||
} else {
|
||||
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, Bundle.ExtractAllTermsReport_search_noFilesInIdxMsg2());
|
||||
}
|
||||
progressPanel.setIndeterminate(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if keyword search module ingest is running (indexing, etc)
|
||||
if (isIngestRunning) {
|
||||
if (KeywordSearchUtil.displayConfirmDialog(Bundle.ExtractAllTermsReport_search_searchIngestInProgressTitle(),
|
||||
Bundle.ExtractAllTermsReport_search_ingestInProgressBody(), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN) == false) {
|
||||
progressPanel.cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Server server = KeywordSearch.getServer();
|
||||
try {
|
||||
progressPanel.updateStatusLabel(Bundle.ExtractAllTermsReport_startExport());
|
||||
Path outputFile = Paths.get(settings.getReportDirectoryPath(), getRelativeFilePath());
|
||||
server.extractAllTermsForDataSource(outputFile, progressPanel);
|
||||
} catch (KeywordSearchModuleException | NoOpenCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while extracting unique terms", ex); //NON-NLS
|
||||
progressPanel.setIndeterminate(false);
|
||||
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, Bundle.ExtractAllTermsReport_export_error());
|
||||
return;
|
||||
}
|
||||
|
||||
progressPanel.setIndeterminate(false);
|
||||
progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE, Bundle.ExtractAllTermsReport_exportComplete());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDataSourceSelection() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"ExtractAllTermsReport.description.text=Extracts all unique words out of the current case. NOTE: The extracted words are lower-cased."})
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return Bundle.ExtractAllTermsReport_description_text();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRelativeFilePath() {
|
||||
return OUTPUT_FILE_NAME;
|
||||
}
|
||||
|
||||
}
|
@ -21,10 +21,8 @@ package org.sleuthkit.autopsy.keywordsearch;
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.appservices.AutopsyService;
|
||||
|
||||
/**
|
||||
@ -32,7 +30,6 @@ import org.sleuthkit.autopsy.appservices.AutopsyService;
|
||||
*/
|
||||
class IndexFinder {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(IndexFinder.class.getName());
|
||||
private static final String KWS_OUTPUT_FOLDER_NAME = "keywordsearch";
|
||||
private static final String KWS_DATA_FOLDER_NAME = "data";
|
||||
private static final String INDEX_FOLDER_NAME = "index";
|
||||
@ -48,7 +45,7 @@ class IndexFinder {
|
||||
return CURRENT_SOLR_SCHEMA_VERSION;
|
||||
}
|
||||
|
||||
static Index findLatestVersionIndexDir(List<Index> allIndexes) {
|
||||
static Index findLatestVersionIndex(List<Index> allIndexes) {
|
||||
for (Index index : allIndexes) {
|
||||
if (index.getSolrVersion().equals(CURRENT_SOLR_VERSION) && index.getSchemaVersion().equals(CURRENT_SOLR_SCHEMA_VERSION)) {
|
||||
return index;
|
||||
@ -57,7 +54,7 @@ class IndexFinder {
|
||||
return null;
|
||||
}
|
||||
|
||||
static Index createLatestVersionIndexDir(Case theCase) throws AutopsyService.AutopsyServiceException {
|
||||
static Index createLatestVersionIndex(Case theCase) throws AutopsyService.AutopsyServiceException {
|
||||
String indexFolderName = "solr" + CURRENT_SOLR_VERSION + "_schema" + CURRENT_SOLR_SCHEMA_VERSION;
|
||||
// new index should be stored in "\ModuleOutput\keywordsearch\data\solrX_schemaY\index"
|
||||
File targetDirPath = Paths.get(theCase.getModuleDirectory(), KWS_OUTPUT_FOLDER_NAME, KWS_DATA_FOLDER_NAME, indexFolderName, INDEX_FOLDER_NAME).toFile(); //NON-NLS
|
||||
|
@ -36,6 +36,7 @@ import java.net.ServerSocket;
|
||||
import java.net.SocketException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.OpenOption;
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@ -52,6 +53,7 @@ import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import org.apache.solr.client.solrj.SolrQuery;
|
||||
import org.apache.solr.client.solrj.SolrRequest;
|
||||
@ -66,8 +68,10 @@ import org.apache.solr.client.solrj.response.CollectionAdminResponse;
|
||||
import org.apache.solr.client.solrj.request.CoreAdminRequest;
|
||||
import org.apache.solr.client.solrj.response.CoreAdminResponse;
|
||||
import org.apache.solr.client.solrj.impl.BaseHttpSolrClient.RemoteSolrException;
|
||||
import org.apache.solr.client.solrj.request.QueryRequest;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.client.solrj.response.TermsResponse;
|
||||
import org.apache.solr.client.solrj.response.TermsResponse.Term;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.SolrException;
|
||||
@ -80,6 +84,7 @@ import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -90,6 +95,8 @@ import org.sleuthkit.autopsy.coreutils.ThreadUtils;
|
||||
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException;
|
||||
import org.sleuthkit.autopsy.report.GeneralReportSettings;
|
||||
import org.sleuthkit.autopsy.report.ReportProgressPanel;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
|
||||
/**
|
||||
@ -1785,6 +1792,34 @@ public class Server {
|
||||
currentCoreLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all unique terms/words from current index.
|
||||
*
|
||||
* @param outputFile Absolute path to the output file
|
||||
* @param progressPanel ReportProgressPanel to update
|
||||
*
|
||||
* @throws NoOpenCoreException
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"Server.getAllTerms.error=Extraction of all unique Solr terms failed:"})
|
||||
void extractAllTermsForDataSource(Path outputFile, ReportProgressPanel progressPanel) throws KeywordSearchModuleException, NoOpenCoreException {
|
||||
try {
|
||||
currentCoreLock.writeLock().lock();
|
||||
if (null == currentCollection) {
|
||||
throw new NoOpenCoreException();
|
||||
}
|
||||
try {
|
||||
currentCollection.extractAllTermsForDataSource(outputFile, progressPanel);
|
||||
} catch (Exception ex) {
|
||||
// intentional "catch all" as Solr is known to throw all kinds of Runtime exceptions
|
||||
logger.log(Level.SEVERE, "Extraction of all unique Solr terms failed: ", ex); //NON-NLS
|
||||
throw new KeywordSearchModuleException(Bundle.Server_getAllTerms_error(), ex);
|
||||
}
|
||||
} finally {
|
||||
currentCoreLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text contents of the given file as stored in SOLR.
|
||||
@ -2132,6 +2167,71 @@ public class Server {
|
||||
|
||||
queryClient.deleteByQuery(deleteQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all unique terms/words from current index. Gets 1,000 terms at a time and
|
||||
* writes them to output file. Updates ReportProgressPanel status.
|
||||
*
|
||||
* @param outputFile Absolute path to the output file
|
||||
* @param progressPanel ReportProgressPanel to update
|
||||
* @throws IOException
|
||||
* @throws SolrServerException
|
||||
* @throws NoCurrentCaseException
|
||||
* @throws KeywordSearchModuleException
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"# {0} - Number of extracted terms",
|
||||
"ExtractAllTermsReport.numberExtractedTerms=Extracted {0} terms..."
|
||||
})
|
||||
private void extractAllTermsForDataSource(Path outputFile, ReportProgressPanel progressPanel) throws IOException, SolrServerException, NoCurrentCaseException, KeywordSearchModuleException {
|
||||
|
||||
Files.deleteIfExists(outputFile);
|
||||
OpenOption[] options = new OpenOption[] { java.nio.file.StandardOpenOption.CREATE, java.nio.file.StandardOpenOption.APPEND };
|
||||
|
||||
// step through the terms
|
||||
int termStep = 1000;
|
||||
long numExtractedTerms = 0;
|
||||
String firstTerm = "";
|
||||
while (true) {
|
||||
SolrQuery query = new SolrQuery();
|
||||
query.setRequestHandler("/terms");
|
||||
query.setTerms(true);
|
||||
query.setTermsLimit(termStep);
|
||||
query.setTermsLower(firstTerm);
|
||||
query.setTermsLowerInclusive(false);
|
||||
|
||||
// Returned terms sorted by "index" order, which is the fastest way. Per Solr documentation:
|
||||
// "Retrieving terms in index order is very fast since the implementation directly uses Lucene’s TermEnum to iterate over the term dictionary."
|
||||
// All other sort criteria return very inconsistent and overlapping resuts.
|
||||
query.setTermsSortString("index");
|
||||
|
||||
// "text" field is the schema field that we populate with (lowercased) terms
|
||||
query.addTermsField(Server.Schema.TEXT.toString());
|
||||
query.setTermsMinCount(0);
|
||||
|
||||
// Unfortunatelly Solr "terms queries" do not support any filtering so we can't filter by data source this way.
|
||||
// query.addFilterQuery(Server.Schema.IMAGE_ID.toString() + ":" + dataSourceId);
|
||||
|
||||
QueryRequest request = new QueryRequest(query);
|
||||
TermsResponse response = request.process(queryClient).getTermsResponse();
|
||||
List<Term> terms = response.getTerms(Server.Schema.TEXT.toString());
|
||||
|
||||
if (terms == null || terms.isEmpty()) {
|
||||
numExtractedTerms += terms.size();
|
||||
progressPanel.updateStatusLabel(Bundle.ExtractAllTermsReport_numberExtractedTerms(numExtractedTerms));
|
||||
break;
|
||||
}
|
||||
|
||||
// set the first term for the next query
|
||||
firstTerm = terms.get(terms.size()-1).getTerm();
|
||||
|
||||
List<String> listTerms = terms.stream().map(Term::getTerm).collect(Collectors.toList());
|
||||
Files.write(outputFile, listTerms, options);
|
||||
|
||||
numExtractedTerms += termStep;
|
||||
progressPanel.updateStatusLabel(Bundle.ExtractAllTermsReport_numberExtractedTerms(numExtractedTerms));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Solr document for indexing. Documents get batched instead of
|
||||
@ -2168,7 +2268,6 @@ public class Server {
|
||||
*
|
||||
* @throws KeywordSearchModuleException
|
||||
*/
|
||||
// ELTODO DECIDE ON SYNCHRONIZATION
|
||||
private void sendBufferedDocs(List<SolrInputDocument> docBuffer) throws KeywordSearchModuleException {
|
||||
|
||||
if (docBuffer.isEmpty()) {
|
||||
|
@ -309,14 +309,14 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService {
|
||||
// new case that doesn't have an existing index. create new index folder
|
||||
progressUnitsCompleted++;
|
||||
progress.progress(Bundle.SolrSearch_creatingNewIndex_msg(), progressUnitsCompleted);
|
||||
currentVersionIndex = IndexFinder.createLatestVersionIndexDir(theCase);
|
||||
currentVersionIndex = IndexFinder.createLatestVersionIndex(theCase);
|
||||
// add current index to the list of indexes that exist for this case
|
||||
indexes.add(currentVersionIndex);
|
||||
} else {
|
||||
// check if one of the existing indexes is for latest Solr version and schema
|
||||
progressUnitsCompleted++;
|
||||
progress.progress(Bundle.SolrSearch_checkingForLatestIndex_msg(), progressUnitsCompleted);
|
||||
currentVersionIndex = IndexFinder.findLatestVersionIndexDir(indexes);
|
||||
currentVersionIndex = IndexFinder.findLatestVersionIndex(indexes);
|
||||
if (currentVersionIndex == null) {
|
||||
// found existing index(es) but none were for latest Solr version and schema version
|
||||
progressUnitsCompleted++;
|
||||
|
@ -2315,8 +2315,9 @@ class ExtractRegistry extends Extract {
|
||||
// "Default Admin User", "Custom Limited Acct"
|
||||
// and "Default Guest Acct"
|
||||
value = userInfo.get(ACCOUNT_TYPE_KEY);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
osAccount.setIsAdmin(value.toLowerCase().contains("Admin"));
|
||||
if (value != null && !value.isEmpty() && value.toLowerCase().contains("admin")) {
|
||||
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_IS_ADMIN,
|
||||
1, osAccount, host, regFile));
|
||||
}
|
||||
|
||||
value = userInfo.get(USER_COMMENT_KEY);
|
||||
|
@ -329,7 +329,8 @@ class TskDbDiff(object):
|
||||
id_legacy_artifact_types = build_id_legacy_artifact_types_table(conn.cursor(), isMultiUser)
|
||||
id_reports_table = build_id_reports_table(conn.cursor(), isMultiUser)
|
||||
id_images_table = build_id_image_names_table(conn.cursor(), isMultiUser)
|
||||
id_obj_path_table = build_id_obj_path_table(id_files_table, id_objects_table, id_artifact_types_table, id_reports_table, id_images_table)
|
||||
id_accounts_table = build_id_accounts_table(conn.cursor(), isMultiUser)
|
||||
id_obj_path_table = build_id_obj_path_table(id_files_table, id_objects_table, id_artifact_types_table, id_reports_table, id_images_table, id_accounts_table)
|
||||
|
||||
if isMultiUser: # Use PostgreSQL
|
||||
os.environ['PGPASSWORD']=pgSettings.password
|
||||
@ -352,7 +353,7 @@ class TskDbDiff(object):
|
||||
if 'INSERT INTO image_gallery_groups_seen' in dump_line:
|
||||
dump_line = ''
|
||||
continue;
|
||||
dump_line = normalize_db_entry(dump_line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table, id_reports_table, id_images_table, id_legacy_artifact_types)
|
||||
dump_line = normalize_db_entry(dump_line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table, id_reports_table, id_images_table, id_legacy_artifact_types, id_accounts_table)
|
||||
db_log.write('%s\n' % dump_line)
|
||||
dump_line = ''
|
||||
postgreSQL_db.close()
|
||||
@ -366,7 +367,7 @@ class TskDbDiff(object):
|
||||
for line in conn.iterdump():
|
||||
if 'INSERT INTO "image_gallery_groups_seen"' in line:
|
||||
continue
|
||||
line = normalize_db_entry(line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table, id_reports_table, id_images_table, id_legacy_artifact_types)
|
||||
line = normalize_db_entry(line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table, id_reports_table, id_images_table, id_legacy_artifact_types, id_accounts_table)
|
||||
db_log.write('%s\n' % line)
|
||||
# Now sort the file
|
||||
srtcmdlst = ["sort", dump_file, "-o", dump_file]
|
||||
@ -419,7 +420,7 @@ class PGSettings(object):
|
||||
return self.password
|
||||
|
||||
|
||||
def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info_table, objects_table, reports_table, images_table, artifact_table):
|
||||
def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info_table, objects_table, reports_table, images_table, artifact_table, accounts_table):
|
||||
""" Make testing more consistent and reasonable by doctoring certain db entries.
|
||||
|
||||
Args:
|
||||
@ -442,6 +443,7 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
|
||||
ig_groups_index = line.find('INSERT INTO "image_gallery_groups"') > -1 or line.find('INSERT INTO image_gallery_groups ') > -1
|
||||
ig_groups_seen_index = line.find('INSERT INTO "image_gallery_groups_seen"') > -1 or line.find('INSERT INTO image_gallery_groups_seen ') > -1
|
||||
os_account_index = line.find('INSERT INTO "tsk_os_accounts"') > -1 or line.find('INSERT INTO tsk_os_accounts') > -1
|
||||
os_account_attr_index = line.find('INSERT INTO "tsk_os_account_attributes"') > -1 or line.find('INSERT INTO tsk_os_account_attributes') > -1
|
||||
|
||||
parens = line[line.find('(') + 1 : line.rfind(')')]
|
||||
no_space_parens = parens.replace(" ", "")
|
||||
@ -569,6 +571,8 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
|
||||
parent_path = fs_info_table[parent_id]
|
||||
elif parent_id in images_table.keys():
|
||||
parent_path = images_table[parent_id]
|
||||
elif parent_id in accounts_table.keys():
|
||||
parent_path = accounts_table[parent_id]
|
||||
elif parent_id == 'NULL':
|
||||
parent_path = "NULL"
|
||||
|
||||
@ -636,7 +640,29 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
|
||||
newLine = ('INSERT INTO "tsk_event_descriptions" VALUES(' + ','.join(fields_list[1:]) + ');') # remove report_id
|
||||
return newLine
|
||||
elif os_account_index:
|
||||
newLine = ('INSERT INTO "tsk_os_accounts" VALUES(' + ','.join(fields_list[1:]) + ');') # remove id
|
||||
newLine = ('INSERT INTO "tsk_os_accounts" VALUES(' + ','.join(fields_list[1:]) + ');') # remove id since value that would be substituted is in diff line already
|
||||
return newLine
|
||||
elif os_account_attr_index:
|
||||
#substitue the account object id for a non changing value
|
||||
os_account_id = int(fields_list[1])
|
||||
fields_list[1] = accounts_table[os_account_id]
|
||||
#substitue the source object id for a non changing value
|
||||
source_obj_id = int(fields_list[3])
|
||||
if source_obj_id in files_table.keys():
|
||||
fields_list[3] = files_table[source_obj_id]
|
||||
elif source_obj_id in vs_parts_table.keys():
|
||||
fields_list[3] = vs_parts_table[source_obj_id]
|
||||
elif source_obj_id in vs_info_table.keys():
|
||||
fields_list[3] = vs_info_table[source_obj_id]
|
||||
elif source_obj_id in fs_info_table.keys():
|
||||
fields_list[3] = fs_info_table[source_obj_id]
|
||||
elif source_obj_id in images_table.keys():
|
||||
fields_list[3] = images_table[source_obj_id]
|
||||
elif source_obj_id in accounts_table.keys():
|
||||
fields_list[3] = accounts_table[source_obj_id]
|
||||
elif source_obj_id == 'NULL':
|
||||
fields_list[3] = "NULL"
|
||||
newLine = ('INSERT INTO "tsk_os_account_attributes" VALUES(' + ','.join(fields_list[1:]) + ');') # remove id
|
||||
return newLine
|
||||
else:
|
||||
return line
|
||||
@ -758,8 +784,18 @@ def build_id_reports_table(db_cursor, isPostgreSQL):
|
||||
mapping = dict([(row[0], row[1]) for row in sql_select_execute(db_cursor, isPostgreSQL, "SELECT obj_id, path FROM reports")])
|
||||
return mapping
|
||||
|
||||
def build_id_accounts_table(db_cursor, isPostgreSQL):
|
||||
"""Build the map of object ids to OS account SIDs.
|
||||
|
||||
def build_id_obj_path_table(files_table, objects_table, artifacts_table, reports_table, images_table):
|
||||
Args:
|
||||
db_cursor: the database cursor
|
||||
"""
|
||||
# for each row in the db, take the object id and account SID then creates a tuple in the dictionary
|
||||
# with the object id as the key and the OS Account's SID as the value
|
||||
mapping = dict([(row[0], row[1]) for row in sql_select_execute(db_cursor, isPostgreSQL, "SELECT os_account_obj_id, unique_id FROM tsk_os_accounts")])
|
||||
return mapping
|
||||
|
||||
def build_id_obj_path_table(files_table, objects_table, artifacts_table, reports_table, images_table, accounts_table):
|
||||
"""Build the map of object ids to artifact ids.
|
||||
|
||||
Args:
|
||||
@ -767,6 +803,8 @@ def build_id_obj_path_table(files_table, objects_table, artifacts_table, reports
|
||||
objects_table: obj_id, par_obj_id, type
|
||||
artifacts_table: obj_id, artifact_type_name
|
||||
reports_table: obj_id, path
|
||||
images_table: obj_id, name
|
||||
accounts_table: obj_id, unique_id
|
||||
"""
|
||||
# make a copy of files_table and update it with new data from artifacts_table and reports_table
|
||||
mapping = files_table.copy()
|
||||
@ -786,6 +824,8 @@ def build_id_obj_path_table(files_table, objects_table, artifacts_table, reports
|
||||
elif par_obj_id in images_table.keys():
|
||||
path = images_table[par_obj_id]
|
||||
mapping[k] = path + "/" + artifacts_table[k]
|
||||
elif k in accounts_table.keys(): # For an OS Account object ID we use its unique_id field which is the account SID
|
||||
mapping[k] = accounts_table[k]
|
||||
elif v[0] not in mapping.keys():
|
||||
if v[0] in artifacts_table.keys():
|
||||
par_obj_id = objects_table[v[0]]
|
||||
|
BIN
thirdparty/aLeapp/aleapp.exe
vendored
BIN
thirdparty/aLeapp/aleapp.exe
vendored
Binary file not shown.
@ -619,8 +619,6 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
size, cTime, crTime, aTime, mTime, true, abstractFile, "",
|
||||
EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType);
|
||||
|
||||
associateAttachmentWithMesssge(messageArtifact, df);
|
||||
|
||||
files.add(df);
|
||||
|
||||
fileAttachments.add(new FileAttachment(df));
|
||||
@ -646,19 +644,6 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TSK_ASSOCIATED_OBJECT artifact between the attachment file and
|
||||
* the message artifact.
|
||||
*/
|
||||
private BlackboardArtifact associateAttachmentWithMesssge(BlackboardArtifact message, AbstractFile attachedFile) throws TskCoreException {
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, EmailParserModuleFactory.getModuleName(), message.getArtifactID()));
|
||||
|
||||
BlackboardArtifact bba = attachedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
|
||||
bba.addAttributes(attributes); //write out to bb
|
||||
return bba;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns a set of unique email addresses found in the input
|
||||
* string
|
||||
|
Loading…
x
Reference in New Issue
Block a user