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

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

View File

@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.imagewriter.ImageWriterService;
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
import org.sleuthkit.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;
}

View File

@ -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);

View File

@ -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());

View File

@ -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;
}

View File

@ -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));

View File

@ -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);
}
/**

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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.

View File

@ -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<>();

View File

@ -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);
}
}

View File

@ -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));

View File

@ -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);
}

View File

@ -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"/>

View File

@ -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));

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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]"/>

View File

@ -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);

View File

@ -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(

View File

@ -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;
}

View File

@ -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();

View File

@ -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);

View File

@ -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"/>

View File

@ -42,8 +42,8 @@ file.reference.javassist-3.12.1.GA.jar=release/modules/ext/javassist-3.12.1.GA.j
file.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-r4.jar
file.reference.jfxtras-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

View File

@ -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>

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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 : ""));

View File

@ -469,9 +469,9 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
# Ignore TIFF size and hash if extracted from PDFs.
# 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'"

Binary file not shown.

View File

@ -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}" />

View File

@ -7,14 +7,16 @@ file.reference.apache-mime4j-dom-0.8.0.jar=release/modules/ext/apache-mime4j-dom
file.reference.apache-mime4j-mbox-iterator-0.8.0.jar=release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar
file.reference.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

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -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);
}
}
/**

View File

@ -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 {

View File

@ -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
}
}