diff --git a/Core/src/org/sleuthkit/autopsy/examples/Bundle.properties b/Core/src/org/sleuthkit/autopsy/examples/Bundle.properties index d52d5fc439..74e5aa8e31 100755 --- a/Core/src/org/sleuthkit/autopsy/examples/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/examples/Bundle.properties @@ -1,4 +1,4 @@ -# To change this template, choose Tools | Templates -# and open the template in the editor. - SampleContentViewer.jLabel1.text=jLabel1 +SampleIngestModuleFactory.moduleName=Sample Ingest Module +SampleIngestModuleFactory.moduleDescription=This module serves as a sample ingest module for software developers. +SampleIngestModuleIngestJobSettingsPanel.skipKnownFilesCheckBox.text=Skip Known Files (NSRL) diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java index 0ebce34e68..a4497b5259 100755 --- a/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java @@ -29,56 +29,199 @@ */ package org.sleuthkit.autopsy.examples; +import java.util.HashMap; import java.util.List; -import org.apache.log4j.Logger; +import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.casemodule.services.Services; -import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleStatusHelper; import org.sleuthkit.autopsy.ingest.IngestModule; -import org.sleuthkit.autopsy.ingest.IngestModuleAdapter; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestMessage; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.datamodel.TskData; /** - * Sample data source ingest module that doesn't do much. Note that the - * IngestModuleAdapter abstract class could have been used as a base class to - * obtain default implementations of many of the DataSourceIngestModule methods. + * Sample data source ingest module that doesn't do much. Demonstrates per + * ingest job module settings, use of a subset of the available ingest services + * and thread-safe sharing of per ingest job resources. + *

+ * IMPORTANT TIP: This sample data source ingest module directly implements + * DataSourceIngestModule, which extends IngestModule. A practical alternative, + * recommended if you do not need to provide implementations of all of the + * IngestModule methods, is to extend the abstract class IngestModuleAdapter to + * get default "do nothing" implementations of the IngestModule methods. */ -// RJCTODO: Remove inheritance from IngestModuleAdapter to show full implementation of interface -// provide better documentation, and provide more extensive demonstration of how to -// use various ingest services. -class SampleDataSourceIngestModule extends IngestModuleAdapter implements DataSourceIngestModule { +class SampleDataSourceIngestModule implements DataSourceIngestModule { - private static final Logger logger = Logger.getLogger(SampleDataSourceIngestModule.class); + private static final HashMap moduleReferenceCountsForIngestJobs = new HashMap<>(); + private static final HashMap fileCountsForIngestJobs = new HashMap<>(); + private static long messageCount = 0; + private final boolean skipKnownFiles; + private IngestJobContext context = null; + SampleDataSourceIngestModule(SampleModuleIngestJobSettings settings) { + this.skipKnownFiles = settings.skipKnownFiles(); + } + + /** + * Invoked by Autopsy to allow an ingest module instance to set up any + * internal data structures and acquire any private resources it will need + * during an ingest job. + *

+ * Autopsy will generally use several instances of an ingest module for each + * ingest job it performs. Completing an ingest job entails processing a + * single data source (e.g., a disk image) and all of the files from the + * data source, including files extracted from archives and any unallocated + * space (made to look like a series of files). The data source is passed + * through one or more pipelines of data source ingest modules. The files + * are passed through one or more pipelines of file ingest modules. + *

+ * Autopsy may use multiple threads to complete an ingest job, but it is + * guaranteed that there will be no more than one module instance per + * thread. However, if the module instances must share resources, the + * modules are responsible for synchronizing access to the shared resources + * and doing reference counting as required to release those resources + * correctly. Also, more than one ingest job may be in progress at any given + * time. This must also be taken into consideration when sharing resources + * between module instances. + *

