diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java new file mode 100644 index 0000000000..ed2ef603b6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java @@ -0,0 +1,356 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sleuthkit.autopsy.casemodule; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPProgressMonitor; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.SleuthkitJNI; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; +import org.sleuthkit.datamodel.TskException; + +/* + * A background task (swingworker) that adds the given image to + * database using the Sleuthkit JNI interface. + * + * It updates the given ProgressMonitor as it works through adding the image, + * and et the end, calls the specified Callback. + */ +public class AddImageTask implements Runnable { + + private Logger logger = Logger.getLogger(AddImageTask.class.getName()); + + private Case currentCase; + // true if the process was requested to stop + private volatile boolean cancelled = false; + //true if revert has been invoked. + private boolean reverted = false; + private boolean hasCritError = false; + + private volatile boolean addImageDone = false; + + private List errorList = new ArrayList(); + + private DSPProgressMonitor progressMonitor; + private DSPCallback callbackObj; + + private final List newContents = Collections.synchronizedList(new ArrayList()); + + private SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess; + private Thread dirFetcher; + + private String imagePath; + private String dataSourcetype; + String timeZone; + boolean noFatOrphans; + + + /* + * A Swingworker that updates the progressMonitor with the name of the + * directory currently being processed by the AddImageTask + */ + private class CurrentDirectoryFetcher implements Runnable { + + DSPProgressMonitor progressMonitor; + SleuthkitJNI.CaseDbHandle.AddImageProcess process; + + CurrentDirectoryFetcher(DSPProgressMonitor aProgressMonitor, SleuthkitJNI.CaseDbHandle.AddImageProcess proc) { + this.progressMonitor = aProgressMonitor; + this.process = proc; + } + + /** + * @return the currently processing directory + */ + @Override + public void run() { + try { + while (!Thread.currentThread().isInterrupted()) { + String currDir = process.currentDirectory(); + if (currDir != null) { + if (!currDir.isEmpty() ) { + progressMonitor.setProgressText("Adding: " + currDir); + } + } + Thread.sleep(2 * 1000); + } + return; + } catch (InterruptedException ie) { + return; + } + } + } + + + protected AddImageTask(String imgPath, String tz, boolean noOrphans, DSPProgressMonitor aProgressMonitor, DSPCallback cbObj ) { + + currentCase = Case.getCurrentCase(); + + + this.imagePath = imgPath; + this.timeZone = tz; + this.noFatOrphans = noOrphans; + + this.callbackObj = cbObj; + this.progressMonitor = aProgressMonitor; + } + + /** + * Starts the addImage process, but does not commit the results. + * + * @return + * + * @throws Exception + */ + @Override + public void run() { + + + errorList.clear(); + + //lock DB for writes in this thread + SleuthkitCase.dbWriteLock(); + + addImageProcess = currentCase.makeAddImageProcess(timeZone, true, noFatOrphans); + dirFetcher = new Thread( new CurrentDirectoryFetcher(progressMonitor, addImageProcess)); + + try { + progressMonitor.setIndeterminate(true); + progressMonitor.setProgress(0); + + dirFetcher.start(); + + addImageProcess.run(new String[]{this.imagePath}); + + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Core errors occurred while running add image. ", ex); + //critical core/system error and process needs to be interrupted + hasCritError = true; + errorList.add(ex.getMessage()); + } catch (TskDataException ex) { + logger.log(Level.WARNING, "Data errors occurred while running add image. ", ex); + errorList.add(ex.getMessage()); + } + finally { + + } + + // handle addImage done + postProcess(); + + // unclock the DB + SleuthkitCase.dbWriteUnlock(); + + return; + } + + /** + * Commit the newly added image to DB + * + * + * @throws Exception if commit or adding the image to the case failed + */ + private void commitImage() throws Exception { + + long imageId = 0; + try { + imageId = addImageProcess.commit(); + } catch (TskException e) { + logger.log(Level.WARNING, "Errors occured while committing the image", e); + errorList.add(e.getMessage()); + } finally { + + if (imageId != 0) { + // get the newly added Image so we can return to caller + Image newImage = currentCase.getSleuthkitCase().getImageById(imageId); + + //while we have the image, verify the size of its contents + String verificationErrors = newImage.verifyImageSize(); + if (verificationErrors.equals("") == false) { + //data error (non-critical) + errorList.add(verificationErrors); + } + + // Add the image to the list of new content + newContents.add(newImage); + } + + logger.log(Level.INFO, "Image committed, imageId: " + imageId); + logger.log(Level.INFO, PlatformUtil.getAllMemUsageInfo()); + } + } + + /** + * Post processing after the addImageProcess is done. + * + */ + private void postProcess() { + + + // cancel the directory fetcher + dirFetcher.interrupt(); + + addImageDone = true; + // attempt actions that might fail and force the process to stop + + if (cancelled || hasCritError) { + logger.log(Level.INFO, "Handling errors or interruption that occured in add image process"); + revert(); + } + if (!errorList.isEmpty()) { + logger.log(Level.INFO, "Handling non-critical errors that occured in add image process"); + } + + // When everything happens without an error: + if (!(cancelled || hasCritError)) { + + try { + // Tell the progress monitor we're done + progressMonitor.setProgress(100); + + if (newContents.isEmpty()) { + if (addImageProcess != null) { // and if we're done configuring ingest + // commit anything + try { + commitImage(); + } catch (Exception ex) { + errorList.add(ex.getMessage()); + // Log error/display warning + logger.log(Level.SEVERE, "Error adding image to case.", ex); + } + } else { + logger.log(Level.SEVERE, "Missing image process object"); + } + } + + else { //already commited? + logger.log(Level.INFO, "Assuming image already committed, will not commit."); + } + + } catch (Exception ex) { + //handle unchecked exceptions post image add + errorList.add(ex.getMessage()); + + logger.log(Level.WARNING, "Unexpected errors occurred while running post add image cleanup. ", ex); + logger.log(Level.SEVERE, "Error adding image to case", ex); + } finally { + + + } + } + + // invoke the callBack, unless the caller cancelled + if (!cancelled) { + doCallBack(); + } + + return; + } + + + + /* + * Call the callback with results, new content, and errors, if any + */ + private void doCallBack() + { + DSPCallback.DSP_Result result; + + if (hasCritError) { + result = DSPCallback.DSP_Result.CRITICAL_ERRORS; + } + else if (!errorList.isEmpty()) { + result = DSPCallback.DSP_Result.NONCRITICAL_ERRORS; + } + else { + result = DSPCallback.DSP_Result.NO_ERRORS; + } + + // invoke the callcak, passing it the result, list of new contents, and list of errors + callbackObj.done(result, errorList, newContents); + } + + /* + * cancel the image addition, if possible + */ + public void cancelTask() { + + cancelled = true; + + if (!addImageDone) { + try { + interrupt(); + } + catch (Exception ex) { + logger.log(Level.SEVERE, "Failed to interrup the add image task..."); + } + } + else { + try { + revert(); + } + catch(Exception ex) { + logger.log(Level.SEVERE, "Failed to revert the add image task..."); + } + } + } + /* + * Interrurp the add image process if it is still running + */ + private void interrupt() throws Exception { + + try { + logger.log(Level.INFO, "interrupt() add image process"); + addImageProcess.stop(); //it might take time to truly stop processing and writing to db + } catch (TskException ex) { + throw new Exception("Error stopping add-image process.", ex); + } + } + + /* + * Revert - if image has already been added but not committed yet + */ + void revert() { + + if (!reverted) { + try { + logger.log(Level.INFO, "Revert after add image process"); + try { + addImageProcess.revert(); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error reverting add image process", ex); + } + } finally { + + } + reverted = true; + } + } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java index 7b0faf13f8..476bbdd1df 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java @@ -18,7 +18,9 @@ */ package org.sleuthkit.autopsy.casemodule; + import java.awt.Color; +import java.awt.EventQueue; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -27,6 +29,7 @@ import javax.swing.event.ChangeListener; import org.openide.WizardDescriptor; import org.openide.util.HelpCtx; import org.openide.util.Lookup; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPProgressMonitor; /** * The final panel of the add image wizard. It displays a progress bar and @@ -50,6 +53,49 @@ class AddImageWizardAddingProgressPanel implements WizardDescriptor.FinishablePa private AddImageWizardAddingProgressVisual component; private final Set listeners = new HashSet<>(1); // or can use ChangeSupport in NB 6.0 + private DSPProgressMonitorImpl dspProgressMonitorImpl = new DSPProgressMonitorImpl(); + + public DSPProgressMonitorImpl getDSPProgressMonitorImpl() { + return dspProgressMonitorImpl; + } + + private class DSPProgressMonitorImpl implements DSPProgressMonitor { + @Override + public void setIndeterminate(final boolean indeterminate) { + // update the progress bar asynchronously + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + getComponent().getProgressBar().setIndeterminate(indeterminate); + } + }); + } + + @Override + public void setProgress(final int progress) { + // update the progress bar asynchronously + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + getComponent().getProgressBar().setValue(progress); + } + }); + } + + @Override + public void setProgressText(final String text) { + // update the progress UI asynchronously + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + getComponent().setProgressMsgText(text); + } + }); + } + + + + } /** * Get the visual component for the panel. In this template, the component * is kept separate. This can be more efficient: if the wizard is created diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.form b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.form index 53febaf0bf..9f3068809b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.form @@ -142,7 +142,7 @@ - + @@ -158,7 +158,7 @@ - + @@ -185,7 +185,7 @@ - + @@ -196,11 +196,11 @@ - + - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.java index a95ad671f2..f0e10fa1e2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.java @@ -71,7 +71,7 @@ public class AddImageWizardAddingProgressVisual extends javax.swing.JPanel { donePanel.setVisible(false); viewLogButton.setVisible(false); //match visual background of panel - this.TextArea_CurrentDirectory.setBackground(this.getBackground()); + this.progressTextArea.setBackground(this.getBackground()); } @@ -95,10 +95,10 @@ public class AddImageWizardAddingProgressVisual extends javax.swing.JPanel { /** * Updates the currently processing directory * - * @param dir the text to update with + * @param msg the text to update with */ - public void setCurrentDirText(String dir) { - this.TextArea_CurrentDirectory.setText(dir); + public void setProgressMsgText(String msg) { + this.progressTextArea.setText(msg); } /** @@ -140,7 +140,7 @@ public class AddImageWizardAddingProgressVisual extends javax.swing.JPanel { inProgressPanel = new javax.swing.JPanel(); progressBar = new javax.swing.JProgressBar(); progressLabel = new javax.swing.JLabel(); - TextArea_CurrentDirectory = new javax.swing.JTextArea(); + progressTextArea = new javax.swing.JTextArea(); subTitle2Label = new javax.swing.JLabel(); subTitle1Label = new javax.swing.JLabel(); @@ -193,14 +193,14 @@ public class AddImageWizardAddingProgressVisual extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(AddImageWizardAddingProgressVisual.class, "AddImageWizardAddingProgressVisual.progressLabel.text")); // NOI18N progressLabel.setPreferredSize(null); - TextArea_CurrentDirectory.setEditable(false); - TextArea_CurrentDirectory.setBackground(new java.awt.Color(240, 240, 240)); - TextArea_CurrentDirectory.setLineWrap(true); - TextArea_CurrentDirectory.setRows(5); - TextArea_CurrentDirectory.setWrapStyleWord(true); - TextArea_CurrentDirectory.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), org.openide.util.NbBundle.getMessage(AddImageWizardAddingProgressVisual.class, "AddImageWizardAddingProgressVisual.TextArea_CurrentDirectory.border.title"))); // NOI18N - TextArea_CurrentDirectory.setCursor(new java.awt.Cursor(java.awt.Cursor.TEXT_CURSOR)); - TextArea_CurrentDirectory.setFocusable(false); + progressTextArea.setEditable(false); + progressTextArea.setBackground(new java.awt.Color(240, 240, 240)); + progressTextArea.setLineWrap(true); + progressTextArea.setRows(5); + progressTextArea.setWrapStyleWord(true); + progressTextArea.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), org.openide.util.NbBundle.getMessage(AddImageWizardAddingProgressVisual.class, "AddImageWizardAddingProgressVisual.progressTextArea.border.title"))); // NOI18N + progressTextArea.setCursor(new java.awt.Cursor(java.awt.Cursor.TEXT_CURSOR)); + progressTextArea.setFocusable(false); org.openide.awt.Mnemonics.setLocalizedText(subTitle2Label, org.openide.util.NbBundle.getMessage(AddImageWizardAddingProgressVisual.class, "AddImageWizardAddingProgressVisual.subTitle2Label.text")); // NOI18N @@ -216,7 +216,7 @@ public class AddImageWizardAddingProgressVisual extends javax.swing.JPanel { .addComponent(subTitle2Label) .addComponent(progressLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(subTitle1Label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(TextArea_CurrentDirectory) + .addComponent(progressTextArea) .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGap(0, 8, Short.MAX_VALUE)) ); @@ -229,7 +229,7 @@ public class AddImageWizardAddingProgressVisual extends javax.swing.JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(TextArea_CurrentDirectory, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(progressTextArea, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(progressLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) @@ -268,12 +268,12 @@ public class AddImageWizardAddingProgressVisual extends javax.swing.JPanel { dialog.setVisible(true); }//GEN-LAST:event_viewLogButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables - protected javax.swing.JTextArea TextArea_CurrentDirectory; protected javax.swing.JPanel donePanel; protected javax.swing.JPanel inProgressPanel; private javax.swing.JPanel loadingPanel; private javax.swing.JProgressBar progressBar; protected javax.swing.JLabel progressLabel; + protected javax.swing.JTextArea progressTextArea; protected javax.swing.JLabel statusLabel; protected javax.swing.JLabel subTitle1Label; protected javax.swing.JLabel subTitle2Label; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java index 39a00baacc..d662439f3b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java @@ -43,6 +43,7 @@ class AddImageWizardChooseDataSourcePanel implements WizardDescriptor.Panel - - - - - - - - - - @@ -65,16 +55,7 @@ - - - - - - - - - - + @@ -89,41 +70,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -160,7 +106,7 @@ - + @@ -192,7 +138,7 @@ - + @@ -204,7 +150,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java index 40323635da..885b5cced0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java @@ -18,49 +18,44 @@ */ package org.sleuthkit.autopsy.casemodule; + import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.Component; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; +import java.util.HashMap; import java.util.List; -import java.util.SimpleTimeZone; -import java.util.TimeZone; -import javax.swing.ComboBoxModel; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; import javax.swing.JPanel; +import javax.swing.JList; +import javax.swing.JSeparator; import javax.swing.event.DocumentEvent; -import javax.swing.event.ListDataListener; -import org.sleuthkit.autopsy.casemodule.ContentTypePanel.ContentType; +import javax.swing.ListCellRenderer; +import org.openide.util.Lookup; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; /** - * visual component for the first panel of add image wizard. Allows user to pick - * data source and timezone. + * visual component for the first panel of add image wizard. + * Allows the user to choose the data source type and then select the data source * */ final class AddImageWizardChooseDataSourceVisual extends JPanel { - enum EVENT { - - UPDATE_UI, FOCUS_NEXT - }; - static final List rawExt = Arrays.asList(new String[]{".img", ".dd", ".001", ".aa", ".raw"}); - static final String rawDesc = "Raw Images (*.img, *.dd, *.001, *.aa, *.raw)"; - static GeneralFilter rawFilter = new GeneralFilter(rawExt, rawDesc); - static final List encaseExt = Arrays.asList(new String[]{".e01"}); - static final String encaseDesc = "Encase Images (*.e01)"; - static GeneralFilter encaseFilter = new GeneralFilter(encaseExt, encaseDesc); - static final List allExt = new ArrayList(); - - static { - allExt.addAll(rawExt); - allExt.addAll(encaseExt); - } - static final String allDesc = "All Supported Types"; - static GeneralFilter allFilter = new GeneralFilter(allExt, allDesc); + static final Logger logger = Logger.getLogger(AddImageWizardChooseDataSourceVisual.class.getName()); + private AddImageWizardChooseDataSourcePanel wizPanel; - private ContentTypeModel model; - private ContentTypePanel currentPanel; + + private JPanel currentPanel; + private Map datasourceProcessorsMap = new HashMap(); + + + List coreDSPTypes = new ArrayList(); /** * Creates new form AddImageVisualPanel1 @@ -70,24 +65,83 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { AddImageWizardChooseDataSourceVisual(AddImageWizardChooseDataSourcePanel wizPanel) { initComponents(); this.wizPanel = wizPanel; - createTimeZoneList(); + customInit(); } private void customInit() { - model = new ContentTypeModel(); - typeComboBox.setModel(model); - typeComboBox.setSelectedIndex(0); + typePanel.setLayout(new BorderLayout()); - updateCurrentPanel(ImageFilePanel.getDefault()); + + discoverDataSourceProcessors(); + + // set up the DSP type combobox + typeComboBox.removeAllItems(); + + Set dspTypes = datasourceProcessorsMap.keySet(); + + // make a list of core DSPs + // ensure that the core DSPs are at the top and in a fixed order + coreDSPTypes.add(ImageDSProcessor.dsType); + coreDSPTypes.add(LocalDiskDSProcessor.dsType); + coreDSPTypes.add(LocalFilesDSProcessor.dsType); + + for(String dspType:coreDSPTypes){ + typeComboBox.addItem(dspType); + } + + // now add any addtional DSPs that haven't already been added + for(String dspType:dspTypes){ + if (!coreDSPTypes.contains(dspType)) { + typeComboBox.addItem(dspType); + } + } + + // set a custom renderer that draws a separator at the end of the core DSPs in the combobox + typeComboBox.setRenderer(new ComboboxSeparatorRenderer(typeComboBox.getRenderer()){ + @Override + protected boolean addSeparatorAfter(JList list, Object value, int index){ + return (index == coreDSPTypes.size() - 1); + } + }); + + //add actionlistner to listen for change + ActionListener cbActionListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dspSelectionChanged(); + } + }; + typeComboBox.addActionListener(cbActionListener); + typeComboBox.setSelectedIndex(0); } + private void discoverDataSourceProcessors() { + + for (DataSourceProcessor dsProcessor: Lookup.getDefault().lookupAll(DataSourceProcessor.class)) { + + if (!datasourceProcessorsMap.containsKey(dsProcessor.getType()) ) { + dsProcessor.reset(); + datasourceProcessorsMap.put(dsProcessor.getType(), dsProcessor); + } + else { + logger.log(Level.SEVERE, "discoverDataSourceProcessors(): A DataSourceProcessor already exists for type = " + dsProcessor.getType() ); + } + } + } + + private void dspSelectionChanged() { + // update the current panel to selection + currentPanel = getCurrentDSProcessor().getPanel(); + updateCurrentPanel(currentPanel); + } + /** * Changes the current panel to the given panel. * * @param panel instance of ImageTypePanel to change to */ - private void updateCurrentPanel(ContentTypePanel panel) { + private void updateCurrentPanel(JPanel panel) { currentPanel = panel; typePanel.removeAll(); typePanel.add((JPanel) currentPanel, BorderLayout.CENTER); @@ -96,28 +150,32 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { currentPanel.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(AddImageWizardChooseDataSourceVisual.EVENT.UPDATE_UI.toString())) { + if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString())) { updateUI(null); } - if (evt.getPropertyName().equals(AddImageWizardChooseDataSourceVisual.EVENT.FOCUS_NEXT.toString())) { + if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString())) { wizPanel.moveFocusToNext(); } } }); - currentPanel.select(); - if (currentPanel.getContentType().equals(ContentType.LOCAL)) { - //disable image specific options - noFatOrphansCheckbox.setEnabled(false); - descLabel.setEnabled(false); - timeZoneComboBox.setEnabled(false); - } else { - noFatOrphansCheckbox.setEnabled(true); - descLabel.setEnabled(true); - timeZoneComboBox.setEnabled(true); - } + updateUI(null); } + /** + * Returns the currently selected DS Processor + * @return DataSourceProcessor the DataSourceProcessor corresponding to the data source type selected in the combobox + */ + public DataSourceProcessor getCurrentDSProcessor() { + // get the type of the currently selected panel and then look up + // the correspodning DS Handler in the map + String dsType = (String) typeComboBox.getSelectedItem(); + DataSourceProcessor dsProcessor = datasourceProcessorsMap.get(dsType); + + return dsProcessor; + + } + /** * Returns the name of the this panel. This name will be shown on the left * panel of the "Add Image" wizard panel. @@ -129,94 +187,6 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { return "Enter Data Source Information"; } - /** - * Gets the data sources path from the Image Path Text Field. - * - * @return data source path, can be comma separated for multiples - */ - public String getContentPaths() { - return currentPanel.getContentPaths(); - } - - /** - * Gets the data sources type selected - * - * @return data source selected - */ - public ContentType getContentType() { - return currentPanel.getContentType(); - } - - /** - * Reset the data sources panel selected - */ - public void reset() { - currentPanel.reset(); - } - - /** - * Sets the image path of the current panel. - * - * @param s the image path to set - */ - public void setContentPath(String s) { - currentPanel.setContentPath(s); - } - - /** - * - * @return true if no fat orphans processing is selected - */ - boolean getNoFatOrphans() { - return noFatOrphansCheckbox.isSelected(); - } - - /** - * Gets the time zone that selected on the drop down list. - * - * @return timeZone the time zone that selected - */ - public String getSelectedTimezone() { - String tz = timeZoneComboBox.getSelectedItem().toString(); - return tz.substring(tz.indexOf(")") + 2).trim(); - } - - // add the timeZone list to the timeZoneComboBox - /** - * Creates the drop down list for the time zones and then makes the local - * machine time zones to be selected. - */ - public void createTimeZoneList() { - // load and add all timezone - String[] ids = SimpleTimeZone.getAvailableIDs(); - for (String id : ids) { - TimeZone zone = TimeZone.getTimeZone(id); - int offset = zone.getRawOffset() / 1000; - int hour = offset / 3600; - int minutes = (offset % 3600) / 60; - String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id); - - /* - * DateFormat dfm = new SimpleDateFormat("z"); - * dfm.setTimeZone(zone); boolean hasDaylight = - * zone.useDaylightTime(); String first = dfm.format(new Date(2010, - * 1, 1)); String second = dfm.format(new Date(2011, 6, 6)); int mid - * = hour * -1; String result = first + Integer.toString(mid); - * if(hasDaylight){ result = result + second; } - * timeZoneComboBox.addItem(item + " (" + result + ")"); - */ - timeZoneComboBox.addItem(item); - } - // get the current timezone - TimeZone thisTimeZone = Calendar.getInstance().getTimeZone(); - int thisOffset = thisTimeZone.getRawOffset() / 1000; - int thisHour = thisOffset / 3600; - int thisMinutes = (thisOffset % 3600) / 60; - String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID()); - - // set the selected timezone - timeZoneComboBox.setSelectedItem(formatted); - } /** * This method is called from within the constructor to initialize the form. @@ -229,14 +199,10 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { buttonGroup1 = new javax.swing.ButtonGroup(); jLabel2 = new javax.swing.JLabel(); nextLabel = new javax.swing.JLabel(); - timeZoneLabel = new javax.swing.JLabel(); - timeZoneComboBox = new javax.swing.JComboBox(); - noFatOrphansCheckbox = new javax.swing.JCheckBox(); - descLabel = new javax.swing.JLabel(); inputPanel = new javax.swing.JPanel(); typeTabel = new javax.swing.JLabel(); typePanel = new javax.swing.JPanel(); - typeComboBox = new javax.swing.JComboBox(); + typeComboBox = new javax.swing.JComboBox(); imgInfoLabel = new javax.swing.JLabel(); org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.jLabel2.text")); // NOI18N @@ -245,15 +211,6 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { org.openide.awt.Mnemonics.setLocalizedText(nextLabel, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.nextLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.timeZoneLabel.text")); // NOI18N - - timeZoneComboBox.setMaximumRowCount(30); - - org.openide.awt.Mnemonics.setLocalizedText(noFatOrphansCheckbox, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.noFatOrphansCheckbox.text")); // NOI18N - noFatOrphansCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.noFatOrphansCheckbox.toolTipText")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(descLabel, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.descLabel.text")); // NOI18N - inputPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); org.openide.awt.Mnemonics.setLocalizedText(typeTabel, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.typeTabel.text")); // NOI18N @@ -269,7 +226,7 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { ); typePanelLayout.setVerticalGroup( typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 77, Short.MAX_VALUE) + .addGap(0, 173, Short.MAX_VALUE) ); javax.swing.GroupLayout inputPanelLayout = new javax.swing.GroupLayout(inputPanel); @@ -295,7 +252,7 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { .addComponent(typeTabel) .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) + .addComponent(typePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 173, Short.MAX_VALUE) .addContainerGap()) ); @@ -313,14 +270,6 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(nextLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createSequentialGroup() - .addComponent(timeZoneLabel) - .addGap(18, 18, 18) - .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 252, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(noFatOrphansCheckbox) - .addGroup(layout.createSequentialGroup() - .addGap(21, 21, 21) - .addComponent(descLabel)) .addComponent(imgInfoLabel)) .addGap(0, 54, Short.MAX_VALUE))) .addContainerGap()) @@ -332,30 +281,18 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { .addComponent(imgInfoLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(inputPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(timeZoneLabel) - .addComponent(timeZoneComboBox, 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.RELATED) - .addComponent(descLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 64, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 45, Short.MAX_VALUE) .addComponent(nextLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0)) ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.ButtonGroup buttonGroup1; - private javax.swing.JLabel descLabel; private javax.swing.JLabel imgInfoLabel; private javax.swing.JPanel inputPanel; private javax.swing.JLabel jLabel2; private javax.swing.JLabel nextLabel; - private javax.swing.JCheckBox noFatOrphansCheckbox; - private javax.swing.JComboBox timeZoneComboBox; - private javax.swing.JLabel timeZoneLabel; - private javax.swing.JComboBox typeComboBox; + private javax.swing.JComboBox typeComboBox; private javax.swing.JPanel typePanel; private javax.swing.JLabel typeTabel; // End of variables declaration//GEN-END:variables @@ -369,44 +306,31 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { * @param e the document event */ public void updateUI(DocumentEvent e) { - this.wizPanel.enableNextButton(currentPanel.enableNext()); + // Enable the Next button if the current DSP panel is valid + this.wizPanel.enableNextButton(getCurrentDSProcessor().validatePanel()); } - /** - * ComboBoxModel to control typeComboBox and supply ImageTypePanels. - */ - private class ContentTypeModel implements ComboBoxModel { + + public abstract class ComboboxSeparatorRenderer implements ListCellRenderer{ + private ListCellRenderer delegate; + private JPanel separatorPanel = new JPanel(new BorderLayout()); + private JSeparator separator = new JSeparator(); - private ContentTypePanel selected; - private ContentTypePanel[] types = ContentTypePanel.getPanels(); - - @Override - public void setSelectedItem(Object anItem) { - selected = (ContentTypePanel) anItem; - updateCurrentPanel(selected); + public ComboboxSeparatorRenderer(ListCellRenderer delegate){ + this.delegate = delegate; } - @Override - public Object getSelectedItem() { - return selected; + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus){ + Component comp = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if(index!=-1 && addSeparatorAfter(list, value, index)){ + separatorPanel.removeAll(); + separatorPanel.add(comp, BorderLayout.CENTER); + separatorPanel.add(separator, BorderLayout.SOUTH); + return separatorPanel; + }else + return comp; } - @Override - public int getSize() { - return types.length; - } - - @Override - public ContentTypePanel getElementAt(int index) { - return types[index]; - } - - @Override - public void addListDataListener(ListDataListener l) { - } - - @Override - public void removeListDataListener(ListDataListener l) { - } + protected abstract boolean addSeparatorAfter(JList list, Object value, int index); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java index 3f5b77e281..4e7ed87ab6 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java @@ -18,41 +18,25 @@ */ package org.sleuthkit.autopsy.casemodule; + import org.sleuthkit.autopsy.ingest.IngestConfigurator; import java.awt.Color; import java.awt.Component; -import java.awt.EventQueue; import java.awt.Window; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.logging.Level; import javax.swing.JButton; import javax.swing.JOptionPane; -import javax.swing.JProgressBar; import javax.swing.SwingUtilities; -import javax.swing.SwingWorker; import javax.swing.event.ChangeListener; import org.openide.WizardDescriptor; import org.openide.util.HelpCtx; import org.openide.util.Lookup; -import org.sleuthkit.autopsy.casemodule.ContentTypePanel.ContentType; -import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.PlatformUtil; -import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.FileSystem; -import org.sleuthkit.datamodel.Image; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskDataException; -import org.sleuthkit.datamodel.TskException; -import org.sleuthkit.datamodel.Volume; -import org.sleuthkit.datamodel.VolumeSystem; - +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; /** * second panel of add image wizard, allows user to configure ingest modules. * @@ -68,28 +52,27 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel newContents = Collections.synchronizedList(new ArrayList()); private boolean ingested = false; private boolean readyToIngest = false; - // the paths of the image files to be added - private String dataSourcePath; - private ContentType dataSourceType; - // the time zone where the image is added - private String timeZone; - //whether to not process FAT filesystem orphans - private boolean noFatOrphans; + // task that will clean up the created database file if the wizard is cancelled before it finishes - private AddImageAction.CleanupTask cleanupImage; // initialized to null in readSettings() - private CurrentDirectoryFetcher fetcher; - private AddImageProcess process; - private AddImageAction action; - private AddImageTask addImageTask; - private AddLocalFilesTask addLocalFilesTask; + private AddImageAction.CleanupTask cleanupTask; + + private AddImageAction addImageAction; + private AddImageWizardAddingProgressPanel progressPanel; + private AddImageWizardChooseDataSourcePanel dataSourcePanel; + + private DataSourceProcessor dsProcessor; + - AddImageWizardIngestConfigPanel(AddImageAction action, AddImageWizardAddingProgressPanel proPanel) { - this.action = action; + AddImageWizardIngestConfigPanel(AddImageWizardChooseDataSourcePanel dsPanel, AddImageAction action, AddImageWizardAddingProgressPanel proPanel) { + this.addImageAction = action; this.progressPanel = proPanel; + this.dataSourcePanel = dsPanel; + ingestConfig = Lookup.getDefault().lookup(IngestConfigurator.class); List messages = ingestConfig.setContext(AddImageWizardIngestConfigPanel.class.getCanonicalName()); if (messages.isEmpty() == false) { @@ -183,24 +166,14 @@ class AddImageWizardIngestConfigPanel implements WizardDescriptor.Panel { - - AddImageTask task; - JProgressBar prog; - AddImageWizardAddingProgressVisual wiz; - AddImageProcess proc; - - CurrentDirectoryFetcher(JProgressBar prog, AddImageWizardAddingProgressVisual wiz, AddImageProcess proc) { - this.wiz = wiz; - this.proc = proc; - this.prog = prog; - } - - /** - * @return the currently processing directory - */ - @Override - 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() { - wiz.setCurrentDirText(proc.currentDirectory()); - } - }); - - Thread.sleep(2 * 1000); - } - return 1; - } catch (InterruptedException ie) { - return -1; - } - } - } - - /** - * Thread that will add logical files to database, and then kick-off ingest - * modules. Note: the add logical 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 - * logical 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 = progressPanel.getComponent().getProgressBar(); - 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 logical files process interrupted."); - //nothing to be cleanedup - } - }; - - cancelledWhileRunning.enable(); - final LocalFilesAddProgressUpdater progUpdater = new LocalFilesAddProgressUpdater(this.progressBar, progressPanel.getComponent()); - try { - final FileManager fileManager = currentCase.getServices().getFileManager(); - progressPanel.setStateStarted(); - String[] paths = dataSourcePath.split(LocalFilesPanel.FILES_SEP); - List absLocalPaths = new ArrayList(); - for (String path : paths) { - absLocalPaths.add(path); - } - newContents.add(fileManager.addLocalFilesDirs(absLocalPaths, progUpdater)); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Errors occurred while running add logical 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); - - //clear updates - // progressPanel.getComponent().setProcessInvis(); - - if (interrupted || hasCritError) { - logger.log(Level.INFO, "Handling errors or interruption that occured in logical files process"); - if (hasCritError) { - //core error - progressPanel.getComponent().showErrors(errorString, true); - } - return; - } else { - if (errorString != null) { - //data error (non-critical) - logger.log(Level.INFO, "Handling non-critical errors that occured in logical files process"); - progressPanel.getComponent().showErrors(errorString, false); - } - } - try { - // When everything happens without an error: - if (errorString == null) { // complete progress bar - progressPanel.getComponent().setProgressBarTextAndColor("*Logical Files added.", 100, Color.black); - } - - // Get attention for the process finish - java.awt.Toolkit.getDefaultToolkit().beep(); //BEEP! - AddImageWizardAddingProgressVisual panel = progressPanel.getComponent(); - if (panel != null) { - Window w = SwingUtilities.getWindowAncestor(panel); - if (w != null) { - w.toFront(); - } - } - - progressPanel.setStateFinished(); - - //notify the case - if (!newContents.isEmpty()) { - Case.getCurrentCase().addLocalDataSource(newContents.get(0)); - } - - // 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); - progressPanel.getComponent().setProgressBarTextAndColor("*Failed to add image.", 0, Color.black); // set error message - logger.log(Level.SEVERE, "Error adding image to case", ex); - } - } - - /** - * Updates the wizard status with logical file/folder - */ - private class LocalFilesAddProgressUpdater implements FileManager.FileAddProgressUpdater { - - private int count = 0; - private JProgressBar prog; - private AddImageWizardAddingProgressVisual wiz; - - LocalFilesAddProgressUpdater(JProgressBar prog, AddImageWizardAddingProgressVisual wiz) { - this.wiz = wiz; - this.prog = prog; - } - + private void startDataSourceProcessing(WizardDescriptor settings) { + + + + // Add a cleanup task to interrupt the background process if the + // wizard exits while the background process is running. + cleanupTask = addImageAction.new CleanupTask() { @Override - public void fileAdded(final AbstractFile newFile) { - if (count++ % 10 == 0 && (prog.getValue() < 100 || prog.isIndeterminate())) { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - wiz.setCurrentDirText(newFile.getParentPath() + "/" + newFile.getName()); - } - }); - - } + void cleanup() throws Exception { + cancelDataSourceProcessing(); } - } + }; + + cleanupTask.enable(); + + // get the selected DSProcessor + dsProcessor = dataSourcePanel.getComponent().getCurrentDSProcessor(); + + DSPCallback cbObj = new DSPCallback () { + @Override + public void doneEDT(DSPCallback.DSP_Result result, List errList, List contents) { + dataSourceProcessorDone(result, errList, contents ); + } + + }; + + progressPanel.setStateStarted(); + + // Kick off the DSProcessor + dsProcessor.run(progressPanel.getDSPProgressMonitorImpl(), cbObj); + } - /** - * Thread that will make the JNI call to add image to database, and then - * kick-off ingest modules. + /* + * Cancels the data source processing - in case the users presses 'Cancel' */ - private class AddImageTask 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 wizDescriptor; - private Logger logger = Logger.getLogger(AddImageTask.class.getName()); - - protected AddImageTask(WizardDescriptor settings) { - this.progressBar = progressPanel.getComponent().getProgressBar(); - currentCase = Case.getCurrentCase(); - this.wizDescriptor = 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 image process interrupted."); - addImageTask.interrupt(); //it might take time to truly interrupt - } - }; - - - try { - //lock DB for writes in EWT thread - //wait until lock acquired in EWT - EventQueue.invokeAndWait(new Runnable() { - @Override - public void run() { - SleuthkitCase.dbWriteLock(); - } - }); - } catch (InterruptedException ex) { - logger.log(Level.WARNING, "Errors occurred while running add image, could not acquire lock. ", ex); - return 0; - - } catch (InvocationTargetException ex) { - logger.log(Level.WARNING, "Errors occurred while running add image, could not acquire lock. ", ex); - return 0; - } - - process = currentCase.makeAddImageProcess(timeZone, true, noFatOrphans); - fetcher = new CurrentDirectoryFetcher(this.progressBar, progressPanel.getComponent(), process); - cancelledWhileRunning.enable(); - try { - progressPanel.setStateStarted(); - fetcher.execute(); - process.run(new String[]{dataSourcePath}); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Core errors occurred while running add image. ", ex); - //critical core/system error and process needs to be interrupted - hasCritError = true; - errorString = ex.getMessage(); - } catch (TskDataException ex) { - logger.log(Level.WARNING, "Data errors occurred while running add image. ", ex); - errorString = ex.getMessage(); - } finally { - // process is over, doesn't need to be dealt with if cancel happens - cancelledWhileRunning.disable(); - - } - - return 0; - } - - /** - * Commit the finished AddImageProcess, and cancel the CleanupTask that - * would have reverted it. - * - * @param settings property set to get AddImageProcess and CleanupTask - * from - * - * @throws Exception if commit or adding the image to the case failed - */ - private void commitImage(WizardDescriptor settings) throws Exception { - - String contentPath = (String) settings.getProperty(AddImageAction.DATASOURCEPATH_PROP); - - String timezone = settings.getProperty(AddImageAction.TIMEZONE_PROP).toString(); - settings.putProperty(AddImageAction.IMAGEID_PROP, ""); - - long imageId = 0; - try { - imageId = process.commit(); - } catch (TskException e) { - logger.log(Level.WARNING, "Errors occured while committing the image", e); - } finally { - //commit done, unlock db write in EWT thread - //before doing anything else - SleuthkitCase.dbWriteUnlock(); - - if (imageId != 0) { - Image newImage = Case.getCurrentCase().addImage(contentPath, imageId, timezone); - - //while we have the image, verify the size of its contents - String verificationErrors = newImage.verifyImageSize(); - if (verificationErrors.equals("") == false) { - //data error (non-critical) - progressPanel.addErrors(verificationErrors, false); - } - - - newContents.add(newImage); - settings.putProperty(AddImageAction.IMAGEID_PROP, imageId); - } - - // Can't bail and revert image add after commit, so disable image cleanup - // task - cleanupImage.disable(); - settings.putProperty(AddImageAction.IMAGECLEANUPTASK_PROP, null); - - logger.log(Level.INFO, "Image committed, imageId: " + imageId); - logger.log(Level.INFO, PlatformUtil.getAllMemUsageInfo()); - + private void cancelDataSourceProcessing() { + dsProcessor.cancel(); + } + + /* + * Callback for the data source processor. + * Invoked by the DSP on the EDT thread, when it finishes processing the data source. + */ + private void dataSourceProcessorDone(DSPCallback.DSP_Result result, List errList, List contents) { + + // disable the cleanup task + cleanupTask.disable(); + + // Get attention for the process finish + java.awt.Toolkit.getDefaultToolkit().beep(); //BEEP! + AddImageWizardAddingProgressVisual panel = progressPanel.getComponent(); + if (panel != null) { + Window w = SwingUtilities.getWindowAncestor(panel); + if (w != null) { + w.toFront(); } } - - /** - * - * (called by EventDispatch Thread after doInBackground finishes) - */ - @Override - protected void done() { - //these are required to stop the CurrentDirectoryFetcher - progressBar.setIndeterminate(false); - setProgress(100); - - // attempt actions that might fail and force the process to stop - - if (interrupted || hasCritError) { - logger.log(Level.INFO, "Handling errors or interruption that occured in add image process"); - revert(); - if (hasCritError) { - //core error - progressPanel.addErrors(errorString, true); - } - return; - } - if (errorString != null) { - //data error (non-critical) - logger.log(Level.INFO, "Handling non-critical errors that occured in add image process"); - progressPanel.addErrors(errorString, false); - } - - - try { - // When everything happens without an error: - - // the add-image process needs to be reverted if the wizard doesn't finish - cleanupImage = action.new CleanupTask() { - //note, CleanupTask runs inside EWT thread - @Override - void cleanup() throws Exception { - logger.log(Level.INFO, "Running cleanup task after add image process"); - revert(); - } - }; - cleanupImage.enable(); - - if (errorString == null) { // complete progress bar - progressPanel.getComponent().setProgressBarTextAndColor("*Data Source added.", 100, Color.black); - } - - // Get attention for the process finish - java.awt.Toolkit.getDefaultToolkit().beep(); //BEEP! - AddImageWizardAddingProgressVisual panel = progressPanel.getComponent(); - if (panel != null) { - Window w = SwingUtilities.getWindowAncestor(panel); - if (w != null) { - w.toFront(); - } - } - - // Tell the panel we're done - progressPanel.setStateFinished(); - - // Commit the image - if (!newContents.isEmpty()) //already commited - { - logger.log(Level.INFO, "Assuming image already committed, will not commit."); - return; - } - - if (process != null) { // and if we're done configuring ingest - // commit anything - try { - commitImage(wizDescriptor); - } catch (Exception ex) { - // Log error/display warning - logger.log(Level.SEVERE, "Error adding image to case.", ex); - } - } else { - logger.log(Level.SEVERE, "Missing image process object"); - } - - - - - // Start ingest if we can - startIngest(); - - } catch (Exception ex) { - //handle unchecked exceptions post image add - - logger.log(Level.WARNING, "Unexpected errors occurred while running post add image cleanup. ", ex); - - progressPanel.getComponent().setProgressBarTextAndColor("*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 { - } + // Tell the panel we're done + progressPanel.setStateFinished(); + + + //check the result and display to user + if (result == DSPCallback.DSP_Result.NO_ERRORS) + progressPanel.getComponent().setProgressBarTextAndColor("*Data Source added.", 100, Color.black); + else + progressPanel.getComponent().setProgressBarTextAndColor("*Errors encountered in adding Data Source.", 100, Color.red); + + + //if errors, display them on the progress panel + for ( String err: errList ) { + // TBD: there probably should be an error level for each error + progressPanel.addErrors(err, false); + } + + newContents.clear(); + newContents.addAll(contents); + + //notify the UI of the new content added to the case + if (!newContents.isEmpty()) { + + Case.getCurrentCase().notifyNewDataSource(newContents.get(0)); } - void interrupt() throws Exception { - interrupted = true; - try { - logger.log(Level.INFO, "interrupt() add image process"); - process.stop(); //it might take time to truly stop processing and writing to db - } catch (TskException ex) { - throw new Exception("Error stopping add-image process.", ex); - } - } - - //runs in EWT - void revert() { - try { - logger.log(Level.INFO, "Revert after add image process"); - try { - process.revert(); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error reverting add image process", ex); - } - } finally { - //unlock db write within EWT thread - SleuthkitCase.dbWriteUnlock(); - } - } + + // Start ingest if we can + progressPanel.setStateStarted(); + startIngest(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java index 2528f0b582..b786b5b171 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java @@ -47,11 +47,16 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator> getPanels() { if (panels == null) { - AddImageWizardAddingProgressPanel wizPanel = new AddImageWizardAddingProgressPanel(); panels = new ArrayList>(); - panels.add(new AddImageWizardChooseDataSourcePanel()); - panels.add(new AddImageWizardIngestConfigPanel(action, wizPanel)); - panels.add(wizPanel); + + AddImageWizardAddingProgressPanel progressPanel = new AddImageWizardAddingProgressPanel(); + + AddImageWizardChooseDataSourcePanel dsPanel = new AddImageWizardChooseDataSourcePanel(progressPanel); + AddImageWizardIngestConfigPanel ingestConfigPanel = new AddImageWizardIngestConfigPanel(dsPanel, action, progressPanel); + + panels.add(dsPanel); + panels.add(ingestConfigPanel); + panels.add(progressPanel); String[] steps = new String[panels.size()]; for (int i = 0; i < panels.size(); i++) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLocalFilesTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLocalFilesTask.java new file mode 100644 index 0000000000..34b7a32089 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLocalFilesTask.java @@ -0,0 +1,185 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPProgressMonitor; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.datamodel.TskCoreException; + + +/** + * Thread that will add logical files to database, and then kick-off ingest + * modules. Note: the add logical 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 + * logical files. + */ +public class AddLocalFilesTask implements Runnable { + + private Logger logger = Logger.getLogger(AddLocalFilesTask.class.getName()); + + private String dataSourcePath; + private DSPProgressMonitor progressMonitor; + private DSPCallback callbackObj; + + private Case currentCase; + // true if the process was requested to stop + private volatile boolean cancelled = false; + private boolean hasCritError = false; + + private List errorList = new ArrayList(); + private final List newContents = Collections.synchronizedList(new ArrayList()); + + + protected AddLocalFilesTask(String dataSourcePath, DSPProgressMonitor aProgressMonitor, DSPCallback cbObj) { + + currentCase = Case.getCurrentCase(); + + this.dataSourcePath = dataSourcePath; + this.callbackObj = cbObj; + this.progressMonitor = aProgressMonitor; + } + + /** + * Starts the addImage process, but does not commit the results. + * + * @return + * + * @throws Exception + */ + @Override + public void run() { + + errorList.clear(); + + final LocalFilesAddProgressUpdater progUpdater = new LocalFilesAddProgressUpdater(progressMonitor); + try { + + progressMonitor.setIndeterminate(true); + progressMonitor.setProgress(0); + + final FileManager fileManager = currentCase.getServices().getFileManager(); + String[] paths = dataSourcePath.split(LocalFilesPanel.FILES_SEP); + List absLocalPaths = new ArrayList(); + for (String path : paths) { + absLocalPaths.add(path); + } + newContents.add(fileManager.addLocalFilesDirs(absLocalPaths, progUpdater)); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Errors occurred while running add logical files. ", ex); + hasCritError = true; + errorList.add(ex.getMessage()); + } finally { + + } + + // handle done + postProcess(); + + return; + } + + /** + * + * (called by EventDispatch Thread after doInBackground finishes) + */ + protected void postProcess() { + + if (cancelled || hasCritError) { + logger.log(Level.WARNING, "Handling errors or interruption that occured in logical files process"); + + } + if (!errorList.isEmpty()) { + //data error (non-critical) + logger.log(Level.WARNING, "Handling non-critical errors that occured in logical files process"); + } + + if (!(cancelled || hasCritError)) { + progressMonitor.setProgress(100); + progressMonitor.setIndeterminate(false); + } + + // invoke the callBack, unless the caller cancelled + if (!cancelled) { + doCallBack(); + } + + } + + /* + * Call the callback with results, new content, and errors, if any + */ + private void doCallBack() + { + DSPCallback.DSP_Result result; + + if (hasCritError) { + result = DSPCallback.DSP_Result.CRITICAL_ERRORS; + } + else if (!errorList.isEmpty()) { + result = DSPCallback.DSP_Result.NONCRITICAL_ERRORS; + } + else { + result = DSPCallback.DSP_Result.NO_ERRORS; + } + + // invoke the callback, passing it the result, list of new contents, and list of errors + callbackObj.done(result, errorList, newContents); + } + + /* + * cancel the files addition, if possible + */ + public void cancelTask() { + cancelled = true; + + } + + /** + * Updates the wizard status with logical file/folder + */ + private class LocalFilesAddProgressUpdater implements FileManager.FileAddProgressUpdater { + + private int count = 0; + private DSPProgressMonitor progressMonitor; + + + LocalFilesAddProgressUpdater(DSPProgressMonitor progressMonitor) { + + this.progressMonitor = progressMonitor; + } + + @Override + public void fileAdded(final AbstractFile newFile) { + if (count++ % 10 == 0) { + progressMonitor.setProgressText("Adding: " + newFile.getParentPath() + "/" + newFile.getName()); + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index ffb3261dd5..1c20ec4025 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -115,7 +115,6 @@ ImageFilePanel.browseButton.text=Browse ImageFilePanel.pathTextField.text= LocalDiskPanel.diskLabel.text=Select a local disk: MissingImageDialog.selectButton.text=Select Image -MissingImageDialog.typeTabel.text=Select input type to add: MissingImageDialog.titleLabel.text=Search for missing image MissingImageDialog.cancelButton.text=Cancel LocalDiskPanel.errorLabel.text=Error Label @@ -134,17 +133,23 @@ LocalFilesPanel.selectButton.actionCommand=Add AddImageWizardIngestConfigVisual.subtitleLabel.text=Configure the ingest modules you would like to run on this data source. AddImageWizardIngestConfigVisual.titleLabel.text=Configure Ingest Modules AddImageWizardAddingProgressVisual.statusLabel.text=File system has been added to the local database. Files are being analyzed. -AddImageWizardChooseDataSourceVisual.noFatOrphansCheckbox.toolTipText= -AddImageWizardChooseDataSourceVisual.noFatOrphansCheckbox.text=Ignore orphan files in FAT file systems -AddImageWizardChooseDataSourceVisual.descLabel.text=(faster results, although some data will not be searched) AddImageWizardChooseDataSourceVisual.typeTabel.text=Select source type to add: AddImageWizardChooseDataSourceVisual.jLabel2.text=jLabel2 -AddImageWizardChooseDataSourceVisual.timeZoneLabel.text=Please select the input timezone: AddImageWizardChooseDataSourceVisual.nextLabel.text= Press 'Next' to analyze the input data, extract volume and file system data, and populate a local database. AddImageWizardChooseDataSourceVisual.imgInfoLabel.text=Enter Data Source Information: AddImageWizardAddingProgressVisual.progressLabel.text= -AddImageWizardAddingProgressVisual.TextArea_CurrentDirectory.border.title=Currently Adding: AddImageWizardAddingProgressVisual.viewLogButton.text=View Log AddImageWizardAddingProgressVisual.titleLabel.text=Adding Data Source AddImageWizardAddingProgressVisual.subTitle1Label.text=File system information is being added to a local database. File analysis will start when this finishes. AddImageWizardAddingProgressVisual.subTitle2Label.text=Processing Data Source and Adding to Database +ImageFilePanel.timeZoneLabel.text=Please select the input timezone: +ImageFilePanel.noFatOrphansCheckbox.text=Ignore orphan files in FAT file systems +ImageFilePanel.noFatOrphansCheckbox.toolTipText= +ImageFilePanel.descLabel.text=(faster results, although some data will not be searched) +LocalDiskPanel.timeZoneLabel.text=Please select the input timezone: +LocalDiskPanel.noFatOrphansCheckbox.toolTipText= +LocalDiskPanel.noFatOrphansCheckbox.text=Ignore orphan files in FAT file systems +LocalDiskPanel.descLabel.text=(faster results, although some data will not be searched) +MissingImageDialog.browseButton.text=Browse +MissingImageDialog.pathNameTextField.text= +AddImageWizardAddingProgressVisual.progressTextArea.border.title=Status diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 08ec802b95..2caf6cbf78 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -384,7 +384,9 @@ public class Case implements SleuthkitCase.ErrorObserver { + "\nPlease note that you will still be able to browse directories and generate reports\n" + "if you choose No, but you will not be able to view file content or run the ingest process.", "Missing Image", JOptionPane.YES_NO_OPTION); if (ret == JOptionPane.YES_OPTION) { + MissingImageDialog.makeDialog(obj_id, db); + } else { logger.log(Level.WARNING, "Selected image files don't match old files!"); } @@ -401,6 +403,7 @@ public class Case implements SleuthkitCase.ErrorObserver { * @param imgId the ID of the image that being added * @param timeZone the timeZone of the image where it's added */ + @Deprecated public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException { logger.log(Level.INFO, "Adding image to Case. imgPath: {0} ID: {1} TimeZone: {2}", new Object[]{imgPath, imgId, timeZone}); @@ -420,11 +423,23 @@ public class Case implements SleuthkitCase.ErrorObserver { * * @param newDataSource new data source added */ + @Deprecated void addLocalDataSource(Content newDataSource) { pcs.firePropertyChange(CASE_ADD_DATA_SOURCE, null, newDataSource); CoreComponentControl.openCoreWindows(); } + /** + * Notifies the UI that a new data source has been added. + * + * + * @param newDataSource new data source added + */ + void notifyNewDataSource(Content newDataSource) { + pcs.firePropertyChange(CASE_ADD_DATA_SOURCE, null, newDataSource); + CoreComponentControl.openCoreWindows(); + } + /** * @return The Services object for this case. */ diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ContentTypePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ContentTypePanel.java deleted file mode 100644 index 07664d9401..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ContentTypePanel.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2012 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.casemodule; - -import java.beans.PropertyChangeListener; -import javax.swing.JPanel; - -abstract class ContentTypePanel extends JPanel { - - public enum ContentType{IMAGE, DISK, LOCAL}; - - /** - * Returns a list off all the panels extending ImageTypePanel. - * @return list of all ImageTypePanels - */ - public static ContentTypePanel[] getPanels() { - return new ContentTypePanel[] {ImageFilePanel.getDefault(), LocalDiskPanel.getDefault(), LocalFilesPanel.getDefault() }; - } - - /** - * Return the path of the selected content in this panel. - * @return paths to selected content (one or more if multiselect supported) - */ - abstract public String getContentPaths(); - - /** - * Set the selected content in this panel to the provided path. - * This function is optional. - * @param s path to selected content - */ - abstract public void setContentPath(String s); - - /** - * Get content type (image, disk, local file) of the source this wizard panel is for - * @return ContentType of the source panel - */ - abstract public ContentType getContentType(); - - /** - * Returns if the next button should be enabled in the current wizard. - * @return true if the next button should be enabled, false otherwise - */ - abstract public boolean enableNext(); - - /** - * Tells this panel to reset itself - */ - abstract public void reset(); - - /** - * Tells this panel it has been selected. - */ - abstract public void select(); - - -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralFilter.java b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralFilter.java index b550a0a275..9b9a091466 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralFilter.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralFilter.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.casemodule; import java.io.File; import java.util.List; +import java.util.Arrays; import javax.swing.filechooser.FileFilter; /** @@ -28,6 +29,16 @@ import javax.swing.filechooser.FileFilter; */ public class GeneralFilter extends FileFilter{ + + // Extensions & Descriptions for commonly used filters + public static final List RAW_IMAGE_EXTS = Arrays.asList(new String[]{".img", ".dd", ".001", ".aa", ".raw", ".bin"}); + public static final String RAW_IMAGE_DESC = "Raw Images (*.img, *.dd, *.001, *.aa, *.raw, *.bin)"; + + public static final List ENCASE_IMAGE_EXTS = Arrays.asList(new String[]{".e01"}); + public static final String ENCASE_IMAGE_DESC = "Encase Images (*.e01)"; + + + private List extensions; private String desc; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java new file mode 100644 index 0000000000..a60cf66468 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java @@ -0,0 +1,218 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule; + + +import java.util.logging.Level; +import javax.swing.JPanel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.swing.filechooser.FileFilter; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPProgressMonitor; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; + +/** + * Image data source processor. + * Handles the addition of "disk images" to Autopsy. + * + * An instance of this class is created via the Netbeans Lookup() method. + * + */ +@ServiceProvider(service = DataSourceProcessor.class) +public class ImageDSProcessor implements DataSourceProcessor { + + + + static final Logger logger = Logger.getLogger(ImageDSProcessor.class.getName()); + + // Data source type handled by this processor + protected final static String dsType = "Image File"; + + // The Config UI panel that plugins into the Choose Data Source Wizard + private ImageFilePanel imageFilePanel; + + // The Background task that does the actual work of adding the image + private AddImageTask addImageTask; + + // true of cancelled by the caller + private boolean cancelled = false; + + DSPCallback callbackObj = null; + + // set to TRUE if the image options have been set via API and config Jpanel should be ignored + private boolean imageOptionsSet = false; + + // image options + private String imagePath; + private String timeZone; + private boolean noFatOrphans; + + + + + static final GeneralFilter rawFilter = new GeneralFilter(GeneralFilter.RAW_IMAGE_EXTS, GeneralFilter.RAW_IMAGE_DESC); + static final GeneralFilter encaseFilter = new GeneralFilter(GeneralFilter.ENCASE_IMAGE_EXTS, GeneralFilter.ENCASE_IMAGE_DESC); + + static final List allExt = new ArrayList(); + static { + allExt.addAll(GeneralFilter.RAW_IMAGE_EXTS); + allExt.addAll(GeneralFilter.ENCASE_IMAGE_EXTS); + } + static final String allDesc = "All Supported Types"; + static final GeneralFilter allFilter = new GeneralFilter(allExt, allDesc); + + static final List filtersList = new ArrayList(); + + static { + filtersList.add(allFilter); + filtersList.add(rawFilter); + filtersList.add(encaseFilter); + } + + + /* + * A no argument constructor is required for the NM lookup() method to create an object + */ + public ImageDSProcessor() { + + // Create the config panel + imageFilePanel = ImageFilePanel.createInstance(ImageDSProcessor.class.getName(), filtersList); + + } + + /** + * Returns the Data source type (string) handled by this DSP + * + * @return String the data source type + **/ + @Override + public String getType() { + return dsType; + } + + /** + * Returns the JPanel for collecting the Data source information + * + * @return JPanel the config panel + **/ + @Override + public JPanel getPanel() { + + + imageFilePanel.readSettings(); + imageFilePanel.select(); + + return imageFilePanel; + } + /** + * Validates the data collected by the JPanel + * + * @return String returns NULL if success, error string if there is any errors + **/ + @Override + public boolean validatePanel() { + + return imageFilePanel.validatePanel(); + } + /** + * Runs the data source processor. + * This must kick off processing the data source in background + * + * @param progressMonitor Progress monitor to report progress during processing + * @param cbObj callback to call when processing is done. + **/ + @Override + public void run(DSPProgressMonitor progressMonitor, DSPCallback cbObj) { + + callbackObj = cbObj; + cancelled = false; + + if (!imageOptionsSet) + { + //tell the panel to save the current settings + imageFilePanel.storeSettings(); + + // get the image options from the panel + imagePath = imageFilePanel.getContentPaths(); + timeZone = imageFilePanel.getTimeZone(); + noFatOrphans = imageFilePanel.getNoFatOrphans(); + } + + addImageTask = new AddImageTask(imagePath, timeZone, noFatOrphans, progressMonitor, cbObj); + new Thread(addImageTask).start(); + + return; + } + + /** + * Cancel the data source processing + **/ + @Override + public void cancel() { + + cancelled = true; + + addImageTask.cancelTask(); + + return; + } + + /** + * Reset the data source processor + **/ + @Override + public void reset() { + + // reset the config panel + imageFilePanel.reset(); + + // reset state + imageOptionsSet = false; + imagePath = null; + timeZone = null; + noFatOrphans = false; + + return; + } + + /** + * Sets the data source options externally. + * To be used by a client that does not have a UI and does not use the JPanel to + * collect this information from a user. + * + * @param imgPath path to thew image or first image + * @param String timeZone + * @param noFat whether to parse FAT orphans + **/ + public void setDataSourceOptions(String imgPath, String tz, boolean noFat) { + + this.imagePath = imgPath; + this.timeZone = tz; + this.noFatOrphans = noFat; + + imageOptionsSet = true; + + } + + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.form index 313ae952b8..886767fa57 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.form @@ -31,8 +31,20 @@ - - + + + + + + + + + + + + + + @@ -45,6 +57,16 @@ + + + + + + + + + + @@ -74,5 +96,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index a71ad31681..03dd184e8c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -22,41 +22,71 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; import java.util.List; +import java.util.SimpleTimeZone; +import java.util.TimeZone; import javax.swing.JFileChooser; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import javax.swing.JPanel; +import javax.swing.filechooser.FileFilter; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; + /** * ImageTypePanel for adding an image file such as .img, .E0x, .00x, etc. */ -public class ImageFilePanel extends ContentTypePanel implements DocumentListener { - private static ImageFilePanel instance = null; +public class ImageFilePanel extends JPanel implements DocumentListener { + + private final String PROP_LASTIMAGE_PATH = "LBL_LastImage_PATH"; + private PropertyChangeSupport pcs = null; private JFileChooser fc = new JFileChooser(); + + // Externally supplied name is used to store settings + private String contextName; /** * Creates new form ImageFilePanel + * @param context a string context name used to read/store last used settings + * @param fileChooserFilters a list of filters to be used with the FileChooser */ - public ImageFilePanel() { + private ImageFilePanel(String context, List fileChooserFilters) { initComponents(); fc.setDragEnabled(false); fc.setFileSelectionMode(JFileChooser.FILES_ONLY); fc.setMultiSelectionEnabled(false); - fc.addChoosableFileFilter(AddImageWizardChooseDataSourceVisual.rawFilter); - fc.addChoosableFileFilter(AddImageWizardChooseDataSourceVisual.encaseFilter); - fc.setFileFilter(AddImageWizardChooseDataSourceVisual.allFilter); + + boolean firstFilter = true; + for (FileFilter filter: fileChooserFilters ) { + if (firstFilter) { // set the first on the list as the default selection + fc.setFileFilter(filter); + firstFilter = false; + } + else { + fc.addChoosableFileFilter(filter); + } + } + + this.contextName = context; + pcs = new PropertyChangeSupport(this); + + createTimeZoneList(); } /** - * Returns the default instance of a ImageFilePanel. + * Creates and returns an instance of a ImageFilePanel. */ - public static synchronized ImageFilePanel getDefault() { - if (instance == null) { - instance = new ImageFilePanel(); - instance.postInit(); - } - return instance; + public static synchronized ImageFilePanel createInstance(String context, List fileChooserFilters) { + + ImageFilePanel instance = new ImageFilePanel(context, fileChooserFilters ); + + instance.postInit(); + + return instance; } //post-constructor initialization to properly initialize listener support @@ -79,6 +109,10 @@ public class ImageFilePanel extends ContentTypePanel implements DocumentListener pathLabel = new javax.swing.JLabel(); browseButton = new javax.swing.JButton(); pathTextField = new javax.swing.JTextField(); + timeZoneLabel = new javax.swing.JLabel(); + timeZoneComboBox = new javax.swing.JComboBox(); + noFatOrphansCheckbox = new javax.swing.JCheckBox(); + descLabel = new javax.swing.JLabel(); setMinimumSize(new java.awt.Dimension(0, 65)); setPreferredSize(new java.awt.Dimension(403, 65)); @@ -94,6 +128,15 @@ public class ImageFilePanel extends ContentTypePanel implements DocumentListener pathTextField.setText(org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.pathTextField.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.timeZoneLabel.text")); // NOI18N + + timeZoneComboBox.setMaximumRowCount(30); + + 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 + + org.openide.awt.Mnemonics.setLocalizedText(descLabel, org.openide.util.NbBundle.getMessage(ImageFilePanel.class, "ImageFilePanel.descLabel.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -104,8 +147,17 @@ public class ImageFilePanel extends ContentTypePanel implements DocumentListener .addComponent(browseButton) .addGap(2, 2, 2)) .addGroup(layout.createSequentialGroup() - .addComponent(pathLabel) - .addGap(0, 284, Short.MAX_VALUE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(timeZoneLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(pathLabel) + .addComponent(noFatOrphansCheckbox) + .addGroup(layout.createSequentialGroup() + .addGap(21, 21, 21) + .addComponent(descLabel))) + .addGap(0, 20, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -114,7 +166,16 @@ public class ImageFilePanel extends ContentTypePanel implements DocumentListener .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))) + .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(timeZoneLabel) + .addComponent(timeZoneComboBox, 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.RELATED) + .addComponent(descLabel) + .addContainerGap(33, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -131,20 +192,23 @@ public class ImageFilePanel extends ContentTypePanel implements DocumentListener String path = fc.getSelectedFile().getPath(); pathTextField.setText(path); } - pcs.firePropertyChange(AddImageWizardChooseDataSourceVisual.EVENT.FOCUS_NEXT.toString(), false, true); + pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString(), false, true); }//GEN-LAST:event_browseButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton browseButton; + private javax.swing.JLabel descLabel; + private javax.swing.JCheckBox noFatOrphansCheckbox; private javax.swing.JLabel pathLabel; private javax.swing.JTextField pathTextField; + private javax.swing.JComboBox timeZoneComboBox; + private javax.swing.JLabel timeZoneLabel; // End of variables declaration//GEN-END:variables /** * Get the path of the user selected image. * @return the image path */ - @Override public String getContentPaths() { return pathTextField.getText(); } @@ -152,33 +216,37 @@ public class ImageFilePanel extends ContentTypePanel implements DocumentListener /** * Set the path of the image file. */ - @Override public void setContentPath(String s) { pathTextField.setText(s); } - @Override - public ContentType getContentType() { - return ContentType.IMAGE; + public String getTimeZone() { + String tz = timeZoneComboBox.getSelectedItem().toString(); + return tz.substring(tz.indexOf(")") + 2).trim(); + } + + public boolean getNoFatOrphans() { + return noFatOrphansCheckbox.isSelected(); + } + + - @Override public void reset() { - //nothing to reset + //reset the UI elements to default + pathTextField.setText(null); } - - /** * Should we enable the next button of the wizard? * @return true if a proper image has been selected, false otherwise */ - @Override - public boolean enableNext() { + public boolean validatePanel() { String path = getContentPaths(); if (path == null || path.isEmpty()) { return false; } + boolean isExist = Case.pathExists(path); boolean isPhysicalDrive = Case.isPhysicalDrive(path); boolean isPartition = Case.isPartition(path); @@ -186,6 +254,57 @@ public class ImageFilePanel extends ContentTypePanel implements DocumentListener return (isExist || isPhysicalDrive || isPartition); } + + public void storeSettings() { + String imagePathName = getContentPaths(); + if (null != imagePathName ) { + String imagePath = imagePathName.substring(0, imagePathName.lastIndexOf(File.separator) + 1); + ModuleSettings.setConfigSetting(contextName, PROP_LASTIMAGE_PATH, imagePath); + } + } + + public void readSettings() { + String lastImagePath = ModuleSettings.getConfigSetting(contextName, PROP_LASTIMAGE_PATH); + if (null != lastImagePath) { + if (!lastImagePath.isEmpty()) + pathTextField.setText(lastImagePath); + } + } + /** + * Creates the drop down list for the time zones and then makes the local + * machine time zone to be selected. + */ + public void createTimeZoneList() { + // load and add all timezone + String[] ids = SimpleTimeZone.getAvailableIDs(); + for (String id : ids) { + TimeZone zone = TimeZone.getTimeZone(id); + int offset = zone.getRawOffset() / 1000; + int hour = offset / 3600; + int minutes = (offset % 3600) / 60; + String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id); + + /* + * DateFormat dfm = new SimpleDateFormat("z"); + * dfm.setTimeZone(zone); boolean hasDaylight = + * zone.useDaylightTime(); String first = dfm.format(new Date(2010, + * 1, 1)); String second = dfm.format(new Date(2011, 6, 6)); int mid + * = hour * -1; String result = first + Integer.toString(mid); + * if(hasDaylight){ result = result + second; } + * timeZoneComboBox.addItem(item + " (" + result + ")"); + */ + timeZoneComboBox.addItem(item); + } + // get the current timezone + TimeZone thisTimeZone = Calendar.getInstance().getTimeZone(); + int thisOffset = thisTimeZone.getRawOffset() / 1000; + int thisHour = thisOffset / 3600; + int thisMinutes = (thisOffset % 3600) / 60; + String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID()); + + // set the selected timezone + timeZoneComboBox.setSelectedItem(formatted); + } /** * Update functions are called by the pathTextField which has this set * as it's DocumentEventListener. Each update function fires a property change @@ -194,34 +313,26 @@ public class ImageFilePanel extends ContentTypePanel implements DocumentListener */ @Override public void insertUpdate(DocumentEvent e) { - pcs.firePropertyChange(AddImageWizardChooseDataSourceVisual.EVENT.UPDATE_UI.toString(), false, true); + pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); } @Override public void removeUpdate(DocumentEvent e) { - pcs.firePropertyChange(AddImageWizardChooseDataSourceVisual.EVENT.UPDATE_UI.toString(), false, true); + pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); } @Override public void changedUpdate(DocumentEvent e) { - pcs.firePropertyChange(AddImageWizardChooseDataSourceVisual.EVENT.UPDATE_UI.toString(), false, true); + pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); } /** * Set the focus to the pathTextField. */ - @Override public void select() { pathTextField.requestFocusInWindow(); } - /** - * @return the string form of this panel - */ - @Override - public String toString() { - return "Image File"; - } @Override public synchronized void addPropertyChangeListener(PropertyChangeListener pcl) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java new file mode 100644 index 0000000000..cbaa520249 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java @@ -0,0 +1,184 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sleuthkit.autopsy.casemodule; + +import javax.swing.JPanel; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPProgressMonitor; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.Logger; + + +@ServiceProvider(service = DataSourceProcessor.class) +public class LocalDiskDSProcessor implements DataSourceProcessor { + + static final Logger logger = Logger.getLogger(ImageDSProcessor.class.getName()); + + // Data source type handled by this processor + static protected final String dsType = "Local Disk"; + + // The Config UI panel that plugins into the Choose Data Source Wizard + private LocalDiskPanel localDiskPanel; + + // The Background task that does the actual work of adding the local Disk + // Adding a local disk is exactly same as adding an Image. + private AddImageTask addDiskTask; + + // true if cancelled by the caller + private boolean cancelled = false; + + DSPCallback callbackObj = null; + + // set to TRUE if the image options have been set via API and config Jpanel should be ignored + private boolean localDiskOptionsSet = false; + + // data source options + private String localDiskPath; + private String timeZone; + private boolean noFatOrphans; + + + + /* + * A no argument constructor is required for the NM lookup() method to create an object + */ + public LocalDiskDSProcessor() { + + // Create the config panel + localDiskPanel = LocalDiskPanel.getDefault(); + + } + + /** + * Returns the Data source type (string) handled by this DSP + * + * @return String the data source type + **/ + @Override + public String getType() { + return dsType; + } + + /** + * Returns the JPanel for collecting the Data source information + * + * @return JPanel the config panel + **/ + @Override + public JPanel getPanel() { + + localDiskPanel.select(); + return localDiskPanel; + } + /** + * Validates the data collected by the JPanel + * + * @return String returns NULL if success, error string if there is any errors + **/ + @Override + public boolean validatePanel() { + return localDiskPanel.validatePanel(); + } + + + + /** + * Runs the data source processor. + * This must kick off processing the data source in background + * + * @param progressMonitor Progress monitor to report progress during processing + * @param cbObj callback to call when processing is done. + **/ + @Override + public void run(DSPProgressMonitor progressMonitor, DSPCallback cbObj) { + + callbackObj = cbObj; + cancelled = false; + + if (!localDiskOptionsSet) { + // get the image options from the panel + localDiskPath = localDiskPanel.getContentPaths(); + timeZone = localDiskPanel.getTimeZone(); + noFatOrphans = localDiskPanel.getNoFatOrphans(); + } + + addDiskTask = new AddImageTask(localDiskPath, timeZone, noFatOrphans, progressMonitor, cbObj); + new Thread(addDiskTask).start(); + + return; + } + + + + + + + + /** + * Cancel the data source processing + **/ + @Override + public void cancel() { + + cancelled = true; + + addDiskTask.cancelTask(); + + return; + } + + /** + * Reset the data source processor + **/ + @Override + public void reset() { + + // reset the config panel + localDiskPanel.reset(); + + // reset state + localDiskOptionsSet = false; + localDiskPath = null; + timeZone = null; + noFatOrphans = false; + + return; + } + + /** + * Sets the data source options externally. + * To be used by a client that does not have a UI and does not use the JPanel to + * collect this information from a user. + * + * @param diskPath path to the local disk + * @param String timeZone + * @param noFat whether to parse FAT orphans + **/ + public void setDataSourceOptions(String diskPath, String tz, boolean noFat) { + + this.localDiskPath = diskPath; + this.timeZone = tz; + this.noFatOrphans = noFat; + + localDiskOptionsSet = true; + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.form index a0b5f3f431..58e493e793 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.form @@ -29,8 +29,18 @@ + + + + + + + + + + - + @@ -40,8 +50,18 @@ - + + + + + + + + + + + @@ -71,5 +91,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java index 6a51382940..ba746b7595 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java @@ -25,7 +25,10 @@ import java.awt.Font; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; +import java.util.Calendar; import java.util.List; +import java.util.SimpleTimeZone; +import java.util.TimeZone; import java.util.concurrent.CancellationException; import java.util.logging.Level; import javax.swing.ComboBoxModel; @@ -37,13 +40,16 @@ import javax.swing.ListCellRenderer; import javax.swing.SwingWorker; import javax.swing.border.EmptyBorder; import javax.swing.event.ListDataListener; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; /** * ImageTypePanel for adding a local disk or partition such as PhysicalDrive0 or C:. */ -public class LocalDiskPanel extends ContentTypePanel { +public class LocalDiskPanel extends JPanel { + private static final Logger logger = Logger.getLogger(LocalDiskPanel.class.getName()); + private static LocalDiskPanel instance; private PropertyChangeSupport pcs = null; private List disks = new ArrayList(); @@ -56,6 +62,9 @@ public class LocalDiskPanel extends ContentTypePanel { public LocalDiskPanel() { initComponents(); customInit(); + + createTimeZoneList(); + } /** @@ -73,8 +82,10 @@ public class LocalDiskPanel extends ContentTypePanel { model = new LocalDiskModel(); diskComboBox.setModel(model); diskComboBox.setRenderer(model); + errorLabel.setText(""); diskComboBox.setEnabled(false); + } /** @@ -89,6 +100,10 @@ public class LocalDiskPanel extends ContentTypePanel { diskLabel = new javax.swing.JLabel(); diskComboBox = new javax.swing.JComboBox(); errorLabel = new javax.swing.JLabel(); + timeZoneLabel = new javax.swing.JLabel(); + timeZoneComboBox = new javax.swing.JComboBox(); + noFatOrphansCheckbox = new javax.swing.JCheckBox(); + descLabel = new javax.swing.JLabel(); setMinimumSize(new java.awt.Dimension(0, 65)); setPreferredSize(new java.awt.Dimension(485, 65)); @@ -98,6 +113,15 @@ public class LocalDiskPanel extends ContentTypePanel { errorLabel.setForeground(new java.awt.Color(255, 0, 0)); org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.errorLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.timeZoneLabel.text")); // NOI18N + + timeZoneComboBox.setMaximumRowCount(30); + + org.openide.awt.Mnemonics.setLocalizedText(noFatOrphansCheckbox, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.noFatOrphansCheckbox.text")); // NOI18N + noFatOrphansCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.noFatOrphansCheckbox.toolTipText")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(descLabel, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.descLabel.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -106,8 +130,16 @@ public class LocalDiskPanel extends ContentTypePanel { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(diskLabel) .addComponent(diskComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 345, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(errorLabel)) - .addGap(0, 140, Short.MAX_VALUE)) + .addComponent(errorLabel) + .addGroup(layout.createSequentialGroup() + .addComponent(timeZoneLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(noFatOrphansCheckbox) + .addGroup(layout.createSequentialGroup() + .addGap(21, 21, 21) + .addComponent(descLabel))) + .addGap(0, 102, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -115,21 +147,34 @@ public class LocalDiskPanel extends ContentTypePanel { .addComponent(diskLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(diskComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(13, 13, 13) + .addComponent(errorLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(timeZoneLabel) + .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(noFatOrphansCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(errorLabel)) + .addComponent(descLabel) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel descLabel; private javax.swing.JComboBox diskComboBox; private javax.swing.JLabel diskLabel; private javax.swing.JLabel errorLabel; + private javax.swing.JCheckBox noFatOrphansCheckbox; + private javax.swing.JComboBox timeZoneComboBox; + private javax.swing.JLabel timeZoneLabel; // End of variables declaration//GEN-END:variables /** * Return the currently selected disk path. * @return String selected disk path */ - @Override + //@Override public String getContentPaths() { if(disks.size() > 0) { LocalDisk selected = (LocalDisk) diskComboBox.getSelectedItem(); @@ -143,7 +188,7 @@ public class LocalDiskPanel extends ContentTypePanel { /** * Set the selected disk. */ - @Override + // @Override public void setContentPath(String s) { for(int i=0; i physical = new ArrayList(); List partitions = new ArrayList(); //private String SELECT = "Select a local disk:"; private String LOADING = "Loading local disks..."; + LocalDiskThread worker = null; + private void loadDisks() { + + // if there is a worker already building the lists, then cancel it first. + if (loadingDisks && worker != null) { + worker.cancel(false); + } + // Clear the lists errorLabel.setText(""); disks = new ArrayList(); @@ -224,9 +315,13 @@ public class LocalDiskPanel extends ContentTypePanel { partitions = new ArrayList(); diskComboBox.setEnabled(false); ready = false; - - LocalDiskThread worker = new LocalDiskThread(); + enableNext = false; + loadingDisks = true; + + worker = new LocalDiskThread(); worker.execute(); + + } @Override @@ -234,7 +329,7 @@ public class LocalDiskPanel extends ContentTypePanel { if(ready) { selected = anItem; enableNext = true; - pcs.firePropertyChange(AddImageWizardChooseDataSourceVisual.EVENT.UPDATE_UI.toString(), false, true); + pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); } } @@ -260,7 +355,7 @@ public class LocalDiskPanel extends ContentTypePanel { @Override public void removeListDataListener(ListDataListener l) { } - + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JPanel panel = new JPanel(new BorderLayout()); @@ -300,8 +395,6 @@ public class LocalDiskPanel extends ContentTypePanel { // Populate the lists physical = PlatformUtil.getPhysicalDrives(); partitions = PlatformUtil.getPartitions(); - disks.addAll(physical); - disks.addAll(partitions); return null; } @@ -337,6 +430,11 @@ public class LocalDiskPanel extends ContentTypePanel { enableNext = false; displayErrors(); ready = true; + worker = null; + loadingDisks = false; + + disks.addAll(physical); + disks.addAll(partitions); if(disks.size() > 0) { diskComboBox.setEnabled(true); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java new file mode 100644 index 0000000000..3c6089d4a3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java @@ -0,0 +1,167 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sleuthkit.autopsy.casemodule; + +import javax.swing.JPanel; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DSPProgressMonitor; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.Logger; + +@ServiceProvider(service = DataSourceProcessor.class) +public class LocalFilesDSProcessor implements DataSourceProcessor { + + static final Logger logger = Logger.getLogger(LocalFilesDSProcessor.class.getName()); + + // Data source type handled by this processor + protected static final String dsType = "Logical Files"; + + // The Config UI panel that plugins into the Choose Data Source Wizard + private LocalFilesPanel localFilesPanel; + + // The Background task that does the actual work of adding the files + private AddLocalFilesTask addFilesTask; + + // true if cancelled by the caller + private boolean cancelled = false; + + DSPCallback callbackObj = null; + + // set to TRUE if the image options have been set via API and config Jpanel should be ignored + private boolean localFilesOptionsSet = false; + + // data source options + private String localFilesPath; + + + + /* + * A no argument constructor is required for the NM lookup() method to create an object + */ + public LocalFilesDSProcessor() { + + // Create the config panel + localFilesPanel = LocalFilesPanel.getDefault(); + } + + /** + * Returns the Data source type (string) handled by this DSP + * + * @return String the data source type + **/ + @Override + public String getType() { + return dsType; + } + + /** + * Returns the JPanel for collecting the Data source information + * + * @return JPanel the config panel + **/ + @Override + public JPanel getPanel() { + localFilesPanel.select(); + return localFilesPanel; + } + /** + * Validates the data collected by the JPanel + * + * @return String returns NULL if success, error string if there is any errors + **/ + @Override + public boolean validatePanel() { + return localFilesPanel.validatePanel(); + } + + + + /** + * Runs the data source processor. + * This must kick off processing the data source in background + * + * @param progressMonitor Progress monitor to report progress during processing + * @param cbObj callback to call when processing is done. + **/ + @Override + public void run(DSPProgressMonitor progressMonitor, DSPCallback cbObj) { + + callbackObj = cbObj; + cancelled = false; + + if (!localFilesOptionsSet) { + // get the selected file paths from the panel + localFilesPath = localFilesPanel.getContentPaths(); + } + + addFilesTask = new AddLocalFilesTask(localFilesPath, progressMonitor, cbObj); + new Thread(addFilesTask).start(); + + return; + } + + /** + * Cancel the data source processing + **/ + @Override + public void cancel() { + + cancelled = true; + addFilesTask.cancelTask(); + + return; + } + + /** + * Reset the data source processor + **/ + @Override + public void reset() { + + // reset the config panel + localFilesPanel.reset(); + + // reset state + localFilesOptionsSet = false; + localFilesPath = null; + + + return; + } + + /** + * Sets the data source options externally. + * To be used by a client that does not have a UI and does not use the JPanel to + * collect this information from a user. + * + * @param filesPath PATH_SEP list of paths to local files + * + **/ + public void setDataSourceOptions(String filesPath) { + + this.localFilesPath = filesPath; + + localFilesOptionsSet = true; + + } + + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java index 7d07033ac1..c9cff7b1d0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java @@ -24,11 +24,13 @@ import java.io.File; import java.util.Set; import java.util.TreeSet; import javax.swing.JFileChooser; +import javax.swing.JPanel; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; /** * Add input wizard subpanel for adding local files / dirs to the case */ -public class LocalFilesPanel extends ContentTypePanel { +public class LocalFilesPanel extends JPanel { private PropertyChangeSupport pcs = null; private Set currentFiles = new TreeSet(); //keep currents in a set to disallow duplicates per add @@ -57,7 +59,7 @@ public class LocalFilesPanel extends ContentTypePanel { } - @Override + //@Override public String getContentPaths() { //TODO consider interface change to return list of paths instead @@ -72,36 +74,37 @@ public class LocalFilesPanel extends ContentTypePanel { return b.toString(); } - @Override + //@Override public void setContentPath(String s) { //for the local file panel we don't need to restore the last paths used //when the wizard restarts } - @Override - public ContentType getContentType() { - return ContentType.LOCAL; + //@Override + public String getContentType() { + return "LOCAL"; } - @Override - public boolean enableNext() { + //@Override + public boolean validatePanel() { return enableNext; } - @Override + //@Override public void select() { reset(); } - @Override + //@Override public void reset() { currentFiles.clear(); selectedPaths.setText(""); enableNext = false; - pcs.firePropertyChange(AddImageWizardChooseDataSourceVisual.EVENT.UPDATE_UI.toString(), false, true); + + //pcs.firePropertyChange(AddImageWizardChooseDataSourceVisual.EVENT.UPDATE_UI.toString(), false, true); } - @Override + @Override public synchronized void addPropertyChangeListener(PropertyChangeListener pcl) { super.addPropertyChangeListener(pcl); @@ -231,7 +234,7 @@ public class LocalFilesPanel extends ContentTypePanel { else { enableNext = false; } - pcs.firePropertyChange(AddImageWizardChooseDataSourceVisual.EVENT.UPDATE_UI.toString(), false, true); + pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); }//GEN-LAST:event_selectButtonActionPerformed private void clearButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearButtonActionPerformed diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.form b/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.form index 24dd272951..d730da08dc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.form @@ -110,65 +110,49 @@ - - - - - - - - - - + + + + + - + - - + + - - - + - - - - - - - - + - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.java index 22d9f11a2f..d54c52fc4b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.java @@ -18,49 +18,71 @@ */ package org.sleuthkit.autopsy.casemodule; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; + import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; import java.util.logging.Level; -import javax.swing.ComboBoxModel; -import javax.swing.JDialog; +import java.io.File; +import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.event.ListDataListener; -import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.casemodule.GeneralFilter; + import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; + + public class MissingImageDialog extends javax.swing.JDialog { private static final Logger logger = Logger.getLogger(MissingImageDialog.class.getName()); long obj_id; SleuthkitCase db; - ContentTypePanel currentPanel; - ImageTypeModel model; + + + + static final GeneralFilter rawFilter = new GeneralFilter(GeneralFilter.RAW_IMAGE_EXTS, GeneralFilter.RAW_IMAGE_DESC); + static final GeneralFilter encaseFilter = new GeneralFilter(GeneralFilter.ENCASE_IMAGE_EXTS, GeneralFilter.ENCASE_IMAGE_DESC); + + static final List allExt = new ArrayList(); + static { + allExt.addAll(GeneralFilter.RAW_IMAGE_EXTS); + allExt.addAll(GeneralFilter.ENCASE_IMAGE_EXTS); + } + static final String allDesc = "All Supported Types"; + static final GeneralFilter allFilter = new GeneralFilter(allExt, allDesc); + + private JFileChooser fc = new JFileChooser(); private MissingImageDialog(long obj_id, SleuthkitCase db) { super(new JFrame(), true); this.obj_id = obj_id; this.db = db; initComponents(); + + fc.setDragEnabled(false); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setMultiSelectionEnabled(false); + + fc.addChoosableFileFilter(rawFilter); + fc.addChoosableFileFilter(encaseFilter); + fc.setFileFilter(allFilter); + + customInit(); } - /** - * Client call to create a MissingImageDialog. - * - * @param obj_id obj_id of the missing image - * @param db the current SleuthkitCase connected to a db - */ +// +// * Client call to create a MissingImageDialog. +// * +// * @param obj_id obj_id of the missing image +// * @param db the current SleuthkitCase connected to a db +// static void makeDialog(long obj_id, SleuthkitCase db) { final MissingImageDialog dialog = new MissingImageDialog(obj_id, db); dialog.addWindowListener(new WindowAdapter() { @@ -73,11 +95,8 @@ public class MissingImageDialog extends javax.swing.JDialog { } private void customInit() { - model = new ImageTypeModel(); - typeComboBox.setModel(model); - typeComboBox.setSelectedIndex(0); - typePanel.setLayout(new BorderLayout()); - updateCurrentPanel(ImageFilePanel.getDefault()); + + selectButton.setEnabled(false); } private void display() { @@ -92,54 +111,31 @@ public class MissingImageDialog extends javax.swing.JDialog { this.setVisible(true); } - /** - * Refresh this panel. - * @param panel current typepanel - */ - private void updateCurrentPanel(ContentTypePanel panel) { - currentPanel = panel; - typePanel.removeAll(); - typePanel.add((JPanel) currentPanel, BorderLayout.CENTER); - typePanel.validate(); - typePanel.repaint(); - this.validate(); - this.repaint(); - currentPanel.addPropertyChangeListener(new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if(evt.getPropertyName().equals(AddImageWizardChooseDataSourceVisual.EVENT.UPDATE_UI.toString())) { - updateSelectButton(); - } - if(evt.getPropertyName().equals(AddImageWizardChooseDataSourceVisual.EVENT.FOCUS_NEXT.toString())) { - moveFocusToSelect(); - } - } - - }); - currentPanel.select(); - updateSelectButton(); - } - - /** - * Focuses the select button for easy enter-pressing access. - */ +// +// * Focuses the select button for easy enter-pressing access. +// private void moveFocusToSelect() { this.selectButton.requestFocusInWindow(); } - /** - * Enables/disables the select button based off the current panel. - */ +// +// * Enables/disables the select button based off the current panel. +// private void updateSelectButton() { - this.selectButton.setEnabled(currentPanel.enableNext()); + + // Enable this based on whether there is a valid path + if (!pathNameTextField.getText().isEmpty()) { + String filePath = pathNameTextField.getText(); + boolean isExist = Case.pathExists(filePath) || Case.driveExists(filePath); + selectButton.setEnabled(isExist); + } } - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ +// +// * This method is called from within the constructor to initialize the form. +// * WARNING: Do NOT modify this code. The content of this method is always +// * regenerated by the Form Editor. +// @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { @@ -148,9 +144,8 @@ public class MissingImageDialog extends javax.swing.JDialog { selectButton = new javax.swing.JButton(); cancelButton = new javax.swing.JButton(); containerPanel = new javax.swing.JPanel(); - typeComboBox = new javax.swing.JComboBox(); - typeTabel = new javax.swing.JLabel(); - typePanel = new javax.swing.JPanel(); + pathNameTextField = new javax.swing.JTextField(); + browseButton = new javax.swing.JButton(); titleLabel = new javax.swing.JLabel(); titleSeparator = new javax.swing.JSeparator(); @@ -191,43 +186,39 @@ public class MissingImageDialog extends javax.swing.JDialog { .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); - org.openide.awt.Mnemonics.setLocalizedText(typeTabel, org.openide.util.NbBundle.getMessage(MissingImageDialog.class, "MissingImageDialog.typeTabel.text")); // NOI18N + pathNameTextField.setText(org.openide.util.NbBundle.getMessage(MissingImageDialog.class, "MissingImageDialog.pathNameTextField.text")); // NOI18N + pathNameTextField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pathNameTextFieldActionPerformed(evt); + } + }); - javax.swing.GroupLayout typePanelLayout = new javax.swing.GroupLayout(typePanel); - typePanel.setLayout(typePanelLayout); - typePanelLayout.setHorizontalGroup( - typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - typePanelLayout.setVerticalGroup( - typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 57, Short.MAX_VALUE) - ); + org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(MissingImageDialog.class, "MissingImageDialog.browseButton.text")); // NOI18N + browseButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + browseButtonActionPerformed(evt); + } + }); javax.swing.GroupLayout containerPanelLayout = new javax.swing.GroupLayout(containerPanel); containerPanel.setLayout(containerPanelLayout); containerPanelLayout.setHorizontalGroup( containerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(containerPanelLayout.createSequentialGroup() - .addGap(10, 10, 10) - .addGroup(containerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(typePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(containerPanelLayout.createSequentialGroup() - .addComponent(typeTabel) - .addGap(18, 18, 18) - .addComponent(typeComboBox, 0, 298, Short.MAX_VALUE))) - .addContainerGap()) + .addContainerGap() + .addComponent(pathNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 285, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(browseButton) + .addContainerGap(83, Short.MAX_VALUE)) ); containerPanelLayout.setVerticalGroup( containerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(containerPanelLayout.createSequentialGroup() - .addGap(0, 0, 0) + .addGap(18, 18, 18) .addGroup(containerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(typeTabel) - .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, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addContainerGap()) + .addComponent(pathNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(browseButton)) + .addContainerGap(62, Short.MAX_VALUE)) ); titleLabel.setFont(new java.awt.Font("Tahoma", 1, 12)); // NOI18N @@ -268,7 +259,7 @@ public class MissingImageDialog extends javax.swing.JDialog { private void selectButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectButtonActionPerformed try { - String newPath = currentPanel.getContentPaths(); + String newPath = pathNameTextField.getText(); //TODO handle local files db.setImagePaths(obj_id, Arrays.asList(new String[]{newPath})); } catch (TskCoreException ex) { @@ -281,21 +272,48 @@ public class MissingImageDialog extends javax.swing.JDialog { cancel(); }//GEN-LAST:event_cancelButtonActionPerformed + private void pathNameTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pathNameTextFieldActionPerformed + // TODO add your handling code here: + + updateSelectButton(); + }//GEN-LAST:event_pathNameTextFieldActionPerformed + + private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed + + + + String oldText = pathNameTextField.getText(); + + // set the current directory of the FileChooser if the ImagePath Field is valid + File currentDir = new File(oldText); + if (currentDir.exists()) { + fc.setCurrentDirectory(currentDir); + } + + int retval = fc.showOpenDialog(this); + if (retval == JFileChooser.APPROVE_OPTION) { + String path = fc.getSelectedFile().getPath(); + pathNameTextField.setText(path); + } + //pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString(), false, true); + + updateSelectButton(); + }//GEN-LAST:event_browseButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton browseButton; private javax.swing.JPanel buttonPanel; private javax.swing.JButton cancelButton; private javax.swing.JPanel containerPanel; + private javax.swing.JTextField pathNameTextField; private javax.swing.JButton selectButton; private javax.swing.JLabel titleLabel; private javax.swing.JSeparator titleSeparator; - private javax.swing.JComboBox typeComboBox; - private javax.swing.JPanel typePanel; - private javax.swing.JLabel typeTabel; // End of variables declaration//GEN-END:variables - /** - * Verify the user wants to cancel searching for the image. - */ +// +// * Verify the user wants to cancel searching for the image. +// void cancel() { int ret = JOptionPane.showConfirmDialog(null, "No image file has been selected, are you sure you\n" + @@ -306,40 +324,5 @@ public class MissingImageDialog extends javax.swing.JDialog { } } - /** - * ComboBoxModel to control typeComboBox and supply ImageTypePanels. - */ - private class ImageTypeModel implements ComboBoxModel { - ContentTypePanel selected; - ContentTypePanel[] types = ContentTypePanel.getPanels(); - @Override - public void setSelectedItem(Object anItem) { - selected = (ContentTypePanel) anItem; - updateCurrentPanel(selected); - } - - @Override - public Object getSelectedItem() { - return selected; - } - - @Override - public int getSize() { - return types.length; - } - - @Override - public Object getElementAt(int index) { - return types[index]; - } - - @Override - public void addListDataListener(ListDataListener l) { - } - - @Override - public void removeListDataListener(ListDataListener l) { - } - } -} +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DSPCallback.java b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DSPCallback.java new file mode 100644 index 0000000000..f3bdf028da --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DSPCallback.java @@ -0,0 +1,66 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sleuthkit.autopsy.corecomponentinterfaces; + +import java.awt.EventQueue; +import java.util.List; +import org.sleuthkit.datamodel.Content; + +/** + * Abstract class for a callback for a DataSourceProcessor. + * + * Ensures that DSP invokes the caller overridden method, doneEDT(), + * in the EDT thread. + * + */ +public abstract class DSPCallback { + + public enum DSP_Result + { + NO_ERRORS, + CRITICAL_ERRORS, + NONCRITICAL_ERRORS, + }; + + /* + * Invoke the caller supplied callback function on the EDT thread + */ + public void done(DSP_Result result, List errList, List newContents) + { + + final DSP_Result resultf = result; + final List errListf = errList; + final List newContentsf = newContents; + + // Invoke doneEDT() that runs on the EDT . + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + doneEDT(resultf, errListf, newContentsf ); + + } + }); + } + + /* + * calling code overrides to provide its own calllback + */ + public abstract void doneEDT(DSP_Result result, List errList, List newContents); +}; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DSPProgressMonitor.java b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DSPProgressMonitor.java new file mode 100644 index 0000000000..88d6b5e04c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DSPProgressMonitor.java @@ -0,0 +1,33 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.corecomponentinterfaces; + +/* + * An GUI agnostic DSPProgressMonitor interface for DataSourceProcesssors to + * indicate progress. + * It models after a JProgressbar though it could use any underlying implementation + */ +public interface DSPProgressMonitor { + + void setIndeterminate(boolean indeterminate); + + void setProgress(int progress); + + void setProgressText(String text); +} diff --git a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessor.java b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessor.java new file mode 100644 index 0000000000..af67e78b68 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataSourceProcessor.java @@ -0,0 +1,92 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.corecomponentinterfaces; + + +import javax.swing.JPanel; + +/* + * Defines an interface used by the Add DataSource wizard to discover different + * Data SourceProcessors. + * + * Each data source may have its unique attributes and may need to be processed + * differently. + * + * The DataSourceProcessor interface defines a uniform mechanism for the Autopsy UI + * to: + * - collect details for the data source to be processed. + * - Process the data source in the background + * - Be notified when the processing is complete + */ +public interface DataSourceProcessor { + + /* + * The DSP Panel may fire Property change events + * The caller must enure to add itself as a listener and + * then react appropriately to the events + */ + enum DSP_PANEL_EVENT { + + UPDATE_UI, // the content of JPanel has changed that MAY warrant updates to the caller UI + FOCUS_NEXT // the caller UI may move focus the the next UI element, floowing the panel. + }; + + + /** + * Returns the type of Data Source it handles. + * This name gets displayed in the drop-down listbox + **/ + String getType(); + + /** + * Returns the picker panel to be displayed along with any other + * runtime options supported by the data source handler. + **/ + JPanel getPanel(); + + /** + * Called to validate the input data in the panel. + * Returns true if no errors, or + * Returns false if there is an error. + **/ + boolean validatePanel(); + + /** + * Called to invoke the handling of Data source in the background. + * Returns after starting the background thread + * @param settings wizard settings to read/store properties + * @param progressPanel progress panel to be updated while processing + * + **/ + void run(DSPProgressMonitor progressPanel, DSPCallback dspCallback); + + + /** + * Called to cancel the background processing. + **/ + void cancel(); + + /** + * Called to reset/reinitialize the DSP. + * + **/ + void reset(); + + +}