Merge branch 'AUT-2487_bitlockerInAutopsy' of github.com:gdicristofaro/autopsy into 25_01_17_outstandingPrs

This commit is contained in:
Greg DiCristofaro 2025-01-17 11:05:18 -05:00
commit ebce83a512
No known key found for this signature in database
21 changed files with 1253 additions and 409 deletions

View File

@ -107,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, imageWriterPath, imageDetails.password);
} else {
return;
}
@ -319,8 +319,9 @@ class AddImageTask implements Runnable {
String sha1;
String sha256;
ImageWriterSettings imageWriterSettings;
String password;
ImageDetails(String deviceId, Image image, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, ImageWriterSettings imageWriterSettings) {
ImageDetails(String deviceId, Image image, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, ImageWriterSettings imageWriterSettings, String password) {
this.deviceId = deviceId;
this.image = image;
this.sectorSize = sectorSize;
@ -330,6 +331,7 @@ class AddImageTask implements Runnable {
this.sha1 = sha1;
this.sha256 = sha256;
this.imageWriterSettings = imageWriterSettings;
this.password = password;
}
String getImagePath() {

View File

@ -50,6 +50,8 @@ final class AddImageWizardDataSourceSettingsVisual extends JPanel {
private final Map<String, DataSourceProcessor> datasourceProcessorsMap = new HashMap<>();
private final PanelUpdateListener panelUpdateListener;
private String currentDsp;
/**
@ -60,6 +62,7 @@ final class AddImageWizardDataSourceSettingsVisual extends JPanel {
AddImageWizardDataSourceSettingsVisual(AddImageWizardDataSourceSettingsPanel wizPanel) {
initComponents();
this.wizPanel = wizPanel;
this.panelUpdateListener = new PanelUpdateListener();
typePanel.setLayout(new BorderLayout());
discoverDataSourceProcessors();
currentDsp = ImageDSProcessor.getType(); //default value to the ImageDSProcessor
@ -107,25 +110,38 @@ final class AddImageWizardDataSourceSettingsVisual extends JPanel {
*/
@SuppressWarnings("deprecation")
private void updateCurrentPanel(JPanel panel) {
cleanupUpdateListener(currentPanel);
currentPanel = panel;
typePanel.removeAll();
typePanel.add(currentPanel, BorderLayout.CENTER);
typePanel.validate();
typePanel.repaint();
currentPanel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString())) {
wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid());
}
if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString())) {
wizPanel.moveFocusToNext();
}
}
});
cleanupUpdateListener(currentPanel);
currentPanel.addPropertyChangeListener(panelUpdateListener);
this.wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid());
}
/**
* Removes PanelUpdateListener from panel if found.
* @param panel The panel from which to remove the listener.
*/
private void cleanupUpdateListener(JPanel panel) {
if (panel == null) {
return;
}
PropertyChangeListener[] listeners = panel.getPropertyChangeListeners();
if (listeners == null) {
return;
}
for (PropertyChangeListener listener: listeners) {
if (listener instanceof PanelUpdateListener) {
panel.removePropertyChangeListener(listener);
}
}
}
/**
* Returns the currently selected DS Processor
*
@ -221,4 +237,20 @@ final class AddImageWizardDataSourceSettingsVisual extends JPanel {
protected abstract boolean addSeparatorAfter(JList list, Object value, int index);
}
/**
* A property change listener that responds to update events.
*/
private class PanelUpdateListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString())) {
wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid());
}
if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString())) {
wizPanel.moveFocusToNext();
}
}
}
}

View File

@ -270,3 +270,6 @@ LocalFilesPanel.timestampToIncludeLabel.text=Timestamps To Include:
LocalFilesPanel.accessTimeCheckBox.text=Access Time - Can be changed when the file is opened
LocalFilesPanel.timeStampToIncludeLabel.text=Timestamps To Include:
LocalFilesPanel.timeStampNoteLabel.text=NOTE: Time stamps may have changed when the files were copied to the current location.
ImageFilePanel.passwordLabel.text=Bitlocker Password (optional):
ImageFilePanel.passwordTextField.text=
ImageFilePanel.loadingLabel.text=loading...

View File

@ -151,7 +151,7 @@ GeneralFilter.encaseImageDesc.text=Encase Images (*.e01)
GeneralFilter.executableDesc.text=Executables (*.exe)
GeneralFilter.graphicImageDesc.text=Images (*.png, *.jpg, *.jpeg, *.gif, *.bmp)
GeneralFilter.rawImageDesc.text=Raw Images (*.img, *.dd, *.001, *.aa, *.raw, *.bin)
GeneralFilter.virtualMachineImageDesc.text=Virtual Machines (*.vmdk, *.vhd)
GeneralFilter.virtualMachineImageDesc.text=Virtual Machines (*.vmdk, *.vhd, *.vhdx)
ImageFilePanel.000.confirmationMessage=The selected file has extenson .001 but there is a .000 file in the sequence of raw images.\nShould the .000 file be used as the start, instead of the selected .001 file?\n
ImageFilePanel.moduleErr=Module Error
ImageFilePanel.moduleErr.msg=A module caused an error listening to ImageFilePanel updates. See log to determine which module. Some data could be incomplete.\n
@ -159,6 +159,10 @@ ImageFilePanel.validatePanel.dataSourceOnCDriveError=Warning: Path to multi-user
ImageFilePanel.validatePanel.invalidMD5=Invalid MD5 hash
ImageFilePanel.validatePanel.invalidSHA1=Invalid SHA1 hash
ImageFilePanel.validatePanel.invalidSHA256=Invalid SHA256 hash
# {0} - imageOpenError
ImageFilePanel_validatePanel_imageOpenError=<html><body><p>An error occurred while opening the image:{0}</p></body></html>
ImageFilePanel_validatePanel_unknownError=<html><body><p>An unknown error occurred while attempting to validate the image</p></body></html>
ImageFilePanel_validatePanel_unknownErrorMsg=<unknown>
IngestJobInfoPanel.IngestJobTableModel.EndTime.header=End Time
IngestJobInfoPanel.IngestJobTableModel.IngestStatus.header=Ingest Status
IngestJobInfoPanel.IngestJobTableModel.StartTime.header=Start Time
@ -504,3 +508,6 @@ LocalFilesPanel.timestampToIncludeLabel.text=Timestamps To Include:
LocalFilesPanel.accessTimeCheckBox.text=Access Time - Can be changed when the file is opened
LocalFilesPanel.timeStampToIncludeLabel.text=Timestamps To Include:
LocalFilesPanel.timeStampNoteLabel.text=NOTE: Time stamps may have changed when the files were copied to the current location.
ImageFilePanel.passwordLabel.text=Bitlocker Password (optional):
ImageFilePanel.passwordTextField.text=
ImageFilePanel.loadingLabel.text=loading...

View File

