diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java index 2db469e371..1e091db81c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java @@ -72,10 +72,12 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour filtersList.add(allFilter); filtersList.add(rawFilter); filtersList.add(encaseFilter); - filtersList.add(virtualMachineFilter); allExt.addAll(GeneralFilter.RAW_IMAGE_EXTS); allExt.addAll(GeneralFilter.ENCASE_IMAGE_EXTS); - allExt.addAll(GeneralFilter.VIRTUAL_MACHINE_EXTS); + if(!System.getProperty("os.name").toLowerCase().contains("mac")){ + filtersList.add(virtualMachineFilter); + allExt.addAll(GeneralFilter.VIRTUAL_MACHINE_EXTS); + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/ingest/GenericIngestModuleJobSettings.java b/Core/src/org/sleuthkit/autopsy/ingest/GenericIngestModuleJobSettings.java new file mode 100644 index 0000000000..1730f81e46 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/ingest/GenericIngestModuleJobSettings.java @@ -0,0 +1,70 @@ +/* + * Autopsy + * + * Copyright 2018 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.ingest; +import java.util.HashMap; +import java.util.Map; + + +/** + * Generic Ingest Job settings class. + * Primary use of this class is for Python modules because classes created in Python + * cannot be serialized / deserialized in Java. + */ +public class GenericIngestModuleJobSettings implements IngestModuleIngestJobSettings { + private static final long serialVersionUID = 1L; + + @Override + public long getVersionNumber(){ + return serialVersionUID; + } + + private final Map settings; + + public GenericIngestModuleJobSettings(){ + this.settings = new HashMap<>(); + } + + /** + * Return the string value for passed key parameter. + * + * @param key The key to lookup + * @return The value or null if the key was not found. + */ + public String getSetting(String key){ + return settings.get(key); + } + + /** + * Adds the passed key value pair + * + * @param key The key to be added to the settings + * @param value The value to be added for the key + */ + public void setSetting(String key, String value){ + settings.put(key, value); + } + + /** + * Removes the key from the settings. + * @param key The key to be removed + */ + public void removeSetting(String key){ + settings.remove(key); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java index d512c6670c..3913ce097a 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java @@ -36,7 +36,6 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.io.NbObjectInputStream; import org.openide.util.io.NbObjectOutputStream; -import org.python.util.PythonObjectInputStream; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.PlatformUtil; @@ -492,25 +491,12 @@ public final class IngestJobSettings { String moduleSettingsFilePath = getModuleSettingsFilePath(factory); File settingsFile = new File(moduleSettingsFilePath); if (settingsFile.exists()) { - if (!isPythonModuleSettingsFile(moduleSettingsFilePath)) { - try (NbObjectInputStream in = new NbObjectInputStream(new FileInputStream(settingsFile.getAbsolutePath()))) { - settings = (IngestModuleIngestJobSettings) in.readObject(); - } catch (IOException | ClassNotFoundException ex) { - String warning = NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.moduleSettingsLoad.warning", factory.getModuleDisplayName(), this.executionContext); //NON-NLS - logger.log(Level.WARNING, warning, ex); - this.warnings.add(warning); - } - } else { - // @@@ BC Jython serialization is currently broken and this - // throws an exception. (-2323). Commenting out so that - // Python modules will at least load with default settings. -// try (PythonObjectInputStream in = new PythonObjectInputStream(new FileInputStream(settingsFile.getAbsolutePath()))) { -// settings = (IngestModuleIngestJobSettings) in.readObject(); -// } catch (IOException | ClassNotFoundException exception) { -// String warning = NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.moduleSettingsLoad.warning", factory.getModuleDisplayName(), this.executionContext); //NON-NLS -// logger.log(Level.WARNING, warning, exception); -// this.warnings.add(warning); -// } + try (NbObjectInputStream in = new NbObjectInputStream(new FileInputStream(settingsFile.getAbsolutePath()))) { + settings = (IngestModuleIngestJobSettings) in.readObject(); + } catch (IOException | ClassNotFoundException ex) { + String warning = NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.moduleSettingsLoad.warning", factory.getModuleDisplayName(), this.executionContext); //NON-NLS + logger.log(Level.WARNING, warning, ex); + this.warnings.add(warning); } } if (settings == null) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java index fa7961db8b..706105cddc 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java @@ -37,6 +37,7 @@ import org.apache.tika.metadata.Metadata; import org.apache.tika.parser.AutoDetectParser; import org.apache.tika.parser.ParseContext; import org.apache.tika.sax.BodyContentHandler; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; @@ -50,21 +51,20 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; - - /** * File ingest module to detect encryption and password protection. */ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter { private static final int FILE_SIZE_MODULUS = 512; - + private static final String MIME_TYPE_OOXML_PROTECTED = "application/x-ooxml-protected"; private static final String MIME_TYPE_MSWORD = "application/msword"; private static final String MIME_TYPE_MSEXCEL = "application/vnd.ms-excel"; @@ -110,6 +110,10 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter } } + @Messages({ + "EncryptionDetectionFileIngestModule.artifactComment.password=Password protection detected.", + "EncryptionDetectionFileIngestModule.artifactComment.suspected=Suspected encryption due to high entropy (%f)." + }) @Override public IngestModule.ProcessResult process(AbstractFile file) { @@ -132,11 +136,13 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter String mimeType = fileTypeDetector.getMIMEType(file); if (mimeType.equals("application/octet-stream")) { if (isFileEncryptionSuspected(file)) { - return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED); + return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, + String.format(Bundle.EncryptionDetectionFileIngestModule_artifactComment_suspected(), calculatedEntropy)); } } else { if (isFilePasswordProtected(file)) { - return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED); + return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED, + Bundle.EncryptionDetectionFileIngestModule_artifactComment_password()); } } } @@ -168,14 +174,18 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter * * @param file The file to be processed. * @param artifactType The type of artifact to create. + * @param comment A comment to be attached to the artifact. * * @return 'OK' if the file was processed successfully, or 'ERROR' if there * was a problem. */ - private IngestModule.ProcessResult flagFile(AbstractFile file, BlackboardArtifact.ARTIFACT_TYPE artifactType) { + private IngestModule.ProcessResult flagFile(AbstractFile file, BlackboardArtifact.ARTIFACT_TYPE artifactType, String comment) { try { BlackboardArtifact artifact = file.newArtifact(artifactType); + artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, + EncryptionDetectionModuleFactory.getModuleName(), comment)); + try { /* * Index the artifact for keyword search. diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index c1cd61e427..a230743a77 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -927,7 +927,7 @@ public class TimeLineController { * @param o The object to un-register. */ synchronized public void unRegisterForEvents(Object o) { - eventbus.unregister(0); + eventbus.unregister(o); } static synchronized public void setTimeZone(TimeZone timeZone) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java index f6600ac0ad..6ec71d48d5 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java @@ -509,7 +509,7 @@ public final class FilteredEventsModel { * @param o The object to un-register. */ synchronized public void unRegisterForEvents(Object o) { - eventbus.unregister(0); + eventbus.unregister(o); } /** diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java index b68553039d..9a80706699 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java @@ -52,6 +52,14 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer { autoIngestMonitor.addObserver(this); } + AutoIngestMonitor getMonitor() { + return autoIngestMonitor; + } + + AinStatusPanel getNodesStatusPanel() { + return nodesPanel; + } + /** * 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 @@ -143,9 +151,10 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer { @Override public void update(Observable o, Object arg) { - if (arg instanceof AutoIngestNodeState) + if (arg instanceof AutoIngestNodeState) { EventQueue.invokeLater(() -> { refreshTables(); }); + } } } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboardTopComponent.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboardTopComponent.java index 1ab19b4a77..6ea478699e 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboardTopComponent.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboardTopComponent.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.experimental.autoingest; +import java.awt.Component; import java.util.List; import java.util.logging.Level; import java.util.stream.Collectors; @@ -74,8 +75,6 @@ final class AinStatusDashboardTopComponent extends TopComponent { tc.add(nodeTab); tc.open(); } - tc.toFront(); - tc.requestActive(); } } @@ -115,6 +114,19 @@ final class AinStatusDashboardTopComponent extends TopComponent { WindowManager.getDefault().setTopComponentFloating(this, true); } + /** + * Get the current AutoIngestDashboard if there is one. + * + * @return the current AutoIngestDashboard or null if there is not one + */ + AinStatusDashboard getAinStatusDashboard() { + for (Component comp : getComponents()) { + if (comp instanceof AinStatusDashboard) { + return (AinStatusDashboard) comp; + } + } + return null; + } /** * 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 diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusNode.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusNode.java index 444a6fce8b..ff4cd3c478 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusNode.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusNode.java @@ -97,6 +97,7 @@ final class AinStatusNode extends AbstractNode { @Messages({"AinStatusNode.hostName.title=Host Name", "AinStatusNode.status.title=Status", "AinStatusNode.status.running=Running", + "AinStatusNode.status.pauseRequested=Pause Requested", "AinStatusNode.status.pausedByUser=Paused By User", "AinStatusNode.status.pausedForError=Paused Due to System Error", "AinStatusNode.status.startingup=Starting Up", @@ -129,6 +130,9 @@ final class AinStatusNode extends AbstractNode { case PAUSED_DUE_TO_SYSTEM_ERROR: status = Bundle.AinStatusNode_status_pausedForError(); break; + case PAUSE_REQUESTED: + status = Bundle.AinStatusNode_status_pauseRequested(); + break; default: break; } @@ -142,12 +146,11 @@ final class AinStatusNode extends AbstractNode { List actions = new ArrayList<>(); if (AutoIngestDashboard.isAdminAutoIngestDashboard()) { if (nodeState.getState() == AutoIngestNodeState.State.PAUSED_BY_REQUEST - || nodeState.getState() == AutoIngestNodeState.State.PAUSED_DUE_TO_SYSTEM_ERROR) { - actions.add(new AutoIngestAdminActions.ResumeAction()); - } else if (nodeState.getState() == AutoIngestNodeState.State.RUNNING){ - actions.add(new AutoIngestAdminActions.PauseAction()); + || nodeState.getState() == AutoIngestNodeState.State.PAUSED_DUE_TO_SYSTEM_ERROR + || nodeState.getState() == AutoIngestNodeState.State.RUNNING) { + actions.add(new AutoIngestAdminActions.AutoIngestNodeControlAction.PauseResumeAction(nodeState)); } - actions.add(new AutoIngestAdminActions.ShutdownAction()); + actions.add(new AutoIngestAdminActions.AutoIngestNodeControlAction.ShutdownAction(nodeState)); } return actions.toArray(new Action[actions.size()]); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusPanel.java index 095badff9a..8516151027 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusPanel.java @@ -99,12 +99,12 @@ final class AinStatusPanel extends javax.swing.JPanel implements ExplorerManager void refresh(AutoIngestMonitor monitor) { outline.setRowSelectionAllowed(false); Node[] selectedNodes = explorerManager.getSelectedNodes(); - AinStatusNode autoIngestNode = new AinStatusNode(monitor); - explorerManager.setRootContext(autoIngestNode); + AinStatusNode ainStatusNode = new AinStatusNode(monitor); + explorerManager.setRootContext(ainStatusNode); outline.setRowSelectionAllowed(true); - if (selectedNodes.length > 0 && autoIngestNode.getChildren().findChild(selectedNodes[0].getName()) != null && outline.isFocusable()) { //don't allow saved selections of empty nodes to be restored + if (selectedNodes.length > 0 && ainStatusNode.getChildren().findChild(selectedNodes[0].getName()) != null && outline.isFocusable()) { //don't allow saved selections of empty nodes to be restored try { - explorerManager.setSelectedNodes(new Node[]{autoIngestNode.getChildren().findChild(selectedNodes[0].getName())}); + explorerManager.setSelectedNodes(new Node[]{ainStatusNode.getChildren().findChild(selectedNodes[0].getName())}); } catch (PropertyVetoException ignore) { //Unable to select previously selected node } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestAdminActions.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestAdminActions.java index 1a5f2657b6..617cf18e3a 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestAdminActions.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestAdminActions.java @@ -18,14 +18,122 @@ */ package org.sleuthkit.autopsy.experimental.autoingest; +import java.awt.Cursor; +import java.awt.EventQueue; import java.awt.event.ActionEvent; +import java.util.logging.Level; import javax.swing.AbstractAction; +import javax.swing.JOptionPane; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMonitor.AutoIngestNodeState; import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotDialog; final class AutoIngestAdminActions { + static abstract class AutoIngestNodeControlAction extends AbstractAction { + + private final AutoIngestNodeState nodeState; + private final Logger logger = Logger.getLogger(AutoIngestNodeControlAction.class.getName()); + + AutoIngestNodeControlAction(AutoIngestNodeState nodeState, String title) { + super(title); + this.nodeState = nodeState; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (nodeState != null) { + final AinStatusDashboardTopComponent tc = (AinStatusDashboardTopComponent) WindowManager.getDefault().findTopComponent(AinStatusDashboardTopComponent.PREFERRED_ID); + if (tc != null) { + AinStatusDashboard dashboard = tc.getAinStatusDashboard(); + if (dashboard != null) { + dashboard.getNodesStatusPanel().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + EventQueue.invokeLater(() -> { + try { + controlAutoIngestNode(dashboard); + } catch (AutoIngestMonitor.AutoIngestMonitorException ex) { + logger.log(Level.WARNING, "Error sending control event to node", ex); + } finally { + dashboard.getNodesStatusPanel().setCursor(Cursor.getDefaultCursor()); + } + }); + } + } + } + } + + protected abstract void controlAutoIngestNode(AinStatusDashboard dashboard) throws AutoIngestMonitor.AutoIngestMonitorException; + + AutoIngestNodeState getNodeState() { + return nodeState; + } + + @NbBundle.Messages({"AutoIngestAdminActions.pause.title=Pause Node", + "AutoIngestAdminActions.resume.title=Resume Node"}) + static final class PauseResumeAction extends AutoIngestNodeControlAction { + + private static final long serialVersionUID = 1L; + + PauseResumeAction(AutoIngestNodeState nodeState) { + super(nodeState, nodeState.getState() == AutoIngestNodeState.State.RUNNING + ? Bundle.AutoIngestAdminActions_pause_title() : Bundle.AutoIngestAdminActions_resume_title()); + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected void controlAutoIngestNode(AinStatusDashboard dashboard) throws AutoIngestMonitor.AutoIngestMonitorException { + if (getNodeState().getState() == AutoIngestNodeState.State.RUNNING) { + dashboard.getMonitor().pauseAutoIngestNode(getNodeState().getName()); + } else { + dashboard.getMonitor().resumeAutoIngestNode(getNodeState().getName()); + } + } + } + + @NbBundle.Messages({"AutoIngestAdminActions.shutdown.title=Shutdown Node", + "AutoIngestAdminActions.shutdown.OK=OK", "AutoIngestAdminActions.shutdown.Cancel=Cancel", + "AutoIngestAdminActions.shutdown.consequences=This will cancel any currently running job on this host. Exiting while a job is running potentially leaves the case in an inconsistent or corrupted state."}) + static final class ShutdownAction extends AutoIngestNodeControlAction { + + private static final long serialVersionUID = 1L; + + ShutdownAction(AutoIngestNodeState nodeState) { + super(nodeState, Bundle.AutoIngestAdminActions_shutdown_title()); + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected void controlAutoIngestNode(AinStatusDashboard dashboard) throws AutoIngestMonitor.AutoIngestMonitorException { + Object[] options = {Bundle.AutoIngestAdminActions_shutdown_OK(), + Bundle.AutoIngestAdminActions_shutdown_Cancel() + }; + + int reply = JOptionPane.showOptionDialog(dashboard.getNodesStatusPanel(), + Bundle.AutoIngestAdminActions_shutdown_consequences(), + NbBundle.getMessage(AutoIngestControlPanel.class, "ConfirmationDialog.ConfirmExitHeader"), + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, + options, + options[JOptionPane.NO_OPTION]); + + if (reply == JOptionPane.OK_OPTION) { + dashboard.getMonitor().shutdownAutoIngestNode(getNodeState().getName()); + } + } + } + } + @NbBundle.Messages({"AutoIngestAdminActions.progressDialogAction.title=Ingest Progress"}) static final class ProgressDialogAction extends AbstractAction { @@ -152,65 +260,4 @@ final class AutoIngestAdminActions { return super.clone(); //To change body of generated methods, choose Tools | Templates. } } - - @NbBundle.Messages({"AutoIngestAdminActions.pause.title=Pause Node"}) - static final class PauseAction extends AbstractAction { - - private static final long serialVersionUID = 1L; - - PauseAction() { - super(Bundle.AutoIngestAdminActions_pause_title()); - } - - @Override - public void actionPerformed(ActionEvent e) { - //TODO JIRA- - } - - @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); //To change body of generated methods, choose Tools | Templates. - } - } - - @NbBundle.Messages({"AutoIngestAdminActions.resume.title=Resume Node"}) - static final class ResumeAction extends AbstractAction { - - private static final long serialVersionUID = 1L; - - ResumeAction() { - super(Bundle.AutoIngestAdminActions_resume_title()); - } - - @Override - public void actionPerformed(ActionEvent e) { - //TODO JIRA- - } - - @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); //To change body of generated methods, choose Tools | Templates. - } - } - - @NbBundle.Messages({"AutoIngestAdminActions.shutdown.title=Shutdown Node"}) - static final class ShutdownAction extends AbstractAction { - - private static final long serialVersionUID = 1L; - - ShutdownAction() { - super(Bundle.AutoIngestAdminActions_shutdown_title()); - } - - @Override - public void actionPerformed(ActionEvent e) { - //TODO JIRA- - } - - @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); //To change body of generated methods, choose Tools | Templates. - } - } - } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java index 3b7d91331f..862d31d4fc 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java @@ -852,7 +852,7 @@ public final class AutoIngestControlPanel extends JPanel implements Observer { case CASE_DELETED: updateExecutor.submit(new UpdateAllJobsTablesTask()); break; - case PAUSED_BY_REQUEST: + case PAUSED_BY_USER_REQUEST: EventQueue.invokeLater(() -> { tbStatusMessage.setText(org.openide.util.NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.bnPause.paused")); bnOptions.setEnabled(true); @@ -881,6 +881,9 @@ public final class AutoIngestControlPanel extends JPanel implements Observer { case JOB_STATUS_UPDATED: updateExecutor.submit(new UpdateRunningJobsTablesTask()); break; + case SHUTTING_DOWN: + LifecycleManager.getDefault().exit(); + break; default: break; } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java index 843089f948..3c777f5bc0 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java @@ -170,7 +170,7 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa AutoIngestJobsNode autoIngestNode = new AutoIngestJobsNode(jobsSnapshot, status); explorerManager.setRootContext(autoIngestNode); outline.setRowSelectionAllowed(true); - if (selectedNodes.length > 0 && outline.isFocusable()) { //don't allow saved selections of empty nodes to be restored + if (selectedNodes.length > 0 && autoIngestNode.getChildren().findChild(selectedNodes[0].getName()) != null && outline.isFocusable()) { //don't allow saved selections of empty nodes to be restored try { explorerManager.setSelectedNodes(new Node[]{autoIngestNode.getChildren().findChild(selectedNodes[0].getName())}); } catch (PropertyVetoException ignore) { diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index b7f90f100f..46fa59daa5 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -94,6 +94,7 @@ import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.Shar import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.AutoIngestJobException; +import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeControlEvent.ControlEventType; import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob.CancellationReason; import org.sleuthkit.autopsy.ingest.IngestJobSettings; @@ -134,7 +135,10 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen Event.JOB_COMPLETED.toString(), Event.CASE_PRIORITIZED.toString(), Event.JOB_STARTED.toString(), - Event.REPORT_STATE.toString()})); + Event.REPORT_STATE.toString(), + ControlEventType.PAUSE.toString(), + ControlEventType.RESUME.toString(), + ControlEventType.SHUTDOWN.toString()})); private static final long JOB_STATUS_EVENT_INTERVAL_SECONDS = 10; private static final String JOB_STATUS_PUBLISHING_THREAD_NAME = "AIM-job-status-event-publisher-%d"; private static final long MAX_MISSED_JOB_STATUS_UPDATES = 10; @@ -282,6 +286,8 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen handleRemoteCaseDeletedEvent((AutoIngestCaseDeletedEvent) event); } else if (event instanceof AutoIngestRequestNodeStateEvent) { handleRemoteRequestNodeStateEvent(); + } else if (event instanceof AutoIngestNodeControlEvent) { + handleRemoteNodeControlEvent((AutoIngestNodeControlEvent) event); } } } @@ -405,6 +411,28 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen eventPublisher.publishRemotely(lastPublishedStateEvent); } + private void handleRemoteNodeControlEvent(AutoIngestNodeControlEvent event) { + if (event.getNodeName().compareToIgnoreCase(LOCAL_HOST_NAME) == 0) { + switch (event.getControlEventType()) { + case PAUSE: + pause(); + break; + case RESUME: + resume(); + break; + case SHUTDOWN: + shutDown(); + // Notify the front end (if any) to shutdown. + setChanged(); + notifyObservers(Event.SHUTTING_DOWN); + break; + default: + SYS_LOGGER.log(Level.WARNING, "Received unsupported control event: {0}", event.getControlEventType()); + break; + } + } + } + /** * Shuts down auto ingest. */ @@ -419,6 +447,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen eventPublisher.removeSubscriber(EVENT_LIST, instance); stopInputFolderScans(); stopJobProcessing(); + eventPublisher.publishRemotely(lastPublishedStateEvent = new AutoIngestNodeStateEvent(Event.SHUTDOWN, AutoIngestManager.LOCAL_HOST_NAME)); eventPublisher.closeRemoteEventChannel(); cleanupJobs(); @@ -1696,14 +1725,13 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * object. */ setChanged(); - notifyObservers(Event.PAUSED_BY_REQUEST); - - /** - * Publish an event to let remote listeners know that the - * node has been paused. - */ - eventPublisher.publishRemotely(lastPublishedStateEvent = new AutoIngestNodeStateEvent(Event.PAUSED_BY_REQUEST, AutoIngestManager.LOCAL_HOST_NAME)); + notifyObservers(Event.PAUSED_BY_USER_REQUEST); } + /** + * Publish an event to let remote listeners know that a pause + * has been requested. + */ + eventPublisher.publishRemotely(lastPublishedStateEvent = new AutoIngestNodeStateEvent(Event.PAUSE_REQUESTED, AutoIngestManager.LOCAL_HOST_NAME)); } } @@ -1746,18 +1774,22 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * if auto ingest is shutting down. */ private void pauseIfRequested() throws InterruptedException { + if (State.SHUTTING_DOWN == state) { + return; + } + synchronized (pauseLock) { if (pauseRequested) { sysLogger.log(Level.INFO, "Job processing paused by request"); pauseRequested = false; setChanged(); - notifyObservers(Event.PAUSED_BY_REQUEST); + notifyObservers(Event.PAUSED_BY_USER_REQUEST); /** * Publish an event to let remote listeners know that the * node has been paused. */ - eventPublisher.publishRemotely(lastPublishedStateEvent = new AutoIngestNodeStateEvent(Event.PAUSED_BY_REQUEST, AutoIngestManager.LOCAL_HOST_NAME)); + eventPublisher.publishRemotely(lastPublishedStateEvent = new AutoIngestNodeStateEvent(Event.PAUSED_BY_USER_REQUEST, AutoIngestManager.LOCAL_HOST_NAME)); pauseLock.wait(); sysLogger.log(Level.INFO, "Job processing resumed after pause request"); @@ -1781,6 +1813,10 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * if auto ingest is shutting down. */ private void pauseForSystemError() throws InterruptedException { + if (State.SHUTTING_DOWN == state) { + return; + } + synchronized (pauseLock) { sysLogger.log(Level.SEVERE, "Job processing paused for system error"); setChanged(); @@ -3062,12 +3098,14 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen JOB_COMPLETED, CASE_PRIORITIZED, CASE_DELETED, - PAUSED_BY_REQUEST, + PAUSE_REQUESTED, + PAUSED_BY_USER_REQUEST, PAUSED_FOR_SYSTEM_ERROR, RESUMED, STARTING_UP, RUNNING, SHUTTING_DOWN, + SHUTDOWN, REPORT_STATE } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java index 229128dae1..87adb717b7 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java @@ -42,6 +42,7 @@ import org.sleuthkit.autopsy.events.AutopsyEventException; import org.sleuthkit.autopsy.events.AutopsyEventPublisher; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.ProcessingStatus; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestManager.Event; +import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeControlEvent.ControlEventType; /** * An auto ingest monitor responsible for monitoring and reporting the * processing of auto ingest jobs. @@ -61,10 +62,12 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen AutoIngestManager.Event.CASE_PRIORITIZED.toString(), AutoIngestManager.Event.JOB_STARTED.toString(), AutoIngestManager.Event.RUNNING.toString(), - AutoIngestManager.Event.PAUSED_BY_REQUEST.toString(), + AutoIngestManager.Event.PAUSE_REQUESTED.toString(), + AutoIngestManager.Event.PAUSED_BY_USER_REQUEST.toString(), AutoIngestManager.Event.PAUSED_FOR_SYSTEM_ERROR.toString(), AutoIngestManager.Event.STARTING_UP.toString(), AutoIngestManager.Event.SHUTTING_DOWN.toString(), + AutoIngestManager.Event.SHUTDOWN.toString(), AutoIngestManager.Event.RESUMED.toString()})); private final AutopsyEventPublisher eventPublisher; private CoordinationService coordinationService; @@ -221,9 +224,10 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen * @param event A node state change event. */ private void handleAutoIngestNodeStateEvent(AutoIngestNodeStateEvent event) { - if (event.getEventType() == AutoIngestManager.Event.SHUTTING_DOWN) { + AutoIngestNodeState oldNodeState = null; + if (event.getEventType() == AutoIngestManager.Event.SHUTDOWN) { // Remove node from collection. - nodeStates.remove(event.getNodeName()); + oldNodeState = nodeStates.remove(event.getNodeName()); } else { // Otherwise either create an entry for the given node name or update // an existing entry in the map. @@ -231,7 +235,7 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen } setChanged(); // Trigger a dashboard refresh. - notifyObservers(nodeStates.get(event.getNodeName())); + notifyObservers(oldNodeState == null ? nodeStates.get(event.getNodeName()) : oldNodeState); } /** @@ -522,6 +526,45 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen } } + /** + * Send the given control event to the given node. + * + * @param eventType The type of control event to send. + * @param nodeName The name of the node to send it to. + */ + private void sendControlEventToNode(ControlEventType eventType, String nodeName) { + new Thread(() -> { + eventPublisher.publishRemotely(new AutoIngestNodeControlEvent(eventType, nodeName)); + }).start(); + } + + /** + * Tell the specified node to pause. + * + * @param nodeName + */ + void pauseAutoIngestNode(String nodeName) { + sendControlEventToNode(ControlEventType.PAUSE, nodeName); + } + + /** + * Tell the specified node to resume. + * + * @param nodeName + */ + void resumeAutoIngestNode(String nodeName) { + sendControlEventToNode(ControlEventType.RESUME, nodeName); + } + + /** + * Tell the specified node to shutdown. + * + * @param nodeName + */ + void shutdownAutoIngestNode(String nodeName) { + sendControlEventToNode(ControlEventType.SHUTDOWN, nodeName); + } + /** * A task that queries the coordination service for auto ingest manifest * node data and converts it to auto ingest jobs for publication top its @@ -677,6 +720,7 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen STARTING_UP, SHUTTING_DOWN, RUNNING, + PAUSE_REQUESTED, PAUSED_BY_REQUEST, PAUSED_DUE_TO_SYSTEM_ERROR, UNKNOWN @@ -697,7 +741,7 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen case RUNNING: nodeState = State.RUNNING; break; - case PAUSED_BY_REQUEST: + case PAUSED_BY_USER_REQUEST: nodeState = State.PAUSED_BY_REQUEST; break; case PAUSED_FOR_SYSTEM_ERROR: @@ -706,6 +750,9 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen case RESUMED: nodeState = State.RUNNING; break; + case PAUSE_REQUESTED: + nodeState = State.PAUSE_REQUESTED; + break; default: nodeState = State.UNKNOWN; break; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestNodeControlEvent.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestNodeControlEvent.java new file mode 100644 index 0000000000..ca16a2bf8b --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestNodeControlEvent.java @@ -0,0 +1,55 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 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.experimental.autoingest; + +import java.io.Serializable; +import org.sleuthkit.autopsy.events.AutopsyEvent; + +/** + * Event published to pause, resume or shutdown an AIN. + */ +final class AutoIngestNodeControlEvent extends AutopsyEvent implements Serializable { + + /** + * The set of available controls. + */ + enum ControlEventType { + PAUSE, + RESUME, + SHUTDOWN + } + + private static final long serialVersionUID = 1L; + private final String nodeName; + private final ControlEventType eventType; + + AutoIngestNodeControlEvent(ControlEventType eventType, String nodeName) { + super(eventType.toString(), null, null); + this.eventType = eventType; + this.nodeName = nodeName; + } + + String getNodeName() { + return nodeName; + } + + ControlEventType getControlEventType() { + return eventType; + } +} diff --git a/KNOWN_ISSUES.txt b/KNOWN_ISSUES.txt deleted file mode 100644 index 0b61890805..0000000000 --- a/KNOWN_ISSUES.txt +++ /dev/null @@ -1,16 +0,0 @@ - Known Issues - - Last Reviewed: June 12, 2012 - - -This lists the bugs and issues thare are known and could effect -investigation results. There are other minor interface bugs that -are not listed here. - -Keyword Search module: -- Slack space of files is not added to the index and therefore will -not be searched. -- Files larger than 100MB AND that are file types that are supported -by Tika (word docs, PDF, HTML, JPEG, etc.) are now added to the index as fully extracted text. -However, due to memory limitations of certain parsers, if full text extraction fails, -only strings extracted from the large file will be added to the index. \ No newline at end of file diff --git a/NEWS.txt b/NEWS.txt index de1a1e576e..73b199ef4f 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,23 +1,26 @@ ---------------- VERSION 4.7.0 -------------- New Features: - A graph visualization was added to the Communications tool to make it easier to find messages and relationships. -- A new Application content viewer provides custom views of media files, SQLite files, and Plists. -- A data source processor that runs Volatility was added to support ingesting memory images. -- Reports (e.g., RegRipper output) generated by ingest modules are now indexed for keyword search. -- Passwords to open password protected archive files can be entered. -- PhotoRec carving module can be configured to keep corrupted files. -- Filters to reduce files processed by ingest modules can have data range conditions. +- A new "Application" content viewer (lower right) that will contain file-type specific viewers (to reduce number of tabs). +- New viewer for SQLite databases (in Application content viewer) +- New viewer for binary PLists (in Appilcation content viewer) - L01 files can be imported as data sources. -- Block size can be supplied for local drives and for images for which SleuthKit auto detect fails. +- Ingest filters can now use date range conditions for triage. +- Passwords to open password protected archive files can be entered (by right clicking on the file). +- Reports (e.g., RegRipper output) generated by ingest modules are now indexed for keyword search. +- PhotoRec carving module can be configured to keep corrupted files. +- Sector size can be specified for local drives and images when E01 is wrong or it is a raw image. +- New data source processor in Experimental module that runs Volatility, adds the outputs as files, and parses the reports to provide INTERESTING_FILE artifacts. - Assorted small enhancements are included. Bug Fixes: -- Memory leaks and other issues revealed by fuzzing the SleuthKit have +- Memory leaks and other issues revealed by fuzzing the The Sleuth Kit have been fixed. - Result views (upper right) and content views (lower right) stay in synch when switching result views. - Concurrency bugs in the ingest tasks scheduler have been fixed. - Assorted small bug fixes are included. + ---------------- VERSION 4.6.0 -------------- New Features: - A new Message content viewer was added to make it easier to view email message contents. diff --git a/Running_Linux_OSX.txt b/Running_Linux_OSX.txt index 386c4c5899..053c18f3eb 100644 --- a/Running_Linux_OSX.txt +++ b/Running_Linux_OSX.txt @@ -43,3 +43,4 @@ Autopsy depends on a specific version of The Sleuth Kit. You need the Java libr * Limitations (Updated May 2018) * - Timeline does not work on OS X - Video thumbnails are not generated (need to get a consistent version of OpenCV) +- VHD and VMDK files not supported on OS X diff --git a/build.xml b/build.xml index a1ff9d665a..5616cb2c66 100644 --- a/build.xml +++ b/build.xml @@ -67,6 +67,16 @@ TSK_HOME: ${env.TSK_HOME} + + + + + + + + + + @@ -90,7 +100,6 @@ -