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

This commit is contained in:
Greg DiCristofaro 2021-02-16 11:09:29 -05:00
commit c32de45abb
40 changed files with 789 additions and 271 deletions

View File

@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.imagewriter.ImageWriterService;
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
import org.sleuthkit.datamodel.AddDataSourceCallbacks; import org.sleuthkit.datamodel.AddDataSourceCallbacks;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.SleuthkitJNI;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -106,7 +107,7 @@ class AddImageTask implements Runnable {
try { try {
synchronized (tskAddImageProcessLock) { synchronized (tskAddImageProcessLock) {
if (!tskAddImageProcessStopped) { if (!tskAddImageProcessStopped) {
tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(imageDetails.timeZone, true, imageDetails.ignoreFatOrphanFiles, imageWriterPath); tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(imageDetails.timeZone, true, imageDetails.ignoreFatOrphanFiles, imageDetails.host, imageWriterPath);
} else { } else {
return; return;
} }
@ -317,9 +318,10 @@ class AddImageTask implements Runnable {
String md5; String md5;
String sha1; String sha1;
String sha256; String sha256;
Host host;
ImageWriterSettings imageWriterSettings; ImageWriterSettings imageWriterSettings;
ImageDetails(String deviceId, Image image, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, ImageWriterSettings imageWriterSettings) { ImageDetails(String deviceId, Image image, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, Host host, ImageWriterSettings imageWriterSettings) {
this.deviceId = deviceId; this.deviceId = deviceId;
this.image = image; this.image = image;
this.sectorSize = sectorSize; this.sectorSize = sectorSize;
@ -328,6 +330,7 @@ class AddImageTask implements Runnable {
this.md5 = md5; this.md5 = md5;
this.sha1 = sha1; this.sha1 = sha1;
this.sha256 = sha256; this.sha256 = sha256;
this.host = host;
this.imageWriterSettings = imageWriterSettings; this.imageWriterSettings = imageWriterSettings;
} }

View File

@ -329,7 +329,6 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
final UUID dataSourceId = UUID.randomUUID(); final UUID dataSourceId = UUID.randomUUID();
newContents.clear(); newContents.clear();
cleanupTask = null; cleanupTask = null;
readyToIngest = false;
dsProcessor = dsp; dsProcessor = dsp;
// Add a cleanup task to interrupt the background process if the // Add a cleanup task to interrupt the background process if the
@ -364,6 +363,8 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
// Kick off the DSProcessor // Kick off the DSProcessor
if (dsProcessor.supportsIngestStream()) { if (dsProcessor.supportsIngestStream()) {
// Set readyToIngest to false to prevent the wizard from starting ingest a second time.
readyToIngest = false;
dsProcessor.runWithIngestStream(ingestJobSettings, getDSPProgressMonitorImpl(), cbObj); dsProcessor.runWithIngestStream(ingestJobSettings, getDSPProgressMonitorImpl(), cbObj);
} else { } else {
dsProcessor.run(getDSPProgressMonitorImpl(), cbObj); dsProcessor.run(getDSPProgressMonitorImpl(), cbObj);

View File

@ -28,6 +28,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskDataException;
@ -42,6 +43,7 @@ class AddLocalFilesTask implements Runnable {
private static final Logger LOGGER = Logger.getLogger(AddLocalFilesTask.class.getName()); private static final Logger LOGGER = Logger.getLogger(AddLocalFilesTask.class.getName());
private final String deviceId; private final String deviceId;
private final String rootVirtualDirectoryName; private final String rootVirtualDirectoryName;
private final Host host;
private final List<String> localFilePaths; private final List<String> localFilePaths;
private final DataSourceProcessorProgressMonitor progress; private final DataSourceProcessorProgressMonitor progress;
private final DataSourceProcessorCallback callback; private final DataSourceProcessorCallback callback;
@ -64,14 +66,16 @@ class AddLocalFilesTask implements Runnable {
* form: LogicalFileSet[N] * form: LogicalFileSet[N]
* @param localFilePaths A list of localFilePaths of local/logical * @param localFilePaths A list of localFilePaths of local/logical
* files and/or directories. * files and/or directories.
* @param host The host for this data source (may be null).
* @param progressMonitor Progress monitor to report progress * @param progressMonitor Progress monitor to report progress
* during processing. * during processing.
* @param callback Callback to call when processing is done. * @param callback Callback to call when processing is done.
*/ */
AddLocalFilesTask(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { AddLocalFilesTask(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
this.deviceId = deviceId; this.deviceId = deviceId;
this.rootVirtualDirectoryName = rootVirtualDirectoryName; this.rootVirtualDirectoryName = rootVirtualDirectoryName;
this.localFilePaths = localFilePaths; this.localFilePaths = localFilePaths;
this.host = host;
this.callback = callback; this.callback = callback;
this.progress = progressMonitor; this.progress = progressMonitor;
} }
@ -88,7 +92,7 @@ class AddLocalFilesTask implements Runnable {
try { try {
progress.setIndeterminate(true); progress.setIndeterminate(true);
FileManager fileManager = Case.getCurrentCaseThrows().getServices().getFileManager(); FileManager fileManager = Case.getCurrentCaseThrows().getServices().getFileManager();
LocalFilesDataSource newDataSource = fileManager.addLocalFilesDataSource(deviceId, rootVirtualDirectoryName, "", localFilePaths, new ProgressUpdater()); LocalFilesDataSource newDataSource = fileManager.addLocalFilesDataSource(deviceId, rootVirtualDirectoryName, "", host, localFilePaths, new ProgressUpdater());
newDataSources.add(newDataSource); newDataSources.add(newDataSource);
} catch (TskDataException | TskCoreException | NoCurrentCaseException ex) { } catch (TskDataException | TskCoreException | NoCurrentCaseException ex) {
errors.add(ex.getMessage()); errors.add(ex.getMessage());

View File

@ -39,6 +39,7 @@ import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestStream; import org.sleuthkit.autopsy.ingest.IngestStream;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.SleuthkitJNI;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -81,6 +82,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
private String md5; private String md5;
private String sha1; private String sha1;
private String sha256; private String sha256;
private Host host = null;
private boolean setDataSourceOptionsCalled; private boolean setDataSourceOptionsCalled;
static { static {
@ -181,11 +183,32 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
*/ */
@Override @Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { 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
* configuration panel. 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.
*
* @param host Host for this data source.
* @param progressMonitor 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 run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
ingestStream = new DefaultIngestStream(); ingestStream = new DefaultIngestStream();
readConfigSettings(); readConfigSettings();
this.host = host;
try { try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(), image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId); new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.host);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex); logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>(); final List<String> errors = new ArrayList<>();
@ -217,14 +240,47 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
@Override @Override
public void runWithIngestStream(IngestJobSettings settings, DataSourceProcessorProgressMonitor progress, public void runWithIngestStream(IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
DataSourceProcessorCallback callBack) { 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.
*
* 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.
*/
@Override
public void runWithIngestStream(Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
DataSourceProcessorCallback callBack) {
// Read the settings from the wizard // Read the settings from the wizard
readConfigSettings(); readConfigSettings();
this.host = host;
// HOSTTODO - remove once passing in a host
try {
this.host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("ImageDSProcessor Host");
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error creating/loading host", ex);
this.host = null;
}
// Set up the data source before creating the ingest stream // Set up the data source before creating the ingest stream
try { try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(), image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId); new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.host);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex); logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>(); final List<String> errors = new ArrayList<>();
@ -370,7 +426,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
return; return;
} }
AddImageTask.ImageDetails imageDetails = new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null); AddImageTask.ImageDetails imageDetails = new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, host, null);
addImageTask = new AddImageTask(imageDetails, addImageTask = new AddImageTask(imageDetails,
progressMonitor, progressMonitor,
new StreamingAddDataSourceCallbacks(ingestStream), new StreamingAddDataSourceCallbacks(ingestStream),
@ -405,6 +461,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
imagePath = null; imagePath = null;
timeZone = null; timeZone = null;
ignoreFatOrphanFiles = false; ignoreFatOrphanFiles = false;
host = null;
configPanel.reset(); configPanel.reset();
setDataSourceOptionsCalled = false; setDataSourceOptionsCalled = false;
} }
@ -428,7 +485,6 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
try { try {
// verify that the image has a file system that TSK can process // verify that the image has a file system that TSK can process
Case currentCase = Case.getCurrentCaseThrows();
if (!DataSourceUtils.imageHasFileSystem(dataSourcePath)) { if (!DataSourceUtils.imageHasFileSystem(dataSourcePath)) {
// image does not have a file system that TSK can process // image does not have a file system that TSK can process
return 0; return 0;
@ -521,6 +577,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
this.sectorSize = 0; this.sectorSize = 0;
this.timeZone = Calendar.getInstance().getTimeZone().getID(); this.timeZone = Calendar.getInstance().getTimeZone().getID();
this.ignoreFatOrphanFiles = ignoreFatOrphanFiles; this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
this.host = null;
setDataSourceOptionsCalled = true; setDataSourceOptionsCalled = true;
} }

View File

@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.SleuthkitJNI;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -56,6 +57,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
private String drivePath; private String drivePath;
private int sectorSize; private int sectorSize;
private String timeZone; private String timeZone;
private Host host;
private ImageWriterSettings imageWriterSettings; private ImageWriterSettings imageWriterSettings;
private boolean ignoreFatOrphanFiles; private boolean ignoreFatOrphanFiles;
private boolean setDataSourceOptionsCalled; private boolean setDataSourceOptionsCalled;
@ -135,6 +137,26 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
*/ */
@Override @Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { 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
* configuration panel. 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.
*
* @param host Host for this data source.
* @param progressMonitor 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 run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
if (!setDataSourceOptionsCalled) { if (!setDataSourceOptionsCalled) {
deviceId = UUID.randomUUID().toString(); deviceId = UUID.randomUUID().toString();
drivePath = configPanel.getContentPath(); drivePath = configPanel.getContentPath();
@ -148,11 +170,20 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
} }
} }
this.host = host;
// HOSTTODO - set to value from config panel
try {
this.host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LocalDiskDSProcessor Host");
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error creating/loading host", ex);
this.host = null;
}
Image image; Image image;
try { try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(), image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{drivePath}, sectorSize, new String[]{drivePath}, sectorSize,
timeZone, null, null, null, deviceId); timeZone, null, null, null, deviceId, this.host);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding local disk with path " + drivePath + " to database", ex); logger.log(Level.SEVERE, "Error adding local disk with path " + drivePath + " to database", ex);
final List<String> errors = new ArrayList<>(); final List<String> errors = new ArrayList<>();
@ -162,7 +193,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
} }
addDiskTask = new AddImageTask( 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, this.host, imageWriterSettings),
progressMonitor, progressMonitor,
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()), new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback)); new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));
@ -230,7 +261,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
return; return;
} }
addDiskTask = new AddImageTask(new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, imageWriterSettings), addDiskTask = new AddImageTask(new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, null, imageWriterSettings),
progressMonitor, progressMonitor,
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()), new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback)); new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));

View File