@ -27,6 +27,7 @@ import java.util.List;
import java.util.logging.Level;
import java.util.UUID;
import javax.swing.filechooser.FileFilter;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
@ -82,6 +83,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
private String sha1;
private String sha256;
private Host host = null;
private String password;
static {
filtersList.add(allFilter);
@ -162,7 +164,9 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
*/
@Override
public boolean isPanelValid() {
return configPanel.validatePanel();
// before attempting to validate the panel (a potentially long running operation),
// check if the validation is loading or on delay.
return !configPanel.isValidationLoading() && configPanel.validatePanel();
}
/**
@ -181,7 +185,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
*/
@Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
run(null, progressMonitor, callback);
run(null, null, progressMonitor, callback);
}
/**
@ -201,12 +205,19 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
*/
@Override
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
run(null, host, progressMonitor, callback);
}
@Override
public void run(String password, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
ingestStream = new DefaultIngestStream();
readConfigSettings();
this.host = host;
this.password = StringUtils.defaultString(password, this.password);
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.host);
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.password, 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<>();
@ -215,117 +226,9 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
return;
}
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progressMonitor, callback);
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, this.password, progressMonitor, callback);
}
/**
* Adds a data source to the case database using a background task in a
* separate thread and the settings provided by the selection and
* configuration panel. Files found during ingest will be sent directly to
* the IngestStream provided. Returns as soon as the background task is
* started. The background task uses a callback object to signal task
* completion and return results.
*
* This method should not be called unless isPanelValid returns true, and
* should only be called for DSPs that support ingest streams.
*
* @param settings The ingest job settings.
* @param progress Progress monitor that will be used by the background task
* to report progress.
* @param callBack Callback that will be used by the background task to
* return results.
*/
@Override
public void runWithIngestStream(IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
DataSourceProcessorCallback callBack) {
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;
// Set up the data source before creating the ingest stream
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.host);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
// Now initialize the ingest stream
try {
ingestStream = IngestManager.getInstance().openIngestStream(image, settings);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error starting ingest modules", ex);
// There was an error with ingest, but the data source has already been added
// so proceed with the defaultIngestStream. Code in openIngestStream
// should have caused a dialog to popup with the errors.
ingestStream = new DefaultIngestStream();
}
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progress, callBack);
}
/**
* Store the options from the config panel.
*/
private void readConfigSettings() {
configPanel.storeSettings();
deviceId = UUID.randomUUID().toString();
imagePath = configPanel.getContentPaths();
sectorSize = configPanel.getSectorSize();
timeZone = configPanel.getTimeZone();
ignoreFatOrphanFiles = configPanel.getNoFatOrphans();
md5 = configPanel.getMd5();
if (md5.isEmpty()) {
md5 = null;
}
sha1 = configPanel.getSha1();
if (sha1.isEmpty()) {
sha1 = null;
}
sha256 = configPanel.getSha256();
if (sha256.isEmpty()) {
sha256 = null;
}
}
/**
* Check if this DSP supports ingest streams.
*
* @return True if this DSP supports an ingest stream, false otherwise.
*/
@Override
public boolean supportsIngestStream() {
return true;
}
/**
* Adds a data source to the case database using a background task in a
@ -352,7 +255,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
ingestStream = new DefaultIngestStream();
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId);
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId, null, null);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
@ -361,9 +264,134 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
return;
}
doAddImageProcess(deviceId, imagePath, 0, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callback);
doAddImageProcess(deviceId, imagePath, 0, timeZone, ignoreFatOrphanFiles, null, null, null, this.password, progressMonitor, callback);
}
/**
* Adds a data source to the case database using a background task in a
* separate thread and the settings provided by the selection and
* configuration panel. Files found during ingest will be sent directly to
* the IngestStream provided. Returns as soon as the background task is
* started. The background task uses a callback object to signal task
* completion and return results.
*
* This method should not be called unless isPanelValid returns true, and
* should only be called for DSPs that support ingest streams.
*
* @param settings The ingest job settings.
* @param progress Progress monitor that will be used by the background task
* to report progress.
* @param callBack Callback that will be used by the background task to
* return results.
*/
@Override
public void runWithIngestStream(IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
DataSourceProcessorCallback callBack) {
runWithIngestStream(null, 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) {
runWithIngestStream(null, host, settings, progress, callBack);
}
@Override
public void runWithIngestStream(String password, Host host, IngestJobSettings settings,
DataSourceProcessorProgressMonitor progress, DataSourceProcessorCallback callBack) {
// Read the settings from the wizard
readConfigSettings();
this.host = host;
this.password = StringUtils.defaultString(password, this.password);
// Set up the data source before creating the ingest stream
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.password, this.host);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
}
// Now initialize the ingest stream
try {
ingestStream = IngestManager.getInstance().openIngestStream(image, settings);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error starting ingest modules", ex);
// There was an error with ingest, but the data source has already been added
// so proceed with the defaultIngestStream. Code in openIngestStream
// should have caused a dialog to popup with the errors.
ingestStream = new DefaultIngestStream();
}
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, this.password, progress, callBack);
}
/**
* Store the options from the config panel.
*/
private void readConfigSettings() {
configPanel.storeSettings();
deviceId = UUID.randomUUID().toString();
imagePath = configPanel.getContentPaths();
sectorSize = configPanel.getSectorSize();
timeZone = configPanel.getTimeZone();
ignoreFatOrphanFiles = configPanel.getNoFatOrphans();
md5 = configPanel.getMd5();
if (md5.isEmpty()) {
md5 = null;
}
sha1 = configPanel.getSha1();
if (sha1.isEmpty()) {
sha1 = null;
}
sha256 = configPanel.getSha256();
if (sha256.isEmpty()) {
sha256 = null;
}
this.password = configPanel.getPassword();
if (this.password.isEmpty()) {
password = null;
}
}
/**
* Check if this DSP supports ingest streams.
*
* @return True if this DSP supports an ingest stream, false otherwise.
*/
@Override
public boolean supportsIngestStream() {
return true;
}
/**
* Adds a data source to the case database using a background task in a
* separate thread and the given settings instead of those provided by the
@ -389,11 +417,12 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
* @param md5 The MD5 hash of the image, may be null.
* @param sha1 The SHA-1 hash of the image, may be null.
* @param sha256 The SHA-256 hash of the image, may be null.
* @param password Password for image decryption. May be null.
* @param progressMonitor Progress monitor for reporting progress
* during processing.
* @param callback Callback to call when processing is done.
*/
private void doAddImageProcess(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
private void doAddImageProcess(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, String password, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
// If the data source or ingest stream haven't been initialized, stop processing
if (ingestStream == null) {
@ -413,7 +442,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, null, password);
addImageTask = new AddImageTask(imageDetails,
progressMonitor,
new StreamingAddDataSourceCallbacks(ingestStream),
@ -449,6 +478,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
timeZone = null;
ignoreFatOrphanFiles = false;
host = null;
password = null;
configPanel.reset();
}
@ -463,6 +493,13 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
@Override
public int canProcess(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
return canProcess(dataSourcePath, null);
}
@Override
public int canProcess(Path dataSourcePath, String password) throws AutoIngestDataSourceProcessorException {
// check file extension for supported types
if (!isAcceptedByFiler(dataSourcePath.toFile(), filtersList)) {
@ -470,11 +507,21 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
}
try {
if (password == null) {
// verify that the image has a file system that TSK can process
if (!DataSourceUtils.imageHasFileSystem(dataSourcePath)) {
// image does not have a file system that TSK can process
return 0;
}
} else {
// verify that the image has a file system that TSK can process
if (!DataSourceUtils.imageHasFileSystem(dataSourcePath, password)) {
// image does not have a file system that TSK can process
return 0;
}
}
} catch (Exception ex) {
throw new AutoIngestDataSourceProcessorException("Exception inside canProcess() method", ex);
}
@ -485,23 +532,29 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
@Override
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
process(deviceId, dataSourcePath, null, progressMonitor, callBack);
process(deviceId, dataSourcePath, null, null, progressMonitor, callBack);
}
@Override
public void process(String deviceId, Path dataSourcePath, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
process(deviceId, dataSourcePath, null, host, progressMonitor, callBack);
}
@Override
public void process(String deviceId, Path dataSourcePath, String password, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
// this method does not use the config panel
this.deviceId = deviceId;
this.imagePath = dataSourcePath.toString();
this.sectorSize = 0;
this.timeZone = Calendar.getInstance().getTimeZone().getID();
this.password = StringUtils.defaultString(password, this.password);
this.host = host;
this.ignoreFatOrphanFiles = false;
ingestStream = new DefaultIngestStream();
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId, host);
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId, this.password, host);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
@ -510,28 +563,36 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
return;
}
doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callBack);
doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, this.password, progressMonitor, callBack);
}
@Override
public IngestStream processWithIngestStream(String deviceId, Path dataSourcePath, IngestJobSettings settings, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
return processWithIngestStream(deviceId, dataSourcePath, null, settings, progressMonitor, callBack);
return processWithIngestStream(deviceId, dataSourcePath, null, null, settings, progressMonitor, callBack);
}
@Override
public IngestStream processWithIngestStream(String deviceId, Path dataSourcePath, Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
return processWithIngestStream(deviceId, dataSourcePath, null, host, settings, progressMonitor, callBack);
}
@Override
public IngestStream processWithIngestStream(String deviceId, Path dataSourcePath, String password, Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
// this method does not use the config panel
this.deviceId = deviceId;
this.imagePath = dataSourcePath.toString();
this.sectorSize = 0;
this.timeZone = Calendar.getInstance().getTimeZone().getID();
this.host = host;
this.password = password;
this.ignoreFatOrphanFiles = false;
// Set up the data source before creating the ingest stream
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, host);
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, password, host);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
@ -551,8 +612,10 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
return null;
}
doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callBack);
doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, password, progressMonitor, callBack);
return ingestStream;
}
}

