/* * Autopsy Forensic Browser * * Copyright 2011-2014 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> 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.awt.Component; import java.awt.Dialog; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.MessageFormat; import java.util.logging.Level; import javax.swing.Action; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openide.DialogDisplayer; import org.openide.WizardDescriptor; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; import org.openide.util.ChangeSupport; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.Presenter; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.Image; /** * The action to add an image to the current Case. This action should be * disabled on creation and it will be enabled on new case creation or case * opened. * * @author jantonius */ // TODO: need annotation because there's a "Lookup.getDefault().lookup(AddImageAction.class)" // used in AddImageWizardPanel1 (among other places). It really shouldn't be done like that. @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.casemodule.AddImageAction") @ActionRegistration(displayName = "#CTL_AddImage", lazy = false) @ActionReferences(value = { @ActionReference(path = "Toolbars/Case", position = 100)}) @ServiceProvider(service = AddImageAction.class) public final class AddImageAction extends CallableSystemAction implements Presenter.Toolbar { // Keys into the WizardDescriptor properties that pass information between stages of the wizard // <TYPE>: <DESCRIPTION> // String: time zone that the image is from static final String TIMEZONE_PROP = "timeZone"; //NON-NLS // String[]: array of paths to each data source selected static final String DATASOURCEPATH_PROP = "dataSrcPath"; //NON-NLS // String data source type selected static final String DATASOURCETYPE_PROP = "dataSrcType"; //NON-NLS // CleanupTask: task to clean up the database file if wizard errors/is cancelled after it is created static final String IMAGECLEANUPTASK_PROP = "finalFileCleanup"; //NON-NLS // int: the next availble id for a new image static final String IMAGEID_PROP = "imageId"; //NON-NLS // AddImageProcess: the next availble id for a new image static final String PROCESS_PROP = "process"; //NON-NLS // boolean: whether or not to lookup files in the hashDB static final String LOOKUPFILES_PROP = "lookupFiles"; //NON-NLS // boolean: whether or not to skip processing orphan files on FAT filesystems static final String NOFATORPHANS_PROP = "nofatorphans"; //NON-NLS static final Logger logger = Logger.getLogger(AddImageAction.class.getName()); static final Dimension SIZE = new Dimension(875, 550); private WizardDescriptor wizardDescriptor; private WizardDescriptor.Iterator<WizardDescriptor> iterator; private Dialog dialog; private JButton toolbarButton = new JButton(); /** * The constructor for AddImageAction class */ public AddImageAction() { putValue(Action.NAME, NbBundle.getMessage(AddImageAction.class, "CTL_AddImage")); // set the action Name // set the action for the toolbar button toolbarButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { AddImageAction.this.actionPerformed(e); } }); this.setEnabled(false); // disable this action class } /** * Pop-up the "Add Image" wizard panel. * * @param e */ @Override public void actionPerformed(ActionEvent e) { if (IngestManager.getInstance().isIngestRunning()) { final String msg = NbBundle.getMessage(this.getClass(), "AddImageAction.ingestConfig.ongoingIngest.msg"); if (JOptionPane.showConfirmDialog(null, msg, NbBundle.getMessage(this.getClass(), "AddImageAction.ingestConfig.ongoingIngest.title"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.NO_OPTION) { return; } } iterator = new AddImageWizardIterator(this); wizardDescriptor = new WizardDescriptor(iterator); wizardDescriptor.setTitle(NbBundle.getMessage(this.getClass(), "AddImageAction.wizard.title")); wizardDescriptor.putProperty(NAME, e); wizardDescriptor.setTitleFormat(new MessageFormat("{0}")); if (dialog != null) { dialog.setVisible(false); // hide the old one } dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor); Dimension d = dialog.getSize(); dialog.setSize(SIZE); dialog.setVisible(true); dialog.toFront(); // Do any cleanup that needs to happen (potentially: stopping the //add-image process, reverting an image) runCleanupTasks(); } /** * Closes the current dialog and wizard, and opens a new one. Used in the * "Add another image" action on the last panel */ void restart() { // Simulate clicking finish for the current dialog wizardDescriptor.setValue(WizardDescriptor.FINISH_OPTION); dialog.setVisible(false); // let the previous call to AddImageAction.actionPerformed() finish up // after the wizard, this will run when its it's done final Runnable r = new Runnable() { @Override public void run() { actionPerformed(null); } }; SwingUtilities.invokeLater(r); } public interface IndexImageTask { void runTask(Image newImage); } /** * This method does nothing. Use the "actionPerformed(ActionEvent e)" * instead of this method. */ @Override public void performAction() { } /** * Gets the name of this action. This may be presented as an item in a menu. * * @return actionName */ @Override public String getName() { return NbBundle.getMessage(AddImageAction.class, "CTL_AddImageButton"); } /** * Gets the HelpCtx associated with implementing object * * @return HelpCtx or HelpCtx.DEFAULT_HELP */ @Override public HelpCtx getHelpCtx() { return HelpCtx.DEFAULT_HELP; } /** * Returns the toolbar component of this action * * @return component the toolbar button */ @Override public Component getToolbarPresenter() { ImageIcon icon = new ImageIcon(getClass().getResource("btn_icon_add_image.png")); //NON-NLS toolbarButton.setIcon(icon); toolbarButton.setText(this.getName()); return toolbarButton; } /** * Set this action to be enabled/disabled * * @param value whether to enable this action or not */ @Override public void setEnabled(boolean value) { super.setEnabled(value); toolbarButton.setEnabled(value); } /** * Set the focus to the button of the given name on this wizard dialog. * * Note: the name of the buttons that available are "Next >", "< Back", * "Cancel", and "Finish". If you change the name of any of those buttons, * use the latest name instead. * * @param buttonText the text of the button */ public void requestFocusButton(String buttonText) { // get all buttons on this wizard panel Object[] wizardButtons = wizardDescriptor.getOptions(); for (int i = 0; i < wizardButtons.length; i++) { JButton tempButton = (JButton) wizardButtons[i]; if (tempButton.getText().equals(buttonText)) { tempButton.setDefaultCapable(true); tempButton.requestFocus(); } } } /** * Run and clear any cleanup tasks for wizard closing that might be * registered. This should be run even when the wizard exits cleanly, so * that no cleanup actions remain the next time the wizard is run. */ private void runCleanupTasks() { cleanupSupport.fireChange(); } ChangeSupport cleanupSupport = new ChangeSupport(this); /** * Instances of this class implement the cleanup() method to run cleanup * code when the wizard exits. * * After enable() has been called on an instance it will run once after the * wizard closes (on both a cancel and a normal finish). * * If disable() is called before the wizard exits, the task will not run. */ abstract class CleanupTask implements ChangeListener { @Override public void stateChanged(ChangeEvent e) { // fired by AddImageAction.runCleanupTasks() after the wizard closes try { cleanup(); } catch (Exception ex) { Logger logger = Logger.getLogger(this.getClass().getName()); logger.log(Level.WARNING, "Error cleaning up from wizard.", ex); //NON-NLS } finally { disable(); // cleanup tasks should only run once. } } /** * Add task to the enabled list to run when the wizard closes. */ public void enable() { cleanupSupport.addChangeListener(this); } /** * Performs cleanup action when called * * @throws Exception */ abstract void cleanup() throws Exception; /** * Remove task from the enabled list. */ public void disable() { cleanupSupport.removeChangeListener(this); } } }