@ -42,6 +42,8 @@ import org.sleuthkit.autopsy.coreutils.ExecUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A local/logical files/logical evidence file(.lo1)/or directories data source * A local/logical files/logical evidence file(.lo1)/or directories data source
@ -137,7 +139,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
return configPanel.validatePanel(); return configPanel.validatePanel();
} }
/** /**
* Adds a data source to the case database using a background task in a * Adds a data source to the case database using a background task in a
* separate thread and the settings provided by the selection and * separate thread and the settings provided by the selection and
* configuration panel. Returns as soon as the background task is started. * configuration panel. Returns as soon as the background task is started.
@ -153,7 +155,36 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
*/ */
@Override @Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { 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
* configuration panel. 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.
*
* @param host Host for this data source.
* @param progressMonitor 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 run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
if (!setDataSourceOptionsCalled) { if (!setDataSourceOptionsCalled) {
// HOSTTODO - use passed in value
try {
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LocalFilesDSProcessor Host");
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error creating/loading host", ex);
host = null;
}
localFilePaths = configPanel.getContentPaths(); localFilePaths = configPanel.getContentPaths();
if (configPanel.subTypeIsLogicalEvidencePanel()) { if (configPanel.subTypeIsLogicalEvidencePanel()) {
try { try {
@ -171,7 +202,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
} }
} }
} }
run(UUID.randomUUID().toString(), configPanel.getFileSetName(), localFilePaths, progressMonitor, callback); run(UUID.randomUUID().toString(), configPanel.getFileSetName(), localFilePaths, host, progressMonitor, callback);
} }
/** /**
@ -285,6 +316,34 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
return executablePath; 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
* 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.
*
* @param deviceId An ASCII-printable identifier for the
* device associated with the data source
* that is intended to be unique across
* multiple cases (e.g., a UUID).
* @param rootVirtualDirectoryName The name to give to the virtual directory
* that will serve as the root for the
* local/logical files and/or directories
* that compose the data source. Pass the
* empty string to get a default name of the
* form: LogicalFileSet[N]
* @param localFilePaths A list of local/logical file and/or
* directory localFilePaths.
* @param host The host for this data source.
* @param progressMonitor Progress monitor for reporting progress
* during processing.
* @param callback Callback to call when processing is done.
*/
void run(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
new Thread(new AddLocalFilesTask(deviceId, rootVirtualDirectoryName, localFilePaths, host, progressMonitor, callback)).start();
}
/** /**
* Adds a data source to the case database using a background task in a * 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 * separate thread and the given settings instead of those provided by the
@ -309,7 +368,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
* @param callback Callback to call when processing is done. * @param callback Callback to call when processing is done.
*/ */
public void run(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { public void run(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
new Thread(new AddLocalFilesTask(deviceId, rootVirtualDirectoryName, localFilePaths, progressMonitor, callback)).start(); run(deviceId, rootVirtualDirectoryName, localFilePaths, null, progressMonitor, callback);
} }
/** /**

View File

@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.DerivedFile;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.LocalDirectory; import org.sleuthkit.datamodel.LocalDirectory;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
@ -490,6 +491,40 @@ public class FileManager implements Closeable {
* directory that does not exist or cannot be read. * directory that does not exist or cannot be read.
*/ */
public synchronized LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootVirtualDirectoryName, String timeZone, List<String> localFilePaths, FileAddProgressUpdater progressUpdater) throws TskCoreException, TskDataException { public synchronized LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootVirtualDirectoryName, String timeZone, List<String> localFilePaths, FileAddProgressUpdater progressUpdater) throws TskCoreException, TskDataException {
return addLocalFilesDataSource(deviceId, rootVirtualDirectoryName, timeZone, null, localFilePaths, progressUpdater);
}
/**
* Adds a set of local/logical files and/or directories to the case database
* as data source.
*
* @param deviceId An ASCII-printable identifier for the
* device associated with the data source
* that is intended to be unique across
* multiple cases (e.g., a UUID).
* @param rootVirtualDirectoryName The name to give to the virtual directory
* that will serve as the root for the
* local/logical files and/or directories
* that compose the data source. Pass the
* empty string to get a default name of the
* form: LogicalFileSet[N]
* @param timeZone The time zone used to process the data
* source, may be the empty string.
* @param host The host for this data source (may be null).
* @param localFilePaths A list of local/logical file and/or
* directory localFilePaths.
* @param progressUpdater Called after each file/directory is added
* to the case database.
*
* @return A local files data source object.
*
* @throws TskCoreException If there is a problem completing a database
* operation.
* @throws TskDataException if any of the local file paths is for a file or
* directory that does not exist or cannot be read.
*/
public synchronized LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootVirtualDirectoryName, String timeZone, Host host,
List<String> localFilePaths, FileAddProgressUpdater progressUpdater) throws TskCoreException, TskDataException {
if (null == caseDb) { if (null == caseDb) {
throw new TskCoreException("File manager has been closed"); throw new TskCoreException("File manager has been closed");
} }
@ -506,7 +541,7 @@ public class FileManager implements Closeable {
* children to the case database. * children to the case database.
*/ */
trans = caseDb.beginTransaction(); trans = caseDb.beginTransaction();
LocalFilesDataSource dataSource = caseDb.addLocalFilesDataSource(deviceId, rootDirectoryName, timeZone, trans); LocalFilesDataSource dataSource = caseDb.addLocalFilesDataSource(deviceId, rootDirectoryName, timeZone, host, trans);
List<AbstractFile> filesAdded = new ArrayList<>(); List<AbstractFile> filesAdded = new ArrayList<>();
for (java.io.File localFile : localFiles) { for (java.io.File localFile : localFiles) {
AbstractFile fileAdded = addLocalFile(trans, dataSource, localFile, TskData.EncodingType.NONE, progressUpdater); AbstractFile fileAdded = addLocalFile(trans, dataSource, localFile, TskData.EncodingType.NONE, progressUpdater);

View File

@ -52,8 +52,8 @@ class AddNewOrganizationDialog extends javax.swing.JDialog {
* Creates new form AddNewOrganizationDialog * Creates new form AddNewOrganizationDialog
*/ */
@Messages({"AddNewOrganizationDialog.addNewOrg.msg=Add New Organization"}) @Messages({"AddNewOrganizationDialog.addNewOrg.msg=Add New Organization"})
AddNewOrganizationDialog() { AddNewOrganizationDialog(javax.swing.JDialog parent) {
super((JFrame) WindowManager.getDefault().getMainWindow(), super(parent,
Bundle.AddNewOrganizationDialog_addNewOrg_msg(), Bundle.AddNewOrganizationDialog_addNewOrg_msg(),
true); // NON-NLS true); // NON-NLS
textBoxes = new ArrayList<>(); textBoxes = new ArrayList<>();
@ -67,8 +67,8 @@ class AddNewOrganizationDialog extends javax.swing.JDialog {
} }
// populates the dialog with existing case information to edit // populates the dialog with existing case information to edit
public AddNewOrganizationDialog(CentralRepoOrganization orgToEdit) { public AddNewOrganizationDialog(javax.swing.JDialog parent, CentralRepoOrganization orgToEdit) {
super((JFrame) WindowManager.getDefault().getMainWindow(), super(parent,
Bundle.AddNewOrganizationDialog_addNewOrg_msg(), Bundle.AddNewOrganizationDialog_addNewOrg_msg(),
true); // NON-NLS true); // NON-NLS
organizationToEdit = orgToEdit; organizationToEdit = orgToEdit;

View File

@ -376,7 +376,7 @@ public final class ManageOrganizationsDialog extends JDialog {
private void deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteButtonActionPerformed private void deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteButtonActionPerformed
CentralRepoOrganization orgToDelete = organizationList.getSelectedValue(); CentralRepoOrganization orgToDelete = organizationList.getSelectedValue();
if (orgToDelete != null) { if (orgToDelete != null) {
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this,
Bundle.ManageOrganizationsDialog_confirmDeletion_message(), Bundle.ManageOrganizationsDialog_confirmDeletion_message(),
Bundle.ManageOrganizationsDialog_confirmDeletion_title(), Bundle.ManageOrganizationsDialog_confirmDeletion_title(),
JOptionPane.YES_NO_OPTION)) { JOptionPane.YES_NO_OPTION)) {
@ -397,7 +397,7 @@ public final class ManageOrganizationsDialog extends JDialog {
}//GEN-LAST:event_closeButtonActionPerformed }//GEN-LAST:event_closeButtonActionPerformed
private void newButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newButtonActionPerformed private void newButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newButtonActionPerformed
AddNewOrganizationDialog dialogO = new AddNewOrganizationDialog(); AddNewOrganizationDialog dialogO = new AddNewOrganizationDialog(this);
if (dialogO.isChanged()) { if (dialogO.isChanged()) {
try { try {
newOrg = dialogO.getNewOrg(); newOrg = dialogO.getNewOrg();
@ -411,7 +411,7 @@ public final class ManageOrganizationsDialog extends JDialog {
private void editButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editButtonActionPerformed private void editButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editButtonActionPerformed
CentralRepoOrganization orgToEdit = organizationList.getSelectedValue(); CentralRepoOrganization orgToEdit = organizationList.getSelectedValue();
if (orgToEdit != null) { if (orgToEdit != null) {
AddNewOrganizationDialog dialogO = new AddNewOrganizationDialog(orgToEdit); AddNewOrganizationDialog dialogO = new AddNewOrganizationDialog(this, orgToEdit);
if (dialogO.isChanged()) { if (dialogO.isChanged()) {
try { try {
newOrg = dialogO.getNewOrg(); newOrg = dialogO.getNewOrg();

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.corecomponentinterfaces;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.datamodel.Host;
/** /**
* Interface implemented by classes that add data sources of a particular type * Interface implemented by classes that add data sources of a particular type
@ -109,6 +110,25 @@ public interface DataSourceProcessor {
*/ */
void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback); void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback 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. 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.
*
* @param host Host for the data source.
* @param progressMonitor 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.
*/
default void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
run(progressMonitor, callback);
}
/** /**
* Adds a data source to the case database using a background task in a * Adds a data source to the case database using a background task in a
* separate thread and the settings provided by the selection and * separate thread and the settings provided by the selection and
@ -132,6 +152,30 @@ public interface DataSourceProcessor {
throw new UnsupportedOperationException("Streaming ingest not supported for this data source processor"); throw new UnsupportedOperationException("Streaming ingest not supported for this data source processor");
} }
/**
* Adds a data source to the case database using a background task in a
* separate thread and the settings provided by the selection and
* configuration panel. Files found during ingest will be sent directly to
* the IngestStream provided. Returns as soon as the background task is
* started. The background task uses a callback object to signal task
* completion and return results.
*
* This method should not be called unless isPanelValid returns true, and
* should only be called for DSPs that support ingest streams. The ingest
* settings must be complete before calling this method.
*
* @param host 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.
*/
default void runWithIngestStream(Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
DataSourceProcessorCallback callBack) {
runWithIngestStream(settings, progress, callBack);
}
/** /**
* Check if this DSP supports ingest streams. * Check if this DSP supports ingest streams.
* *

View File

@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -47,6 +48,7 @@ final class AddRawImageTask implements Runnable {
private final String imageFilePath; private final String imageFilePath;
private final String timeZone; private final String timeZone;
private final long chunkSize; private final long chunkSize;
private final Host host;
private final DataSourceProcessorProgressMonitor progressMonitor; private final DataSourceProcessorProgressMonitor progressMonitor;
private final DataSourceProcessorCallback callback; private final DataSourceProcessorCallback callback;
private boolean criticalErrorOccurred; private boolean criticalErrorOccurred;
@ -68,11 +70,12 @@ final class AddRawImageTask implements Runnable {
* progressMonitor during processing. * progressMonitor during processing.
* @param callback Callback to call when processing is done. * @param callback Callback to call when processing is done.
*/ */
AddRawImageTask(String deviceId, String imageFilePath, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { AddRawImageTask(String deviceId, String imageFilePath, String timeZone, long chunkSize, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
this.deviceId = deviceId; this.deviceId = deviceId;
this.imageFilePath = imageFilePath; this.imageFilePath = imageFilePath;
this.timeZone = timeZone; this.timeZone = timeZone;
this.chunkSize = chunkSize; this.chunkSize = chunkSize;
this.host = host;
this.callback = callback; this.callback = callback;
this.progressMonitor = progressMonitor; this.progressMonitor = progressMonitor;
} }
@ -150,7 +153,7 @@ final class AddRawImageTask implements Runnable {
/* /*
* Get Image that will be added to case * Get Image that will be added to case
*/ */
Image dataSource = caseDatabase.addImageInfo(0, imageFilePaths, timeZone); //TODO: change hard coded deviceId. Image dataSource = caseDatabase.addImageInfo(0, imageFilePaths, timeZone, host); //TODO: change hard coded deviceId.
dataSources.add(dataSource); dataSources.add(dataSource);
List<TskFileRange> fileRanges = new ArrayList<>(); List<TskFileRange> fileRanges = new ArrayList<>();

View File

@ -29,10 +29,13 @@ import javax.swing.filechooser.FileFilter;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders; import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.GeneralFilter; import org.sleuthkit.autopsy.casemodule.GeneralFilter;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A Raw data source processor that implements the DataSourceProcessor service * A Raw data source processor that implements the DataSourceProcessor service
@ -135,8 +138,37 @@ public class RawDSProcessor implements DataSourceProcessor, AutoIngestDataSource
*/ */
@Override @Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { 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
* configuration panel. 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.
*
* @param host Host for the data source.
* @param progressMonitor 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 run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
configPanel.storeSettings(); configPanel.storeSettings();
run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getTimeZone(), configPanel.getChunkSize(), progressMonitor, callback);
// HOSTTODO - use passed in value
try {
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("RawDSProcessor Host");
} catch (TskCoreException ex) {
// It's not worth adding a logger for temporary code
//logger.log(Level.SEVERE, "Error creating/loading host", ex);
host = null;
}
run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getTimeZone(), configPanel.getChunkSize(), host, progressMonitor, callback);
} }
/** /**
@ -157,12 +189,13 @@ public class RawDSProcessor implements DataSourceProcessor, AutoIngestDataSource
* @param chunkSize The maximum size of each chunk of the raw * @param chunkSize The maximum size of each chunk of the raw
* data source as it is divided up into virtual * data source as it is divided up into virtual
* unallocated space files. * unallocated space files.
* @param host The host for this data source.
* @param progressMonitor Progress monitor for reporting progress * @param progressMonitor Progress monitor for reporting progress
* during processing. * during processing.
* @param callback Callback to call when processing is done. * @param callback Callback to call when processing is done.
*/ */
private void run(String deviceId, String imageFilePath, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { private void run(String deviceId, String imageFilePath, String timeZone, long chunkSize, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
AddRawImageTask addImageTask = new AddRawImageTask(deviceId, imageFilePath, timeZone, chunkSize, progressMonitor, callback); AddRawImageTask addImageTask = new AddRawImageTask(deviceId, imageFilePath, timeZone, chunkSize, host, progressMonitor, callback);
new Thread(addImageTask).start(); new Thread(addImageTask).start();
} }
@ -206,7 +239,7 @@ public class RawDSProcessor implements DataSourceProcessor, AutoIngestDataSource
@Override @Override
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) { public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
run(deviceId, dataSourcePath.toString(), Calendar.getInstance().getTimeZone().getID(), DEFAULT_CHUNK_SIZE, progressMonitor, callBack); run(deviceId, dataSourcePath.toString(), Calendar.getInstance().getTimeZone().getID(), DEFAULT_CHUNK_SIZE, null, progressMonitor, callBack);
} }
} }

View File

@ -49,6 +49,7 @@ import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Blackboard.BlackboardException; import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskDataException;
@ -193,12 +194,40 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
* in isPanelValid(). * in isPanelValid().
*/ */
@Override @Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
run(null, progressMonitor, callback);
}
/**
* Processes the XRY folder that the examiner selected. The heavy lifting is
* done off of the EDT, so this function will return while the
* path is still being processed.
*
* This function assumes the calling thread has sufficient privileges to
* read the folder and its child content, which should have been validated
* in isPanelValid().
*
* @param host Host for the data source.
* @param progressMonitor 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
@NbBundle.Messages({ @NbBundle.Messages({
"XRYDataSourceProcessor.noCurrentCase=No case is open." "XRYDataSourceProcessor.noCurrentCase=No case is open."
}) })
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
progressMonitor.setIndeterminate(true); progressMonitor.setIndeterminate(true);
// HOSTTODO - use passed in value
try {
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("XRYDSProcessor Host");
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error creating/loading host", ex);
host = null;
}
String selectedFilePath = configPanel.getSelectedFilePath(); String selectedFilePath = configPanel.getSelectedFilePath();
File selectedFile = new File(selectedFilePath); File selectedFile = new File(selectedFilePath);
Path selectedPath = selectedFile.toPath(); Path selectedPath = selectedFile.toPath();
@ -209,7 +238,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
String uniqueUUID = UUID.randomUUID().toString(); String uniqueUUID = UUID.randomUUID().toString();
//Move heavy lifting to a background task. //Move heavy lifting to a background task.
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor, swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
callback, currentCase, uniqueUUID); callback, currentCase, uniqueUUID, host);
swingWorker.execute(); swingWorker.execute();
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex); logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
@ -241,7 +270,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
Case currentCase = Case.getCurrentCaseThrows(); Case currentCase = Case.getCurrentCaseThrows();
//Move heavy lifting to a background task. //Move heavy lifting to a background task.
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor, swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
callBack, currentCase, deviceId); callBack, currentCase, deviceId, null);
swingWorker.execute(); swingWorker.execute();
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex); logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
@ -275,17 +304,19 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
private final Case currentCase; private final Case currentCase;
private final XRYFolder xryFolder; private final XRYFolder xryFolder;
private final String uniqueUUID; private final String uniqueUUID;
private final Host host;
public XRYReportProcessorSwingWorker(XRYFolder folder, public XRYReportProcessorSwingWorker(XRYFolder folder,
DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorProgressMonitor progressMonitor,
DataSourceProcessorCallback callback, DataSourceProcessorCallback callback,
Case currentCase, String uniqueUUID) { Case currentCase, String uniqueUUID, Host host) {
this.xryFolder = folder; this.xryFolder = folder;
this.progressMonitor = progressMonitor; this.progressMonitor = progressMonitor;
this.callback = callback; this.callback = callback;
this.currentCase = currentCase; this.currentCase = currentCase;
this.uniqueUUID = uniqueUUID; this.uniqueUUID = uniqueUUID;
this.host = host;
} }
@Override @Override
@ -306,6 +337,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
uniqueUUID, uniqueUUID,
"XRY Text Export", //Name "XRY Text Export", //Name
"", //Timezone "", //Timezone
host,
filePaths, filePaths,
new ProgressMonitorAdapter(progressMonitor)); new ProgressMonitorAdapter(progressMonitor));

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import java.awt.Dimension;
import java.awt.Point; import java.awt.Point;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -63,6 +64,7 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
for (int i = 0; i < tableModel.getColumnCount(); ++i) { for (int i = 0; i < tableModel.getColumnCount(); ++i) {
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer); artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
} }
setMinimumSize(new Dimension(125, 20));
artifactsTable.getRowSorter().toggleSortOrder(0); artifactsTable.getRowSorter().toggleSortOrder(0);
artifactsTable.getRowSorter().toggleSortOrder(0); artifactsTable.getRowSorter().toggleSortOrder(0);
} }

View File

@ -4,8 +4,11 @@
<NonVisualComponents> <NonVisualComponents>
<Container class="javax.swing.JSplitPane" name="mainSplitPane"> <Container class="javax.swing.JSplitPane" name="mainSplitPane">
<Properties> <Properties>
<Property name="dividerLocation" type="int" value="350"/> <Property name="dividerLocation" type="int" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Property name="resizeWeight" type="double" value="0.1"/> <Connection code="dividerLocation" type="code"/>
</Property>
<Property name="resizeWeight" type="double" value="0.2"/>
<Property name="lastDividerLocation" type="int" value="250"/>
</Properties> </Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>

View File

@ -20,8 +20,12 @@ package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
@ -41,13 +45,18 @@ final class DomainArtifactsTabPanel extends JPanel {
private final ArtifactsListPanel listPanel; private final ArtifactsListPanel listPanel;
private final BlackboardArtifact.ARTIFACT_TYPE artifactType; private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
private AbstractArtifactDetailsPanel rightPanel = null; private AbstractArtifactDetailsPanel rightPanel = null;
private int dividerLocation = 300;
private final PropertyChangeListener dividerListener;
private ArtifactRetrievalStatus status = ArtifactRetrievalStatus.UNPOPULATED; private ArtifactRetrievalStatus status = ArtifactRetrievalStatus.UNPOPULATED;
private final ListSelectionListener listener = new ListSelectionListener() { private final ListSelectionListener listener = new ListSelectionListener() {
@Override @Override
public void valueChanged(ListSelectionEvent event) { public void valueChanged(ListSelectionEvent event) {
if (!event.getValueIsAdjusting()) { if (!event.getValueIsAdjusting()) {
mainSplitPane.removePropertyChangeListener(dividerListener);
rightPanel.setArtifact(listPanel.getSelectedArtifact()); rightPanel.setArtifact(listPanel.getSelectedArtifact());
mainSplitPane.setDividerLocation(dividerLocation);
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
} }
} }
}; };
@ -60,12 +69,27 @@ final class DomainArtifactsTabPanel extends JPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainArtifactsTabPanel(BlackboardArtifact.ARTIFACT_TYPE type) { DomainArtifactsTabPanel(BlackboardArtifact.ARTIFACT_TYPE type) {
initComponents(); initComponents();
dividerListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)
&& evt.getNewValue() instanceof Integer
&& evt.getOldValue() instanceof Integer
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())) {
dividerLocation = (int) evt.getNewValue();
}
}
};
this.artifactType = type; this.artifactType = type;
listPanel = new ArtifactsListPanel(artifactType); listPanel = new ArtifactsListPanel(artifactType);
listPanel.setPreferredSize(new Dimension(100, 20));
listPanel.addMouseListener(new ArtifactMenuMouseAdapter(listPanel)); listPanel.addMouseListener(new ArtifactMenuMouseAdapter(listPanel));
mainSplitPane.setLeftComponent(listPanel); mainSplitPane.setLeftComponent(listPanel);
add(mainSplitPane); add(mainSplitPane);
setRightComponent(); setRightComponent();
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
dividerLocation = mainSplitPane.getDividerLocation();
listPanel.addSelectionListener(listener); listPanel.addSelectionListener(listener);
} }
@ -121,6 +145,7 @@ final class DomainArtifactsTabPanel extends JPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void setStatus(ArtifactRetrievalStatus status) { void setStatus(ArtifactRetrievalStatus status) {
this.status = status; this.status = status;
mainSplitPane.removePropertyChangeListener(dividerListener);
if (status == ArtifactRetrievalStatus.UNPOPULATED) { if (status == ArtifactRetrievalStatus.UNPOPULATED) {
listPanel.clearList(); listPanel.clearList();
removeAll(); removeAll();
@ -132,6 +157,8 @@ final class DomainArtifactsTabPanel extends JPanel {
removeAll(); removeAll();
add(new LoadingPanel(artifactType.getDisplayName())); add(new LoadingPanel(artifactType.getDisplayName()));
} }
mainSplitPane.setDividerLocation(dividerLocation);
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
} }
/** /**
@ -144,6 +171,7 @@ final class DomainArtifactsTabPanel extends JPanel {
void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) { void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) {
if (artifactType == artifactresultEvent.getArtifactType() && status == ArtifactRetrievalStatus.POPULATING) { if (artifactType == artifactresultEvent.getArtifactType() && status == ArtifactRetrievalStatus.POPULATING) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
mainSplitPane.removePropertyChangeListener(dividerListener);
listPanel.removeSelectionListener(listener); listPanel.removeSelectionListener(listener);
listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts()); listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts());
status = ArtifactRetrievalStatus.POPULATED; status = ArtifactRetrievalStatus.POPULATED;
@ -152,6 +180,8 @@ final class DomainArtifactsTabPanel extends JPanel {
listPanel.selectFirst(); listPanel.selectFirst();
removeAll(); removeAll();
add(mainSplitPane); add(mainSplitPane);
mainSplitPane.setDividerLocation(dividerLocation);
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
if (artifactresultEvent.shouldGrabFocus()) { if (artifactresultEvent.shouldGrabFocus()) {
focusList(); focusList();
} }
@ -188,8 +218,9 @@ final class DomainArtifactsTabPanel extends JPanel {
mainSplitPane = new javax.swing.JSplitPane(); mainSplitPane = new javax.swing.JSplitPane();
mainSplitPane.setDividerLocation(350); mainSplitPane.setDividerLocation(dividerLocation);
mainSplitPane.setResizeWeight(0.1); mainSplitPane.setResizeWeight(0.2);
mainSplitPane.setLastDividerLocation(250);
setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
setMinimumSize(new java.awt.Dimension(0, 0)); setMinimumSize(new java.awt.Dimension(0, 0));

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import java.awt.Dimension;
import java.awt.Point; import java.awt.Point;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -39,7 +40,7 @@ import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Panel to display list of artifacts types and descriptions. * Panel to display list of artifacts types and descriptions.
*/ */
class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel { final class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final TypeDescriptionTableModel tableModel; private final TypeDescriptionTableModel tableModel;
@ -61,6 +62,7 @@ class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel {
for (int i = 0; i < tableModel.getColumnCount(); ++i) { for (int i = 0; i < tableModel.getColumnCount(); ++i) {
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer); artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
} }
setMinimumSize(new Dimension(125, 20));
artifactsTable.getRowSorter().toggleSortOrder(0); artifactsTable.getRowSorter().toggleSortOrder(0);
artifactsTable.getRowSorter().toggleSortOrder(0); artifactsTable.getRowSorter().toggleSortOrder(0);
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import java.awt.Dimension;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.JPanel; import javax.swing.JPanel;
@ -33,7 +34,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
/** /**
* Panel to display list of dates and counts. * Panel to display list of dates and counts.
*/ */
class MiniTimelineDateListPanel extends JPanel { final class MiniTimelineDateListPanel extends JPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final DateCountTableModel tableModel = new DateCountTableModel(); private final DateCountTableModel tableModel = new DateCountTableModel();
@ -49,6 +50,7 @@ class MiniTimelineDateListPanel extends JPanel {
for (int i = 0; i < tableModel.getColumnCount(); ++i) { for (int i = 0; i < tableModel.getColumnCount(); ++i) {
jTable1.getColumnModel().getColumn(i).setCellRenderer(renderer); jTable1.getColumnModel().getColumn(i).setCellRenderer(renderer);
} }
setMinimumSize(new Dimension(125, 20));
jTable1.getRowSorter().toggleSortOrder(0); jTable1.getRowSorter().toggleSortOrder(0);
jTable1.getRowSorter().toggleSortOrder(0); jTable1.getRowSorter().toggleSortOrder(0);
} }

View File

@ -4,8 +4,10 @@
<NonVisualComponents> <NonVisualComponents>
<Container class="javax.swing.JSplitPane" name="mainSplitPane"> <Container class="javax.swing.JSplitPane" name="mainSplitPane">
<Properties> <Properties>
<Property name="dividerLocation" type="int" value="400"/> <Property name="dividerLocation" type="int" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Property name="resizeWeight" type="double" value="0.1"/> <Connection code="mainSplitPaneDividerLocation" type="code"/>
</Property>
<Property name="resizeWeight" type="double" value="0.2"/>
<Property name="toolTipText" type="java.lang.String" value=""/> <Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/> <Dimension value="[0, 0]"/>
@ -16,7 +18,9 @@
<SubComponents> <SubComponents>
<Container class="javax.swing.JSplitPane" name="leftSplitPane"> <Container class="javax.swing.JSplitPane" name="leftSplitPane">
<Properties> <Properties>
<Property name="dividerLocation" type="int" value="198"/> <Property name="dividerLocation" type="int" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="leftSplitPaneDividerLocation" type="code"/>
</Property>
<Property name="resizeWeight" type="double" value="0.5"/> <Property name="resizeWeight" type="double" value="0.5"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/> <Dimension value="[0, 0]"/>

View File

@ -19,6 +19,10 @@
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
@ -42,6 +46,9 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
private String selectedDomain = null; private String selectedDomain = null;
private final ListSelectionListener artifactListener; private final ListSelectionListener artifactListener;
private final ListSelectionListener dateListener; private final ListSelectionListener dateListener;
private int leftSplitPaneDividerLocation = 125;
private int mainSplitPaneDividerLocation = 300;
private final PropertyChangeListener dividerListener;
@NbBundle.Messages({"MiniTimelinePanel.loadingPanel.details=the Timeline view"}) @NbBundle.Messages({"MiniTimelinePanel.loadingPanel.details=the Timeline view"})
/** /**
@ -62,22 +69,51 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
} else { } else {
rightPanel = new GeneralPurposeArtifactViewer(); rightPanel = new GeneralPurposeArtifactViewer();
} }
leftSplitPane.removePropertyChangeListener(dividerListener);
mainSplitPane.removePropertyChangeListener(dividerListener);
mainSplitPane.setRightComponent(rightPanel.getComponent()); mainSplitPane.setRightComponent(rightPanel.getComponent());
rightPanel.setArtifact(artifact); rightPanel.setArtifact(artifact);
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
leftSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
validate(); validate();
repaint(); repaint();
} }
} }
}; };
dividerListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)
&& evt.getNewValue() instanceof Integer
&& evt.getOldValue() instanceof Integer
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())) {
if (evt.getSource().equals(leftSplitPane)) {
leftSplitPaneDividerLocation = (int) evt.getNewValue();
} else if (evt.getSource().equals(mainSplitPane)) {
mainSplitPaneDividerLocation = (int) evt.getNewValue();
}
}
}
};
dateListener = new ListSelectionListener() { dateListener = new ListSelectionListener() {
@Override @Override
public void valueChanged(ListSelectionEvent event) { public void valueChanged(ListSelectionEvent event) {
if (!event.getValueIsAdjusting()) { if (!event.getValueIsAdjusting()) {
artifactListPanel.removeSelectionListener(artifactListener); artifactListPanel.removeSelectionListener(artifactListener);
leftSplitPane.removePropertyChangeListener(dividerListener);
mainSplitPane.removePropertyChangeListener(dividerListener);
artifactListPanel.clearList(); artifactListPanel.clearList();
artifactListPanel.addArtifacts(dateListPanel.getArtifactsForSelectedDate()); artifactListPanel.addArtifacts(dateListPanel.getArtifactsForSelectedDate());
artifactListPanel.addSelectionListener(artifactListener); artifactListPanel.addSelectionListener(artifactListener);
artifactListPanel.selectFirst(); artifactListPanel.selectFirst();
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
leftSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
validate(); validate();
repaint(); repaint();
} }
@ -85,10 +121,16 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
}; };
dateListPanel.addSelectionListener(dateListener); dateListPanel.addSelectionListener(dateListener);
artifactListPanel.addSelectionListener(artifactListener); artifactListPanel.addSelectionListener(artifactListener);
dateListPanel.setPreferredSize(new Dimension(100, 20));
leftSplitPane.setLeftComponent(dateListPanel); leftSplitPane.setLeftComponent(dateListPanel);
artifactListPanel.setPreferredSize(new Dimension(100, 20));
leftSplitPane.setRightComponent(artifactListPanel); leftSplitPane.setRightComponent(artifactListPanel);
mainSplitPane.setRightComponent(rightPanel.getComponent()); mainSplitPane.setRightComponent(rightPanel.getComponent());
add(mainSplitPane); add(mainSplitPane);
leftSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
leftSplitPaneDividerLocation = leftSplitPane.getDividerLocation();
mainSplitPaneDividerLocation = mainSplitPane.getDividerLocation();
} }
/** /**
@ -145,6 +187,8 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
if (miniTimelineResultEvent.getDomain().equals(selectedDomain)) { if (miniTimelineResultEvent.getDomain().equals(selectedDomain)) {
dateListPanel.removeListSelectionListener(dateListener); dateListPanel.removeListSelectionListener(dateListener);
artifactListPanel.removeSelectionListener(artifactListener); artifactListPanel.removeSelectionListener(artifactListener);
leftSplitPane.removePropertyChangeListener(dividerListener);
mainSplitPane.removePropertyChangeListener(dividerListener);
dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList()); dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList());
status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED; status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED;
setEnabled(!dateListPanel.isEmpty()); setEnabled(!dateListPanel.isEmpty());
@ -156,6 +200,10 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
} }
removeAll(); removeAll();
add(mainSplitPane); add(mainSplitPane);
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
leftSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
revalidate(); revalidate();
repaint(); repaint();
} }
@ -174,12 +222,12 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
mainSplitPane = new javax.swing.JSplitPane(); mainSplitPane = new javax.swing.JSplitPane();
leftSplitPane = new javax.swing.JSplitPane(); leftSplitPane = new javax.swing.JSplitPane();
mainSplitPane.setDividerLocation(400); mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
mainSplitPane.setResizeWeight(0.1); mainSplitPane.setResizeWeight(0.2);
mainSplitPane.setToolTipText(""); mainSplitPane.setToolTipText("");
mainSplitPane.setMinimumSize(new java.awt.Dimension(0, 0)); mainSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
leftSplitPane.setDividerLocation(198); leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
leftSplitPane.setResizeWeight(0.5); leftSplitPane.setResizeWeight(0.5);
leftSplitPane.setMinimumSize(new java.awt.Dimension(0, 0)); leftSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
mainSplitPane.setLeftComponent(leftSplitPane); mainSplitPane.setLeftComponent(leftSplitPane);

View File

@ -48,6 +48,7 @@ import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -71,6 +72,7 @@ final class AddLogicalImageTask implements Runnable {
private final String timeZone; private final String timeZone;
private final File src; private final File src;
private final File dest; private final File dest;
private final Host host;
private final DataSourceProcessorCallback callback; private final DataSourceProcessorCallback callback;
private final DataSourceProcessorProgressMonitor progressMonitor; private final DataSourceProcessorProgressMonitor progressMonitor;
private final Blackboard blackboard; private final Blackboard blackboard;
@ -87,7 +89,7 @@ final class AddLogicalImageTask implements Runnable {
AddLogicalImageTask(String deviceId, AddLogicalImageTask(String deviceId,
String timeZone, String timeZone,
File src, File dest, File src, File dest, Host host,
DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorProgressMonitor progressMonitor,
DataSourceProcessorCallback callback DataSourceProcessorCallback callback
) throws NoCurrentCaseException { ) throws NoCurrentCaseException {
@ -95,6 +97,7 @@ final class AddLogicalImageTask implements Runnable {
this.timeZone = timeZone; this.timeZone = timeZone;
this.src = src; this.src = src;
this.dest = dest; this.dest = dest;
this.host = host;
this.progressMonitor = progressMonitor; this.progressMonitor = progressMonitor;
this.callback = callback; this.callback = callback;
this.currentCase = Case.getCurrentCase(); this.currentCase = Case.getCurrentCase();
@ -229,7 +232,7 @@ final class AddLogicalImageTask implements Runnable {
try { try {
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingExtractedFiles()); progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingExtractedFiles());
interestingFileMap = addExtractedFiles(dest, resultsPath, newDataSources); interestingFileMap = addExtractedFiles(dest, resultsPath, host, newDataSources);
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingExtractedFiles()); progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingExtractedFiles());
} catch (IOException | TskCoreException ex) { } catch (IOException | TskCoreException ex) {
errorList.add(ex.getMessage()); errorList.add(ex.getMessage());
@ -248,7 +251,7 @@ final class AddLogicalImageTask implements Runnable {
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
return; return;
} }
addMultipleImagesTask = new AddMultipleImagesTask(deviceId, imagePaths, timeZone , progressMonitor); addMultipleImagesTask = new AddMultipleImagesTask(deviceId, imagePaths, timeZone, host, progressMonitor);
} }
addMultipleImagesTask.run(); addMultipleImagesTask.run();
if (addMultipleImagesTask.getResult() == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) { if (addMultipleImagesTask.getResult() == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) {
@ -474,14 +477,14 @@ final class AddLogicalImageTask implements Runnable {
@Messages({ @Messages({
"# {0} - file number", "# {1} - total files", "AddLogicalImageTask.addingExtractedFile=Adding extracted files ({0}/{1})" "# {0} - file number", "# {1} - total files", "AddLogicalImageTask.addingExtractedFile=Adding extracted files ({0}/{1})"
}) })
private Map<String, List<Long>> addExtractedFiles(File src, Path resultsPath, List<Content> newDataSources) throws TskCoreException, IOException { private Map<String, List<Long>> addExtractedFiles(File src, Path resultsPath, Host host, List<Content> newDataSources) throws TskCoreException, IOException {
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
SleuthkitCase.CaseDbTransaction trans = null; SleuthkitCase.CaseDbTransaction trans = null;
Map<String, List<Long>> interestingFileMap = new HashMap<>(); Map<String, List<Long>> interestingFileMap = new HashMap<>();
try { try {
trans = skCase.beginTransaction(); trans = skCase.beginTransaction();
LocalFilesDataSource localFilesDataSource = skCase.addLocalFilesDataSource(deviceId, this.src.getName(), timeZone, trans); LocalFilesDataSource localFilesDataSource = skCase.addLocalFilesDataSource(deviceId, this.src.getName(), timeZone, host, trans);
LocalFileImporter fileImporter = new LocalFileImporter(skCase, trans); LocalFileImporter fileImporter = new LocalFileImporter(skCase, trans);
try (BufferedReader br = new BufferedReader(new InputStreamReader( try (BufferedReader br = new BufferedReader(new InputStreamReader(

View File

@ -30,6 +30,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DefaultAddDataSourceCallbacks; import org.sleuthkit.datamodel.DefaultAddDataSourceCallbacks;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.SleuthkitJNI;
@ -53,6 +54,7 @@ class AddMultipleImagesTask implements Runnable {
private final String deviceId; private final String deviceId;
private final List<String> imageFilePaths; private final List<String> imageFilePaths;
private final String timeZone; private final String timeZone;
private final Host host;
private final long chunkSize = TWO_GB; private final long chunkSize = TWO_GB;
private final DataSourceProcessorProgressMonitor progressMonitor; private final DataSourceProcessorProgressMonitor progressMonitor;
private final Case currentCase; private final Case currentCase;
@ -85,6 +87,7 @@ class AddMultipleImagesTask implements Runnable {
* @param timeZone The time zone to use when processing dates and * @param timeZone The time zone to use when processing dates and
* times for the image, obtained from * times for the image, obtained from
* java.util.TimeZone.getID. * java.util.TimeZone.getID.
* @param host Host for this data source (may be null).
* @param progressMonitor Progress monitor for reporting progress during * @param progressMonitor Progress monitor for reporting progress during
* processing. * processing.
* *
@ -94,11 +97,12 @@ class AddMultipleImagesTask implements Runnable {
"# {0} - file", "AddMultipleImagesTask.addingFileAsLogicalFile=Adding: {0} as an unallocated space file.", "# {0} - file", "AddMultipleImagesTask.addingFileAsLogicalFile=Adding: {0} as an unallocated space file.",
"# {0} - deviceId", "# {1} - exceptionMessage", "# {0} - deviceId", "# {1} - exceptionMessage",
"AddMultipleImagesTask.errorAddingImgWithoutFileSystem=Error adding images without file systems for device {0}: {1}",}) "AddMultipleImagesTask.errorAddingImgWithoutFileSystem=Error adding images without file systems for device {0}: {1}",})
AddMultipleImagesTask(String deviceId, List<String> imageFilePaths, String timeZone, AddMultipleImagesTask(String deviceId, List<String> imageFilePaths, String timeZone, Host host,
DataSourceProcessorProgressMonitor progressMonitor) throws NoCurrentCaseException { DataSourceProcessorProgressMonitor progressMonitor) throws NoCurrentCaseException {
this.deviceId = deviceId; this.deviceId = deviceId;
this.imageFilePaths = imageFilePaths; this.imageFilePaths = imageFilePaths;
this.timeZone = timeZone; this.timeZone = timeZone;
this.host = host;
this.progressMonitor = progressMonitor; this.progressMonitor = progressMonitor;
currentCase = Case.getCurrentCaseThrows(); currentCase = Case.getCurrentCaseThrows();
this.criticalErrorOccurred = false; this.criticalErrorOccurred = false;
@ -124,7 +128,7 @@ class AddMultipleImagesTask implements Runnable {
for (String imageFilePath : imageFilePaths) { for (String imageFilePath : imageFilePaths) {
try { try {
currentImage = SleuthkitJNI.addImageToDatabase(currentCase.getSleuthkitCase(), new String[]{imageFilePath}, currentImage = SleuthkitJNI.addImageToDatabase(currentCase.getSleuthkitCase(), new String[]{imageFilePath},
0, timeZone, "", "", "", deviceId); 0, timeZone, "", "", "", deviceId, host);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error adding image " + imageFilePath + " to database", ex); LOGGER.log(Level.SEVERE, "Error adding image " + imageFilePath + " to database", ex);
errorMessages.add(Bundle.AddMultipleImagesTask_imageError(imageFilePath)); errorMessages.add(Bundle.AddMultipleImagesTask_imageError(imageFilePath));
@ -134,7 +138,7 @@ class AddMultipleImagesTask implements Runnable {
synchronized (tskAddImageProcessLock) { synchronized (tskAddImageProcessLock) {
if (!tskAddImageProcessStopped) { if (!tskAddImageProcessStopped) {
addImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, false, false, ""); addImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, false, false, host, "");
} else { } else {
return; return;
} }

View File

@ -38,6 +38,8 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.coreutils.TimeStampUtils; import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A Logical Imager data source processor that implements the * A Logical Imager data source processor that implements the
@ -128,6 +130,26 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
* @param callback Callback that will be used by the background task * @param callback Callback that will be used by the background task
* to return results. * to return results.
*/ */
@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
* configuration panel. 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.
*
* @param host Host for the data source.
* @param progressMonitor 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.
*/
@Messages({ @Messages({
"# {0} - imageDirPath", "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.", "# {0} - imageDirPath", "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.",
"# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}", "# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}",
@ -137,9 +159,18 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
"LogicalImagerDSProcessor.noCurrentCase=No current case", "LogicalImagerDSProcessor.noCurrentCase=No current case",
}) })
@Override @Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
configPanel.storeSettings(); configPanel.storeSettings();
// HOSTTODO - set to value from config panel
try {
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LogicalImagerDSProcessor Host");
} catch (TskCoreException ex) {
// It's not worth adding a logger for temporary code
//logger.log(Level.SEVERE, "Error creating/loading host", ex);
host = null;
}
Path imageDirPath = configPanel.getImageDirPath(); Path imageDirPath = configPanel.getImageDirPath();
List<String> errorList = new ArrayList<>(); List<String> errorList = new ArrayList<>();
List<Content> emptyDataSources = new ArrayList<>(); List<Content> emptyDataSources = new ArrayList<>();
@ -187,7 +218,7 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
try { try {
String deviceId = UUID.randomUUID().toString(); String deviceId = UUID.randomUUID().toString();
String timeZone = Calendar.getInstance().getTimeZone().getID(); String timeZone = Calendar.getInstance().getTimeZone().getID();
run(deviceId, timeZone, src, dest, run(deviceId, timeZone, src, dest, host,
progressMonitor, callback); progressMonitor, callback);
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
String msg = Bundle.LogicalImagerDSProcessor_noCurrentCase(); String msg = Bundle.LogicalImagerDSProcessor_noCurrentCase();
@ -211,15 +242,16 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
* java.util.TimeZone.getID. * java.util.TimeZone.getID.
* @param src The source directory of image. * @param src The source directory of image.
* @param dest The destination directory to copy the source. * @param dest The destination directory to copy the source.
* @param host The host for this data source.
* @param progressMonitor Progress monitor for reporting progress during * @param progressMonitor Progress monitor for reporting progress during
* processing. * processing.
* @param callback Callback to call when processing is done. * @param callback Callback to call when processing is done.
*/ */
private void run(String deviceId, String timeZone, private void run(String deviceId, String timeZone,
File src, File dest, File src, File dest, Host host,
DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback
) throws NoCurrentCaseException { ) throws NoCurrentCaseException {
addLogicalImageTask = new AddLogicalImageTask(deviceId, timeZone, src, dest, addLogicalImageTask = new AddLogicalImageTask(deviceId, timeZone, src, dest, host,
progressMonitor, callback); progressMonitor, callback);
Thread thread = new Thread(addLogicalImageTask); Thread thread = new Thread(addLogicalImageTask);
thread.start(); thread.start();

View File

@ -71,6 +71,7 @@ import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.FileSystem; import org.sleuthkit.datamodel.FileSystem;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.Pool; import org.sleuthkit.datamodel.Pool;
@ -1079,12 +1080,20 @@ public class PortableCaseReportModule implements ReportModule {
BlackboardArtifact artifactToCopy = (BlackboardArtifact) content; BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
newContent = copyArtifact(parentId, artifactToCopy); newContent = copyArtifact(parentId, artifactToCopy);
} else { } else {
// Get or create the host (if needed) before beginning transaction.
Host newHost = null;
if (content instanceof DataSource) {
Host oldHost = ((DataSource)content).getHost();
newHost = portableSkCase.getHostManager().getOrCreateHost(oldHost.getName());
}
CaseDbTransaction trans = portableSkCase.beginTransaction(); CaseDbTransaction trans = portableSkCase.beginTransaction();
try { try {
if (content instanceof Image) { if (content instanceof Image) {
Image image = (Image) content; Image image = (Image) content;
newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(), newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
new ArrayList<>(), image.getTimeZone(), image.getMd5(), image.getSha1(), image.getSha256(), image.getDeviceId(), trans); new ArrayList<>(), image.getTimeZone(), image.getMd5(), image.getSha1(), image.getSha256(), image.getDeviceId(), newHost, trans);
} else if (content instanceof VolumeSystem) { } else if (content instanceof VolumeSystem) {
VolumeSystem vs = (VolumeSystem) content; VolumeSystem vs = (VolumeSystem) content;
newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans); newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
@ -1108,7 +1117,7 @@ public class PortableCaseReportModule implements ReportModule {
if (abstractFile instanceof LocalFilesDataSource) { if (abstractFile instanceof LocalFilesDataSource) {
LocalFilesDataSource localFilesDS = (LocalFilesDataSource) abstractFile; LocalFilesDataSource localFilesDS = (LocalFilesDataSource) abstractFile;
newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), trans); newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), newHost, trans);
} else { } else {
if (abstractFile.isDir()) { if (abstractFile.isDir()) {
newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans); newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);

View File

@ -14,7 +14,7 @@
<!-- for viewers --> <!-- for viewers -->
<dependency conf="autopsy_core->*" org="org.freedesktop.gstreamer" name="gst1-java-core" rev="1.0.0"/> <dependency conf="autopsy_core->*" org="org.freedesktop.gstreamer" name="gst1-java-core" rev="1.0.0"/>
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna-platform" rev="5.6.0"/> <dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna-platform" rev="5.7.0"/>
<!-- for file search --> <!-- for file search -->
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/> <dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>

View File

@ -42,8 +42,8 @@ file.reference.javassist-3.12.1.GA.jar=release/modules/ext/javassist-3.12.1.GA.j
file.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-r4.jar file.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-r4.jar
file.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4.jar file.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4.jar
file.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4.jar file.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4.jar
file.reference.jna-5.6.0.jar=release/modules/ext/jna-5.6.0.jar file.reference.jna-5.7.0.jar=release/modules/ext/jna-5.7.0.jar
file.reference.jna-platform-5.6.0.jar=release/modules/ext/jna-platform-5.6.0.jar file.reference.jna-platform-5.7.0.jar=release/modules/ext/jna-platform-5.7.0.jar
file.reference.joda-time-2.4.jar=release/modules/ext/joda-time-2.4.jar file.reference.joda-time-2.4.jar=release/modules/ext/joda-time-2.4.jar
file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar
file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1.jar file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1.jar

View File

@ -923,8 +923,8 @@
<binary-origin>release/modules/ext/commons-compress-1.18.jar</binary-origin> <binary-origin>release/modules/ext/commons-compress-1.18.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/jna-platform-5.6.0.jar</runtime-relative-path> <runtime-relative-path>ext/jna-platform-5.7.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jna-platform-5.6.0.jar</binary-origin> <binary-origin>release\modules\ext\jna-platform-5.7.0.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/opencv-248.jar</runtime-relative-path> <runtime-relative-path>ext/opencv-248.jar</runtime-relative-path>
@ -951,8 +951,8 @@
<binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin> <binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/jna-5.6.0.jar</runtime-relative-path> <runtime-relative-path>ext/jna-5.7.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jna-5.6.0.jar</binary-origin> <binary-origin>release\modules\ext\jna-5.7.0.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path> <runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path>

View File

@ -34,6 +34,7 @@ import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Host;
/* /*
* A runnable that adds a memory image data source to a case database. * A runnable that adds a memory image data source to a case database.
@ -44,6 +45,7 @@ final class AddMemoryImageTask implements Runnable {
private final String deviceId; private final String deviceId;
private final String memoryImagePath; private final String memoryImagePath;
private final String timeZone; private final String timeZone;
private final Host host;
private final List<String> pluginsToRun; private final List<String> pluginsToRun;
private final DataSourceProcessorProgressMonitor progressMonitor; private final DataSourceProcessorProgressMonitor progressMonitor;
private final DataSourceProcessorCallback callback; private final DataSourceProcessorCallback callback;
@ -63,16 +65,18 @@ final class AddMemoryImageTask implements Runnable {
* @param timeZone The time zone to use when processing dates and * @param timeZone The time zone to use when processing dates and
* times for the image, obtained from * times for the image, obtained from
* java.util.TimeZone.getID. * java.util.TimeZone.getID.
* @param host The host for this data source (may be null).
* @param progressMonitor Progress monitor for reporting progressMonitor * @param progressMonitor Progress monitor for reporting progressMonitor
* during processing. * during processing.
* @param callback Callback to call when processing is done. * @param callback Callback to call when processing is done.
*/ */
AddMemoryImageTask(String deviceId, String memoryImagePath, String profile, List<String> pluginsToRun, String timeZone, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { AddMemoryImageTask(String deviceId, String memoryImagePath, String profile, List<String> pluginsToRun, String timeZone, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
this.deviceId = deviceId; this.deviceId = deviceId;
this.memoryImagePath = memoryImagePath; this.memoryImagePath = memoryImagePath;
this.profile = profile; this.profile = profile;
this.pluginsToRun = pluginsToRun; this.pluginsToRun = pluginsToRun;
this.timeZone = timeZone; this.timeZone = timeZone;
this.host = host;
this.callback = callback; this.callback = callback;
this.progressMonitor = progressMonitor; this.progressMonitor = progressMonitor;
} }
@ -128,7 +132,7 @@ final class AddMemoryImageTask implements Runnable {
/** /**
* Attempts to add the input memory image to the case as a data source. * Attempts to add the input memory image to the case as a data source.
* *
* @return The Image object representation of the memeory image file data * @return The Image object representation of the memory image file data
* source. * source.
* *
* @throws NoCurrentCaseException If there is no current case. * @throws NoCurrentCaseException If there is no current case.
@ -163,7 +167,7 @@ final class AddMemoryImageTask implements Runnable {
* will need to be changed when a Device abstraction is added to the * will need to be changed when a Device abstraction is added to the
* SleuthKit data model. * SleuthKit data model.
*/ */
Image dataSource = caseDatabase.addImageInfo(0, new ArrayList<>(Arrays.asList(memoryImagePath)), timeZone); Image dataSource = caseDatabase.addImageInfo(0, new ArrayList<>(Arrays.asList(memoryImagePath)), timeZone, host);
return dataSource; return dataSource;
} }

View File

@ -23,9 +23,12 @@ import java.util.List;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A memory image data source processor that implements the DataSourceProcessor * A memory image data source processor that implements the DataSourceProcessor
@ -116,8 +119,37 @@ public class MemoryDSProcessor implements DataSourceProcessor {
*/ */
@Override @Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { 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
* configuration panel. 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.
*
* @param host Host for the data source.
* @param progressMonitor 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 run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
configPanel.storeSettings(); configPanel.storeSettings();
run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getProfile(), configPanel.getPluginsToRun(), configPanel.getTimeZone(), progressMonitor, callback);
// HOSTTODO - replace with a call to configPanel().getHost()
try {
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("MemoryDSProcessor Host");
} catch (TskCoreException ex) {
// It's not worth adding a logger for temporary code
//logger.log(Level.SEVERE, "Error creating/loading host", ex);
host = null;
}
run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getProfile(), configPanel.getPluginsToRun(), configPanel.getTimeZone(), host, progressMonitor, callback);
} }
/** /**
@ -136,12 +168,13 @@ public class MemoryDSProcessor implements DataSourceProcessor {
* @param timeZone The time zone to use when processing dates and * @param timeZone The time zone to use when processing dates and
* times for the image, obtained from * times for the image, obtained from
* java.util.TimeZone.getID. * java.util.TimeZone.getID.
* @param host The host for this data source (may be null)
* @param progressMonitor Progress monitor for reporting progress during * @param progressMonitor Progress monitor for reporting progress during
* processing. * processing.
* @param callback Callback to call when processing is done. * @param callback Callback to call when processing is done.
*/ */
private void run(String deviceId, String memoryImagePath, String profile, List<String> pluginsToRun, String timeZone, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { private void run(String deviceId, String memoryImagePath, String profile, List<String> pluginsToRun, String timeZone, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
addImageTask = new AddMemoryImageTask(deviceId, memoryImagePath, profile, pluginsToRun, timeZone, progressMonitor, callback); addImageTask = new AddMemoryImageTask(deviceId, memoryImagePath, profile, pluginsToRun, timeZone, host, progressMonitor, callback);
new Thread(addImageTask).start(); new Thread(addImageTask).start();
} }

View File

@ -267,7 +267,7 @@ class ExtractIE extends Extract {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
RecentActivityExtracterModuleFactory.getModuleName(), url)); RecentActivityExtracterModuleFactory.getModuleName(), url));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
RecentActivityExtracterModuleFactory.getModuleName(), datetime)); RecentActivityExtracterModuleFactory.getModuleName(), datetime));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
RecentActivityExtracterModuleFactory.getModuleName(), (name != null) ? name : "")); RecentActivityExtracterModuleFactory.getModuleName(), (name != null) ? name : ""));

View File

@ -469,9 +469,9 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
# Ignore TIFF size and hash if extracted from PDFs. # Ignore TIFF size and hash if extracted from PDFs.
# See JIRA-6951 for more details. # See JIRA-6951 for more details.
# index -1 = last element in the list, which is extension # index -3 = 3rd from the end, which is extension
# index -3 = 3rd from the end, which is the parent path. # index -5 = 5th from the end, which is the parent path.
if fields_list[-1] == "'tif'" and fields_list[-3].endswith(".pdf/'"): if fields_list[-3] == "'tif'" and fields_list[-5].endswith(".pdf/'"):
fields_list[15] = "'SIZE_IGNORED'" fields_list[15] = "'SIZE_IGNORED'"
fields_list[23] = "'MD5_IGNORED'" fields_list[23] = "'MD5_IGNORED'"
fields_list[24] = "'SHA256_IGNORED'" fields_list[24] = "'SHA256_IGNORED'"

Binary file not shown.

View File

@ -19,7 +19,7 @@
<target name="get-thirdparty-jars" description="get third-party jar dependencies"> <target name="get-thirdparty-jars" description="get third-party jar dependencies">
<mkdir dir="${ext.dir}"/> <mkdir dir="${ext.dir}"/>
<copy file="${thirdparty.dir}/java-libpst/java-libpst-1.0-SNAPSHOT.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/java-libpst/java-libpst-0.9.5-SNAPSHOT.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-core-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-core-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-dom-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-dom-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" /> <copy file="${thirdparty.dir}/apache-mime4j/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar" todir="${ext.dir}" />

View File

@ -7,14 +7,16 @@ file.reference.apache-mime4j-dom-0.8.0.jar=release/modules/ext/apache-mime4j-dom
file.reference.apache-mime4j-mbox-iterator-0.8.0.jar=release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar file.reference.apache-mime4j-mbox-iterator-0.8.0.jar=release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar
file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar
file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar
file.reference.java-libpst-1.0-SNAPSHOT.jar=release/modules/ext/java-libpst-1.0-SNAPSHOT.jar
file.reference.ez-vcard-0.10.5.jar=release/modules/ext/ez-vcard-0.10.5.jar file.reference.ez-vcard-0.10.5.jar=release/modules/ext/ez-vcard-0.10.5.jar
file.reference.java-libpst-0.9.5-SNAPSHOT.jar=release/modules/ext/java-libpst-0.9.5-SNAPSHOT.jar
file.reference.vinnie-2.0.2.jar=release/modules/ext/vinnie-2.0.2.jar file.reference.vinnie-2.0.2.jar=release/modules/ext/vinnie-2.0.2.jar
javac.source=1.8 javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial javac.compilerargs=-Xlint -Xlint:-serial
javadoc.reference.guava-19.0.jar=release/modules/ext/guava-19.0-javadoc.jar javadoc.reference.guava-19.0.jar=release/modules/ext/guava-19.0-javadoc.jar
javadoc.reference.java-libpst-0.9.5-SNAPSHOT.jar=release/modules/ext/java-libpst-0.9.5-SNAPSHOT.jar
license.file=../LICENSE-2.0.txt license.file=../LICENSE-2.0.txt
nbm.homepage=http://www.sleuthkit.org/autopsy/ nbm.homepage=http://www.sleuthkit.org/autopsy/
nbm.needs.restart=true nbm.needs.restart=true
source.reference.guava-19.0.jar=release/modules/ext/guava-19.0-sources.jar source.reference.guava-19.0.jar=release/modules/ext/guava-19.0-sources.jar
source.reference.java-libpst-0.9.5-SNAPSHOT.jar=release/modules/ext/java-libpst-0.9.5-SNAPSHOT.jar
spec.version.base=4.0 spec.version.base=4.0

View File

@ -84,14 +84,14 @@
<runtime-relative-path>ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar</runtime-relative-path> <runtime-relative-path>ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar</runtime-relative-path>
<binary-origin>release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar</binary-origin> <binary-origin>release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/java-libpst-1.0-SNAPSHOT.jar</runtime-relative-path>
<binary-origin>release/modules/ext/java-libpst-1.0-SNAPSHOT.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/guava-19.0.jar</runtime-relative-path> <runtime-relative-path>ext/guava-19.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/guava-19.0.jar</binary-origin> <binary-origin>release/modules/ext/guava-19.0.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/java-libpst-0.9.5-SNAPSHOT.jar</runtime-relative-path>
<binary-origin>release/modules/ext/java-libpst-0.9.5-SNAPSHOT.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar</runtime-relative-path> <runtime-relative-path>ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar</runtime-relative-path>
<binary-origin>release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar</binary-origin> <binary-origin>release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar</binary-origin>

View File

@ -43,7 +43,7 @@ class EmailMessage {
private String localPath = ""; private String localPath = "";
private boolean hasAttachment = false; private boolean hasAttachment = false;
private long sentDate = 0L; private long sentDate = 0L;
private List<Attachment> attachments = new ArrayList<>(); private final List<Attachment> attachments = new ArrayList<>();
private long id = -1L; private long id = -1L;
private String messageID = ""; private String messageID = "";
private String inReplyToID = ""; private String inReplyToID = "";
@ -410,4 +410,16 @@ class EmailMessage {
} }
} }
static class AttachedEmailMessage extends Attachment {
private final EmailMessage emailMessage;
AttachedEmailMessage(EmailMessage emailMessage) {
this.emailMessage = emailMessage;
}
EmailMessage getEmailMessage() {
return emailMessage;
}
}
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -26,12 +26,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.james.mime4j.dom.BinaryBody;
import org.apache.james.mime4j.dom.Body; import org.apache.james.mime4j.dom.Body;
import org.apache.james.mime4j.dom.Entity; import org.apache.james.mime4j.dom.Entity;
import org.apache.james.mime4j.dom.Message; import org.apache.james.mime4j.dom.Message;
import org.apache.james.mime4j.dom.MessageWriter;
import org.apache.james.mime4j.dom.Multipart; import org.apache.james.mime4j.dom.Multipart;
import org.apache.james.mime4j.dom.SingleBody;
import org.apache.james.mime4j.dom.TextBody; import org.apache.james.mime4j.dom.TextBody;
import org.apache.james.mime4j.dom.address.AddressList; import org.apache.james.mime4j.dom.address.AddressList;
import org.apache.james.mime4j.dom.address.Mailbox; import org.apache.james.mime4j.dom.address.Mailbox;
@ -39,6 +38,7 @@ import org.apache.james.mime4j.dom.address.MailboxList;
import org.apache.james.mime4j.dom.field.ContentDispositionField; import org.apache.james.mime4j.dom.field.ContentDispositionField;
import org.apache.james.mime4j.dom.field.ContentTypeField; import org.apache.james.mime4j.dom.field.ContentTypeField;
import org.apache.james.mime4j.message.DefaultMessageBuilder; import org.apache.james.mime4j.message.DefaultMessageBuilder;
import org.apache.james.mime4j.message.DefaultMessageWriter;
import org.apache.james.mime4j.stream.Field; import org.apache.james.mime4j.stream.Field;
import org.apache.james.mime4j.stream.MimeConfig; import org.apache.james.mime4j.stream.MimeConfig;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -293,7 +293,7 @@ class MimeJ4MessageParser implements AutoCloseable{
* @param e * @param e
*/ */
@NbBundle.Messages({"MimeJ4MessageParser.handleAttch.noOpenCase.errMsg=Exception while getting open case."}) @NbBundle.Messages({"MimeJ4MessageParser.handleAttch.noOpenCase.errMsg=Exception while getting open case."})
private static void handleAttachment(EmailMessage email, Entity e, long fileID, int index) { private void handleAttachment(EmailMessage email, Entity e, long fileID, int index) {
String outputDirPath; String outputDirPath;
String relModuleOutputPath; String relModuleOutputPath;
try { try {
@ -322,25 +322,31 @@ class MimeJ4MessageParser implements AutoCloseable{
String outPath = outputDirPath + uniqueFilename; String outPath = outputDirPath + uniqueFilename;
Body body = e.getBody(); Body body = e.getBody();
if (body instanceof SingleBody) { if (body != null) {
long fileLength; long fileLength;
try (EncodedFileOutputStream fos = new EncodedFileOutputStream(new FileOutputStream(outPath), TskData.EncodingType.XOR1)) { try (EncodedFileOutputStream fos = new EncodedFileOutputStream(new FileOutputStream(outPath), TskData.EncodingType.XOR1)) {
((SingleBody) body).writeTo(fos);
EmailMessage.Attachment attach;
MessageWriter msgWriter = new DefaultMessageWriter();
if(body instanceof Message) {
msgWriter.writeMessage((Message)body, fos);
attach = new EmailMessage.AttachedEmailMessage(extractEmail((Message)body, email.getLocalPath(), fileID));
} else {
msgWriter.writeBody(body, fos);
attach = new EmailMessage.Attachment();
}
fileLength = fos.getBytesWritten(); fileLength = fos.getBytesWritten();
attach.setName(filename);
attach.setLocalPath(relModuleOutputPath + uniqueFilename);
attach.setSize(fileLength);
attach.setEncodingType(TskData.EncodingType.XOR1);
email.addAttachment(attach);
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Failed to create file output stream for: " + outPath, ex); //NON-NLS logger.log(Level.WARNING, "Failed to create file output stream for: " + outPath, ex); //NON-NLS
return;
} }
EmailMessage.Attachment attach = new EmailMessage.Attachment();
attach.setName(filename);
attach.setLocalPath(relModuleOutputPath + uniqueFilename);
attach.setSize(fileLength);
attach.setEncodingType(TskData.EncodingType.XOR1);
email.addAttachment(attach);
} }
} }
/** /**

View File

@ -277,16 +277,30 @@ class PstParser implements AutoCloseable{
*/ */
private EmailMessage extractEmailMessage(PSTMessage msg, String localPath, long fileID) { private EmailMessage extractEmailMessage(PSTMessage msg, String localPath, long fileID) {
EmailMessage email = new EmailMessage(); EmailMessage email = new EmailMessage();
email.setRecipients(msg.getDisplayTo()); String toAddress = msg.getDisplayTo();
email.setCc(msg.getDisplayCC()); String ccAddress = msg.getDisplayCC();
email.setBcc(msg.getDisplayBCC()); String bccAddress = msg.getDisplayBCC();
email.setSender(getSender(msg.getSenderName(), msg.getSenderEmailAddress())); String receivedByName = msg.getReceivedByName();
String receivedBySMTPAddress = msg.getReceivedBySMTPAddress();
if (toAddress.contains(receivedByName)) {
toAddress = toAddress.replace(receivedByName, receivedBySMTPAddress);
}
if (ccAddress.contains(receivedByName)) {
ccAddress = ccAddress.replace(receivedByName, receivedBySMTPAddress);
}
if (bccAddress.contains(receivedByName)) {
bccAddress = bccAddress.replace(receivedByName, receivedBySMTPAddress);
}
email.setRecipients(toAddress);
email.setCc(ccAddress);
email.setBcc(bccAddress);
email.setSender(getSender(msg.getSenderName(), msg.getSentRepresentingSMTPAddress()));
email.setSentDate(msg.getMessageDeliveryTime()); email.setSentDate(msg.getMessageDeliveryTime());
email.setTextBody(msg.getBody()); email.setTextBody(msg.getBody());
if (false == msg.getTransportMessageHeaders().isEmpty()) { if (false == msg.getTransportMessageHeaders().isEmpty()) {
email.setHeaders("\n-----HEADERS-----\n\n" + msg.getTransportMessageHeaders() + "\n\n---END HEADERS--\n\n"); email.setHeaders("\n-----HEADERS-----\n\n" + msg.getTransportMessageHeaders() + "\n\n---END HEADERS--\n\n");
} }
email.setHtmlBody(msg.getBodyHTML()); email.setHtmlBody(msg.getBodyHTML());
String rtf = ""; String rtf = "";
try { try {

View File

@ -48,6 +48,7 @@ import org.sleuthkit.autopsy.ingest.IngestModule.ProcessResult;
import org.sleuthkit.autopsy.ingest.IngestMonitor; import org.sleuthkit.autopsy.ingest.IngestMonitor;
import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.autopsy.thunderbirdparser.EmailMessage.AttachedEmailMessage;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.AccountFileInstance; import org.sleuthkit.datamodel.AccountFileInstance;
@ -72,6 +73,7 @@ import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.Fil
* structure and metadata. * structure and metadata.
*/ */
public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
private static final Logger logger = Logger.getLogger(ThunderbirdMboxFileIngestModule.class.getName()); private static final Logger logger = Logger.getLogger(ThunderbirdMboxFileIngestModule.class.getName());
private final IngestServices services = IngestServices.getInstance(); private final IngestServices services = IngestServices.getInstance();
private FileManager fileManager; private FileManager fileManager;
@ -89,7 +91,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} }
@Override @Override
@Messages ({"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."}) @Messages({"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
public void startUp(IngestJobContext context) throws IngestModuleException { public void startUp(IngestJobContext context) throws IngestModuleException {
this.context = context; this.context = context;
try { try {
@ -112,8 +114,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} }
//skip unalloc //skip unalloc
if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) || if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS))
(abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) { || (abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
return ProcessResult.OK; return ProcessResult.OK;
} }
@ -145,7 +147,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
return ProcessResult.OK; return ProcessResult.OK;
} }
if (isMbox || isEMLFile || isPstFile || isVcardFile ) { if (isMbox || isEMLFile || isPstFile || isVcardFile) {
try { try {
communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(), communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(),
EmailParserModuleFactory.getModuleName(), abstractFile, Account.Type.EMAIL); EmailParserModuleFactory.getModuleName(), abstractFile, Account.Type.EMAIL);
@ -186,7 +188,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
String fileName; String fileName;
try { try {
fileName = getTempPath() + File.separator + abstractFile.getName() fileName = getTempPath() + File.separator + abstractFile.getName()
+ "-" + String.valueOf(abstractFile.getId()); + "-" + String.valueOf(abstractFile.getId());
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
return ProcessResult.ERROR; return ProcessResult.ERROR;
@ -204,7 +206,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
return ProcessResult.OK; return ProcessResult.OK;
} }
try (PstParser parser = new PstParser(services)){ try (PstParser parser = new PstParser(services)) {
try { try {
ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled); ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
} catch (IOException ex) { } catch (IOException ex) {
@ -214,7 +216,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
PstParser.ParseResult result = parser.open(file, abstractFile.getId()); PstParser.ParseResult result = parser.open(file, abstractFile.getId());
switch( result) { switch (result) {
case OK: case OK:
Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator(); Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
if (pstMsgIterator != null) { if (pstMsgIterator != null) {
@ -238,20 +240,20 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
// encrypted pst: Add encrypted file artifact // encrypted pst: Add encrypted file artifact
try { try {
BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED); BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel"))); artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel")));
try { try {
// index the artifact for keyword search // index the artifact for keyword search
blackboard.postArtifact(artifact, EmailParserModuleFactory.getModuleName()); blackboard.postArtifact(artifact, EmailParserModuleFactory.getModuleName());
} catch (Blackboard.BlackboardException ex) { } catch (Blackboard.BlackboardException ex) {
MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName()); MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName());
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
}
} catch (TskCoreException ex) {
logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS
} }
break; } catch (TskCoreException ex) {
logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS
}
break;
default: default:
// parsing error: log message // parsing error: log message
postErrorMessage( postErrorMessage(
@ -262,8 +264,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS
return ProcessResult.ERROR; return ProcessResult.ERROR;
} }
} catch(Exception ex) { } catch (Exception ex) {
logger.log(Level.WARNING, String.format("Failed to close temp pst file %s", file.getAbsolutePath())); logger.log(Level.WARNING, String.format("Failed to close temp pst file %s", file.getAbsolutePath()));
} finally { } finally {
file.delete(); file.delete();
} }
@ -294,7 +296,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
String fileName; String fileName;
try { try {
fileName = getTempPath() + File.separator + abstractFile.getName() fileName = getTempPath() + File.separator + abstractFile.getName()
+ "-" + String.valueOf(abstractFile.getId()); + "-" + String.valueOf(abstractFile.getId());
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
return ProcessResult.ERROR; return ProcessResult.ERROR;
@ -321,18 +323,18 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
return ProcessResult.OK; return ProcessResult.OK;
} }
try{ try {
processMboxFile(file, abstractFile, emailFolder); processMboxFile(file, abstractFile, emailFolder);
if (context.fileIngestIsCancelled()) { if (context.fileIngestIsCancelled()) {
return ProcessResult.OK; return ProcessResult.OK;
} }
}finally { } finally {
file.delete(); file.delete();
} }
} else { } else {
List<Long> mboxSplitOffsets = new ArrayList<>(); List<Long> mboxSplitOffsets = new ArrayList<>();
try{ try {
mboxSplitOffsets = findMboxSplitOffset(abstractFile, file); mboxSplitOffsets = findMboxSplitOffset(abstractFile, file);
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, String.format("Failed finding split offsets for mbox file {0}.", fileName), ex); //NON-NLS logger.log(Level.WARNING, String.format("Failed finding split offsets for mbox file {0}.", fileName), ex); //NON-NLS
@ -348,7 +350,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
logger.log(Level.WARNING, "Failed writing split mbox file to disk.", ex); //NON-NLS logger.log(Level.WARNING, "Failed writing split mbox file to disk.", ex); //NON-NLS
return ProcessResult.OK; return ProcessResult.OK;
} }
try{ try {
processMboxFile(splitFile, abstractFile, emailFolder); processMboxFile(splitFile, abstractFile, emailFolder);
startingOffset = mboxSplitOffset; startingOffset = mboxSplitOffset;
} finally { } finally {
@ -374,10 +376,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
int len = in.read(buffer); int len = in.read(buffer);
while (len != -1) { while (len != -1) {
len = in.read(buffer); len = in.read(buffer);
if (buffer[0] == 13 && buffer[1] == 10 && buffer[2] == 70 && buffer[3] == 114 && if (buffer[0] == 13 && buffer[1] == 10 && buffer[2] == 70 && buffer[3] == 114
buffer[4] == 111 && buffer[5] == 109 && buffer[6] == 32) { && buffer[4] == 111 && buffer[5] == 109 && buffer[6] == 32) {
mboxSplitOffset.add(in.getCurPosition() - 5 ); mboxSplitOffset.add(in.getCurPosition() - 5);
in.skip(MBOX_SIZE_TO_SPLIT); in.skip(MBOX_SIZE_TO_SPLIT);
} }
} }
@ -385,18 +387,17 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} }
private void processMboxFile(File file, AbstractFile abstractFile, String emailFolder) { private void processMboxFile(File file, AbstractFile abstractFile, String emailFolder) {
try(MboxParser emailIterator = MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId())) { try (MboxParser emailIterator = MboxParser.getEmailIterator(emailFolder, file, abstractFile.getId())) {
List<EmailMessage> emails = new ArrayList<>(); List<EmailMessage> emails = new ArrayList<>();
if(emailIterator != null) { if (emailIterator != null) {
while(emailIterator.hasNext()) { while (emailIterator.hasNext()) {
if (context.fileIngestIsCancelled()) { if (context.fileIngestIsCancelled()) {
return; return;
} }
EmailMessage emailMessage = emailIterator.next(); EmailMessage emailMessage = emailIterator.next();
if(emailMessage != null) { if (emailMessage != null) {
emails.add(emailMessage); emails.add(emailMessage);
} }
} }
@ -408,8 +409,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
abstractFile.getName()), errors); abstractFile.getName()), errors);
} }
} }
processEmails(emails, MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()), abstractFile); processEmails(emails, MboxParser.getEmailIterator(emailFolder, file, abstractFile.getId()), abstractFile);
} catch(Exception ex) { } catch (Exception ex) {
logger.log(Level.WARNING, String.format("Failed to close mbox temp file %s", file.getAbsolutePath())); logger.log(Level.WARNING, String.format("Failed to close mbox temp file %s", file.getAbsolutePath()));
} }
@ -450,13 +451,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
List<AbstractFile> derivedFiles = new ArrayList<>(); List<AbstractFile> derivedFiles = new ArrayList<>();
AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase); AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
BlackboardArtifact msgArtifact = addEmailArtifact(message, abstractFile, accountFileInstanceCache); createEmailArtifact(message, abstractFile, accountFileInstanceCache, derivedFiles);
accountFileInstanceCache.clear(); accountFileInstanceCache.clear();
if ((msgArtifact != null) && (message.hasAttachment())) {
derivedFiles.addAll(handleAttachments(message.getAttachments(), abstractFile, msgArtifact));
}
if (derivedFiles.isEmpty() == false) { if (derivedFiles.isEmpty() == false) {
for (AbstractFile derived : derivedFiles) { for (AbstractFile derived : derivedFiles) {
services.fireModuleContentEvent(new ModuleContentEvent(derived)); services.fireModuleContentEvent(new ModuleContentEvent(derived));
@ -535,40 +532,35 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
// Putting try/catch around this to catch any exception and still allow // Putting try/catch around this to catch any exception and still allow
// the creation of the artifacts to continue. // the creation of the artifacts to continue.
try{ try {
EmailMessageThreader.threadMessages(partialEmailsForThreading); EmailMessageThreader.threadMessages(partialEmailsForThreading);
} catch(Exception ex) { } catch (Exception ex) {
logger.log(Level.WARNING, String.format("Exception thrown parsing emails from %s", abstractFile.getName()), ex); logger.log(Level.WARNING, String.format("Exception thrown parsing emails from %s", abstractFile.getName()), ex);
} }
List<AbstractFile> derivedFiles = new ArrayList<>(); List<AbstractFile> derivedFiles = new ArrayList<>();
int msgCnt = 0; int msgCnt = 0;
while(fullMessageIterator.hasNext()) { while (fullMessageIterator.hasNext()) {
if (context.fileIngestIsCancelled()) { if (context.fileIngestIsCancelled()) {
return; return;
} }
EmailMessage current = fullMessageIterator.next(); EmailMessage current = fullMessageIterator.next();
if(current == null) { if (current == null) {
continue; continue;
} }
if(partialEmailsForThreading.size() > msgCnt) { if (partialEmailsForThreading.size() > msgCnt) {
EmailMessage threaded = partialEmailsForThreading.get(msgCnt++); EmailMessage threaded = partialEmailsForThreading.get(msgCnt++);
if(threaded.getMessageID().equals(current.getMessageID()) && if (threaded.getMessageID().equals(current.getMessageID())
threaded.getSubject().equals(current.getSubject())) { && threaded.getSubject().equals(current.getSubject())) {
current.setMessageThreadID(threaded.getMessageThreadID()); current.setMessageThreadID(threaded.getMessageThreadID());
} }
} }
createEmailArtifact(current, abstractFile, accountFileInstanceCache, derivedFiles);
BlackboardArtifact msgArtifact = addEmailArtifact(current, abstractFile, accountFileInstanceCache);
if ((msgArtifact != null) && (current.hasAttachment())) {
derivedFiles.addAll(handleAttachments(current.getAttachments(), abstractFile, msgArtifact ));
}
} }
if (derivedFiles.isEmpty() == false) { if (derivedFiles.isEmpty() == false) {
@ -581,6 +573,21 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} }
context.addFilesToJob(derivedFiles); context.addFilesToJob(derivedFiles);
} }
void createEmailArtifact(EmailMessage email, AbstractFile abstractFile, AccountFileInstanceCache accountFileInstanceCache, List<AbstractFile> derivedFiles) {
BlackboardArtifact msgArtifact = addEmailArtifact(email, abstractFile, accountFileInstanceCache);
if ((msgArtifact != null) && (email.hasAttachment())) {
derivedFiles.addAll(handleAttachments(email.getAttachments(), abstractFile, msgArtifact));
for (EmailMessage.Attachment attach : email.getAttachments()) {
if (attach instanceof AttachedEmailMessage) {
createEmailArtifact(((AttachedEmailMessage) attach).getEmailMessage(), abstractFile, accountFileInstanceCache, derivedFiles);
}
}
}
}
/** /**
* Add the given attachments as derived files and reschedule them for * Add the given attachments as derived files and reschedule them for
* ingest. * ingest.
@ -592,8 +599,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
* @return List of attachments * @return List of attachments
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message." "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message."
}) })
private List<AbstractFile> handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) { private List<AbstractFile> handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
List<AbstractFile> files = new ArrayList<>(); List<AbstractFile> files = new ArrayList<>();
List<FileAttachment> fileAttachments = new ArrayList<>(); List<FileAttachment> fileAttachments = new ArrayList<>();
@ -627,14 +634,13 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} }
} }
try { try {
communicationArtifactsHelper.addAttachments(messageArtifact, new MessageAttachments(fileAttachments, Collections.emptyList())); communicationArtifactsHelper.addAttachments(messageArtifact, new MessageAttachments(fileAttachments, Collections.emptyList()));
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
postErrorMessage( postErrorMessage(
NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"),
""); "");
logger.log(Level.INFO, "Failed to add attachments to email message.", ex); logger.log(Level.INFO, "Failed to add attachments to email message.", ex);
} }
return files; return files;
@ -654,7 +660,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} }
/** /**
* Finds and returns a set of unique email addresses found in the input string * Finds and returns a set of unique email addresses found in the input
* string
* *
* @param input - input string, like the To/CC line from an email header * @param input - input string, like the To/CC line from an email header
* *
@ -662,11 +669,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
*/ */
private Set<String> findEmailAddresess(String input) { private Set<String> findEmailAddresess(String input) {
Pattern p = Pattern.compile("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b", Pattern p = Pattern.compile("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",
Pattern.CASE_INSENSITIVE); Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(input); Matcher m = p.matcher(input);
Set<String> emailAddresses = new HashSet<>(); Set<String> emailAddresses = new HashSet<>();
while (m.find()) { while (m.find()) {
emailAddresses.add( m.group()); emailAddresses.add(m.group());
} }
return emailAddresses; return emailAddresses;
} }
@ -674,9 +681,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
/** /**
* Add a blackboard artifact for the given e-mail message. * Add a blackboard artifact for the given e-mail message.
* *
* @param email The e-mail message. * @param email The e-mail message.
* @param abstractFile The associated file. * @param abstractFile The associated file.
* @param accountFileInstanceCache The current cache of account instances. * @param accountFileInstanceCache The current cache of account instances.
* *
* @return The generated e-mail message artifact. * @return The generated e-mail message artifact.
*/ */
@ -712,13 +719,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
senderAddress = senderAddressList.get(0); senderAddress = senderAddressList.get(0);
try { try {
senderAccountInstance = accountFileInstanceCache.getAccountInstance(senderAddress); senderAccountInstance = accountFileInstanceCache.getAccountInstance(senderAddress);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS
} }
catch(TskCoreException ex) { } else {
logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS logger.log(Level.WARNING, "Failed to find sender address, from = {0}", from); //NON-NLS
}
}
else {
logger.log(Level.WARNING, "Failed to find sender address, from = {0}", from); //NON-NLS
} }
if (context.fileIngestIsCancelled()) { if (context.fileIngestIsCancelled()) {
@ -738,8 +743,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
try { try {
AccountFileInstance recipientAccountInstance = accountFileInstanceCache.getAccountInstance(addr); AccountFileInstance recipientAccountInstance = accountFileInstanceCache.getAccountInstance(addr);
recipientAccountInstances.add(recipientAccountInstance); recipientAccountInstances.add(recipientAccountInstance);
} } catch (TskCoreException ex) {
catch(TskCoreException ex) {
logger.log(Level.WARNING, "Failed to create account for email address " + addr, ex); //NON-NLS logger.log(Level.WARNING, "Failed to create account for email address " + addr, ex); //NON-NLS
} }
} }
@ -765,7 +769,6 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
addArtifactAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes); addArtifactAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes);
addArtifactAttribute(threadID, ATTRIBUTE_TYPE.TSK_THREAD_ID, bbattributes); addArtifactAttribute(threadID, ATTRIBUTE_TYPE.TSK_THREAD_ID, bbattributes);
try { try {
if (context.fileIngestIsCancelled()) { if (context.fileIngestIsCancelled()) {
return null; return null;
@ -779,7 +782,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} }
// Add account relationships // Add account relationships
currentCase.getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL); currentCase.getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart, Relationship.Type.MESSAGE, dateL);
if (context.fileIngestIsCancelled()) { if (context.fileIngestIsCancelled()) {
return null; return null;
@ -787,7 +790,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
try { try {
// index the artifact for keyword search // index the artifact for keyword search
blackboard.postArtifact(bbart, EmailParserModuleFactory.getModuleName()); blackboard.postArtifact(bbart, EmailParserModuleFactory.getModuleName());
} catch (Blackboard.BlackboardException ex) { } catch (Blackboard.BlackboardException ex) {
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getArtifactID(), ex); //NON-NLS logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getArtifactID(), ex); //NON-NLS
MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName()); MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName());
@ -802,7 +805,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
/** /**
* Add an attribute of a specified type to a supplied Collection. * Add an attribute of a specified type to a supplied Collection.
* *
* @param stringVal The attribute value. * @param stringVal The attribute value.
* @param attrType The type of attribute to be added. * @param attrType The type of attribute to be added.
* @param bbattributes The Collection to which the attribute will be added. * @param bbattributes The Collection to which the attribute will be added.
*/ */
@ -839,22 +842,24 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} }
/** /**
* Cache for storing AccountFileInstance. * Cache for storing AccountFileInstance. The idea is that emails will be
* The idea is that emails will be used multiple times in a file and * used multiple times in a file and we shouldn't do a database lookup each
* we shouldn't do a database lookup each time. * time.
*/ */
static private class AccountFileInstanceCache { static private class AccountFileInstanceCache {
private final Map<String, AccountFileInstance> cacheMap; private final Map<String, AccountFileInstance> cacheMap;
private final AbstractFile file; private final AbstractFile file;
private final Case currentCase; private final Case currentCase;
/** /**
* Create a new cache. Caches are linked to a specific file. * Create a new cache. Caches are linked to a specific file.
*
* @param file * @param file
* @param currentCase * @param currentCase
*/ */
AccountFileInstanceCache(AbstractFile file, Case currentCase) { AccountFileInstanceCache(AbstractFile file, Case currentCase) {
cacheMap= new HashMap<>(); cacheMap = new HashMap<>();
this.file = file; this.file = file;
this.currentCase = currentCase; this.currentCase = currentCase;
} }
@ -873,9 +878,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
return cacheMap.get(email); return cacheMap.get(email);
} }
AccountFileInstance accountInstance = AccountFileInstance accountInstance
currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email, = currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email,
EmailParserModuleFactory.getModuleName(), file); EmailParserModuleFactory.getModuleName(), file);
cacheMap.put(email, accountInstance); cacheMap.put(email, accountInstance);
return accountInstance; return accountInstance;
} }