diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.form b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.form
index 1e9eac474b..dae110a36c 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.form
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.form
@@ -74,7 +74,7 @@
-
+
@@ -158,7 +158,7 @@
-
+
@@ -184,7 +184,7 @@
-
+
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.java
index 86a527b60c..40582677f8 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.java
@@ -248,7 +248,7 @@ final class AddImageVisualPanel1 extends JPanel {
typePanel.setLayout(typePanelLayout);
typePanelLayout.setHorizontalGroup(
typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGap(0, 0, Short.MAX_VALUE)
+ .addGap(0, 544, Short.MAX_VALUE)
);
typePanelLayout.setVerticalGroup(
typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -279,7 +279,7 @@ final class AddImageVisualPanel1 extends JPanel {
.addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(typePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 77, Short.MAX_VALUE)
- .addGap(0, 0, 0))
+ .addContainerGap())
);
imgInfoLabel.setFont(new java.awt.Font("Tahoma", 1, 14)); // NOI18N
@@ -323,7 +323,7 @@ final class AddImageVisualPanel1 extends JPanel {
.addComponent(noFatOrphansCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(descLabel)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 75, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 64, Short.MAX_VALUE)
.addComponent(nextLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0))
);
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java
index c4a9f6fc00..b2ce7ed1be 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java
@@ -76,7 +76,8 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
private CurrentDirectoryFetcher fetcher;
private AddImageProcess process;
private AddImageAction action;
- private AddContentTask addImageTask;
+ private AddImageTask addImageTask;
+ private AddLocalFilesTask addLocalFilesTask;
private AddImageWizardPanel2 wizPanel;
AddImageWizardPanel3(AddImageAction action, AddImageWizardPanel2 wizPanel) {
@@ -165,7 +166,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
public void readSettings(WizardDescriptor settings) {
JButton cancel = new JButton("Cancel");
cancel.setEnabled(false);
- settings.setOptions(new Object[] {WizardDescriptor.PREVIOUS_OPTION, WizardDescriptor.NEXT_OPTION, WizardDescriptor.FINISH_OPTION, cancel});
+ settings.setOptions(new Object[]{WizardDescriptor.PREVIOUS_OPTION, WizardDescriptor.NEXT_OPTION, WizardDescriptor.FINISH_OPTION, cancel});
cleanupImage = null;
readyToIngest = false;
imgAdded = false;
@@ -176,9 +177,15 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
noFatOrphans = ((Boolean) settings.getProperty(AddImageAction.NOFATORPHANS_PROP)).booleanValue();
//start the process of adding the content
- addImageTask = new AddContentTask(settings);
- addImageTask.execute();
-
+ if (dataSourceType.equals(ContentType.LOCAL) ) {
+ addLocalFilesTask = new AddLocalFilesTask(settings);
+ addLocalFilesTask.execute();
+ }
+ else {
+ //disk or image
+ addImageTask = new AddImageTask(settings);
+ addImageTask.execute();
+ }
}
/**
@@ -216,14 +223,14 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
* Class for getting the currently processing directory.
*
*/
-
- private static class CurrentDirectoryFetcher extends SwingWorker {
- AddContentTask task;
+ private static class CurrentDirectoryFetcher extends SwingWorker {
+
+ AddImageTask task;
JProgressBar prog;
AddImageVisualPanel2 wiz;
AddImageProcess proc;
-
- CurrentDirectoryFetcher(JProgressBar prog, AddImageVisualPanel2 wiz, AddImageProcess proc){
+
+ CurrentDirectoryFetcher(JProgressBar prog, AddImageVisualPanel2 wiz, AddImageProcess proc) {
this.wiz = wiz;
this.proc = proc;
this.prog = prog;
@@ -233,10 +240,10 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
* @return the currently processing directory
*/
@Override
- protected Integer doInBackground(){
- try{
- while(prog.getValue() < 100 || prog.isIndeterminate()){ //TODO Rely on state variable in AddImgTask class
-
+ protected Integer doInBackground() {
+ try {
+ while (prog.getValue() < 100 || prog.isIndeterminate()) { //TODO Rely on state variable in AddImgTask class
+
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
@@ -261,13 +268,134 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
}
}
+ /**
+ * Thread that will add local files to database, and then kick-off ingest
+ * modules. Note: the add local files task cannot currently be reverted as
+ * the add image task can.
+ * This is a separate task from AddImgTask because it is much simpler and does not require locks,
+ * since the underlying file manager methods acquire the locks for each transaction when adding local files.
+ */
+ private class AddLocalFilesTask extends SwingWorker {
+ private JProgressBar progressBar;
+ private Case currentCase;
+ // true if the process was requested to stop
+ private boolean interrupted = false;
+ private boolean hasCritError = false;
+ private String errorString = null;
+ private WizardDescriptor settings;
+ private Logger logger = Logger.getLogger(AddLocalFilesTask.class.getName());
+
+ protected AddLocalFilesTask(WizardDescriptor settings) {
+ this.progressBar = wizPanel.getComponent().getCrDbProgressBar();
+ currentCase = Case.getCurrentCase();
+ this.settings = settings;
+ }
+
+ /**
+ * Starts the addImage process, but does not commit the results.
+ *
+ * @return
+ * @throws Exception
+ */
+ @Override
+ protected Integer doInBackground() {
+ this.setProgress(0);
+ // Add a cleanup task to interupt the backgroud process if the
+ // wizard exits while the background process is running.
+ AddImageAction.CleanupTask cancelledWhileRunning = action.new CleanupTask() {
+ @Override
+ void cleanup() throws Exception {
+ logger.log(Level.INFO, "Add local files process interrupted.");
+ //nothing to be cleanedup
+ }
+ };
+
+ cancelledWhileRunning.enable();
+ try {
+ wizPanel.setStateStarted();
+ String [] paths = dataSourcePath.split(LocalFilesPanel.FILES_SEP);
+ ListabsLocalPaths = new ArrayList();
+ for (String path : paths) {
+ absLocalPaths.add(path);
+ }
+ currentCase.getServices().getFileManager().addLocalFilesDirs(absLocalPaths);
+ } catch (TskCoreException ex) {
+ logger.log(Level.WARNING, "Errors occurred while running add local files. ", ex);
+ hasCritError = true;
+ errorString = ex.getMessage();
+ } finally {
+ // process is over, doesn't need to be dealt with if cancel happens
+ cancelledWhileRunning.disable();
+ //enqueue what would be in done() to EDT thread
+ EventQueue.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ postProcess();
+ }
+ });
+ }
+ return 0;
+ }
+
+ /**
+ *
+ * (called by EventDispatch Thread after doInBackground finishes)
+ */
+ protected void postProcess() {
+ progressBar.setIndeterminate(false);
+ setProgress(100);
+
+ if (interrupted || hasCritError) {
+ logger.log(Level.INFO, "Handling errors or interruption that occured in local files process");
+ if (hasCritError) {
+ //core error
+ wizPanel.getComponent().setErrors(errorString, true);
+ }
+ return;
+ } else if (errorString != null) {
+ //data error (non-critical)
+ logger.log(Level.INFO, "Handling non-critical errors that occured in local files process");
+ wizPanel.getComponent().setErrors(errorString, false);
+ }
+ try {
+ // When everything happens without an error:
+ if (errorString == null) { // complete progress bar
+ wizPanel.getComponent().changeProgressBarTextAndColor("*Local Files added.", 100, Color.black);
+ }
+
+ // Get attention for the process finish
+ java.awt.Toolkit.getDefaultToolkit().beep(); //BEEP!
+ AddImageVisualPanel2 panel = wizPanel.getComponent();
+ if (panel != null) {
+ Window w = SwingUtilities.getWindowAncestor(panel);
+ if (w != null) {
+ w.toFront();
+ }
+ }
+
+ wizPanel.setStateFinished();
+
+ settings.putProperty(AddImageAction.IMAGEID_PROP, "");
+ settings.putProperty(AddImageAction.IMAGECLEANUPTASK_PROP, null);
+
+ // Start ingest if we can
+ startIngest();
+
+ } catch (Exception ex) {
+ //handle unchecked exceptions
+ logger.log(Level.WARNING, "Unexpected errors occurred while running post add image cleanup. ", ex);
+ wizPanel.getComponent().changeProgressBarTextAndColor("*Failed to add image.", 0, Color.black); // set error message
+ logger.log(Level.SEVERE, "Error adding image to case", ex);
+ }
+ }
+ }
/**
- * Thread that will make the JNI call to add image to database, and then kick-off
- * ingest modules.
+ * Thread that will make the JNI call to add image to database, and then
+ * kick-off ingest modules.
*/
- private class AddContentTask extends SwingWorker {
+ private class AddImageTask extends SwingWorker {
private JProgressBar progressBar;
private Case currentCase;
@@ -277,9 +405,9 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
private String errorString = null;
private long start;
private WizardDescriptor settings;
- private Logger logger = Logger.getLogger(AddContentTask.class.getName());
+ private Logger logger = Logger.getLogger(AddImageTask.class.getName());
- protected AddContentTask(WizardDescriptor settings) {
+ protected AddImageTask(WizardDescriptor settings) {
this.progressBar = wizPanel.getComponent().getCrDbProgressBar();
currentCase = Case.getCurrentCase();
this.settings = settings;
@@ -326,7 +454,6 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
return 0;
}
-
process = currentCase.makeAddImageProcess(timeZone, true, noFatOrphans);
fetcher = new CurrentDirectoryFetcher(this.progressBar, wizPanel.getComponent(), process);
cancelledWhileRunning.enable();
@@ -370,9 +497,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
private void commitImage(WizardDescriptor settings) throws Exception {
String contentPath = (String) settings.getProperty(AddImageAction.DATASOURCEPATH_PROP);
- ContentType contentType = (ContentType) settings.getProperty(AddImageAction.DATASOURCETYPE_PROP);
- //TODO check type and skip commit if local files
-
+
String timezone = settings.getProperty(AddImageAction.TIMEZONE_PROP).toString();
settings.putProperty(AddImageAction.IMAGEID_PROP, "");
@@ -396,7 +521,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
// task
cleanupImage.disable();
settings.putProperty(AddImageAction.IMAGECLEANUPTASK_PROP, null);
-
+
logger.log(Level.INFO, "Image committed, imageId: " + imageId);
logger.log(Level.INFO, PlatformUtil.getAllMemUsageInfo());
}
@@ -460,7 +585,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
wizPanel.setStateFinished();
// Commit the image
- if (! newContents.isEmpty()) //already commited
+ if (!newContents.isEmpty()) //already commited
{
logger.log(Level.INFO, "Assuming image already committed, will not commit.");
return;
@@ -489,7 +614,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel {
wizPanel.getComponent().changeProgressBarTextAndColor("*Failed to add image.", 0, Color.black); // set error message
// Log error/display warning
-
+
logger.log(Level.SEVERE, "Error adding image to case", ex);
} finally {
}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java
index ede1498434..7f7db756c5 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java
@@ -34,6 +34,7 @@ public class LocalFilesPanel extends ContentTypePanel {
private List currentFiles = new ArrayList();
private boolean enableNext = false;
private static LocalFilesPanel instance;
+ public static final String FILES_SEP = ",";
/**
* Creates new form LocalFilesPanel
@@ -59,13 +60,15 @@ public class LocalFilesPanel extends ContentTypePanel {
@Override
public String getContentPaths() {
+ //TODO consider interface change to return list of paths instead
+
if (currentFiles == null) {
return "";
}
StringBuilder b = new StringBuilder();
for (File f : currentFiles) {
b.append(f.getAbsolutePath() );
- b.append(",");
+ b.append(FILES_SEP);
}
return b.toString();
}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java
index 412400ba21..b57632a603 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java
@@ -89,6 +89,7 @@ public final class NewCaseWizardAction extends CallableSystemAction {
// if the finish button is pressed (not cancelled)
if (finished) {
// now start the 'Add Image' wizard
+ //TODO fix for local
AddImageAction addImageAction = SystemAction.get(AddImageAction.class);
addImageAction.actionPerformed(null);
}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java
index a7558c03d2..f4cb44e708 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java
@@ -24,9 +24,11 @@ package org.sleuthkit.autopsy.casemodule.services;
import java.io.Closeable;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DerivedFile;
import org.sleuthkit.datamodel.FsContent;
@@ -141,11 +143,66 @@ public class FileManager implements Closeable {
isFile, parentFile, rederiveDetails, toolName, toolVersion, otherDetails);
}
+ /**
+ * Add local files and dirs - the general method that does it all for files
+ * and dirs and in bulk/
+ * Sends event to inform listeners of new content (note, this is temporary, we will be using
+ * datamodel observer and node auto-refresh in future)
+ *
+ * @param localAbsPaths list of absolute paths to local files and dirs
+ * @return list of root AbstractFile objects created to represent roots of
+ * added local files/dirs
+ * @throws TskCoreException exception thrown if the object creation failed
+ * due to a critical system error or of the file manager has already been
+ * closed. There is no "revert" logic if one of the additions fails. The
+ * addition stops with the first error encountered.
+ */
+ public synchronized List addLocalFilesDirs(List localAbsPaths) throws TskCoreException {
+ final List added = new ArrayList();
+
+ final List rootsToAdd = new ArrayList();
+ //first validate all the inputs before any additions
+ for (String absPath : localAbsPaths) {
+ java.io.File localFile = new java.io.File(absPath);
+ if (!localFile.exists() || !localFile.canRead()) {
+ String msg = "One of the local files/dirs to add is not readable: " + localFile.getAbsolutePath() + ", aborting the process before any files added";
+ logger.log(Level.SEVERE, msg);
+ throw new TskCoreException(msg);
+ }
+ rootsToAdd.add(localFile);
+ }
+
+ for (java.io.File localRootToAdd : rootsToAdd) {
+ AbstractFile localFileAdded = null;
+ if (localRootToAdd.isFile()) {
+ localFileAdded = addLocalFileSingle(localRootToAdd.getAbsolutePath());
+ } else {
+ localFileAdded = this.addLocalDir(localRootToAdd.getAbsolutePath());
+ }
+ if (localFileAdded == null) {
+ String msg = "One of the local files/dirs could not be added: " + localRootToAdd.getAbsolutePath();
+ logger.log(Level.SEVERE, msg);
+ throw new TskCoreException(msg);
+ } else {
+ added.add(localFileAdded);
+ }
+ }
+
+ if (! added.isEmpty()) {
+ //TODO use datamodel observer that sends such events, and node-autorefresh
+ // DirectoryTreeTopComponent.getDefault().refreshContentTreeSafe();
+ }
+
+
+ return added;
+ }
+
/**
* Helper (internal) to add child of local dir recursively
+ *
* @param parentVd
* @param childLocalFile
- * @throws TskCoreException
+ * @throws TskCoreException
*/
private void addLocalDirectoryRecInt(VirtualDirectory parentVd,
java.io.File childLocalFile) throws TskCoreException {
@@ -167,22 +224,23 @@ public class FileManager implements Closeable {
}
/**
- * Add a local directory and its children recursively.
- * Parent container of the local dir is added for context.
- *
- * Does not refresh the views of data (client must do it currently,
- * will be addressed in future with node auto-refresh support)
- *
+ * Add a local directory and its children recursively. Parent container of
+ * the local dir is added for context.
+ *
+ * Does not refresh the views of data (client must do it currently, will be
+ * addressed in future with node auto-refresh support)
+ *
*
* @param localAbsPath local absolute path of root folder whose children are
- * to be added recursively. If there is a parent dir, it is added as a container, for context.
+ * to be added recursively. If there is a parent dir, it is added as a
+ * container, for context.
* @return parent virtual directory folder created representing the
* localAbsPath node
* @throws TskCoreException exception thrown if the object creation failed
* due to a critical system error or of the file manager has already been
* closed, or if the localAbsPath could not be accessed
*/
- public synchronized VirtualDirectory addLocalDir(String localAbsPath) throws TskCoreException {
+ private synchronized VirtualDirectory addLocalDir(String localAbsPath) throws TskCoreException {
if (tskCase == null) {
throw new TskCoreException("Attempted to use FileManager after it was closed.");
}
@@ -198,7 +256,7 @@ public class FileManager implements Closeable {
if (!localDir.isDirectory()) {
throw new TskCoreException("Attempted to add a local dir that is not a directory: " + localAbsPath);
}
-
+
String parentName = null;
java.io.File parentDir = localDir.getParentFile();
if (parentDir != null) {
@@ -212,8 +270,7 @@ public class FileManager implements Closeable {
final long localFilesRootId = tskCase.getLocalFilesRootDirectoryId();
if (parentName == null) {
rootVd = tskCase.addVirtualDirectory(localFilesRootId, rootVdName);
- }
- else {
+ } else {
//add parent dir for context
final VirtualDirectory contextDir = tskCase.addVirtualDirectory(localFilesRootId, parentName);
rootVd = tskCase.addVirtualDirectory(contextDir.getId(), rootVdName);
@@ -244,7 +301,7 @@ public class FileManager implements Closeable {
/**
* Creates a single local file under $LocalFiles for the case, adds it to
- * the database and returns it. Does not refresh the views of data.
+ * the database and returns it. Does not refresh the views of data.
*
* @param localAbsPath local absolute path of the local file, including the
* file name.
@@ -254,7 +311,7 @@ public class FileManager implements Closeable {
* closed, or if the localAbsPath could not be accessed
*
*/
- public synchronized LocalFile addLocalFileSingle(String localAbsPath) throws TskCoreException {
+ private synchronized LocalFile addLocalFileSingle(String localAbsPath) throws TskCoreException {
if (tskCase == null) {
throw new TskCoreException("Attempted to use FileManager after it was closed.");
@@ -265,7 +322,7 @@ public class FileManager implements Closeable {
/**
* Creates a single local file under parentFile for the case, adds it to the
- * database and returns it. Does not refresh the views of data.
+ * database and returns it. Does not refresh the views of data.
*
* @param localAbsPath local absolute path of the local file, including the
* file name
@@ -277,7 +334,7 @@ public class FileManager implements Closeable {
* closed
*
*/
- public synchronized LocalFile addLocalFileSingle(String localAbsPath, AbstractFile parentFile) throws TskCoreException {
+ private synchronized LocalFile addLocalFileSingle(String localAbsPath, AbstractFile parentFile) throws TskCoreException {
if (tskCase == null) {
throw new TskCoreException("Attempted to use FileManager after it was closed.");
@@ -306,7 +363,7 @@ public class FileManager implements Closeable {
ctime, crtime, atime, mtime,
isFile, parentFile);
-
+
return lf;
}