mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Merge branch 'develop' of github.com:sleuthkit/autopsy into 7292-personHostSheets
This commit is contained in:
commit
c32de45abb
@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.imagewriter.ImageWriterService;
|
||||
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
|
||||
import org.sleuthkit.datamodel.AddDataSourceCallbacks;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.SleuthkitJNI;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -106,7 +107,7 @@ class AddImageTask implements Runnable {
|
||||
try {
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
@ -317,9 +318,10 @@ class AddImageTask implements Runnable {
|
||||
String md5;
|
||||
String sha1;
|
||||
String sha256;
|
||||
Host host;
|
||||
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.image = image;
|
||||
this.sectorSize = sectorSize;
|
||||
@ -328,6 +330,7 @@ class AddImageTask implements Runnable {
|
||||
this.md5 = md5;
|
||||
this.sha1 = sha1;
|
||||
this.sha256 = sha256;
|
||||
this.host = host;
|
||||
this.imageWriterSettings = imageWriterSettings;
|
||||
}
|
||||
|
||||
|
@ -329,7 +329,6 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
||||
final UUID dataSourceId = UUID.randomUUID();
|
||||
newContents.clear();
|
||||
cleanupTask = null;
|
||||
readyToIngest = false;
|
||||
dsProcessor = dsp;
|
||||
|
||||
// Add a cleanup task to interrupt the background process if the
|
||||
@ -364,6 +363,8 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
||||
|
||||
// Kick off the DSProcessor
|
||||
if (dsProcessor.supportsIngestStream()) {
|
||||
// Set readyToIngest to false to prevent the wizard from starting ingest a second time.
|
||||
readyToIngest = false;
|
||||
dsProcessor.runWithIngestStream(ingestJobSettings, getDSPProgressMonitorImpl(), cbObj);
|
||||
} else {
|
||||
dsProcessor.run(getDSPProgressMonitorImpl(), cbObj);
|
||||
|
@ -28,6 +28,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
@ -42,6 +43,7 @@ class AddLocalFilesTask implements Runnable {
|
||||
private static final Logger LOGGER = Logger.getLogger(AddLocalFilesTask.class.getName());
|
||||
private final String deviceId;
|
||||
private final String rootVirtualDirectoryName;
|
||||
private final Host host;
|
||||
private final List<String> localFilePaths;
|
||||
private final DataSourceProcessorProgressMonitor progress;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
@ -64,14 +66,16 @@ class AddLocalFilesTask implements Runnable {
|
||||
* form: LogicalFileSet[N]
|
||||
* @param localFilePaths A list of localFilePaths of local/logical
|
||||
* files and/or directories.
|
||||
* @param host The host for this data source (may be null).
|
||||
* @param progressMonitor Progress monitor to report progress
|
||||
* during processing.
|
||||
* @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.rootVirtualDirectoryName = rootVirtualDirectoryName;
|
||||
this.localFilePaths = localFilePaths;
|
||||
this.host = host;
|
||||
this.callback = callback;
|
||||
this.progress = progressMonitor;
|
||||
}
|
||||
@ -88,7 +92,7 @@ class AddLocalFilesTask implements Runnable {
|
||||
try {
|
||||
progress.setIndeterminate(true);
|
||||
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);
|
||||
} catch (TskDataException | TskCoreException | NoCurrentCaseException ex) {
|
||||
errors.add(ex.getMessage());
|
||||
|
@ -39,6 +39,7 @@ import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.IngestStream;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.SleuthkitJNI;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -81,6 +82,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
private String md5;
|
||||
private String sha1;
|
||||
private String sha256;
|
||||
private Host host = null;
|
||||
private boolean setDataSourceOptionsCalled;
|
||||
|
||||
static {
|
||||
@ -181,11 +183,32 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
*/
|
||||
@Override
|
||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
run(null, progressMonitor, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the settings provided by the selection and
|
||||
* 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();
|
||||
readConfigSettings();
|
||||
this.host = host;
|
||||
try {
|
||||
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) {
|
||||
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
|
||||
final List<String> errors = new ArrayList<>();
|
||||
@ -217,14 +240,47 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
@Override
|
||||
public void runWithIngestStream(IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
|
||||
DataSourceProcessorCallback callBack) {
|
||||
runWithIngestStream(null, settings, progress, callBack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the settings provided by the selection and
|
||||
* configuration panel. Files found during ingest will be sent directly to the
|
||||
* IngestStream provided. Returns as soon as the background task is started.
|
||||
* The background task uses a callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* 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
|
||||
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
|
||||
try {
|
||||
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) {
|
||||
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
|
||||
final List<String> errors = new ArrayList<>();
|
||||
@ -370,7 +426,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
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,
|
||||
progressMonitor,
|
||||
new StreamingAddDataSourceCallbacks(ingestStream),
|
||||
@ -405,6 +461,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
imagePath = null;
|
||||
timeZone = null;
|
||||
ignoreFatOrphanFiles = false;
|
||||
host = null;
|
||||
configPanel.reset();
|
||||
setDataSourceOptionsCalled = false;
|
||||
}
|
||||
@ -428,7 +485,6 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
|
||||
try {
|
||||
// verify that the image has a file system that TSK can process
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
if (!DataSourceUtils.imageHasFileSystem(dataSourcePath)) {
|
||||
// image does not have a file system that TSK can process
|
||||
return 0;
|
||||
@ -521,6 +577,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
this.sectorSize = 0;
|
||||
this.timeZone = Calendar.getInstance().getTimeZone().getID();
|
||||
this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
|
||||
this.host = null;
|
||||
setDataSourceOptionsCalled = true;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.SleuthkitJNI;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -56,6 +57,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
private String drivePath;
|
||||
private int sectorSize;
|
||||
private String timeZone;
|
||||
private Host host;
|
||||
private ImageWriterSettings imageWriterSettings;
|
||||
private boolean ignoreFatOrphanFiles;
|
||||
private boolean setDataSourceOptionsCalled;
|
||||
@ -135,6 +137,26 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
*/
|
||||
@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 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) {
|
||||
deviceId = UUID.randomUUID().toString();
|
||||
drivePath = configPanel.getContentPath();
|
||||
@ -147,12 +169,21 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
imageWriterSettings = null;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
try {
|
||||
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
||||
new String[]{drivePath}, sectorSize,
|
||||
timeZone, null, null, null, deviceId);
|
||||
timeZone, null, null, null, deviceId, this.host);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error adding local disk with path " + drivePath + " to database", ex);
|
||||
final List<String> errors = new ArrayList<>();
|
||||
@ -162,7 +193,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
}
|
||||
|
||||
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,
|
||||
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
|
||||
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));
|
||||
@ -230,7 +261,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
||||
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,
|
||||
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
|
||||
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));
|
||||
|
@ -42,6 +42,8 @@ import org.sleuthkit.autopsy.coreutils.ExecUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
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
|
||||
@ -137,7 +139,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
return configPanel.validatePanel();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the settings provided by the selection and
|
||||
* configuration panel. Returns as soon as the background task is started.
|
||||
@ -153,7 +155,36 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
*/
|
||||
@Override
|
||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
run(null, progressMonitor, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the settings provided by the selection and
|
||||
* 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) {
|
||||
|
||||
// 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();
|
||||
if (configPanel.subTypeIsLogicalEvidencePanel()) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -284,6 +315,34 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
|
||||
return executablePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the given settings instead of those provided by the
|
||||
* 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
|
||||
@ -309,7 +368,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DerivedFile;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.LayoutFile;
|
||||
import org.sleuthkit.datamodel.LocalDirectory;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
@ -490,6 +491,40 @@ public class FileManager implements Closeable {
|
||||
* 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 {
|
||||
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) {
|
||||
throw new TskCoreException("File manager has been closed");
|
||||
}
|
||||
@ -506,7 +541,7 @@ public class FileManager implements Closeable {
|
||||
* children to the case database.
|
||||
*/
|
||||
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<>();
|
||||
for (java.io.File localFile : localFiles) {
|
||||
AbstractFile fileAdded = addLocalFile(trans, dataSource, localFile, TskData.EncodingType.NONE, progressUpdater);
|
||||
|
@ -52,8 +52,8 @@ class AddNewOrganizationDialog extends javax.swing.JDialog {
|
||||
* Creates new form AddNewOrganizationDialog
|
||||
*/
|
||||
@Messages({"AddNewOrganizationDialog.addNewOrg.msg=Add New Organization"})
|
||||
AddNewOrganizationDialog() {
|
||||
super((JFrame) WindowManager.getDefault().getMainWindow(),
|
||||
AddNewOrganizationDialog(javax.swing.JDialog parent) {
|
||||
super(parent,
|
||||
Bundle.AddNewOrganizationDialog_addNewOrg_msg(),
|
||||
true); // NON-NLS
|
||||
textBoxes = new ArrayList<>();
|
||||
@ -67,8 +67,8 @@ class AddNewOrganizationDialog extends javax.swing.JDialog {
|
||||
}
|
||||
|
||||
// populates the dialog with existing case information to edit
|
||||
public AddNewOrganizationDialog(CentralRepoOrganization orgToEdit) {
|
||||
super((JFrame) WindowManager.getDefault().getMainWindow(),
|
||||
public AddNewOrganizationDialog(javax.swing.JDialog parent, CentralRepoOrganization orgToEdit) {
|
||||
super(parent,
|
||||
Bundle.AddNewOrganizationDialog_addNewOrg_msg(),
|
||||
true); // NON-NLS
|
||||
organizationToEdit = orgToEdit;
|
||||
|
@ -376,7 +376,7 @@ public final class ManageOrganizationsDialog extends JDialog {
|
||||
private void deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteButtonActionPerformed
|
||||
CentralRepoOrganization orgToDelete = organizationList.getSelectedValue();
|
||||
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_title(),
|
||||
JOptionPane.YES_NO_OPTION)) {
|
||||
@ -397,7 +397,7 @@ public final class ManageOrganizationsDialog extends JDialog {
|
||||
}//GEN-LAST:event_closeButtonActionPerformed
|
||||
|
||||
private void newButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newButtonActionPerformed
|
||||
AddNewOrganizationDialog dialogO = new AddNewOrganizationDialog();
|
||||
AddNewOrganizationDialog dialogO = new AddNewOrganizationDialog(this);
|
||||
if (dialogO.isChanged()) {
|
||||
try {
|
||||
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
|
||||
CentralRepoOrganization orgToEdit = organizationList.getSelectedValue();
|
||||
if (orgToEdit != null) {
|
||||
AddNewOrganizationDialog dialogO = new AddNewOrganizationDialog(orgToEdit);
|
||||
AddNewOrganizationDialog dialogO = new AddNewOrganizationDialog(this, orgToEdit);
|
||||
if (dialogO.isChanged()) {
|
||||
try {
|
||||
newOrg = dialogO.getNewOrg();
|
||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.corecomponentinterfaces;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* separate thread and the settings provided by the selection and
|
||||
@ -131,6 +151,30 @@ public interface DataSourceProcessor {
|
||||
DataSourceProcessorCallback callBack) {
|
||||
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.
|
||||
|
@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -47,6 +48,7 @@ final class AddRawImageTask implements Runnable {
|
||||
private final String imageFilePath;
|
||||
private final String timeZone;
|
||||
private final long chunkSize;
|
||||
private final Host host;
|
||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
private boolean criticalErrorOccurred;
|
||||
@ -68,11 +70,12 @@ final class AddRawImageTask implements Runnable {
|
||||
* progressMonitor during processing.
|
||||
* @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.imageFilePath = imageFilePath;
|
||||
this.timeZone = timeZone;
|
||||
this.chunkSize = chunkSize;
|
||||
this.host = host;
|
||||
this.callback = callback;
|
||||
this.progressMonitor = progressMonitor;
|
||||
}
|
||||
@ -150,7 +153,7 @@ final class AddRawImageTask implements Runnable {
|
||||
/*
|
||||
* 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);
|
||||
List<TskFileRange> fileRanges = new ArrayList<>();
|
||||
|
||||
|
@ -29,10 +29,13 @@ import javax.swing.filechooser.FileFilter;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.openide.util.lookup.ServiceProviders;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
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
|
||||
@ -135,8 +138,37 @@ public class RawDSProcessor implements DataSourceProcessor, AutoIngestDataSource
|
||||
*/
|
||||
@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.
|
||||
*/
|
||||
@Override
|
||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
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
|
||||
* data source as it is divided up into virtual
|
||||
* unallocated space files.
|
||||
* @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.
|
||||
*/
|
||||
private void run(String deviceId, String imageFilePath, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
AddRawImageTask addImageTask = new AddRawImageTask(deviceId, imageFilePath, timeZone, chunkSize, progressMonitor, 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, host, progressMonitor, callback);
|
||||
new Thread(addImageTask).start();
|
||||
}
|
||||
|
||||
@ -206,7 +239,7 @@ public class RawDSProcessor implements DataSourceProcessor, AutoIngestDataSource
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
|
||||
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
@ -193,12 +194,40 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
||||
* in isPanelValid().
|
||||
*/
|
||||
@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({
|
||||
"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);
|
||||
|
||||
// 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();
|
||||
File selectedFile = new File(selectedFilePath);
|
||||
Path selectedPath = selectedFile.toPath();
|
||||
@ -209,7 +238,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
||||
String uniqueUUID = UUID.randomUUID().toString();
|
||||
//Move heavy lifting to a background task.
|
||||
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
|
||||
callback, currentCase, uniqueUUID);
|
||||
callback, currentCase, uniqueUUID, host);
|
||||
swingWorker.execute();
|
||||
} catch (NoCurrentCaseException 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();
|
||||
//Move heavy lifting to a background task.
|
||||
swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
|
||||
callBack, currentCase, deviceId);
|
||||
callBack, currentCase, deviceId, null);
|
||||
swingWorker.execute();
|
||||
} catch (NoCurrentCaseException 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 XRYFolder xryFolder;
|
||||
private final String uniqueUUID;
|
||||
private final Host host;
|
||||
|
||||
public XRYReportProcessorSwingWorker(XRYFolder folder,
|
||||
DataSourceProcessorProgressMonitor progressMonitor,
|
||||
DataSourceProcessorCallback callback,
|
||||
Case currentCase, String uniqueUUID) {
|
||||
Case currentCase, String uniqueUUID, Host host) {
|
||||
|
||||
this.xryFolder = folder;
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.callback = callback;
|
||||
this.currentCase = currentCase;
|
||||
this.uniqueUUID = uniqueUUID;
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -306,6 +337,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
||||
uniqueUUID,
|
||||
"XRY Text Export", //Name
|
||||
"", //Timezone
|
||||
host,
|
||||
filePaths,
|
||||
new ProgressMonitorAdapter(progressMonitor));
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.discovery.ui;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -63,6 +64,7 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
|
||||
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
|
||||
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
|
||||
}
|
||||
setMinimumSize(new Dimension(125, 20));
|
||||
artifactsTable.getRowSorter().toggleSortOrder(0);
|
||||
artifactsTable.getRowSorter().toggleSortOrder(0);
|
||||
}
|
||||
|
@ -4,8 +4,11 @@
|
||||
<NonVisualComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="350"/>
|
||||
<Property name="resizeWeight" type="double" value="0.1"/>
|
||||
<Property name="dividerLocation" type="int" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="dividerLocation" type="code"/>
|
||||
</Property>
|
||||
<Property name="resizeWeight" type="double" value="0.2"/>
|
||||
<Property name="lastDividerLocation" type="int" value="250"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
|
@ -20,8 +20,12 @@ package org.sleuthkit.autopsy.discovery.ui;
|
||||
|
||||
import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.awt.Dimension;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
@ -41,13 +45,18 @@ final class DomainArtifactsTabPanel extends JPanel {
|
||||
private final ArtifactsListPanel listPanel;
|
||||
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
|
||||
private AbstractArtifactDetailsPanel rightPanel = null;
|
||||
private int dividerLocation = 300;
|
||||
private final PropertyChangeListener dividerListener;
|
||||
|
||||
private ArtifactRetrievalStatus status = ArtifactRetrievalStatus.UNPOPULATED;
|
||||
private final ListSelectionListener listener = new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent event) {
|
||||
if (!event.getValueIsAdjusting()) {
|
||||
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||
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)
|
||||
DomainArtifactsTabPanel(BlackboardArtifact.ARTIFACT_TYPE type) {
|
||||
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;
|
||||
listPanel = new ArtifactsListPanel(artifactType);
|
||||
listPanel.setPreferredSize(new Dimension(100, 20));
|
||||
listPanel.addMouseListener(new ArtifactMenuMouseAdapter(listPanel));
|
||||
|
||||
mainSplitPane.setLeftComponent(listPanel);
|
||||
add(mainSplitPane);
|
||||
setRightComponent();
|
||||
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||
dividerLocation = mainSplitPane.getDividerLocation();
|
||||
listPanel.addSelectionListener(listener);
|
||||
}
|
||||
|
||||
@ -121,6 +145,7 @@ final class DomainArtifactsTabPanel extends JPanel {
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
void setStatus(ArtifactRetrievalStatus status) {
|
||||
this.status = status;
|
||||
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||
if (status == ArtifactRetrievalStatus.UNPOPULATED) {
|
||||
listPanel.clearList();
|
||||
removeAll();
|
||||
@ -132,6 +157,8 @@ final class DomainArtifactsTabPanel extends JPanel {
|
||||
removeAll();
|
||||
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) {
|
||||
if (artifactType == artifactresultEvent.getArtifactType() && status == ArtifactRetrievalStatus.POPULATING) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||
listPanel.removeSelectionListener(listener);
|
||||
listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts());
|
||||
status = ArtifactRetrievalStatus.POPULATED;
|
||||
@ -152,6 +180,8 @@ final class DomainArtifactsTabPanel extends JPanel {
|
||||
listPanel.selectFirst();
|
||||
removeAll();
|
||||
add(mainSplitPane);
|
||||
mainSplitPane.setDividerLocation(dividerLocation);
|
||||
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||
if (artifactresultEvent.shouldGrabFocus()) {
|
||||
focusList();
|
||||
}
|
||||
@ -188,8 +218,9 @@ final class DomainArtifactsTabPanel extends JPanel {
|
||||
|
||||
mainSplitPane = new javax.swing.JSplitPane();
|
||||
|
||||
mainSplitPane.setDividerLocation(350);
|
||||
mainSplitPane.setResizeWeight(0.1);
|
||||
mainSplitPane.setDividerLocation(dividerLocation);
|
||||
mainSplitPane.setResizeWeight(0.2);
|
||||
mainSplitPane.setLastDividerLocation(250);
|
||||
|
||||
setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
|
||||
setMinimumSize(new java.awt.Dimension(0, 0));
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.discovery.ui;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -39,7 +40,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
/**
|
||||
* 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 final TypeDescriptionTableModel tableModel;
|
||||
@ -61,6 +62,7 @@ class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel {
|
||||
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
|
||||
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
|
||||
}
|
||||
setMinimumSize(new Dimension(125, 20));
|
||||
artifactsTable.getRowSorter().toggleSortOrder(0);
|
||||
artifactsTable.getRowSorter().toggleSortOrder(0);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.discovery.ui;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.JPanel;
|
||||
@ -33,7 +34,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
/**
|
||||
* Panel to display list of dates and counts.
|
||||
*/
|
||||
class MiniTimelineDateListPanel extends JPanel {
|
||||
final class MiniTimelineDateListPanel extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final DateCountTableModel tableModel = new DateCountTableModel();
|
||||
@ -49,6 +50,7 @@ class MiniTimelineDateListPanel extends JPanel {
|
||||
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
|
||||
jTable1.getColumnModel().getColumn(i).setCellRenderer(renderer);
|
||||
}
|
||||
setMinimumSize(new Dimension(125, 20));
|
||||
jTable1.getRowSorter().toggleSortOrder(0);
|
||||
jTable1.getRowSorter().toggleSortOrder(0);
|
||||
}
|
||||
|
@ -4,8 +4,10 @@
|
||||
<NonVisualComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="400"/>
|
||||
<Property name="resizeWeight" type="double" value="0.1"/>
|
||||
<Property name="dividerLocation" type="int" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="mainSplitPaneDividerLocation" type="code"/>
|
||||
</Property>
|
||||
<Property name="resizeWeight" type="double" value="0.2"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value=""/>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 0]"/>
|
||||
@ -16,7 +18,9 @@
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="leftSplitPane">
|
||||
<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="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 0]"/>
|
||||
|
@ -19,6 +19,10 @@
|
||||
package org.sleuthkit.autopsy.discovery.ui;
|
||||
|
||||
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.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
@ -42,6 +46,9 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
||||
private String selectedDomain = null;
|
||||
private final ListSelectionListener artifactListener;
|
||||
private final ListSelectionListener dateListener;
|
||||
private int leftSplitPaneDividerLocation = 125;
|
||||
private int mainSplitPaneDividerLocation = 300;
|
||||
private final PropertyChangeListener dividerListener;
|
||||
|
||||
@NbBundle.Messages({"MiniTimelinePanel.loadingPanel.details=the Timeline view"})
|
||||
/**
|
||||
@ -62,22 +69,51 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
||||
} else {
|
||||
rightPanel = new GeneralPurposeArtifactViewer();
|
||||
}
|
||||
leftSplitPane.removePropertyChangeListener(dividerListener);
|
||||
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||
mainSplitPane.setRightComponent(rightPanel.getComponent());
|
||||
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();
|
||||
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() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent event) {
|
||||
if (!event.getValueIsAdjusting()) {
|
||||
artifactListPanel.removeSelectionListener(artifactListener);
|
||||
leftSplitPane.removePropertyChangeListener(dividerListener);
|
||||
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||
artifactListPanel.clearList();
|
||||
artifactListPanel.addArtifacts(dateListPanel.getArtifactsForSelectedDate());
|
||||
artifactListPanel.addSelectionListener(artifactListener);
|
||||
artifactListPanel.selectFirst();
|
||||
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
|
||||
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
|
||||
leftSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||
validate();
|
||||
repaint();
|
||||
}
|
||||
@ -85,10 +121,16 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
||||
};
|
||||
dateListPanel.addSelectionListener(dateListener);
|
||||
artifactListPanel.addSelectionListener(artifactListener);
|
||||
dateListPanel.setPreferredSize(new Dimension(100, 20));
|
||||
leftSplitPane.setLeftComponent(dateListPanel);
|
||||
artifactListPanel.setPreferredSize(new Dimension(100, 20));
|
||||
leftSplitPane.setRightComponent(artifactListPanel);
|
||||
mainSplitPane.setRightComponent(rightPanel.getComponent());
|
||||
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)) {
|
||||
dateListPanel.removeListSelectionListener(dateListener);
|
||||
artifactListPanel.removeSelectionListener(artifactListener);
|
||||
leftSplitPane.removePropertyChangeListener(dividerListener);
|
||||
mainSplitPane.removePropertyChangeListener(dividerListener);
|
||||
dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList());
|
||||
status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED;
|
||||
setEnabled(!dateListPanel.isEmpty());
|
||||
@ -156,6 +200,10 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
||||
}
|
||||
removeAll();
|
||||
add(mainSplitPane);
|
||||
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
|
||||
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
|
||||
leftSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||
mainSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, dividerListener);
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
@ -174,12 +222,12 @@ final class MiniTimelinePanel extends javax.swing.JPanel {
|
||||
mainSplitPane = new javax.swing.JSplitPane();
|
||||
leftSplitPane = new javax.swing.JSplitPane();
|
||||
|
||||
mainSplitPane.setDividerLocation(400);
|
||||
mainSplitPane.setResizeWeight(0.1);
|
||||
mainSplitPane.setDividerLocation(mainSplitPaneDividerLocation);
|
||||
mainSplitPane.setResizeWeight(0.2);
|
||||
mainSplitPane.setToolTipText("");
|
||||
mainSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
|
||||
|
||||
leftSplitPane.setDividerLocation(198);
|
||||
leftSplitPane.setDividerLocation(leftSplitPaneDividerLocation);
|
||||
leftSplitPane.setResizeWeight(0.5);
|
||||
leftSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
|
||||
mainSplitPane.setLeftComponent(leftSplitPane);
|
||||
|
@ -48,6 +48,7 @@ import org.sleuthkit.datamodel.Blackboard;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -71,6 +72,7 @@ final class AddLogicalImageTask implements Runnable {
|
||||
private final String timeZone;
|
||||
private final File src;
|
||||
private final File dest;
|
||||
private final Host host;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||
private final Blackboard blackboard;
|
||||
@ -87,7 +89,7 @@ final class AddLogicalImageTask implements Runnable {
|
||||
|
||||
AddLogicalImageTask(String deviceId,
|
||||
String timeZone,
|
||||
File src, File dest,
|
||||
File src, File dest, Host host,
|
||||
DataSourceProcessorProgressMonitor progressMonitor,
|
||||
DataSourceProcessorCallback callback
|
||||
) throws NoCurrentCaseException {
|
||||
@ -95,6 +97,7 @@ final class AddLogicalImageTask implements Runnable {
|
||||
this.timeZone = timeZone;
|
||||
this.src = src;
|
||||
this.dest = dest;
|
||||
this.host = host;
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.callback = callback;
|
||||
this.currentCase = Case.getCurrentCase();
|
||||
@ -229,7 +232,7 @@ final class AddLogicalImageTask implements Runnable {
|
||||
|
||||
try {
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingExtractedFiles());
|
||||
interestingFileMap = addExtractedFiles(dest, resultsPath, newDataSources);
|
||||
interestingFileMap = addExtractedFiles(dest, resultsPath, host, newDataSources);
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingExtractedFiles());
|
||||
} catch (IOException | TskCoreException ex) {
|
||||
errorList.add(ex.getMessage());
|
||||
@ -248,7 +251,7 @@ final class AddLogicalImageTask implements Runnable {
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
addMultipleImagesTask = new AddMultipleImagesTask(deviceId, imagePaths, timeZone , progressMonitor);
|
||||
addMultipleImagesTask = new AddMultipleImagesTask(deviceId, imagePaths, timeZone, host, progressMonitor);
|
||||
}
|
||||
addMultipleImagesTask.run();
|
||||
if (addMultipleImagesTask.getResult() == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) {
|
||||
@ -474,14 +477,14 @@ final class AddLogicalImageTask implements Runnable {
|
||||
@Messages({
|
||||
"# {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.CaseDbTransaction trans = null;
|
||||
Map<String, List<Long>> interestingFileMap = new HashMap<>();
|
||||
|
||||
try {
|
||||
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);
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(
|
||||
|
@ -30,6 +30,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DefaultAddDataSourceCallbacks;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SleuthkitJNI;
|
||||
@ -53,6 +54,7 @@ class AddMultipleImagesTask implements Runnable {
|
||||
private final String deviceId;
|
||||
private final List<String> imageFilePaths;
|
||||
private final String timeZone;
|
||||
private final Host host;
|
||||
private final long chunkSize = TWO_GB;
|
||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||
private final Case currentCase;
|
||||
@ -85,6 +87,7 @@ class AddMultipleImagesTask implements Runnable {
|
||||
* @param timeZone The time zone to use when processing dates and
|
||||
* times for the image, obtained from
|
||||
* java.util.TimeZone.getID.
|
||||
* @param host Host for this data source (may be null).
|
||||
* @param progressMonitor Progress monitor for reporting progress during
|
||||
* processing.
|
||||
*
|
||||
@ -94,11 +97,12 @@ class AddMultipleImagesTask implements Runnable {
|
||||
"# {0} - file", "AddMultipleImagesTask.addingFileAsLogicalFile=Adding: {0} as an unallocated space file.",
|
||||
"# {0} - deviceId", "# {1} - exceptionMessage",
|
||||
"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 {
|
||||
this.deviceId = deviceId;
|
||||
this.imageFilePaths = imageFilePaths;
|
||||
this.timeZone = timeZone;
|
||||
this.host = host;
|
||||
this.progressMonitor = progressMonitor;
|
||||
currentCase = Case.getCurrentCaseThrows();
|
||||
this.criticalErrorOccurred = false;
|
||||
@ -124,7 +128,7 @@ class AddMultipleImagesTask implements Runnable {
|
||||
for (String imageFilePath : imageFilePaths) {
|
||||
try {
|
||||
currentImage = SleuthkitJNI.addImageToDatabase(currentCase.getSleuthkitCase(), new String[]{imageFilePath},
|
||||
0, timeZone, "", "", "", deviceId);
|
||||
0, timeZone, "", "", "", deviceId, host);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding image " + imageFilePath + " to database", ex);
|
||||
errorMessages.add(Bundle.AddMultipleImagesTask_imageError(imageFilePath));
|
||||
@ -134,7 +138,7 @@ class AddMultipleImagesTask implements Runnable {
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
|
||||
if (!tskAddImageProcessStopped) {
|
||||
addImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, false, false, "");
|
||||
addImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, false, false, host, "");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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({
|
||||
"# {0} - imageDirPath", "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.",
|
||||
"# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}",
|
||||
@ -137,9 +159,18 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
|
||||
"LogicalImagerDSProcessor.noCurrentCase=No current case",
|
||||
})
|
||||
@Override
|
||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
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();
|
||||
List<String> errorList = new ArrayList<>();
|
||||
List<Content> emptyDataSources = new ArrayList<>();
|
||||
@ -187,7 +218,7 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
|
||||
try {
|
||||
String deviceId = UUID.randomUUID().toString();
|
||||
String timeZone = Calendar.getInstance().getTimeZone().getID();
|
||||
run(deviceId, timeZone, src, dest,
|
||||
run(deviceId, timeZone, src, dest, host,
|
||||
progressMonitor, callback);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
String msg = Bundle.LogicalImagerDSProcessor_noCurrentCase();
|
||||
@ -211,15 +242,16 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
|
||||
* java.util.TimeZone.getID.
|
||||
* @param src The source directory of image.
|
||||
* @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
|
||||
* processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
private void run(String deviceId, String timeZone,
|
||||
File src, File dest,
|
||||
File src, File dest, Host host,
|
||||
DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback
|
||||
) throws NoCurrentCaseException {
|
||||
addLogicalImageTask = new AddLogicalImageTask(deviceId, timeZone, src, dest,
|
||||
addLogicalImageTask = new AddLogicalImageTask(deviceId, timeZone, src, dest, host,
|
||||
progressMonitor, callback);
|
||||
Thread thread = new Thread(addLogicalImageTask);
|
||||
thread.start();
|
||||
|
@ -71,6 +71,7 @@ import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.FileSystem;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
import org.sleuthkit.datamodel.Pool;
|
||||
@ -1079,12 +1080,20 @@ public class PortableCaseReportModule implements ReportModule {
|
||||
BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
|
||||
newContent = copyArtifact(parentId, artifactToCopy);
|
||||
} 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();
|
||||
try {
|
||||
if (content instanceof Image) {
|
||||
Image image = (Image) content;
|
||||
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) {
|
||||
VolumeSystem vs = (VolumeSystem) content;
|
||||
newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
|
||||
@ -1108,7 +1117,7 @@ public class PortableCaseReportModule implements ReportModule {
|
||||
|
||||
if (abstractFile instanceof LocalFilesDataSource) {
|
||||
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 {
|
||||
if (abstractFile.isDir()) {
|
||||
newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
<!-- for viewers -->
|
||||
<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 -->
|
||||
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
|
||||
|
@ -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-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.jna-5.6.0.jar=release/modules/ext/jna-5.6.0.jar
|
||||
file.reference.jna-platform-5.6.0.jar=release/modules/ext/jna-platform-5.6.0.jar
|
||||
file.reference.jna-5.7.0.jar=release/modules/ext/jna-5.7.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.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
|
||||
|
@ -923,8 +923,8 @@
|
||||
<binary-origin>release/modules/ext/commons-compress-1.18.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jna-platform-5.6.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jna-platform-5.6.0.jar</binary-origin>
|
||||
<runtime-relative-path>ext/jna-platform-5.7.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jna-platform-5.7.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<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>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jna-5.6.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jna-5.6.0.jar</binary-origin>
|
||||
<runtime-relative-path>ext/jna-5.7.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jna-5.7.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path>
|
||||
|
@ -34,6 +34,7 @@ import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
|
||||
/*
|
||||
* 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 memoryImagePath;
|
||||
private final String timeZone;
|
||||
private final Host host;
|
||||
private final List<String> pluginsToRun;
|
||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
@ -63,16 +65,18 @@ final class AddMemoryImageTask implements Runnable {
|
||||
* @param timeZone The time zone to use when processing dates and
|
||||
* times for the image, obtained from
|
||||
* java.util.TimeZone.getID.
|
||||
* @param host The host for this data source (may be null).
|
||||
* @param progressMonitor Progress monitor for reporting progressMonitor
|
||||
* during processing.
|
||||
* @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.memoryImagePath = memoryImagePath;
|
||||
this.profile = profile;
|
||||
this.pluginsToRun = pluginsToRun;
|
||||
this.timeZone = timeZone;
|
||||
this.host = host;
|
||||
this.callback = callback;
|
||||
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.
|
||||
*
|
||||
* @return The Image object representation of the memeory image file data
|
||||
* @return The Image object representation of the memory image file data
|
||||
* source.
|
||||
*
|
||||
* @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
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,12 @@ import java.util.List;
|
||||
import javax.swing.JPanel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
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
|
||||
@ -116,8 +119,37 @@ public class MemoryDSProcessor implements DataSourceProcessor {
|
||||
*/
|
||||
@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.
|
||||
*/
|
||||
@Override
|
||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
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
|
||||
* times for the image, obtained from
|
||||
* java.util.TimeZone.getID.
|
||||
* @param host The host for this data source (may be null)
|
||||
* @param progressMonitor Progress monitor for reporting progress during
|
||||
* processing.
|
||||
* @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) {
|
||||
addImageTask = new AddMemoryImageTask(deviceId, memoryImagePath, profile, pluginsToRun, timeZone, progressMonitor, 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, host, progressMonitor, callback);
|
||||
new Thread(addImageTask).start();
|
||||
}
|
||||
|
||||
|
@ -267,7 +267,7 @@ class ExtractIE extends Extract {
|
||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
|
||||
RecentActivityExtracterModuleFactory.getModuleName(), url));
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
|
||||
RecentActivityExtracterModuleFactory.getModuleName(), datetime));
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
|
||||
RecentActivityExtracterModuleFactory.getModuleName(), (name != null) ? name : ""));
|
||||
|
@ -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.
|
||||
# See JIRA-6951 for more details.
|
||||
# index -1 = last element in the list, which is extension
|
||||
# index -3 = 3rd from the end, which is the parent path.
|
||||
if fields_list[-1] == "'tif'" and fields_list[-3].endswith(".pdf/'"):
|
||||
# index -3 = 3rd from the end, which is extension
|
||||
# index -5 = 5th from the end, which is the parent path.
|
||||
if fields_list[-3] == "'tif'" and fields_list[-5].endswith(".pdf/'"):
|
||||
fields_list[15] = "'SIZE_IGNORED'"
|
||||
fields_list[23] = "'MD5_IGNORED'"
|
||||
fields_list[24] = "'SHA256_IGNORED'"
|
||||
|
BIN
thirdparty/java-libpst/java-libpst-0.9.5-SNAPSHOT.jar
vendored
Normal file
BIN
thirdparty/java-libpst/java-libpst-0.9.5-SNAPSHOT.jar
vendored
Normal file
Binary file not shown.
BIN
thirdparty/java-libpst/java-libpst-1.0-SNAPSHOT.jar
vendored
BIN
thirdparty/java-libpst/java-libpst-1.0-SNAPSHOT.jar
vendored
Binary file not shown.
@ -19,7 +19,7 @@
|
||||
|
||||
<target name="get-thirdparty-jars" description="get third-party jar dependencies">
|
||||
<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-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}" />
|
||||
|
@ -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.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.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.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
|
||||
javac.source=1.8
|
||||
javac.compilerargs=-Xlint -Xlint:-serial
|
||||
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
|
||||
nbm.homepage=http://www.sleuthkit.org/autopsy/
|
||||
nbm.needs.restart=true
|
||||
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
|
||||
|
@ -84,14 +84,14 @@
|
||||
<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>
|
||||
</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>
|
||||
<runtime-relative-path>ext/guava-19.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/guava-19.0.jar</binary-origin>
|
||||
</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>
|
||||
<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>
|
||||
|
@ -43,7 +43,7 @@ class EmailMessage {
|
||||
private String localPath = "";
|
||||
private boolean hasAttachment = false;
|
||||
private long sentDate = 0L;
|
||||
private List<Attachment> attachments = new ArrayList<>();
|
||||
private final List<Attachment> attachments = new ArrayList<>();
|
||||
private long id = -1L;
|
||||
private String messageID = "";
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.UUID;
|
||||
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.Entity;
|
||||
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.SingleBody;
|
||||
import org.apache.james.mime4j.dom.TextBody;
|
||||
import org.apache.james.mime4j.dom.address.AddressList;
|
||||
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.ContentTypeField;
|
||||
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.MimeConfig;
|
||||
import org.openide.util.NbBundle;
|
||||
@ -293,7 +293,7 @@ class MimeJ4MessageParser implements AutoCloseable{
|
||||
* @param e
|
||||
*/
|
||||
@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 relModuleOutputPath;
|
||||
try {
|
||||
@ -322,25 +322,31 @@ class MimeJ4MessageParser implements AutoCloseable{
|
||||
String outPath = outputDirPath + uniqueFilename;
|
||||
|
||||
Body body = e.getBody();
|
||||
if (body instanceof SingleBody) {
|
||||
if (body != null) {
|
||||
long fileLength;
|
||||
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();
|
||||
attach.setName(filename);
|
||||
attach.setLocalPath(relModuleOutputPath + uniqueFilename);
|
||||
attach.setSize(fileLength);
|
||||
attach.setEncodingType(TskData.EncodingType.XOR1);
|
||||
email.addAttachment(attach);
|
||||
|
||||
} catch (IOException ex) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -277,16 +277,30 @@ class PstParser implements AutoCloseable{
|
||||
*/
|
||||
private EmailMessage extractEmailMessage(PSTMessage msg, String localPath, long fileID) {
|
||||
EmailMessage email = new EmailMessage();
|
||||
email.setRecipients(msg.getDisplayTo());
|
||||
email.setCc(msg.getDisplayCC());
|
||||
email.setBcc(msg.getDisplayBCC());
|
||||
email.setSender(getSender(msg.getSenderName(), msg.getSenderEmailAddress()));
|
||||
String toAddress = msg.getDisplayTo();
|
||||
String ccAddress = msg.getDisplayCC();
|
||||
String bccAddress = msg.getDisplayBCC();
|
||||
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.setTextBody(msg.getBody());
|
||||
if (false == msg.getTransportMessageHeaders().isEmpty()) {
|
||||
email.setHeaders("\n-----HEADERS-----\n\n" + msg.getTransportMessageHeaders() + "\n\n---END HEADERS--\n\n");
|
||||
}
|
||||
|
||||
email.setHtmlBody(msg.getBodyHTML());
|
||||
String rtf = "";
|
||||
try {
|
||||
|
@ -48,6 +48,7 @@ import org.sleuthkit.autopsy.ingest.IngestModule.ProcessResult;
|
||||
import org.sleuthkit.autopsy.ingest.IngestMonitor;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
||||
import org.sleuthkit.autopsy.thunderbirdparser.EmailMessage.AttachedEmailMessage;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.AccountFileInstance;
|
||||
@ -72,13 +73,14 @@ import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.Fil
|
||||
* structure and metadata.
|
||||
*/
|
||||
public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ThunderbirdMboxFileIngestModule.class.getName());
|
||||
private final IngestServices services = IngestServices.getInstance();
|
||||
private FileManager fileManager;
|
||||
private IngestJobContext context;
|
||||
private Blackboard blackboard;
|
||||
private CommunicationArtifactsHelper communicationArtifactsHelper;
|
||||
|
||||
|
||||
private static final int MBOX_SIZE_TO_SPLIT = 1048576000;
|
||||
private Case currentCase;
|
||||
|
||||
@ -89,7 +91,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
}
|
||||
|
||||
@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 {
|
||||
this.context = context;
|
||||
try {
|
||||
@ -112,8 +114,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
}
|
||||
|
||||
//skip unalloc
|
||||
if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) ||
|
||||
(abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
|
||||
if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS))
|
||||
|| (abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
@ -124,7 +126,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
// check its signature
|
||||
boolean isMbox = false;
|
||||
boolean isEMLFile = false;
|
||||
|
||||
|
||||
try {
|
||||
byte[] t = new byte[64];
|
||||
if (abstractFile.getSize() > 64) {
|
||||
@ -137,15 +139,15 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
} catch (TskException ex) {
|
||||
logger.log(Level.WARNING, null, ex);
|
||||
}
|
||||
|
||||
|
||||
boolean isPstFile = PstParser.isPstFile(abstractFile);
|
||||
boolean isVcardFile = VcardParser.isVcardFile(abstractFile);
|
||||
|
||||
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
if (isMbox || isEMLFile || isPstFile || isVcardFile ) {
|
||||
|
||||
if (isMbox || isEMLFile || isPstFile || isVcardFile) {
|
||||
try {
|
||||
communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(),
|
||||
EmailParserModuleFactory.getModuleName(), abstractFile, Account.Type.EMAIL);
|
||||
@ -158,7 +160,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
if (isMbox) {
|
||||
return processMBox(abstractFile);
|
||||
}
|
||||
|
||||
|
||||
if (isEMLFile) {
|
||||
return processEMLFile(abstractFile);
|
||||
}
|
||||
@ -166,11 +168,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
if (isPstFile) {
|
||||
return processPst(abstractFile);
|
||||
}
|
||||
|
||||
|
||||
if (isVcardFile) {
|
||||
return processVcard(abstractFile);
|
||||
}
|
||||
|
||||
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
@ -186,7 +188,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
String fileName;
|
||||
try {
|
||||
fileName = getTempPath() + File.separator + abstractFile.getName()
|
||||
+ "-" + String.valueOf(abstractFile.getId());
|
||||
+ "-" + String.valueOf(abstractFile.getId());
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
@ -203,8 +205,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
services.postMessage(msg);
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
try (PstParser parser = new PstParser(services)){
|
||||
|
||||
try (PstParser parser = new PstParser(services)) {
|
||||
try {
|
||||
ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
|
||||
} catch (IOException ex) {
|
||||
@ -214,7 +216,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
|
||||
PstParser.ParseResult result = parser.open(file, abstractFile.getId());
|
||||
|
||||
switch( result) {
|
||||
switch (result) {
|
||||
case OK:
|
||||
Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
|
||||
if (pstMsgIterator != null) {
|
||||
@ -238,20 +240,20 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
// encrypted pst: Add encrypted file artifact
|
||||
try {
|
||||
|
||||
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")));
|
||||
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")));
|
||||
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
blackboard.postArtifact(artifact, EmailParserModuleFactory.getModuleName());
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName());
|
||||
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
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
blackboard.postArtifact(artifact, EmailParserModuleFactory.getModuleName());
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName());
|
||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
|
||||
}
|
||||
break;
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// parsing error: log message
|
||||
postErrorMessage(
|
||||
@ -262,8 +264,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
logger.log(Level.WARNING, String.format("Failed to close temp pst file %s", file.getAbsolutePath()));
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING, String.format("Failed to close temp pst file %s", file.getAbsolutePath()));
|
||||
} finally {
|
||||
file.delete();
|
||||
}
|
||||
@ -294,7 +296,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
String fileName;
|
||||
try {
|
||||
fileName = getTempPath() + File.separator + abstractFile.getName()
|
||||
+ "-" + String.valueOf(abstractFile.getId());
|
||||
+ "-" + String.valueOf(abstractFile.getId());
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
@ -313,7 +315,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
}
|
||||
|
||||
if (abstractFile.getSize() < MBOX_SIZE_TO_SPLIT) {
|
||||
|
||||
|
||||
try {
|
||||
ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
|
||||
} catch (IOException ex) {
|
||||
@ -321,25 +323,25 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
try{
|
||||
try {
|
||||
processMboxFile(file, abstractFile, emailFolder);
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
}finally {
|
||||
} finally {
|
||||
file.delete();
|
||||
}
|
||||
} else {
|
||||
|
||||
List<Long> mboxSplitOffsets = new ArrayList<>();
|
||||
try{
|
||||
List<Long> mboxSplitOffsets = new ArrayList<>();
|
||||
try {
|
||||
mboxSplitOffsets = findMboxSplitOffset(abstractFile, file);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, String.format("Failed finding split offsets for mbox file {0}.", fileName), ex); //NON-NLS
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
long startingOffset = 0;
|
||||
long startingOffset = 0;
|
||||
for (Long mboxSplitOffset : mboxSplitOffsets) {
|
||||
File splitFile = new File(fileName + "-" + mboxSplitOffset);
|
||||
try {
|
||||
@ -348,55 +350,54 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
logger.log(Level.WARNING, "Failed writing split mbox file to disk.", ex); //NON-NLS
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
try{
|
||||
try {
|
||||
processMboxFile(splitFile, abstractFile, emailFolder);
|
||||
startingOffset = mboxSplitOffset;
|
||||
startingOffset = mboxSplitOffset;
|
||||
} finally {
|
||||
splitFile.delete();
|
||||
}
|
||||
|
||||
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
|
||||
private List<Long> findMboxSplitOffset(AbstractFile abstractFile, File file) throws IOException {
|
||||
|
||||
|
||||
List<Long> mboxSplitOffset = new ArrayList<>();
|
||||
|
||||
|
||||
byte[] buffer = new byte[7];
|
||||
ReadContentInputStream in = new ReadContentInputStream(abstractFile);
|
||||
in.skip(MBOX_SIZE_TO_SPLIT);
|
||||
in.skip(MBOX_SIZE_TO_SPLIT);
|
||||
int len = in.read(buffer);
|
||||
while (len != -1) {
|
||||
len = in.read(buffer);
|
||||
if (buffer[0] == 13 && buffer[1] == 10 && buffer[2] == 70 && buffer[3] == 114 &&
|
||||
buffer[4] == 111 && buffer[5] == 109 && buffer[6] == 32) {
|
||||
mboxSplitOffset.add(in.getCurPosition() - 5 );
|
||||
in.skip(MBOX_SIZE_TO_SPLIT);
|
||||
if (buffer[0] == 13 && buffer[1] == 10 && buffer[2] == 70 && buffer[3] == 114
|
||||
&& buffer[4] == 111 && buffer[5] == 109 && buffer[6] == 32) {
|
||||
mboxSplitOffset.add(in.getCurPosition() - 5);
|
||||
in.skip(MBOX_SIZE_TO_SPLIT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return mboxSplitOffset;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
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<>();
|
||||
if(emailIterator != null) {
|
||||
while(emailIterator.hasNext()) {
|
||||
if (emailIterator != null) {
|
||||
while (emailIterator.hasNext()) {
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
return;
|
||||
}
|
||||
EmailMessage emailMessage = emailIterator.next();
|
||||
if(emailMessage != null) {
|
||||
if (emailMessage != null) {
|
||||
emails.add(emailMessage);
|
||||
}
|
||||
}
|
||||
@ -408,13 +409,13 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
abstractFile.getName()), errors);
|
||||
}
|
||||
}
|
||||
processEmails(emails, MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()), abstractFile);
|
||||
} catch(Exception ex) {
|
||||
processEmails(emails, MboxParser.getEmailIterator(emailFolder, file, abstractFile.getId()), abstractFile);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING, String.format("Failed to close mbox temp file %s", file.getAbsolutePath()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse and extract data from a vCard file.
|
||||
*
|
||||
@ -438,8 +439,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
}
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
private ProcessResult processEMLFile(AbstractFile abstractFile) {
|
||||
|
||||
private ProcessResult processEMLFile(AbstractFile abstractFile) {
|
||||
try {
|
||||
EmailMessage message = EMLParser.parse(abstractFile);
|
||||
|
||||
@ -450,13 +451,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
List<AbstractFile> derivedFiles = new ArrayList<>();
|
||||
|
||||
AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
|
||||
BlackboardArtifact msgArtifact = addEmailArtifact(message, abstractFile, accountFileInstanceCache);
|
||||
createEmailArtifact(message, abstractFile, accountFileInstanceCache, derivedFiles);
|
||||
accountFileInstanceCache.clear();
|
||||
|
||||
if ((msgArtifact != null) && (message.hasAttachment())) {
|
||||
derivedFiles.addAll(handleAttachments(message.getAttachments(), abstractFile, msgArtifact));
|
||||
}
|
||||
|
||||
if (derivedFiles.isEmpty() == false) {
|
||||
for (AbstractFile derived : derivedFiles) {
|
||||
services.fireModuleContentEvent(new ModuleContentEvent(derived));
|
||||
@ -493,7 +490,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
|
||||
/**
|
||||
* Get a module output folder.
|
||||
*
|
||||
*
|
||||
* @throws NoCurrentCaseException if there is no open case.
|
||||
*
|
||||
* @return the module output folder
|
||||
@ -527,48 +524,43 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
* @param fullMessageIterator
|
||||
* @param abstractFile
|
||||
*/
|
||||
private void processEmails(List<EmailMessage> partialEmailsForThreading, Iterator<EmailMessage> fullMessageIterator,
|
||||
private void processEmails(List<EmailMessage> partialEmailsForThreading, Iterator<EmailMessage> fullMessageIterator,
|
||||
AbstractFile abstractFile) {
|
||||
|
||||
|
||||
// Create cache for accounts
|
||||
AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
|
||||
|
||||
|
||||
// Putting try/catch around this to catch any exception and still allow
|
||||
// the creation of the artifacts to continue.
|
||||
try{
|
||||
try {
|
||||
EmailMessageThreader.threadMessages(partialEmailsForThreading);
|
||||
} catch(Exception ex) {
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING, String.format("Exception thrown parsing emails from %s", abstractFile.getName()), ex);
|
||||
}
|
||||
|
||||
|
||||
List<AbstractFile> derivedFiles = new ArrayList<>();
|
||||
|
||||
int msgCnt = 0;
|
||||
while(fullMessageIterator.hasNext()) {
|
||||
while (fullMessageIterator.hasNext()) {
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
EmailMessage current = fullMessageIterator.next();
|
||||
|
||||
if(current == null) {
|
||||
|
||||
if (current == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(partialEmailsForThreading.size() > msgCnt) {
|
||||
if (partialEmailsForThreading.size() > msgCnt) {
|
||||
EmailMessage threaded = partialEmailsForThreading.get(msgCnt++);
|
||||
|
||||
if(threaded.getMessageID().equals(current.getMessageID()) &&
|
||||
threaded.getSubject().equals(current.getSubject())) {
|
||||
|
||||
if (threaded.getMessageID().equals(current.getMessageID())
|
||||
&& threaded.getSubject().equals(current.getSubject())) {
|
||||
current.setMessageThreadID(threaded.getMessageThreadID());
|
||||
}
|
||||
}
|
||||
|
||||
BlackboardArtifact msgArtifact = addEmailArtifact(current, abstractFile, accountFileInstanceCache);
|
||||
|
||||
if ((msgArtifact != null) && (current.hasAttachment())) {
|
||||
derivedFiles.addAll(handleAttachments(current.getAttachments(), abstractFile, msgArtifact ));
|
||||
}
|
||||
createEmailArtifact(current, abstractFile, accountFileInstanceCache, derivedFiles);
|
||||
}
|
||||
|
||||
if (derivedFiles.isEmpty() == false) {
|
||||
@ -581,6 +573,21 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
}
|
||||
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
|
||||
* ingest.
|
||||
@ -592,8 +599,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
* @return List of attachments
|
||||
*/
|
||||
@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) {
|
||||
List<AbstractFile> files = new ArrayList<>();
|
||||
List<FileAttachment> fileAttachments = new ArrayList<>();
|
||||
@ -611,11 +618,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
DerivedFile df = fileManager.addDerivedFile(filename, relPath,
|
||||
size, cTime, crTime, aTime, mTime, true, abstractFile, "",
|
||||
EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType);
|
||||
|
||||
|
||||
associateAttachmentWithMesssge(messageArtifact, df);
|
||||
|
||||
|
||||
files.add(df);
|
||||
|
||||
|
||||
fileAttachments.add(new FileAttachment(df));
|
||||
} catch (TskCoreException ex) {
|
||||
postErrorMessage(
|
||||
@ -626,17 +633,16 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
logger.log(Level.INFO, "", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
communicationArtifactsHelper.addAttachments(messageArtifact, new MessageAttachments(fileAttachments, Collections.emptyList()));
|
||||
} catch (TskCoreException ex) {
|
||||
postErrorMessage(
|
||||
NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"),
|
||||
"");
|
||||
logger.log(Level.INFO, "Failed to add attachments to email message.", ex);
|
||||
postErrorMessage(
|
||||
NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"),
|
||||
"");
|
||||
logger.log(Level.INFO, "Failed to add attachments to email message.", ex);
|
||||
}
|
||||
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
@ -652,32 +658,33 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
bba.addAttributes(attributes); //write out to bb
|
||||
return bba;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
*
|
||||
* @return Set<String>: set of email addresses found in the input string
|
||||
*/
|
||||
private Set<String> findEmailAddresess(String input) {
|
||||
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);
|
||||
Set<String> emailAddresses = new HashSet<>();
|
||||
while (m.find()) {
|
||||
emailAddresses.add( m.group());
|
||||
emailAddresses.add(m.group());
|
||||
}
|
||||
return emailAddresses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a blackboard artifact for the given e-mail message.
|
||||
*
|
||||
* @param email The e-mail message.
|
||||
* @param abstractFile The associated file.
|
||||
* @param accountFileInstanceCache The current cache of account instances.
|
||||
*
|
||||
* @param email The e-mail message.
|
||||
* @param abstractFile The associated file.
|
||||
* @param accountFileInstanceCache The current cache of account instances.
|
||||
*
|
||||
* @return The generated e-mail message artifact.
|
||||
*/
|
||||
@Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
|
||||
@ -701,35 +708,33 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
List<String> senderAddressList = new ArrayList<>();
|
||||
String senderAddress;
|
||||
senderAddressList.addAll(findEmailAddresess(from));
|
||||
|
||||
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
AccountFileInstance senderAccountInstance = null;
|
||||
|
||||
if (senderAddressList.size() == 1) {
|
||||
senderAddress = senderAddressList.get(0);
|
||||
try {
|
||||
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) {
|
||||
logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS
|
||||
}
|
||||
} else {
|
||||
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()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
List<String> recipientAddresses = new ArrayList<>();
|
||||
recipientAddresses.addAll(findEmailAddresess(to));
|
||||
recipientAddresses.addAll(findEmailAddresess(cc));
|
||||
recipientAddresses.addAll(findEmailAddresess(bcc));
|
||||
|
||||
|
||||
List<AccountFileInstance> recipientAccountInstances = new ArrayList<>();
|
||||
for (String addr : recipientAddresses) {
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
@ -738,56 +743,54 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
try {
|
||||
AccountFileInstance recipientAccountInstance = accountFileInstanceCache.getAccountInstance(addr);
|
||||
recipientAccountInstances.add(recipientAccountInstance);
|
||||
}
|
||||
catch(TskCoreException ex) {
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Failed to create account for email address " + addr, ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addArtifactAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes);
|
||||
addArtifactAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes);
|
||||
addArtifactAttribute(to, ATTRIBUTE_TYPE.TSK_EMAIL_TO, bbattributes);
|
||||
addArtifactAttribute(subject, ATTRIBUTE_TYPE.TSK_SUBJECT, bbattributes);
|
||||
|
||||
|
||||
addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, bbattributes);
|
||||
addArtifactAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_SENT, bbattributes);
|
||||
|
||||
|
||||
addArtifactAttribute(body, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, bbattributes);
|
||||
|
||||
addArtifactAttribute(((id < 0L) ? NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id)),
|
||||
|
||||
addArtifactAttribute(((id < 0L) ? NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id)),
|
||||
ATTRIBUTE_TYPE.TSK_MSG_ID, bbattributes);
|
||||
|
||||
addArtifactAttribute(((localPath.isEmpty() == false) ? localPath : ""),
|
||||
|
||||
addArtifactAttribute(((localPath.isEmpty() == false) ? localPath : ""),
|
||||
ATTRIBUTE_TYPE.TSK_PATH, bbattributes);
|
||||
|
||||
|
||||
addArtifactAttribute(cc, ATTRIBUTE_TYPE.TSK_EMAIL_CC, bbattributes);
|
||||
addArtifactAttribute(bodyHTML, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, bbattributes);
|
||||
addArtifactAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes);
|
||||
addArtifactAttribute(threadID, ATTRIBUTE_TYPE.TSK_THREAD_ID, bbattributes);
|
||||
|
||||
|
||||
|
||||
try {
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
|
||||
bbart.addAttributes(bbattributes);
|
||||
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// 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()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
blackboard.postArtifact(bbart, EmailParserModuleFactory.getModuleName());
|
||||
blackboard.postArtifact(bbart, EmailParserModuleFactory.getModuleName());
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getArtifactID(), ex); //NON-NLS
|
||||
MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName());
|
||||
@ -798,11 +801,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
|
||||
return bbart;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 bbattributes The Collection to which the attribute will be added.
|
||||
*/
|
||||
@ -814,7 +817,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
|
||||
/**
|
||||
* Add an attribute of a specified type to a supplied Collection.
|
||||
*
|
||||
*
|
||||
* @param stringVal The attribute value.
|
||||
* @param attrType The type of attribute to be added.
|
||||
* @param bbattributes The Collection to which the attribute will be added.
|
||||
@ -824,10 +827,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add an attribute of a specified type to a supplied Collection.
|
||||
*
|
||||
*
|
||||
* @param longVal The attribute value.
|
||||
* @param attrType The type of attribute to be added.
|
||||
* @param bbattributes The Collection to which the attribute will be added.
|
||||
@ -837,49 +840,51 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), longVal));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cache for storing AccountFileInstance.
|
||||
* The idea is that emails will be used multiple times in a file and
|
||||
* we shouldn't do a database lookup each time.
|
||||
* Cache for storing AccountFileInstance. The idea is that emails will be
|
||||
* used multiple times in a file and we shouldn't do a database lookup each
|
||||
* time.
|
||||
*/
|
||||
static private class AccountFileInstanceCache {
|
||||
|
||||
private final Map<String, AccountFileInstance> cacheMap;
|
||||
private final AbstractFile file;
|
||||
private final Case currentCase;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new cache. Caches are linked to a specific file.
|
||||
*
|
||||
* @param file
|
||||
* @param currentCase
|
||||
* @param currentCase
|
||||
*/
|
||||
AccountFileInstanceCache(AbstractFile file, Case currentCase) {
|
||||
cacheMap= new HashMap<>();
|
||||
cacheMap = new HashMap<>();
|
||||
this.file = file;
|
||||
this.currentCase = currentCase;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the account file instance from the cache or the database.
|
||||
*
|
||||
*
|
||||
* @param email The email for this account.
|
||||
*
|
||||
*
|
||||
* @return The corresponding AccountFileInstance
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
AccountFileInstance getAccountInstance(String email) throws TskCoreException {
|
||||
if (cacheMap.containsKey(email)) {
|
||||
return cacheMap.get(email);
|
||||
}
|
||||
|
||||
AccountFileInstance accountInstance =
|
||||
currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email,
|
||||
EmailParserModuleFactory.getModuleName(), file);
|
||||
|
||||
AccountFileInstance accountInstance
|
||||
= currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email,
|
||||
EmailParserModuleFactory.getModuleName(), file);
|
||||
cacheMap.put(email, accountInstance);
|
||||
return accountInstance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears the cache.
|
||||
*/
|
||||
@ -887,10 +892,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
cacheMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Post an error message for the user.
|
||||
*
|
||||
*
|
||||
* @param subj The error subject.
|
||||
* @param details The error details.
|
||||
*/
|
||||
@ -901,7 +906,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
|
||||
/**
|
||||
* Get the IngestServices object.
|
||||
*
|
||||
*
|
||||
* @return The IngestServices object.
|
||||
*/
|
||||
IngestServices getServices() {
|
||||
@ -912,5 +917,5 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
public void shutDown() {
|
||||
// nothing to shut down
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user