+ * An ingest module that does not require initialization may extend the + * abstract IngestModuleAdapter class to get a default "do nothing" + * implementation of this method. + * + * @param context Provides data and services specific to the ingest job and + * the ingest pipeline of which the module is a part. + * @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException + */ + @Override + public void startUp(IngestJobContext context) throws IngestModuleException { + this.context = context; + + // This method is thread-safe with per ingest job reference counting. + initFileCount(context.getJobId()); + } + + /** + * Processes a data source. + * + * @param dataSource The data source to process. + * @param statusHelper A status helper to be used to report progress and + * detect ingest job cancellation. + * @return A result code indicating success or failure of the processing. + */ @Override public ProcessResult process(Content dataSource, DataSourceIngestModuleStatusHelper statusHelper) { - Case case1 = Case.getCurrentCase(); - SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); - + Case autopsyCase = Case.getCurrentCase(); + SleuthkitCase sleuthkitCase = autopsyCase.getSleuthkitCase(); Services services = new Services(sleuthkitCase); FileManager fileManager = services.getFileManager(); try { + // Get count of files with .doc extension. + long fileCount = 0; List docFiles = fileManager.findFiles(dataSource, "%.doc"); - for (AbstractFile file : docFiles) { - // do something with each doc file + for (AbstractFile docFile : docFiles) { + if (!skipKnownFiles || docFile.getKnown() != TskData.FileKnown.KNOWN) { + ++fileCount; + } } - + + // Get files by creation time. long currentTime = System.currentTimeMillis() / 1000; long minTime = currentTime - (14 * 24 * 60 * 60); // Go back two weeks. List otherFiles = sleuthkitCase.findFilesWhere("crtime > " + minTime); - // do something with these files... + for (FsContent otherFile : otherFiles) { + if (!skipKnownFiles || otherFile.getKnown() != TskData.FileKnown.KNOWN) { + ++fileCount; + } + } + + // This method is thread-safe and keeps per ingest job counters. + addToFileCount(context.getJobId(), fileCount); } catch (TskCoreException ex) { - logger.fatal("Error retrieving files from database: " + ex.getLocalizedMessage()); - return IngestModule.ProcessResult.OK; + IngestServices ingestServices = IngestServices.getInstance(); + Logger logger = ingestServices.getLogger(SampleIngestModuleFactory.getModuleName()); + logger.log(Level.SEVERE, "File query failed", ex); + return IngestModule.ProcessResult.ERROR; } - + return IngestModule.ProcessResult.OK; } + + /** + * Invoked by Autopsy when an ingest job is completed, before the ingest + * module instance is discarded. The module should respond by doing things + * like releasing private resources, submitting final results, and posting a + * final ingest message. + *

+ * Autopsy will generally use several instances of an ingest module for each + * ingest job it performs. Completing an ingest job entails processing a + * single data source (e.g., a disk image) and all of the files from the + * data source, including files extracted from archives and any unallocated + * space (made to look like a series of files). The data source is passed + * through one or more pipelines of data source ingest modules. The files + * are passed through one or more pipelines of file ingest modules. + *

+ * Autopsy may use multiple threads to complete an ingest job, but it is + * guaranteed that there will be no more than one module instance per + * thread. However, if the module instances must share resources, the + * modules are responsible for synchronizing access to the shared resources + * and doing reference counting as required to release those resources + * correctly. Also, more than one ingest job may be in progress at any given + * time. This must also be taken into consideration when sharing resources + * between module instances. + *

+ * An ingest module that does not require initialization may extend the + * abstract IngestModuleAdapter class to get a default "do nothing" + * implementation of this method. + */ + @Override + public void shutDown(boolean ingestJobCancelled) { + // This method is thread-safe with per ingest job reference counting. + postFileCount(context.getJobId()); + } + + synchronized static void initFileCount(long ingestJobId) { + Integer moduleReferenceCount; + if (!moduleReferenceCountsForIngestJobs.containsKey(ingestJobId)) { + moduleReferenceCount = 1; + fileCountsForIngestJobs.put(ingestJobId, 0L); + } else { + moduleReferenceCount = moduleReferenceCountsForIngestJobs.get(ingestJobId); + ++moduleReferenceCount; + } + moduleReferenceCountsForIngestJobs.put(ingestJobId, moduleReferenceCount); + } + + synchronized static void addToFileCount(long ingestJobId, long countToAdd) { + Long fileCount = fileCountsForIngestJobs.get(ingestJobId); + fileCount += countToAdd; + fileCountsForIngestJobs.put(ingestJobId, fileCount); + } + + synchronized static void postFileCount(long ingestJobId) { + Integer moduleReferenceCount = moduleReferenceCountsForIngestJobs.remove(ingestJobId); + --moduleReferenceCount; + if (moduleReferenceCount == 0) { + Long filesCount = fileCountsForIngestJobs.remove(ingestJobId); + String msgText = String.format("Found %d files", filesCount); + IngestMessage message = IngestMessage.createMessage( + ++messageCount, + IngestMessage.MessageType.DATA, + SampleIngestModuleFactory.getModuleName(), + msgText); + IngestServices.getInstance().postMessage(message); + } else { + moduleReferenceCountsForIngestJobs.put(ingestJobId, moduleReferenceCount); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java index 8510646957..f967a6af7b 100755 --- a/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java @@ -29,85 +29,143 @@ */ package org.sleuthkit.autopsy.examples; -import org.apache.log4j.Logger; -import org.openide.util.Exceptions; +import java.util.HashMap; +import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.FileIngestModule; import org.sleuthkit.autopsy.ingest.IngestModule; -import org.sleuthkit.autopsy.ingest.IngestModuleAdapter; import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestMessage; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskData; /** - * This is a sample and simple module. It is a file-level ingest module, meaning - * that it will get called on each file in the disk image / logical file set. It - * does a stupid calculation of the number of null bytes in the beginning of the - * file in order to show the basic flow. - * - * Autopsy has been hard coded to ignore this module based on the it's package - * name. IngestModuleLoader will not load things from the - * org.sleuthkit.autopsy.examples package. Either change the package or the - * loading code to make this module actually run. + * Sample file ingest module that doesn't do much. Demonstrates per ingest job + * module settings, use of a subset of the available ingest services and + * thread-safe sharing of per ingest job resources. + *

+ * IMPORTANT TIP: This sample data source ingest module directly implements + * FileIngestModule, which extends IngestModule. A practical alternative, + * recommended if you do not need to provide implementations of all of the + * IngestModule methods, is to extend the abstract class IngestModuleAdapter to + * get default "do nothing" implementations of the IngestModule methods. */ -// RJCTODO: Remove inheritance from IngestModuleAdapter to show full implementation of interface -// provide better documentation, and provide more extensive demonstration of how to -// use various ingest services. -class SampleFileIngestModule extends IngestModuleAdapter implements FileIngestModule { +class SampleFileIngestModule implements FileIngestModule { - private int attrId = -1; + private static final HashMap moduleReferenceCountsForIngestJobs = new HashMap<>(); + private static final HashMap artifactCountsForIngestJobs = new HashMap<>(); + private static long messageCount = 0; + private static int attrId = -1; + private final boolean skipKnownFiles; + private IngestJobContext context = null; - @Override - public void startUp(IngestJobContext initContext) { - /* For this demo, we are going to make a private attribute to post our - * results to the blackbaord with. There are many standard blackboard artifact - * and attribute types and you should first consider using one of those before - * making private ones because other modules won't know about provate ones. - * Because our demo has results that have no real value, we do not have an - * official attribute for them. - */ - Case case1 = Case.getCurrentCase(); - SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); - - // see if the type already exists in the blackboard. - try { - attrId = sleuthkitCase.getAttrTypeID("ATTR_SAMPLE"); - } catch (TskCoreException ex) { - // create it if not - try { - attrId = sleuthkitCase.addAttrType("ATTR_SAMPLE", "Sample Attribute"); - } catch (TskCoreException ex1) { - Logger log = Logger.getLogger(SampleFileIngestModule.class); - log.fatal("Error adding attribute type: " + ex1.getLocalizedMessage()); - attrId = -1; - } - } + SampleFileIngestModule(SampleModuleIngestJobSettings settings) { + this.skipKnownFiles = settings.skipKnownFiles(); } + /** + * Invoked by Autopsy to allow an ingest module instance to set up any + * internal data structures and acquire any private resources it will need + * during an ingest job. + *

+ * Autopsy will generally use several instances of an ingest module for each + * ingest job it performs. Completing an ingest job entails processing a + * single data source (e.g., a disk image) and all of the files from the + * data source, including files extracted from archives and any unallocated + * space (made to look like a series of files). The data source is passed + * through one or more pipelines of data source ingest modules. The files + * are passed through one or more pipelines of file ingest modules. + *

+ * Autopsy may use multiple threads to complete an ingest job, but it is + * guaranteed that there will be no more than one module instance per + * thread. However, if the module instances must share resources, the + * modules are responsible for synchronizing access to the shared resources + * and doing reference counting as required to release those resources + * correctly. Also, more than one ingest job may be in progress at any given + * time. This must also be taken into consideration when sharing resources + * between module instances. + *

+ * An ingest module that does not require initialization may extend the + * abstract IngestModuleAdapter class to get a default "do nothing" + * implementation of this method. + * + * @param context Provides data and services specific to the ingest job and + * the ingest pipeline of which the module is a part. + * @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException + */ @Override - public IngestModule.ProcessResult process(AbstractFile abstractFile) { - // skip non-files - if ((abstractFile.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) - || (abstractFile.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) { + public void startUp(IngestJobContext context) throws IngestModuleException { + this.context = context; + + synchronized (SampleFileIngestModule.class) { + if (attrId == -1) { + // For this sample, make a new attribute type to use to post + // results to the blackboard. There are many standard blackboard + // artifact and attribute types and you should use them instead + // creating new ones to facilitate use of your results by other + // modules. + Case autopsyCase = Case.getCurrentCase(); + SleuthkitCase sleuthkitCase = autopsyCase.getSleuthkitCase(); + + // See if the attribute type has already been defined. + try { + attrId = sleuthkitCase.getAttrTypeID("ATTR_SAMPLE"); + } catch (TskCoreException e) { + // If not, create the the attribute type. + try { + attrId = sleuthkitCase.addAttrType("ATTR_SAMPLE", "Sample Attribute"); + } catch (TskCoreException ex) { + IngestServices ingestServices = IngestServices.getInstance(); + Logger logger = ingestServices.getLogger(SampleIngestModuleFactory.getModuleName()); + logger.log(Level.SEVERE, "Failed to create blackboard attribute", ex); + attrId = -1; + throw new IngestModuleException(ex.getLocalizedMessage()); + } + } + } + } + + // This method is thread-safe with per ingest job reference counting. + initBlackboardPostCount(context.getJobId()); + } + + /** + * Processes a file. + * + * @param file The file. + * @return A result code indicating success or failure of the processing. + */ + @Override + public IngestModule.ProcessResult process(AbstractFile file) { + if (attrId != -1) { + return IngestModule.ProcessResult.ERROR; + } + + // Skip anything other than actual file system files. + if ((file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) + || (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) { return IngestModule.ProcessResult.OK; } - // skip NSRL / known files - if (abstractFile.getKnown() == TskData.FileKnown.KNOWN) { + // Skip NSRL / known files. + if (skipKnownFiles && file.getKnown() == TskData.FileKnown.KNOWN) { return IngestModule.ProcessResult.OK; } - /* Do a non-sensical calculation of the number of 0x00 bytes - * in the first 1024-bytes of the file. This is for demo - * purposes only. - */ + // Do a nonsensical calculation of the number of 0x00 bytes + // in the first 1024-bytes of the file. This is for demo + // purposes only. try { byte buffer[] = new byte[1024]; - int len = abstractFile.read(buffer, 0, 1024); + int len = file.read(buffer, 0, 1024); int count = 0; for (int i = 0; i < len; i++) { if (buffer[i] == 0x00) { @@ -115,23 +173,98 @@ class SampleFileIngestModule extends IngestModuleAdapter implements FileIngestMo } } - if (attrId != -1) { - // Make an attribute using the ID for the private type that we previously created. - BlackboardAttribute attr = new BlackboardAttribute(attrId, "SampleFileIngestModule", count); // RJCTODO: Set up factory with static module name function as example + // Make an attribute using the ID for the attribute type that + // was previously created. + BlackboardAttribute attr = new BlackboardAttribute(attrId, SampleIngestModuleFactory.getModuleName(), count); - /* add it to the general info artifact. In real modules, you would likely have - * more complex data types and be making more specific artifacts. - */ - BlackboardArtifact art = abstractFile.getGenInfoArtifact(); - art.addAttribute(attr); - } + // Add the to the general info artifact for the file. In a + // real module, you would likely have more complex data types + // and be making more specific artifacts. + BlackboardArtifact art = file.getGenInfoArtifact(); + art.addAttribute(attr); + + // Thread-safe. + addToBlackboardPostCount(context.getJobId(), 1L); + + // Fire an event to notify any listeners for blackboard postings. + ModuleDataEvent event = new ModuleDataEvent(SampleIngestModuleFactory.getModuleName(), ARTIFACT_TYPE.TSK_GEN_INFO); + IngestServices.getInstance().fireModuleDataEvent(event); return IngestModule.ProcessResult.OK; + } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); + IngestServices ingestServices = IngestServices.getInstance(); + Logger logger = ingestServices.getLogger(SampleIngestModuleFactory.getModuleName()); + logger.log(Level.SEVERE, "Error processing file (id = " + file.getId() + ")", ex); return IngestModule.ProcessResult.ERROR; } } - // RJCTODO: Add a module factory with service provider annotation (commented out) + /** + * Invoked by Autopsy when an ingest job is completed, before the ingest + * module instance is discarded. The module should respond by doing things + * like releasing private resources, submitting final results, and posting a + * final ingest message. + *

+ * Autopsy will generally use several instances of an ingest module for each + * ingest job it performs. Completing an ingest job entails processing a + * single data source (e.g., a disk image) and all of the files from the + * data source, including files extracted from archives and any unallocated + * space (made to look like a series of files). The data source is passed + * through one or more pipelines of data source ingest modules. The files + * are passed through one or more pipelines of file ingest modules. + *

+ * Autopsy may use multiple threads to complete an ingest job, but it is + * guaranteed that there will be no more than one module instance per + * thread. However, if the module instances must share resources, the + * modules are responsible for synchronizing access to the shared resources + * and doing reference counting as required to release those resources + * correctly. Also, more than one ingest job may be in progress at any given + * time. This must also be taken into consideration when sharing resources + * between module instances. + *

+ * An ingest module that does not require initialization may extend the + * abstract IngestModuleAdapter class to get a default "do nothing" + * implementation of this method. + */ + @Override + public void shutDown(boolean ingestJobCancelled) { + // This method is thread-safe with per ingest job reference counting. + reportBlackboardPostCount(context.getJobId()); + } + + synchronized static void initBlackboardPostCount(long ingestJobId) { + Integer moduleReferenceCount; + if (!moduleReferenceCountsForIngestJobs.containsKey(ingestJobId)) { + moduleReferenceCount = 1; + artifactCountsForIngestJobs.put(ingestJobId, 0L); + } else { + moduleReferenceCount = moduleReferenceCountsForIngestJobs.get(ingestJobId); + ++moduleReferenceCount; + } + moduleReferenceCountsForIngestJobs.put(ingestJobId, moduleReferenceCount); + } + + synchronized static void addToBlackboardPostCount(long ingestJobId, long countToAdd) { + Long fileCount = artifactCountsForIngestJobs.get(ingestJobId); + fileCount += countToAdd; + artifactCountsForIngestJobs.put(ingestJobId, fileCount); + } + + synchronized static void reportBlackboardPostCount(long ingestJobId) { + Integer moduleReferenceCount = moduleReferenceCountsForIngestJobs.remove(ingestJobId); + --moduleReferenceCount; + if (moduleReferenceCount == 0) { + Long filesCount = artifactCountsForIngestJobs.remove(ingestJobId); + String msgText = String.format("Posted %d times to the blackboard", filesCount); + IngestMessage message = IngestMessage.createMessage( + ++messageCount, + IngestMessage.MessageType.INFO, + SampleIngestModuleFactory.getModuleName(), + msgText); + IngestServices.getInstance().postMessage(message); + } else { + moduleReferenceCountsForIngestJobs.put(ingestJobId, moduleReferenceCount); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleFactory.java new file mode 100755 index 0000000000..dfa22c4218 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleFactory.java @@ -0,0 +1,326 @@ +/* + * Sample ingest module factory in the public domain. + * Feel free to use this as a template for your inget module factories. + * + * Contact: Brian Carrier [carrier sleuthkit [dot] org] + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +package org.sleuthkit.autopsy.examples; + +// The following import is required for the ServiceProvider annotation (see +// below) used by the Autopsy ingest framework to locate ingest module +// factories. You will need to add a dependency on the Lookup API NetBeans +// module to your NetBeans module to use this import. +import org.openide.util.lookup.ServiceProvider; + +// The following import is required to participate in Autopsy +// internationalization and localization. Autopsy core is currently localized +// for Japan. Please consult the NetBeans documentation for details. +import org.openide.util.NbBundle; + +import org.sleuthkit.autopsy.ingest.IngestModuleFactory; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; +import org.sleuthkit.autopsy.ingest.FileIngestModule; +import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSetttingsPanel; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; + +/** + * A factory that creates sample data source and file ingest modules. + *

+ * This factory implements an interface that must be implemented by all + * providers of Autopsy ingest modules. An ingest module factory is used to + * create instances of a type of data source ingest module, a type of file + * ingest module, or both. + *

+ * Autopsy will generally use the factory to several instances of each type of + * module for each ingest job it performs. Completing an ingest job entails + * processing a single data source (e.g., a disk image) and all of the files + * from the data source, including files extracted from archives and any + * unallocated space (made to look like a series of files). The data source is + * passed through one or more pipelines of data source ingest modules. The files + * are passed through one or more pipelines of file ingest modules. + *

+ * Autopsy may use multiple threads to complete an ingest job, but it is + * guaranteed that there will be no more than one module instance per thread. + * However, if the module instances must share resources, the modules are + * responsible for synchronizing access to the shared resources and doing + * reference counting as required to release those resources correctly. Also, + * more than one ingest job may be in progress at any given time. This must also + * be taken into consideration when sharing resources between module instances. + *

+ * An ingest module factory may provide global and per ingest job settings user + * interface panels. The global settings should apply to all module instances. + * The per ingest job settings should apply to all module instances working on a + * particular ingest job. Autopsy supports context-sensitive and persistent per + * ingest job settings, so per ingest job settings must be serializable. + *

+ * To be discovered at runtime by the ingest framework, IngestModuleFactory + * implementations must be marked with the following NetBeans Service provider + * annotation (see below). + *

+ * IMPORTANT TIP: This sample ingest module factory directly implements + * IngestModuleFactory. A practical alternative, recommended if you do not need + * to provide implementations of all of the IngestModuleFactory methods, is to + * extend the abstract class IngestModuleFactoryAdapter to get default + * implementations of most of the IngestModuleFactory methods. + */ +// @ServiceProvider(service = IngestModuleFactory.class) +public class SampleIngestModuleFactory implements IngestModuleFactory { + + private static final String VERSION_NUMBER = "1.0.0"; + + // This class method allows the ingest module instances created by this + // factory to use the same display name that is provided to the Autopsy + // ingest framework by the factory. + static String getModuleName() { + return NbBundle.getMessage(SampleIngestModuleFactory.class, "SampleIngestModuleFactory.moduleName"); + } + + /** + * Gets the display name that identifies the family of ingest modules the + * factory creates. Autopsy uses this string to identify the module in user + * interface components and log messages. The module name must be unique. so + * a brief but distinctive name is recommended. + * + * @return The module family display name. + */ + @Override + public String getModuleDisplayName() { + return getModuleName(); + } + + /** + * Gets a brief, user-friendly description of the family of ingest modules + * the factory creates. Autopsy uses this string to describe the module in + * user interface components. + * + * @return The module family description. + */ + @Override + public String getModuleDescription() { + return NbBundle.getMessage(SampleIngestModuleFactory.class, "SampleIngestModuleFactory.moduleDescription"); + } + + /** + * Gets the version number of the family of ingest modules the factory + * creates. + * + * @return The module family version number. + */ + @Override + public String getModuleVersionNumber() { + return VERSION_NUMBER; + } + + /** + * Queries the factory to determine if it provides a user interface panel to + * allow a user to change settings that are used by all instances of the + * family of ingest modules the factory creates. For example, the Autopsy + * core hash lookup ingest module factory provides a global settings panel + * to import and create hash databases. The hash databases are then enabled + * or disabled per ingest job using an ingest job settings panel. If the + * module family does not have global settings, the factory may extend + * IngestModuleFactoryAdapter to get an implementation of this method that + * returns false. + * + * @return True if the factory provides a global settings panel. + */ + @Override + public boolean hasGlobalSettingsPanel() { + return false; + } + + /** + * Gets a user interface panel that allows a user to change settings that + * are used by all instances of the family of ingest modules the factory + * creates. For example, the Autopsy core hash lookup ingest module factory + * provides a global settings panel to import and create hash databases. The + * imported hash databases are then enabled or disabled per ingest job using + * ingest an ingest job settings panel. If the module family does not have a + * global settings, the factory may extend IngestModuleFactoryAdapter to get + * an implementation of this method that throws an + * UnsupportedOperationException. + * + * @return A global settings panel. + */ + @Override + public IngestModuleGlobalSetttingsPanel getGlobalSettingsPanel() { + throw new UnsupportedOperationException(); + } + + /** + * Gets the default per ingest job settings for instances of the family of + * ingest modules the factory creates. For example, the Autopsy core hash + * lookup ingest modules family uses hash databases imported or created + * using its global settings panel. All of the hash databases are enabled by + * default for an ingest job. If the module family does not have per ingest + * job settings, the factory may extend IngestModuleFactoryAdapter to get an + * implementation of this method that returns an instance of the + * NoIngestModuleJobSettings class. + * + * @return The default ingest job settings. + */ + @Override + public IngestModuleIngestJobSettings getDefaultIngestJobSettings() { + return new SampleModuleIngestJobSettings(); + } + + /** + * Queries the factory to determine if it provides user a interface panel to + * allow a user to make per ingest job settings for instances of the family + * of ingest modules the factory creates. For example, the Autopsy core hash + * lookup ingest module factory provides an ingest job settings panels to + * enable or disable hash databases per ingest job. If the module family + * does not have per ingest job settings, the factory may extend + * IngestModuleFactoryAdapter to get an implementation of this method that + * returns false. + * + * @return True if the factory provides ingest job settings panels. + */ + @Override + public boolean hasIngestJobSettingsPanel() { + return true; + } + + /** + * Gets a user interface panel that can be used to set per ingest job + * settings for instances of the family of ingest modules the factory + * creates. For example, the core hash lookup ingest module factory provides + * an ingest job settings panel to enable or disable hash databases per + * ingest job. If the module family does not have per ingest job settings, + * the factory may extend IngestModuleFactoryAdapter to get an + * implementation of this method that throws an + * UnsupportedOperationException. + * + * @param setting Per ingest job settings to initialize the panel. + * @return An ingest job settings panel. + */ + @Override + public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) { + if (!(settings instanceof SampleModuleIngestJobSettings)) { + throw new IllegalArgumentException("Expected settings argument to be instanceof SampleModuleIngestJobSettings"); + } + return new SampleIngestModuleIngestJobSettingsPanel((SampleModuleIngestJobSettings) settings); + } + + /** + * Queries the factory to determine if it is capable of creating data source + * ingest modules. If the module family does not include data source ingest + * modules, the factory may extend IngestModuleFactoryAdapter to get an + * implementation of this method that returns false. + * + * @return True if the factory can create data source ingest modules. + */ + @Override + public boolean isDataSourceIngestModuleFactory() { + return true; + } + + /** + * Creates a data source ingest module instance. + *

+ * Autopsy will generally use the factory to several instances of each type + * of module for each ingest job it performs. Completing an ingest job + * entails processing a single data source (e.g., a disk image) and all of + * the files from the data source, including files extracted from archives + * and any unallocated space (made to look like a series of files). The data + * source is passed through one or more pipelines of data source ingest + * modules. The files are passed through one or more pipelines of file + * ingest modules. + *

+ * The ingest framework may use multiple threads to complete an ingest job, + * but it is guaranteed that there will be no more than one module instance + * per thread. However, if the module instances must share resources, the + * modules are responsible for synchronizing access to the shared resources + * and doing reference counting as required to release those resources + * correctly. Also, more than one ingest job may be in progress at any given + * time. This must also be taken into consideration when sharing resources + * between module instances. modules. + *

+ * If the module family does not include data source ingest modules, the + * factory may extend IngestModuleFactoryAdapter to get an implementation of + * this method that throws an UnsupportedOperationException. + * + * @param settings The settings for the ingest job. + * @return A data source ingest module instance. + */ + @Override + public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings) { + if (!(settings instanceof SampleModuleIngestJobSettings)) { + throw new IllegalArgumentException("Expected settings argument to be instanceof SampleModuleIngestJobSettings"); + } + return new SampleDataSourceIngestModule((SampleModuleIngestJobSettings) settings); + } + + /** + * Queries the factory to determine if it is capable of creating file ingest + * modules. If the module family does not include file ingest modules, the + * factory may extend IngestModuleFactoryAdapter to get an implementation of + * this method that returns false. + * + * @return True if the factory can create file ingest modules. + */ + @Override + public boolean isFileIngestModuleFactory() { + return true; + } + + /** + * Creates a file ingest module instance. + *

+ * Autopsy will generally use the factory to several instances of each type + * of module for each ingest job it performs. Completing an ingest job + * entails processing a single data source (e.g., a disk image) and all of + * the files from the data source, including files extracted from archives + * and any unallocated space (made to look like a series of files). The data + * source is passed through one or more pipelines of data source ingest + * modules. The files are passed through one or more pipelines of file + * ingest modules. + *

+ * The ingest framework may use multiple threads to complete an ingest job, + * but it is guaranteed that there will be no more than one module instance + * per thread. However, if the module instances must share resources, the + * modules are responsible for synchronizing access to the shared resources + * and doing reference counting as required to release those resources + * correctly. Also, more than one ingest job may be in progress at any given + * time. This must also be taken into consideration when sharing resources + * between module instances. modules. + *

+ * If the module family does not include file ingest modules, the factory + * may extend IngestModuleFactoryAdapter to get an implementation of this + * method that throws an UnsupportedOperationException. + * + * @param settings The settings for the ingest job. + * @return A file ingest module instance. + */ + @Override + public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) { + if (!(settings instanceof SampleModuleIngestJobSettings)) { + throw new IllegalArgumentException("Expected settings argument to be instanceof SampleModuleIngestJobSettings"); + } + return new SampleFileIngestModule((SampleModuleIngestJobSettings) settings); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleIngestJobSettingsPanel.form new file mode 100755 index 0000000000..4ea5377716 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleIngestJobSettingsPanel.form @@ -0,0 +1,45 @@ + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleIngestJobSettingsPanel.java new file mode 100755 index 0000000000..2d0f511963 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleIngestJobSettingsPanel.java @@ -0,0 +1,96 @@ +/* + * Sample module ingest job settings panel in the public domain. + * Feel free to use this as a template for your module ingest job settings + * panels. + * + * Contact: Brian Carrier [carrier sleuthkit [dot] org] + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +package org.sleuthkit.autopsy.examples; + +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; + +/** + * UI component used to make per ingest job settings for sample ingest modules. + */ +public class SampleIngestModuleIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel { + + /** + * Creates new form SampleIngestModuleIngestJobSettings + */ + public SampleIngestModuleIngestJobSettingsPanel(SampleModuleIngestJobSettings settings) { + initComponents(); + customizeComponents(settings); + } + + private void customizeComponents(SampleModuleIngestJobSettings settings) { + skipKnownFilesCheckBox.setSelected(settings.skipKnownFiles()); + } + + /** + * Gets the ingest job settings for an ingest module. + * + * @return The ingest settings. + */ + @Override + public IngestModuleIngestJobSettings getSettings() { + return new SampleModuleIngestJobSettings(skipKnownFilesCheckBox.isSelected()); + } + + /** + * 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() { + + skipKnownFilesCheckBox = new javax.swing.JCheckBox(); + + org.openide.awt.Mnemonics.setLocalizedText(skipKnownFilesCheckBox, org.openide.util.NbBundle.getMessage(SampleIngestModuleIngestJobSettingsPanel.class, "SampleIngestModuleIngestJobSettingsPanel.skipKnownFilesCheckBox.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(skipKnownFilesCheckBox) + .addContainerGap(255, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(skipKnownFilesCheckBox) + .addContainerGap(270, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox skipKnownFilesCheckBox; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleModuleIngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/examples/SampleModuleIngestJobSettings.java new file mode 100755 index 0000000000..0a7e34d81f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleModuleIngestJobSettings.java @@ -0,0 +1,56 @@ +/* + * Sample module ingest job settings in the public domain. + * Feel free to use this as a template for your module job settings. + * + * Contact: Brian Carrier [carrier sleuthkit [dot] org] + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.sleuthkit.autopsy.examples; + +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; + +/** + * Ingest job options for sample ingest module instances. + */ +public class SampleModuleIngestJobSettings implements IngestModuleIngestJobSettings { + + private boolean skipKnownFiles = true; + + SampleModuleIngestJobSettings() { + } + + SampleModuleIngestJobSettings(boolean skipKnownFiles) { + this.skipKnownFiles = skipKnownFiles; + } + + void setSkipKnownFiles(boolean enabled) { + skipKnownFiles = enabled; + } + + boolean skipKnownFiles() { + return skipKnownFiles; + } +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobLauncher.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobLauncher.java index 0d4675d4a0..7324c3d023 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobLauncher.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobLauncher.java @@ -87,7 +87,7 @@ public final class IngestJobLauncher { // NOTE: In the future, this code will be modified to get the // module settings for the current context, if available, from // storage; for now always use the defaults. - IngestModuleTemplate moduleTemplate = new IngestModuleTemplate(moduleFactory, moduleFactory.getDefaultModuleSettings()); + IngestModuleTemplate moduleTemplate = new IngestModuleTemplate(moduleFactory, moduleFactory.getDefaultIngestJobSettings()); String moduleName = moduleTemplate.getModuleName(); if (enabledModuleNames.contains(moduleName)) { moduleTemplate.setEnabled(true); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModule.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModule.java index 970677ec9e..364df37257 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModule.java @@ -20,28 +20,110 @@ package org.sleuthkit.autopsy.ingest; /** * The interface that must be implemented by all ingest modules. + *

+ * Autopsy will generally use several instances of an ingest module for each + * ingest job it performs. Completing an ingest job entails processing a single + * data source (e.g., a disk image) and all of the files from the data source, + * including files extracted from archives and any unallocated space (made to + * look like a series of files). The data source is passed through one or more + * pipelines of data source ingest modules. The files are passed through one or + * more pipelines of file ingest modules. + *

+ * Autopsy may use multiple threads to complete an ingest job, but it is + * guaranteed that there will be no more than one module instance per thread. + * However, if the module instances must share resources, the modules are + * responsible for synchronizing access to the shared resources and doing + * reference counting as required to release those resources correctly. Also, + * more than one ingest job may be in progress at any given time. This must also + * be taken into consideration when sharing resources between module instances. + *

+ * TIP: An ingest module that does not require initialization or clean up may + * extend the abstract IngestModuleAdapter class to get a default "do nothing" + * implementation of this interface. + * */ public interface IngestModule { + /** + * A return code for derived class process() methods. + */ public enum ProcessResult { + OK, ERROR }; - + + /** + * A custom exception for the use of ingest modules. + */ public class IngestModuleException extends Exception { + + public IngestModuleException() { + } + public IngestModuleException(String message) { super(message); } } - - // RJCTODO: Write header comment, make sure to mention "one module instance per thread" + /** - * Invoked by the ingest frame - * @param context - * @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException + * Invoked by Autopsy to allow an ingest module instance to set up any + * internal data structures and acquire any private resources it will need + * during an ingest job. + *

+ * Autopsy will generally use several instances of an ingest module for each + * ingest job it performs. Completing an ingest job entails processing a + * single data source (e.g., a disk image) and all of the files from the + * data source, including files extracted from archives and any unallocated + * space (made to look like a series of files). The data source is passed + * through one or more pipelines of data source ingest modules. The files + * are passed through one or more pipelines of file ingest modules. + *

+ * Autopsy may use multiple threads to complete an ingest job, but it is + * guaranteed that there will be no more than one module instance per + * thread. However, if the module instances must share resources, the + * modules are responsible for synchronizing access to the shared resources + * and doing reference counting as required to release those resources + * correctly. Also, more than one ingest job may be in progress at any given + * time. This must also be taken into consideration when sharing resources + * between module instances. + *

+ * An ingest module that does not require initialization may extend the + * abstract IngestModuleAdapter class to get a default "do nothing" + * implementation of this method. + * + * @param context Provides data and services specific to the ingest job and + * the ingest pipeline of which the module is a part. + * @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException */ void startUp(IngestJobContext context) throws IngestModuleException; - // RJCTODO: Write header comment, make sure to mention "one module instance per thread" + /** + * Invoked by Autopsy when an ingest job is completed, before the ingest + * module instance is discarded. The module should respond by doing things + * like releasing private resources, submitting final results, and posting a + * final ingest message. + *

+ * Autopsy will generally use several instances of an ingest module for each + * ingest job it performs. Completing an ingest job entails processing a + * single data source (e.g., a disk image) and all of the files from the + * data source, including files extracted from archives and any unallocated + * space (made to look like a series of files). The data source is passed + * through one or more pipelines of data source ingest modules. The files + * are passed through one or more pipelines of file ingest modules. + *

+ * Autopsy may use multiple threads to complete an ingest job, but it is + * guaranteed that there will be no more than one module instance per + * thread. However, if the module instances must share resources, the + * modules are responsible for synchronizing access to the shared resources + * and doing reference counting as required to release those resources + * correctly. Also, more than one ingest job may be in progress at any given + * time. This must also be taken into consideration when sharing resources + * between module instances. + *

+ * An ingest module that does not require initialization may extend the + * abstract IngestModuleAdapter class to get a default "do nothing" + * implementation of this method. + */ void shutDown(boolean ingestJobWasCancelled); } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactory.java index 252585e6aa..a9baf59e32 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactory.java @@ -19,34 +19,50 @@ package org.sleuthkit.autopsy.ingest; /** - * An interface that must be implemented by all providers of ingest modules. An - * ingest module factory will be used to create instances of a type of data - * source ingest module, a type of file ingest module, or both. - *

- * IMPORTANT: The factory should be stateless to support context-sensitive use - * of the factory. The ingest framework is responsible for managing context - * switching and the persistence of resource configurations and per ingest job - * options. + * An interface that must be implemented by all providers of Autopsy ingest + * modules. An ingest module factory is used to create instances of a type of + * data source ingest module, a type of file ingest module, or both. *

- * IMPORTANT: The ingest framework will create one or more instances of each - * module type for each ingest job it performs. The ingest framework may use - * multiple threads to complete an ingest job, but it is guaranteed that there - * will be no more than one module instance per thread. However, if these - * instances must share resources, the modules are responsible for synchronizing - * access to the shared resources and doing reference counting as required to - * release those resources correctly. + * Autopsy will generally use the factory to several instances of each type of + * module for each ingest job it performs. Completing an ingest job entails + * processing a single data source (e.g., a disk image) and all of the files + * from the data source, including files extracted from archives and any + * unallocated space (made to look like a series of files). The data source is + * passed through one or more pipelines of data source ingest modules. The files + * are passed through one or more pipelines of file ingest modules. *

- * IMPORTANT: To be discovered at runtime by the ingest framework, - * IngestModuleFactory implementations must be marked with the following - * NetBeans Service provider annotation: + * Autopsy may use multiple threads to complete an ingest job, but it is + * guaranteed that there will be no more than one module instance per thread. + * However, if the module instances must share resources, the modules are + * responsible for synchronizing access to the shared resources and doing + * reference counting as required to release those resources correctly. Also, + * more than one ingest job may be in progress at any given time. This must also + * be taken into consideration when sharing resources between module instances. + *

+ * An ingest module factory may provide global and per ingest job settings user + * interface panels. The global settings should apply to all module instances. + * The per ingest job settings should apply to all module instances working on a + * particular ingest job. Autopsy supports context-sensitive and persistent per + * ingest job settings, so per ingest job settings must be serializable. + *

+ * To be discovered at runtime by the ingest framework, IngestModuleFactory + * implementations must be marked with the following NetBeans Service provider + * annotation: * * @ServiceProvider(service=IngestModuleFactory.class) + *

+ * IMPORTANT TIP: If an implementation of IngestModuleFactory does not need to + * provide implementations of all of the IngestModuleFactory methods, it can + * extend the abstract class IngestModuleFactoryAdapter to get default + * implementations of most of the IngestModuleFactory methods. */ public interface IngestModuleFactory { /** * Gets the display name that identifies the family of ingest modules the - * factory creates. + * factory creates. Autopsy uses this string to identify the module in user + * interface components and log messages. The module name must be unique. so + * a brief but distinctive name is recommended. * * @return The module family display name. */ @@ -54,7 +70,8 @@ public interface IngestModuleFactory { /** * Gets a brief, user-friendly description of the family of ingest modules - * the factory creates. + * the factory creates. Autopsy uses this string to describe the module in + * user interface components. * * @return The module family description. */ @@ -69,147 +86,154 @@ public interface IngestModuleFactory { String getModuleVersionNumber(); /** - * Queries the factory to determine if it provides user interface panels to - * configure resources to be used by instances of the family of ingest - * modules the factory creates. For example, the core hash lookup ingest - * module factory provides resource configuration panels to import and - * create hash databases. The hash databases are then enabled or disabled - * per ingest job using ingest job options panels. If the module family does - * not have a resources configuration, the factory should extend + * Queries the factory to determine if it provides a user interface panel to + * allow a user to change settings that are used by all instances of the + * family of ingest modules the factory creates. For example, the Autopsy + * core hash lookup ingest module factory provides a global settings panel + * to import and create hash databases. The hash databases are then enabled + * or disabled per ingest job using an ingest job settings panel. If the + * module family does not have global settings, the factory may extend * IngestModuleFactoryAdapter to get an implementation of this method that * returns false. * - * @return True if the factory provides resource configuration panels. + * @return True if the factory provides a global settings panel. */ boolean hasGlobalSettingsPanel(); /** - * Gets a user interface panel that can be used to configure resources for - * instances of the family of ingest modules the factory creates. For - * example, the core hash lookup ingest module factory provides a resource - * configuration panel to import and create hash databases. The imported - * hash databases are then enabled or disabled per ingest job using ingest - * options panels. If the module family does not have a resources - * configuration, the factory should extend IngestModuleFactoryAdapter to - * get an implementation of this method that throws an + * Gets a user interface panel that allows a user to change settings that + * are used by all instances of the family of ingest modules the factory + * creates. For example, the Autopsy core hash lookup ingest module factory + * provides a global settings panel to import and create hash databases. The + * imported hash databases are then enabled or disabled per ingest job using + * ingest an ingest job settings panel. If the module family does not have a + * global settings, the factory may extend IngestModuleFactoryAdapter to get + * an implementation of this method that throws an * UnsupportedOperationException. - *

- * IMPORTANT: The ingest framework assumes that ingest module factories are - * stateless to support context-sensitive use of the factory, with the - * ingest framework managing context switching and the persistence of - * resource configurations and per ingest job options. A factory should not - * retain references to the resources configuration panels it creates. * - * @param resourcesConfig A resources configuration with which to initialize - * the panel. - * @return A user interface panel for configuring ingest module resources. + * @return A global settings panel. */ IngestModuleGlobalSetttingsPanel getGlobalSettingsPanel(); /** - * Gets the default per ingest job options for instances of the family of - * ingest modules the factory creates. For example, the core hash lookup - * ingest modules family has a resources configuration consisting of hash - * databases, all of which are enabled by default for an ingest job. If the - * module family does not have per ingest job options, the factory should - * extend IngestModuleFactoryAdapter to get an implementation of this method - * that returns an instance of the NoIngestJobOptions class. + * Gets the default per ingest job settings for instances of the family of + * ingest modules the factory creates. For example, the Autopsy core hash + * lookup ingest modules family uses hash databases imported or created + * using its global settings panel. All of the hash databases are enabled by + * default for an ingest job. If the module family does not have per ingest + * job settings, the factory may extend IngestModuleFactoryAdapter to get an + * implementation of this method that returns an instance of the + * NoIngestModuleJobSettings class. * - * @return The ingest options. + * @return The default ingest job settings. */ - IngestModuleIngestJobSettings getDefaultModuleSettings(); + IngestModuleIngestJobSettings getDefaultIngestJobSettings(); /** - * Queries the factory to determine if it provides user interface panels to - * set per ingest job options for instances of the family of ingest modules - * the factory creates. For example, the core hash lookup ingest module - * factory provides ingest options panels to enable or disable hash - * databases per ingest job. If the module family does not have per ingest - * job options, the factory should extend IngestModuleFactoryAdapter to get - * an implementation of this method that returns false. + * Queries the factory to determine if it provides user a interface panel to + * allow a user to make per ingest job settings for instances of the family + * of ingest modules the factory creates. For example, the Autopsy core hash + * lookup ingest module factory provides an ingest job settings panels to + * enable or disable hash databases per ingest job. If the module family + * does not have per ingest job settings, the factory may extend + * IngestModuleFactoryAdapter to get an implementation of this method that + * returns false. * - * @return True if the factory provides ingest job options panels. + * @return True if the factory provides ingest job settings panels. */ - boolean hasModuleSettingsPanel(); + boolean hasIngestJobSettingsPanel(); /** * Gets a user interface panel that can be used to set per ingest job - * options for instances of the family of ingest modules the factory + * settings for instances of the family of ingest modules the factory * creates. For example, the core hash lookup ingest module factory provides - * ingest options panels to enable or disable hash databases per ingest job. - * If the module family does not have ingest job options, the factory should - * extend IngestModuleFactoryAdapter to get an implementation of this method - * that throws an UnsupportedOperationException. - *

- * IMPORTANT: The ingest framework assumes that ingest module factories are - * stateless to support context-sensitive use of the factory. The ingest - * framework is responsible for managing context switching and the - * persistence of resource configurations and per ingest job options. A - * factory should not retain references to the ingest job options panels it - * creates. + * an ingest job settings panel to enable or disable hash databases per + * ingest job. If the module family does not have per ingest job settings, + * the factory may extend IngestModuleFactoryAdapter to get an + * implementation of this method that throws an + * UnsupportedOperationException. * - * @param resourcesConfig - * @param ingestOptions Per ingest job options to initialize the panel. - * @return A user interface panel. + * @param setting Per ingest job settings to initialize the panel. + * @return An ingest job settings panel. */ - IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings settings); // RJCTODO: Can these be made into generics? + IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings); /** - * Queries the factory to determine if it is capable of creating file ingest - * modules. + * Queries the factory to determine if it is capable of creating data source + * ingest modules. If the module family does not include data source ingest + * modules, the factory may extend IngestModuleFactoryAdapter to get an + * implementation of this method that returns false. * - * @return True if the factory can create file ingest modules. + * @return True if the factory can create data source ingest modules. */ boolean isDataSourceIngestModuleFactory(); /** * Creates a data source ingest module instance. *

- * IMPORTANT: The factory should be stateless to support context-sensitive - * use of the factory. The ingest framework is responsible for managing - * context switching and the persistence of resource configurations and per - * ingest job options. A factory should not retain references to the data - * source ingest module instances it creates. + * Autopsy will generally use the factory to several instances of each type + * of module for each ingest job it performs. Completing an ingest job + * entails processing a single data source (e.g., a disk image) and all of + * the files from the data source, including files extracted from archives + * and any unallocated space (made to look like a series of files). The data + * source is passed through one or more pipelines of data source ingest + * modules. The files are passed through one or more pipelines of file + * ingest modules. *

- * IMPORTANT: The ingest framework will create one or more data source - * ingest module instances for each ingest job it performs. The ingest - * framework may use multiple threads to complete an ingest job, but it is - * guaranteed that there will be no more than one module instance per - * thread. However, if these instances must share resources, the modules are - * responsible for synchronizing access to the shared resources and doing - * reference counting as required to release those resources correctly. + * The ingest framework may use multiple threads to complete an ingest job, + * but it is guaranteed that there will be no more than one module instance + * per thread. However, if the module instances must share resources, the + * modules are responsible for synchronizing access to the shared resources + * and doing reference counting as required to release those resources + * correctly. Also, more than one ingest job may be in progress at any given + * time. This must also be taken into consideration when sharing resources + * between module instances. modules. + *

+ * If the module family does not include data source ingest modules, the + * factory may extend IngestModuleFactoryAdapter to get an implementation of + * this method that throws an UnsupportedOperationException. * - * @param ingestOptions The ingest options for the module instance. + * @param settings The settings for the ingest job. * @return A data source ingest module instance. */ DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings); /** * Queries the factory to determine if it is capable of creating file ingest - * module instances. + * modules. If the module family does not include file ingest modules, the + * factory may extend IngestModuleFactoryAdapter to get an implementation of + * this method that returns false. * - * @return True if the factory can create file ingest module instances. + * @return True if the factory can create file ingest modules. */ boolean isFileIngestModuleFactory(); /** * Creates a file ingest module instance. *

- * IMPORTANT: The factory should be stateless to support context-sensitive - * use of the factory. The ingest framework is responsible for managing - * context switching and the persistence of resource configurations and per - * ingest job options. A factory should not retain references to the file - * ingest module instances it creates. + * Autopsy will generally use the factory to several instances of each type + * of module for each ingest job it performs. Completing an ingest job + * entails processing a single data source (e.g., a disk image) and all of + * the files from the data source, including files extracted from archives + * and any unallocated space (made to look like a series of files). The data + * source is passed through one or more pipelines of data source ingest + * modules. The files are passed through one or more pipelines of file + * ingest modules. *

- * IMPORTANT: The ingest framework will create one or more file ingest - * module instances for each ingest job it performs. The ingest framework - * may use multiple threads to complete an ingest job, but it is guaranteed - * that there will be no more than one module instance per thread. However, - * if these instances must share resources, the modules are responsible for - * synchronizing access to the shared resources and doing reference counting - * as required to release those resources correctly. + * The ingest framework may use multiple threads to complete an ingest job, + * but it is guaranteed that there will be no more than one module instance + * per thread. However, if the module instances must share resources, the + * modules are responsible for synchronizing access to the shared resources + * and doing reference counting as required to release those resources + * correctly. Also, more than one ingest job may be in progress at any given + * time. This must also be taken into consideration when sharing resources + * between module instances. modules. + *

+ * If the module family does not include file ingest modules, the factory + * may extend IngestModuleFactoryAdapter to get an implementation of this + * method that throws an UnsupportedOperationException. * - * @param ingestOptions The ingest options for the module instance. + * @param settings The settings for the ingest job. * @return A file ingest module instance. */ FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryAdapter.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryAdapter.java index 9c24944b29..60bea92713 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryAdapter.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryAdapter.java @@ -44,17 +44,17 @@ public abstract class IngestModuleFactoryAdapter implements IngestModuleFactory } @Override - public IngestModuleIngestJobSettings getDefaultModuleSettings() { + public IngestModuleIngestJobSettings getDefaultIngestJobSettings() { return new NoIngestModuleIngestJobSettings(); } @Override - public boolean hasModuleSettingsPanel() { + public boolean hasIngestJobSettingsPanel() { return false; } @Override - public IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings ingestOptions) { + public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings ingestOptions) { throw new UnsupportedOperationException(); } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleGlobalSetttingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleGlobalSetttingsPanel.java index 160ab6df83..f6de631e9b 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleGlobalSetttingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleGlobalSetttingsPanel.java @@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.ingest; import javax.swing.JPanel; /** - * Base class for ingest module resources configuration panels. + * Base class for ingest module global settings panels. */ public abstract class IngestModuleGlobalSetttingsPanel extends JPanel { diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleTemplate.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleTemplate.java index 45c194e320..f536b7916e 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleTemplate.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleTemplate.java @@ -50,11 +50,11 @@ final class IngestModuleTemplate { } boolean hasModuleSettingsPanel() { - return moduleFactory.hasModuleSettingsPanel(); + return moduleFactory.hasIngestJobSettingsPanel(); } IngestModuleIngestJobSettingsPanel getModuleSettingsPanel() { - return moduleFactory.getModuleSettingsPanel(settings); + return moduleFactory.getIngestJobSettingsPanel(settings); } boolean hasGlobalSettingsPanel() { diff --git a/Core/src/org/sleuthkit/autopsy/ingest/ModuleDataEvent.java b/Core/src/org/sleuthkit/autopsy/ingest/ModuleDataEvent.java index acfae1a779..e500ae13d2 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/ModuleDataEvent.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/ModuleDataEvent.java @@ -88,7 +88,4 @@ public class ModuleDataEvent extends ChangeEvent { public String getModuleName() { return moduleName; } - - - } diff --git a/FileExtMismatch/src/org/sleuthkit/autopsy/fileextmismatch/FileExtMismatchDetectorModuleFactory.java b/FileExtMismatch/src/org/sleuthkit/autopsy/fileextmismatch/FileExtMismatchDetectorModuleFactory.java index e91d8198ea..855c586870 100755 --- a/FileExtMismatch/src/org/sleuthkit/autopsy/fileextmismatch/FileExtMismatchDetectorModuleFactory.java +++ b/FileExtMismatch/src/org/sleuthkit/autopsy/fileextmismatch/FileExtMismatchDetectorModuleFactory.java @@ -57,17 +57,17 @@ public class FileExtMismatchDetectorModuleFactory extends IngestModuleFactoryAda } @Override - public IngestModuleIngestJobSettings getDefaultModuleSettings() { + public IngestModuleIngestJobSettings getDefaultIngestJobSettings() { return new FileExtMismatchDetectorModuleSettings(); } @Override - public boolean hasModuleSettingsPanel() { + public boolean hasIngestJobSettingsPanel() { return true; } @Override - public IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings settings) { + public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) { assert settings instanceof FileExtMismatchDetectorModuleSettings; if (!(settings instanceof FileExtMismatchDetectorModuleSettings)) { throw new IllegalArgumentException("Expected settings argument to be instanceof FileExtMismatchDetectorModuleSettings"); diff --git a/FileTypeId/src/org/sleuthkit/autopsy/filetypeid/FileTypeIdModuleFactory.java b/FileTypeId/src/org/sleuthkit/autopsy/filetypeid/FileTypeIdModuleFactory.java index 5f55378f97..36004c26e0 100755 --- a/FileTypeId/src/org/sleuthkit/autopsy/filetypeid/FileTypeIdModuleFactory.java +++ b/FileTypeId/src/org/sleuthkit/autopsy/filetypeid/FileTypeIdModuleFactory.java @@ -56,17 +56,17 @@ public class FileTypeIdModuleFactory extends IngestModuleFactoryAdapter { } @Override - public IngestModuleIngestJobSettings getDefaultModuleSettings() { + public IngestModuleIngestJobSettings getDefaultIngestJobSettings() { return new FileTypeIdModuleSettings(); } @Override - public boolean hasModuleSettingsPanel() { + public boolean hasIngestJobSettingsPanel() { return true; } @Override - public IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings settings) { + public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) { assert settings instanceof FileTypeIdModuleSettings; if (!(settings instanceof FileTypeIdModuleSettings)) { throw new IllegalArgumentException("Expected settings argument to be instanceof FileTypeIdModuleSettings"); diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashLookupModuleFactory.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashLookupModuleFactory.java index ea4c1e73fc..3e2934f323 100755 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashLookupModuleFactory.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashLookupModuleFactory.java @@ -58,7 +58,7 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter { } @Override - public IngestModuleIngestJobSettings getDefaultModuleSettings() { + public IngestModuleIngestJobSettings getDefaultIngestJobSettings() { // All available hash sets are enabled by default. HashDbManager hashDbManager = HashDbManager.getInstance(); List knownHashSetNames = getHashSetNames(hashDbManager.getKnownFileHashSets()); @@ -75,12 +75,12 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter { } @Override - public boolean hasModuleSettingsPanel() { + public boolean hasIngestJobSettingsPanel() { return true; } @Override - public IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings settings) { + public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) { if (!(settings instanceof HashLookupModuleSettings)) { throw new IllegalArgumentException("Expected settings argument to be instanceof HashLookupModuleSettings"); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchModuleFactory.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchModuleFactory.java index fabb5b8d5b..4ce36d9f94 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchModuleFactory.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchModuleFactory.java @@ -59,7 +59,7 @@ public class KeywordSearchModuleFactory extends IngestModuleFactoryAdapter { } @Override - public IngestModuleIngestJobSettings getDefaultModuleSettings() { + public IngestModuleIngestJobSettings getDefaultIngestJobSettings() { KeywordSearchListsXML listManager = KeywordSearchListsXML.getCurrent(); List enabledKeywordLists = new ArrayList<>(); List keywordLists = listManager.getListsL(); @@ -71,12 +71,12 @@ public class KeywordSearchModuleFactory extends IngestModuleFactoryAdapter { } @Override - public boolean hasModuleSettingsPanel() { + public boolean hasIngestJobSettingsPanel() { return true; } @Override - public IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings settings) { + public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) { assert settings instanceof KeywordSearchJobSettings; if (!(settings instanceof KeywordSearchJobSettings)) { throw new IllegalArgumentException("Expected settings argument to be instanceof KeywordSearchJobSettings");