View File

@ -19,108 +19,10 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-38,0,0,2,46"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="pathTextField" max="32767" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
<Component id="noFatOrphansCheckbox" alignment="0" min="-2" pref="262" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="368" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="errorLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0">
<Group type="102" attributes="0">
<Component id="timeZoneLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="timeZoneComboBox" min="-2" pref="455" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="sectorSizeLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="sectorSizeComboBox" min="-2" pref="455" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="md5HashLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="md5HashTextField" min="-2" pref="455" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="sha1HashLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="sha1HashTextField" min="-2" pref="455" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="sha256HashLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="sha256HashTextField" min="-2" pref="455" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="hashValuesNoteLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="hashValuesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="noFatOrphansCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="timeZoneComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="timeZoneLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="sectorSizeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="sectorSizeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
<Component id="hashValuesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="md5HashTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="md5HashLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="sha1HashTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="sha1HashLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="sha256HashTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="sha256HashLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="hashValuesNoteLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="pathLabel">
<Properties>
@ -128,6 +30,11 @@
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="ImageFilePanel.pathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="3" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="browseButton">
<Properties>
@ -138,6 +45,11 @@
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="2" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JTextField" name="pathTextField">
<Properties>
@ -145,6 +57,11 @@
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="ImageFilePanel.pathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="timeZoneLabel">
<Properties>
@ -152,6 +69,11 @@
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="ImageFilePanel.timeZoneLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JComboBox" name="timeZoneComboBox">
<Properties>
@ -163,6 +85,11 @@
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="3" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JCheckBox" name="noFatOrphansCheckbox">
<Properties>
@ -173,6 +100,11 @@
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="ImageFilePanel.noFatOrphansCheckbox.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="3" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="errorLabel">
<Properties>
@ -182,7 +114,22 @@
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="ImageFilePanel.errorLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="verticalAlignment" type="int" value="1"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[500, 60]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 60]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="11" gridWidth="3" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="sectorSizeLabel">
<Properties>
@ -190,6 +137,11 @@
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="ImageFilePanel.sectorSizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JComboBox" name="sectorSizeComboBox">
<Properties>
@ -200,6 +152,11 @@
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="4" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="sha256HashLabel">
<Properties>
@ -208,6 +165,11 @@
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="9" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JTextField" name="sha256HashTextField">
<Properties>
@ -216,6 +178,11 @@
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="9" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JTextField" name="sha1HashTextField">
<Properties>
@ -224,6 +191,11 @@
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="8" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JTextField" name="md5HashTextField">
<Properties>
@ -232,6 +204,11 @@
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="7" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="sha1HashLabel">
<Properties>
@ -240,6 +217,11 @@
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="8" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="md5HashLabel">
<Properties>
@ -248,6 +230,11 @@
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="7" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="hashValuesLabel">
<Properties>
@ -256,6 +243,11 @@
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="6" gridWidth="3" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="hashValuesNoteLabel">
<Properties>
@ -264,6 +256,84 @@
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="10" gridWidth="3" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="passwordLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="ImageFilePanel.passwordLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="5" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JTextField" name="passwordTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="ImageFilePanel.passwordTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="5" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JPanel" name="spacer">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="12" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Container>
<Component class="javax.swing.JLabel" name="loadingLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/working_spinner.gif"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="ImageFilePanel.loadingLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="verticalAlignment" type="int" value="1"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[500, 60]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 60]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="11" gridWidth="3" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -18,9 +18,16 @@
*/
package org.sleuthkit.autopsy.casemodule;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.File;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import java.util.logging.Level;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
@ -32,11 +39,14 @@ import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.DriveUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PathValidator;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
import org.sleuthkit.datamodel.HashUtility;
import org.sleuthkit.datamodel.SleuthkitJNI;
import org.sleuthkit.datamodel.SleuthkitJNI.TestOpenImageResult;
/**
* Panel for adding an image file such as .img, .E0x, .00x, etc. Allows the user
@ -44,7 +54,9 @@ import org.sleuthkit.datamodel.HashUtility;
* files in FAT32.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class ImageFilePanel extends JPanel implements DocumentListener {
public class ImageFilePanel extends JPanel {
private static final Logger logger = Logger.getLogger(AddImageTask.class.getName());
private static final long serialVersionUID = 1L;
private static final String PROP_LASTIMAGE_PATH = "LBL_LastImage_PATH"; //NON-NLS
@ -54,6 +66,15 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
private final String contextName;
private final List<FileFilter> fileChooserFilters;
private static int VALIDATE_TIMEOUT_MILLIS = 1000;
static ScheduledThreadPoolExecutor delayedValidationService = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("ImageFilePanel delayed validation").build());
private final ReentrantLock validationWaitingLock = new ReentrantLock();
private final ReentrantLock validationLock = new ReentrantLock();
private Runnable validateAction = null;
private Future<?> validateFuture = null;
/**
* Creates new form ImageFilePanel
*
@ -76,6 +97,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
sectorSizeComboBox.setSelectedIndex(0);
errorLabel.setVisible(false);
loadingLabel.setVisible(false);
this.fileChooserFilters = fileChooserFilters;
}
@ -105,11 +127,17 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
*/
public static synchronized ImageFilePanel createInstance(String context, List<FileFilter> fileChooserFilters) {
ImageFilePanel instance = new ImageFilePanel(context, fileChooserFilters);
DocumentListener delayedValidationListener = instance.new DelayedValidationDocListener();
// post-constructor initialization of listener support without leaking references of uninitialized objects
instance.getPathTextField().getDocument().addDocumentListener(instance);
instance.getMd5TextFieldField().getDocument().addDocumentListener(instance);
instance.getSha1TextField().getDocument().addDocumentListener(instance);
instance.getSha256TextField().getDocument().addDocumentListener(instance);
for (JTextField textField: List.of(
instance.getPathTextField(),
instance.getMd5TextFieldField(),
instance.getSha1TextField(),
instance.getSha256TextField(),
instance.getPasswordTextField())) {
textField.getDocument().addDocumentListener(delayedValidationListener);
}
return instance;
}
@ -129,6 +157,10 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
return sha256HashTextField;
}
private JTextField getPasswordTextField() {
return passwordTextField;
}
private JFileChooser getChooser() {
if(fileChooser == null) {
fileChooser = fileChooserHelper.getChooser();
@ -151,6 +183,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
pathLabel = new javax.swing.JLabel();
browseButton = new javax.swing.JButton();
@ -169,11 +202,25 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
md5HashLabel = new javax.swing.JLabel();
hashValuesLabel = new javax.swing.JLabel();
hashValuesNoteLabel = new javax.swing.JLabel();
passwordLabel = new javax.swing.JLabel();
passwordTextField = new javax.swing.JTextField();
javax.swing.JPanel spacer = new javax.swing.JPanel();
loadingLabel = new javax.swing.JLabel();
setMinimumSize(new java.awt.Dimension(0, 65));
setPreferredSize(new java.awt.Dimension(403, 65));
setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.pathLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
add(pathLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.browseButton.text")); // NOI18N
browseButton.addActionListener(new java.awt.event.ActionListener() {
@ -181,125 +228,227 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
browseButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
add(browseButton, gridBagConstraints);
pathTextField.setText(org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.pathTextField.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(pathTextField, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.timeZoneLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(timeZoneLabel, gridBagConstraints);
timeZoneComboBox.setMaximumRowCount(30);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
add(timeZoneComboBox, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(noFatOrphansCheckbox, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.noFatOrphansCheckbox.text")); // NOI18N
noFatOrphansCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.noFatOrphansCheckbox.toolTipText")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(noFatOrphansCheckbox, gridBagConstraints);
errorLabel.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.errorLabel.text")); // NOI18N
errorLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP);
errorLabel.setMaximumSize(new java.awt.Dimension(500, 60));
errorLabel.setMinimumSize(new java.awt.Dimension(200, 20));
errorLabel.setPreferredSize(new java.awt.Dimension(200, 60));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 11;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
add(errorLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sectorSizeLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.sectorSizeLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 4;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(sectorSizeLabel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 4;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
add(sectorSizeComboBox, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sha256HashLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.sha256HashLabel.text")); // NOI18N
sha256HashLabel.setEnabled(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 9;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(sha256HashLabel, gridBagConstraints);
sha256HashTextField.setText(org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.sha256HashTextField.text")); // NOI18N
sha256HashTextField.setEnabled(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 9;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
add(sha256HashTextField, gridBagConstraints);
sha1HashTextField.setText(org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.sha1HashTextField.text")); // NOI18N
sha1HashTextField.setEnabled(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 8;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
add(sha1HashTextField, gridBagConstraints);
md5HashTextField.setText(org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.md5HashTextField.text")); // NOI18N
md5HashTextField.setEnabled(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 7;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
add(md5HashTextField, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sha1HashLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.sha1HashLabel.text")); // NOI18N
sha1HashLabel.setEnabled(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 8;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(sha1HashLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(md5HashLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.md5HashLabel.text")); // NOI18N
md5HashLabel.setEnabled(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 7;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(md5HashLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(hashValuesLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.hashValuesLabel.text")); // NOI18N
hashValuesLabel.setEnabled(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 6;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
add(hashValuesLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(hashValuesNoteLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.hashValuesNoteLabel.text")); // NOI18N
hashValuesNoteLabel.setEnabled(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 10;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(hashValuesNoteLabel, gridBagConstraints);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(pathTextField)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(browseButton)
.addGap(2, 2, 2))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(pathLabel)
.addComponent(noFatOrphansCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 262, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 368, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(errorLabel)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addGroup(layout.createSequentialGroup()
.addComponent(timeZoneLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 455, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addComponent(sectorSizeLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(sectorSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 455, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addComponent(md5HashLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(md5HashTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 455, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addComponent(sha1HashLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(sha1HashTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 455, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addComponent(sha256HashLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(sha256HashTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 455, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addComponent(hashValuesNoteLabel)
.addComponent(hashValuesLabel))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
org.openide.awt.Mnemonics.setLocalizedText(passwordLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.passwordLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 5;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(passwordLabel, gridBagConstraints);
passwordTextField.setText(org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.passwordTextField.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 5;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
add(passwordTextField, gridBagConstraints);
javax.swing.GroupLayout spacerLayout = new javax.swing.GroupLayout(spacer);
spacer.setLayout(spacerLayout);
spacerLayout.setHorizontalGroup(
spacerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(pathLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(browseButton)
.addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(noFatOrphansCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(timeZoneLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(sectorSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(sectorSizeLabel))
.addGap(39, 39, 39)
.addComponent(hashValuesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(md5HashTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(md5HashLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(sha1HashTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(sha1HashLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(sha256HashTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(sha256HashLabel))
.addGap(18, 18, 18)
.addComponent(hashValuesNoteLabel)
.addGap(18, 18, 18)
.addComponent(errorLabel)
.addContainerGap(51, Short.MAX_VALUE))
spacerLayout.setVerticalGroup(
spacerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 12;
gridBagConstraints.weighty = 1.0;
add(spacer, gridBagConstraints);
loadingLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/working_spinner.gif"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(loadingLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.loadingLabel.text")); // NOI18N
loadingLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP);
loadingLabel.setMaximumSize(new java.awt.Dimension(500, 60));
loadingLabel.setMinimumSize(new java.awt.Dimension(200, 20));
loadingLabel.setPreferredSize(new java.awt.Dimension(200, 60));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 11;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
add(loadingLabel, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
@NbBundle.Messages({"ImageFilePanel.000.confirmationMessage=The selected file"
@ -349,9 +498,12 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
private javax.swing.JLabel errorLabel;
private javax.swing.JLabel hashValuesLabel;
private javax.swing.JLabel hashValuesNoteLabel;
private javax.swing.JLabel loadingLabel;
private javax.swing.JLabel md5HashLabel;
private javax.swing.JTextField md5HashTextField;
private javax.swing.JCheckBox noFatOrphansCheckbox;
private javax.swing.JLabel passwordLabel;
private javax.swing.JTextField passwordTextField;
private javax.swing.JLabel pathLabel;
private javax.swing.JTextField pathTextField;
private javax.swing.JComboBox<String> sectorSizeComboBox;
@ -437,12 +589,34 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
return this.sha256HashTextField.getText();
}
String getPassword() {
return this.passwordTextField.getText();
}
public void reset() {
//reset the UI elements to default
pathTextField.setText(null);
this.md5HashTextField.setText(null);
this.sha1HashTextField.setText(null);
this.sha256HashTextField.setText(null);
this.passwordTextField.setText(null);
}
/**
* Sets UI enabled state.
*
* @param enabled True
*/
private void setUIEnabled(boolean enabled, boolean validNonE01) {
this.browseButton.setEnabled(enabled);
this.noFatOrphansCheckbox.setEnabled(enabled);
this.passwordTextField.setEnabled(enabled);
this.pathTextField.setEnabled(enabled);
this.sectorSizeComboBox.setEnabled(enabled);
this.md5HashTextField.setEnabled(enabled && validNonE01);
this.sha1HashTextField.setEnabled(enabled && validNonE01);
this.sha256HashTextField.setEnabled(enabled && validNonE01);
this.timeZoneComboBox.setEnabled(enabled);
}
/**
@ -454,44 +628,99 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
"ImageFilePanel.validatePanel.dataSourceOnCDriveError=Warning: Path to multi-user data source is on \"C:\" drive",
"ImageFilePanel.validatePanel.invalidMD5=Invalid MD5 hash",
"ImageFilePanel.validatePanel.invalidSHA1=Invalid SHA1 hash",
"ImageFilePanel.validatePanel.invalidSHA256=Invalid SHA256 hash",})
"ImageFilePanel.validatePanel.invalidSHA256=Invalid SHA256 hash",
"# {0} - imageOpenError",
"ImageFilePanel_validatePanel_imageOpenError=<html><body><p>An error occurred while opening the image:{0}</p></body></html>",
"ImageFilePanel_validatePanel_unknownErrorMsg=<unknown>",
"ImageFilePanel_validatePanel_unknownError=<html><body><p>An unknown error occurred while attempting to validate the image</p></body></html>"
})
public boolean validatePanel() {
errorLabel.setVisible(false);
return runWithLock(this.validationLock, () -> {
boolean validNonE01 = true;
try {
// acquire field values at the beginning to minimize chance of changing while validating.
String path = getContentPaths();
if (!isImagePathValid()) {
validNonE01 = isValidNonE01(path);
setUIEnabled(false, validNonE01);
String md5 = getMd5();
String sha1 = getSha1();
String sha256 = getSha256();
String password = getPassword();
if (!isImagePathValid(path)) {
showError(null);
return false;
}
if (!StringUtils.isBlank(getMd5()) && !HashUtility.isValidMd5Hash(getMd5())) {
errorLabel.setVisible(true);
errorLabel.setText(Bundle.ImageFilePanel_validatePanel_invalidMD5());
if (!StringUtils.isBlank(md5) && !HashUtility.isValidMd5Hash(md5)) {
showError(Bundle.ImageFilePanel_validatePanel_invalidMD5());
return false;
}
if (!StringUtils.isBlank(getSha1()) && !HashUtility.isValidSha1Hash(getSha1())) {
errorLabel.setVisible(true);
errorLabel.setText(Bundle.ImageFilePanel_validatePanel_invalidSHA1());
if (!StringUtils.isBlank(sha1) && !HashUtility.isValidSha1Hash(sha1)) {
showError(Bundle.ImageFilePanel_validatePanel_invalidSHA1());
return false;
}
if (!StringUtils.isBlank(getSha256()) && !HashUtility.isValidSha256Hash(getSha256())) {
errorLabel.setVisible(true);
errorLabel.setText(Bundle.ImageFilePanel_validatePanel_invalidSHA256());
if (!StringUtils.isBlank(sha256) && !HashUtility.isValidSha256Hash(sha256)) {
showError(Bundle.ImageFilePanel_validatePanel_invalidSHA256());
return false;
}
try {
TestOpenImageResult testResult = SleuthkitJNI.testOpenImage(path, password);
if (!testResult.wasSuccessful()) {
showError(Bundle.ImageFilePanel_validatePanel_imageOpenError(
StringUtils.defaultIfBlank(
testResult.getMessage(),
Bundle.ImageFilePanel_validatePanel_unknownErrorMsg())));
return false;
}
} catch (Throwable t) {
logger.log(Level.SEVERE, "An unknown error occurred test opening image: " + path, t);
showError(Bundle.ImageFilePanel_validatePanel_unknownError());
return false;
}
if (!PathValidator.isValidForCaseType(path, Case.getCurrentCase().getCaseType())) {
errorLabel.setVisible(true);
errorLabel.setText(Bundle.ImageFilePanel_validatePanel_dataSourceOnCDriveError());
showError(Bundle.ImageFilePanel_validatePanel_dataSourceOnCDriveError());
} else {
showError(null);
}
return true;
} finally {
setUIEnabled(true, validNonE01);
}
});
}
private boolean isImagePathValid() {
String path = getContentPaths();
/**
* Show an error message if error message is non-empty. Otherwise, hide
* error message. Either way, hide loading label.
*
* @param errorMessage The error message to show or null for no error.
*/
private void showError(String errorMessage) {
loadingLabel.setVisible(false);
if (StringUtils.isNotBlank(errorMessage)) {
errorLabel.setVisible(true);
errorLabel.setText(errorMessage);
} else {
errorLabel.setVisible(false);
errorLabel.setText("");
}
}
/**
* Returns true if path is valid for processing.
*
* @param path The path.
* @return True if valid for processing.
*/
private boolean isImagePathValid(String path) {
if (StringUtils.isBlank(path) || (!(new File(path).isFile() || DriveUtils.isPhysicalDrive(path) || DriveUtils.isPartition(path)))) {
return false;
}
@ -499,6 +728,15 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
return true;
}
/**
* Returns true if the path is a valid image that is not an E01.
* @param path The path.
* @return True if valid image and not E01.
*/
private boolean isValidNonE01(String path) {
return StringUtils.isNotBlank(path) && isImagePathValid(path) && !path.toLowerCase().endsWith(".e01");
}
public void storeSettings() {
String imagePathName = getContentPaths();
if (null != imagePathName) {
@ -514,21 +752,6 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
}
}
@Override
public void insertUpdate(DocumentEvent e) {
updateHelper();
}
@Override
public void removeUpdate(DocumentEvent e) {
updateHelper();
}
@Override
public void changedUpdate(DocumentEvent e) {
updateHelper();
}
/**
* Update functions are called by the pathTextField which has this set as
* it's DocumentEventListener. Each update function fires a property change
@ -539,7 +762,8 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
"ImageFilePanel.moduleErr.msg=A module caused an error listening to ImageFilePanel updates."
+ " See log to determine which module. Some data could be incomplete.\n"})
private void updateHelper() {
if (isImagePathValid() && !getContentPaths().toLowerCase().endsWith(".e01")) {
String path = getContentPaths();
if (isValidNonE01(path)) {
setHashValuesComponentsEnabled(true);
} else {
setHashValuesComponentsEnabled(false);
@ -554,4 +778,119 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
public void select() {
pathTextField.requestFocusInWindow();
}
/**
* Runs the supplier action with the reentrant lock or blocks until
* acquired.
*
* @param <T>
* @param lock The reentrant lock.
* @param action The action to run.
* @return The value of the supplier.
*/
private <T> T runWithLock(ReentrantLock lock, Supplier<T> action) {
try {
lock.lock();
return action.get();
} finally {
lock.unlock();
}
}
/**
* @return True if the panel is on a delay for validating (i.e. typing a
* password for bitlocker).
*/
public boolean isValidationLoading() {
return runWithLock(this.validationWaitingLock, () -> this.validateFuture != null
&& !this.validateFuture.isCancelled()
&& !this.validateFuture.isDone());
}
/**
* This class validates on a delay canceling any tasks previously scheduled
* so that password validation doesn't lock up the system.
*/
private class DelayedValidationDocListener implements DocumentListener {
@Override
public void insertUpdate(DocumentEvent e) {
delayValidate();
}
@Override
public void removeUpdate(DocumentEvent e) {
delayValidate();
}
@Override
public void changedUpdate(DocumentEvent e) {
delayValidate();
}
/**
* Run validation on a delay to avoid password checking too many times
* while typing.
*/
private void delayValidate() {
boolean triggerUpdate = runWithLock(validationWaitingLock, () -> {
boolean toRetTriggerUpdate = false;
if (isValidationLoading()) {
validateFuture.cancel(true);
toRetTriggerUpdate = true;
}
validateAction = new ValidationRunnable();
validateFuture = delayedValidationService.schedule(
validateAction,
VALIDATE_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
return toRetTriggerUpdate;
});
errorLabel.setVisible(false);
loadingLabel.setVisible(true);
// trigger invalidation after setting up new runnable if not already triggered
if (triggerUpdate) {
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
}
}
/**
* Runnable to run the updateHelper if the validation action remains
* this runnable.
*/
private class ValidationRunnable implements Runnable {
@Override
public void run() {
boolean isRunningAction = runWithLock(validationWaitingLock, () -> {
if (validateAction != this) {
return false;
}
// set the validation action to null to indicate that this is done running and can be validated.
validateAction = null;
validateFuture = null;
return true;
});
if (!isRunningAction) {
return;
} else if (Thread.interrupted()) {
return;
}
ImageFilePanel.this.updateHelper();
}
}
}
}

View File

@ -59,6 +59,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
private Host host;
private ImageWriterSettings imageWriterSettings;
private boolean ignoreFatOrphanFiles;
private String password;
/**
* Constructs a local drive data source processor that implements the
@ -135,7 +136,12 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
*/
@Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
run(null, progressMonitor, callback);
run(null, null, progressMonitor, callback);
}
@Override
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
run(null, host, progressMonitor, callback);
}
/**
@ -154,7 +160,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
* to return results.
*/
@Override
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
public void run(String password, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
deviceId = UUID.randomUUID().toString();
drivePath = configPanel.getContentPath();
sectorSize = configPanel.getSectorSize();
@ -167,12 +173,13 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
}
this.host = host;
this.password = password;
Image image;
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{drivePath}, sectorSize,
timeZone, null, null, null, deviceId, this.host);
timeZone, null, null, null, deviceId, this.password, 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<>();
@ -182,7 +189,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, imageWriterSettings, this.password),
progressMonitor,
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));
@ -241,7 +248,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{drivePath}, sectorSize,
timeZone, null, null, null, deviceId);
timeZone, null, null, null, deviceId, this.password, null);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding local disk with path " + drivePath + " to database", ex);
final List<String> errors = new ArrayList<>();
@ -250,7 +257,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, imageWriterSettings, this.password),
progressMonitor,
new StreamingAddDataSourceCallbacks(new DefaultIngestStream()),
new StreamingAddImageTaskCallback(new DefaultIngestStream(), callback));

View File

@ -49,7 +49,9 @@ class CommandLineCommand {
DATA_SOURCE_PATH,
DATA_SOURCE_ID,
INGEST_PROFILE_NAME,
REPORT_PROFILE_NAME;
REPORT_PROFILE_NAME,
BITLOCKER_KEY,
;
}
private final CommandType type;

View File

@ -198,7 +198,8 @@ public class CommandLineIngestManager extends CommandLineManager {
}
String dataSourcePath = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
dataSource = new AutoIngestDataSource(UUID.randomUUID().toString(), Paths.get(dataSourcePath));
String password = inputs.get(CommandLineCommand.InputType.BITLOCKER_KEY.name());
dataSource = new AutoIngestDataSource(UUID.randomUUID().toString(), Paths.get(dataSourcePath), password);
runDataSourceProcessor(caseForJob, dataSource);
String outputDirPath = getOutputDirPath(caseForJob);
@ -406,7 +407,7 @@ public class CommandLineIngestManager extends CommandLineManager {
// Get an ordered list of data source processors to try
List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
try {
validDataSourceProcessors = DataSourceProcessorUtility.getOrderedListOfDataSourceProcessors(dataSource.getPath());
validDataSourceProcessors = DataSourceProcessorUtility.getOrderedListOfDataSourceProcessors(dataSource.getPath(), dataSource.getPassword());
} catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException ex) {
LOGGER.log(Level.SEVERE, "Exception while determining best data source processor for {0}", dataSource.getPath());
// rethrow the exception.
@ -429,7 +430,7 @@ public class CommandLineIngestManager extends CommandLineManager {
DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId, ingestLock);
caseForJob.notifyAddingDataSource(taskId);
LOGGER.log(Level.INFO, "Identified data source type for {0} as {1}", new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
selectedProcessor.process(dataSource.getDeviceId(), dataSource.getPath(), progressMonitor, callBack);
selectedProcessor.process(dataSource.getDeviceId(), dataSource.getPath(), dataSource.getPassword(), null, progressMonitor, callBack);
ingestLock.wait();
// at this point we got the content object(s) from the current DSP.

View File

@ -30,6 +30,7 @@ import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.ArrayUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.netbeans.api.sendopts.CommandException;
import org.netbeans.spi.sendopts.Env;
@ -52,6 +53,7 @@ public class CommandLineOptionProcessor extends OptionProcessor {
private final Option dataSourcePathOption = Option.requiredArgument('s', "dataSourcePath");
private final Option dataSourceObjectIdOption = Option.requiredArgument('i', "dataSourceObjectId");
private final Option addDataSourceCommandOption = Option.withoutArgument('a', "addDataSource");
private final Option bitlockerKeyCommandOption = Option.requiredArgument('k', "key");
private final Option runIngestCommandOption = Option.optionalArgument('r', "runIngest");
private final Option listAllDataSourcesCommandOption = Option.withoutArgument('l', "listAllDataSources");
private final Option generateReportsOption = Option.optionalArgument('g', "generateReports");
@ -81,20 +83,21 @@ public class CommandLineOptionProcessor extends OptionProcessor {
@Override
protected Set<Option> getOptions() {
Set<Option> set = new HashSet<>();
set.add(createCaseCommandOption);
set.add(caseNameOption);
set.add(caseTypeOption);
set.add(caseBaseDirOption);
set.add(dataSourcePathOption);
set.add(addDataSourceCommandOption);
set.add(dataSourceObjectIdOption);
set.add(runIngestCommandOption);
set.add(listAllDataSourcesCommandOption);
set.add(generateReportsOption);
set.add(listAllIngestProfileOption);
set.add(defaultArgument);
return set;
return Set.of(
createCaseCommandOption,
caseNameOption,
caseTypeOption,
caseBaseDirOption,
dataSourcePathOption,
addDataSourceCommandOption,
bitlockerKeyCommandOption,
dataSourceObjectIdOption,
runIngestCommandOption,
listAllDataSourcesCommandOption,
generateReportsOption,
listAllIngestProfileOption,
defaultArgument
);
}
@Override
@ -203,6 +206,9 @@ public class CommandLineOptionProcessor extends OptionProcessor {
}
}
String[] keysArgs = values.get(bitlockerKeyCommandOption);
String bitlockerKey = ArrayUtils.isNotEmpty(keysArgs) ? keysArgs[0] : null;
String dataSourceId = "";
if (values.containsKey(dataSourceObjectIdOption)) {
@ -247,6 +253,11 @@ public class CommandLineOptionProcessor extends OptionProcessor {
newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
newCommand.addInputValue(CommandLineCommand.InputType.DATA_SOURCE_PATH.name(), dataSourcePath);
if (bitlockerKey != null) {
newCommand.addInputValue(CommandLineCommand.InputType.BITLOCKER_KEY.name(), bitlockerKey);
}
commands.add(newCommand);
runFromCommandLine(true);
}

View File

@ -129,6 +129,27 @@ public interface DataSourceProcessor {
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
* 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 password The password
* @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(String password, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
run(host, 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
@ -176,6 +197,31 @@ public interface DataSourceProcessor {
runWithIngestStream(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. The ingest
* settings must be complete before calling this method.
*
* @param password The password to decrypt the data source.
* @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(String password, Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progress,
DataSourceProcessorCallback callBack) {
runWithIngestStream(host, settings, progress, callBack);
}
/**
* Check if this DSP supports ingest streams.
*

View File

@ -21,11 +21,15 @@ package org.sleuthkit.autopsy.coreutils;
import org.sleuthkit.datamodel.SleuthkitJNI;
import java.io.IOException;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
/**
* Utility methods for working with data sources.
*/
public class DataSourceUtils {
private static final Logger logger = Logger.getLogger(DataSourceUtils.class.getName());
/**
* Calls TSK to determine whether a
@ -41,4 +45,38 @@ public class DataSourceUtils {
public static boolean imageHasFileSystem(Path dataSourcePath) throws IOException {
return SleuthkitJNI.isImageSupported(dataSourcePath.toString());
}
/**
* Calls TSK to determine whether a
* potential data source has a file system.
*
* @param dataSourcePath The path to the data source.
* @param password The password to decrypt the image.
*
* @return True or false.
*
* @throws IOException if an error occurs while trying to determine if the
* data source has a file system.
*/
public static boolean imageHasFileSystem(Path dataSourcePath, String password) throws IOException {
try {
SleuthkitJNI.TestOpenImageResult openImageResult = SleuthkitJNI.testOpenImage(dataSourcePath.toString(), password);
if (openImageResult.wasSuccessful()) {
return true;
} else {
String message = MessageFormat.format("An error occurred while opening {0}: {1}",
dataSourcePath.toString(),
openImageResult == null || StringUtils.isBlank(openImageResult.getMessage())
? "<unknown>"
: openImageResult.getMessage());
logger.log(Level.INFO, message);
return false;
}
} catch (Throwable ex) {
String message = "An error occurred while opening " + dataSourcePath.toString();
logger.log(Level.WARNING, message);
return false;
}
}
}

View File

@ -30,13 +30,20 @@ public class AutoIngestDataSource {
private final String deviceId;
private final Path path;
private final String password;
private DataSourceProcessorResult resultCode;
private List<String> errorMessages;
private List<Content> content;
public AutoIngestDataSource(String deviceId, Path path) {
public AutoIngestDataSource(String deviceId, Path path, String password) {
this.deviceId = deviceId;
this.path = path;
this.password = password;
}
public AutoIngestDataSource(String deviceId, Path path) {
this(deviceId, path, null);
}
public String getDeviceId() {
@ -47,6 +54,13 @@ public class AutoIngestDataSource {
return this.path;
}
/**
* @return The password to decrypt the data source.
*/
public String getPassword() {
return password;
}
public synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List<String> errorMessages, List<Content> content) {
this.resultCode = result;
this.errorMessages = new ArrayList<>(errorMessages);

View File

@ -50,6 +50,25 @@ public interface AutoIngestDataSourceProcessor extends DataSourceProcessor {
*/
int canProcess(Path dataSourcePath) throws AutoIngestDataSourceProcessorException;
/**
* Indicates whether the DataSourceProcessor is capable of processing the
* data source. Returns a confidence value. Method can throw an exception
* for a system level problem. The exception should not be thrown for an issue
* related to bad input data.
*
* @param dataSourcePath Path to the data source.
* @param password The password to decrypt the data source.
* @return Confidence value. Values between 0 and 100 are recommended. Zero
* or less means the data source is not supported by the
* DataSourceProcessor. Value of 100 indicates high certainty in
* being able to process the data source.
* @throws org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
default int canProcess(Path dataSourcePath, String password) throws AutoIngestDataSourceProcessorException {
return canProcess(dataSourcePath);
}
/**
* Adds a data source to the case database using a background task in a
* separate thread by calling DataSourceProcessor.run() method. Returns as
@ -91,6 +110,30 @@ public interface AutoIngestDataSourceProcessor extends DataSourceProcessor {
process(deviceId, dataSourcePath, progressMonitor, callBack);
}
/**
* Adds a data source to the case database using a background task in a
* separate thread by calling DataSourceProcessor.run() method. Returns as
* soon as the background task is started. The background task uses a
* callback object to signal task completion and return results. Method can
* throw an exception for a system level problem. The exception should not
* be thrown for an issue related to bad input data.
*
* @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 dataSourcePath Path to the data source.
* @param password The password to decrypt the datasource.
* @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.
*/
default void process(String deviceId, Path dataSourcePath, String password, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
process(deviceId, dataSourcePath, progressMonitor, callBack);
}
/**
* Adds a data source to the case database using a background task in a
* separate thread by calling DataSourceProcessor.run() method. Returns as
@ -140,6 +183,33 @@ public interface AutoIngestDataSourceProcessor extends DataSourceProcessor {
return processWithIngestStream(deviceId, dataSourcePath, settings, progressMonitor, callBack);
}
/**
* Adds a data source to the case database using a background task in a
* separate thread by calling DataSourceProcessor.run() method. Returns as
* soon as the background task is started. The background task uses a
* callback object to signal task completion and return results. Method can
* throw an exception for a system level problem. The exception should not
* be thrown for an issue related to bad input data.
*
* @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 dataSourcePath Path to the data source.
* @param password The password to decrypt the datasource.
* @param host The host for this data source.
* @param settings The ingest job settings.
* @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.
*
* @return The new ingest stream or null if an error occurred. Errors will be handled by the callback.
*/
default IngestStream processWithIngestStream(String deviceId, Path dataSourcePath, String password, Host host, IngestJobSettings settings, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
return processWithIngestStream(deviceId, dataSourcePath, settings, progressMonitor, callBack);
}
/**
* A custom exception for the use of AutomatedIngestDataSourceProcessor.
*/

View File

@ -49,9 +49,27 @@ public class DataSourceProcessorUtility {
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
public static Map<AutoIngestDataSourceProcessor, Integer> getDataSourceProcessorForFile(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
return getDataSourceProcessorForFile(dataSourcePath, null, processorCandidates);
}
/**
* A utility method to find all Data Source Processors (DSP) that are able
* to process the input data source. Only the DSPs that implement
* AutoIngestDataSourceProcessor interface are used.
*
* @param dataSourcePath Full path to the data source
* @param password The password to decrypt the data source.
* @param processorCandidates Possible DSPs that can handle the data source
*
* @return Hash map of all DSPs that can process the data source along with
* their confidence score
* @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
public static Map<AutoIngestDataSourceProcessor, Integer> getDataSourceProcessorForFile(Path dataSourcePath, String password, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = new HashMap<>();
for (AutoIngestDataSourceProcessor processor : processorCandidates) {
int confidence = processor.canProcess(dataSourcePath);
int confidence = processor.canProcess(dataSourcePath, password);
if (confidence > 0) {
validDataSourceProcessorsMap.put(processor, confidence);
}
@ -76,9 +94,29 @@ public class DataSourceProcessorUtility {
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
public static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
return getOrderedListOfDataSourceProcessors(dataSourcePath, (String) null);
}
/**
* A utility method to find all Data Source Processors (DSP) that are able
* to process the input data source. Only the DSPs that implement
* AutoIngestDataSourceProcessor interface are used. Returns ordered list of
* data source processors. DSPs are ordered in descending order from highest
* confidence to lowest.
*
* @param dataSourcePath Full path to the data source
* @param password The password to decrypt the data source.
*
* @return Ordered list of data source processors. DSPs are ordered in
* descending order from highest confidence to lowest.
*
* @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
public static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath, String password) throws AutoIngestDataSourceProcessorException {
// lookup all AutomatedIngestDataSourceProcessors
Collection<? extends AutoIngestDataSourceProcessor> processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class);
return getOrderedListOfDataSourceProcessors(dataSourcePath, processorCandidates);
return getOrderedListOfDataSourceProcessors(dataSourcePath, password, processorCandidates);
}
/**
@ -98,7 +136,28 @@ public class DataSourceProcessorUtility {
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
public static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = getDataSourceProcessorForFile(dataSourcePath, processorCandidates);
return getOrderedListOfDataSourceProcessors(dataSourcePath, null, processorCandidates);
}
/**
* A utility method to find all Data Source Processors (DSP) that are able
* to process the input data source. Only the DSPs that implement
* AutoIngestDataSourceProcessor interface are used. Returns ordered list of
* data source processors. DSPs are ordered in descending order from highest
* confidence to lowest.
*
* @param dataSourcePath Full path to the data source
* @param password The password to decrypt the data source.
* @param processorCandidates Collection of AutoIngestDataSourceProcessor objects to use
*
* @return Ordered list of data source processors. DSPs are ordered in
* descending order from highest confidence to lowest.
*
* @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
public static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath, String password, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = getDataSourceProcessorForFile(dataSourcePath, password, processorCandidates);
return orderDataSourceProcessorsByConfidence(validDataSourceProcessorsMap);
}

View File

@ -106,6 +106,12 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, IngestProgressSn
*/
private boolean ocrEnabled;
/**
* Version 5 fields
*/
@GuardedBy("this")
private String password;
/**
* Constructs a new automated ingest job. All job state not specified in the
* job manifest is set to the default state for a new job.
@ -166,7 +172,7 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, IngestProgressSn
/*
* Version 0 fields.
*/
this.manifest = new Manifest(nodeData.getManifestFilePath(), nodeData.getManifestFileDate(), nodeData.getCaseName(), nodeData.getDeviceId(), nodeData.getDataSourcePath(), Collections.emptyMap());
this.manifest = new Manifest(nodeData.getManifestFilePath(), nodeData.getManifestFileDate(), nodeData.getCaseName(), nodeData.getDeviceId(), nodeData.getDataSourcePath(), nodeData.getPassword(), Collections.emptyMap());
this.nodeName = nodeData.getProcessingHostName();
this.caseDirectoryPath = nodeData.getCaseDirectoryPath().toString();
this.priority = nodeData.getPriority();
@ -204,6 +210,11 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, IngestProgressSn
*/
this.ocrEnabled = nodeData.getOcrEnabled();
/**
* Version 5 fields
*/
this.password = nodeData.getPassword();
} catch (Exception ex) {
throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex);
}
@ -282,6 +293,21 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, IngestProgressSn
this.ocrEnabled = enabled;
}
/**
* @return The password to decrypt the image.
*/
synchronized String getPassword() {
return password;
}
/**
* @param password The password to decrypt the image.
*/
synchronized void setPassword(String password) {
this.password = password;
}
/**
* Sets the processing stage of the job. The start date/time for the stage
* is set when the stage is set.

View File

@ -24,6 +24,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import javax.lang.model.type.TypeKind;
import org.apache.commons.lang3.StringUtils;
/**
* An object that converts auto ingest job data for an auto ingest job
@ -31,7 +32,7 @@ import javax.lang.model.type.TypeKind;
*/
final class AutoIngestJobNodeData {
private static final int CURRENT_VERSION = 3;
private static final int CURRENT_VERSION = 4;
private static final int DEFAULT_PRIORITY = 0;
/*
@ -84,6 +85,12 @@ final class AutoIngestJobNodeData {
*/
private boolean ocrEnabled;
/**
* Version 4 fields.
*/
private String password; // password to decrypt the image
/**
* Gets the current version of the auto ingest job coordination service node
* data.
@ -121,6 +128,7 @@ final class AutoIngestJobNodeData {
setProcessingStageDetails(job.getProcessingStageDetails());
setDataSourceSize(job.getDataSourceSize());
setOcrEnabled(job.getOcrEnabled());
setPassword(manifest.getPassword());
}
/**
@ -157,6 +165,7 @@ final class AutoIngestJobNodeData {
this.processingStageDetailsStartDate = 0L;
this.dataSourceSize = 0L;
this.ocrEnabled = false;
this.password = "";
/*
* Get fields from node data.
@ -208,6 +217,13 @@ final class AutoIngestJobNodeData {
this.ocrEnabled = (1 == ocrFlag);
}
if (buffer.hasRemaining()) {
/*
* Get version 4 fields.
*/
setPassword(getStringFromBuffer(buffer, TypeKind.SHORT));
}
} catch (BufferUnderflowException ex) {
throw new InvalidDataException("Node data is incomplete", ex);
}
@ -471,6 +487,22 @@ final class AutoIngestJobNodeData {
}
}
/**
* @return The password to decrypt the image. Empty indicates no password.
*/
public String getPassword() {
return password;
}
/**
* @param password The password to decrypt the image.
*/
public void setPassword(String password) {
this.password = StringUtils.defaultString(password);
}
/**
* Get the processing stage of the job.
*
@ -604,6 +636,10 @@ final class AutoIngestJobNodeData {
if (this.version >= 3) {
buffer.putInt(this.ocrEnabled ? 1 : 0);
}
if (this.version >= 4) {
putStringIntoBuffer(this.password, buffer, TypeKind.SHORT);
}
}
// Prepare the array

View File

@ -2573,7 +2573,8 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
return null;
}
String deviceId = manifest.getDeviceId();
return new AutoIngestDataSource(deviceId, dataSourcePath);
String password = manifest.getPassword();
return new AutoIngestDataSource(deviceId, dataSourcePath, password);
}
/**
@ -2603,7 +2604,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
// Get an ordered list of data source processors to try
List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
try {
validDataSourceProcessors = DataSourceProcessorUtility.getOrderedListOfDataSourceProcessors(dataSource.getPath());
validDataSourceProcessors = DataSourceProcessorUtility.getOrderedListOfDataSourceProcessors(dataSource.getPath(), dataSource.getPassword());
} catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException ex) {
sysLogger.log(Level.SEVERE, "Exception while determining best data source processor for {0}", dataSource.getPath());
// rethrow the exception. It will get caught & handled upstream and will result in AIM auto-pause.
@ -2641,7 +2642,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
jobLogger.logIngestJobSettingsErrors();
throw new AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException("Error(s) in ingest job settings for " + manifestPath);
}
currentIngestStream = selectedProcessor.processWithIngestStream(dataSource.getDeviceId(), dataSource.getPath(), ingestJobSettings, progressMonitor, callBack);
currentIngestStream = selectedProcessor.processWithIngestStream(dataSource.getDeviceId(), dataSource.getPath(), dataSource.getPassword(), null, ingestJobSettings, progressMonitor, callBack);
if (currentIngestStream == null) {
// Either there was a failure to add the data source object to the database or the ingest settings were bad.
// An error in the ingest settings is the more likely scenario.
@ -2651,7 +2652,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
throw new AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException("Error initializing processing for " + manifestPath + ", probably due to an ingest settings error");
}
} else {
selectedProcessor.process(dataSource.getDeviceId(), dataSource.getPath(), progressMonitor, callBack);
selectedProcessor.process(dataSource.getDeviceId(), dataSource.getPath(), dataSource.getPassword(), null, progressMonitor, callBack);
}
ingestLock.wait();

View File

@ -45,6 +45,7 @@ public final class AutopsyManifestFileParser implements ManifestFileParser {
private static final String MANIFEST_FILE_NAME_SIGNATURE = "_MANIFEST.XML";
private static final String ROOT_ELEM_TAG_NAME = "AutopsyManifest";
private static final String CASE_NAME_XPATH = "/AutopsyManifest/CaseName/text()";
private static final String PASSWORD_XPATH = "/AutopsyManifest/Password/text()";
private static final String DEVICE_ID_XPATH = "/AutopsyManifest/DeviceId/text()";
private static final String DATA_SOURCE_NAME_XPATH = "/AutopsyManifest/DataSource/text()";
private static final Logger logger = Logger.getLogger(AutopsyManifestFileParser.class.getName());
@ -102,7 +103,10 @@ public final class AutopsyManifestFileParser implements ManifestFileParser {
}
Path dataSourcePath = filePath.getParent().resolve(dataSourceName);
return new Manifest(filePath, dateFileCreated, caseName, deviceId, dataSourcePath, new HashMap<>());
expr = xpath.compile(PASSWORD_XPATH);
String password = (String) expr.evaluate(doc, XPathConstants.STRING);
return new Manifest(filePath, dateFileCreated, caseName, deviceId, dataSourcePath, password, new HashMap<>());
} catch (Exception ex) {
throw new ManifestFileParserException(String.format("Error parsing manifest %s", filePath), ex);
} finally {

View File

@ -42,9 +42,14 @@ public final class Manifest implements Serializable {
private final String caseName;
private final String deviceId;
private final String dataSourceFileName;
private final String password;
private final Map<String, String> manifestProperties;
public Manifest(Path manifestFilePath, Date dateFileCreated, String caseName, String deviceId, Path dataSourcePath, Map<String, String> manifestProperties) {
this(manifestFilePath, dateFileCreated, caseName, deviceId, dataSourcePath, null, manifestProperties);
}
public Manifest(Path manifestFilePath, Date dateFileCreated, String caseName, String deviceId, Path dataSourcePath, String password, Map<String, String> manifestProperties) {
this.filePathString = manifestFilePath.toString();
this.filePath = Paths.get(filePathString);
@ -60,6 +65,7 @@ public final class Manifest implements Serializable {
this.dataSourcePath = Paths.get("");
dataSourceFileName = "";
}
this.password = password;
this.manifestProperties = new HashMap<>(manifestProperties);
}
@ -91,6 +97,13 @@ public final class Manifest implements Serializable {
return dataSourcePath;
}
/**
* @return The password to decrypt the data source (may be null).
*/
public String getPassword() {
return password;
}
public String getDataSourceFileName() {
return dataSourceFileName;
}