Merge branch 'develop' of https://www.github.com/sleuthkit/autopsy into develop

This commit is contained in:
alexjacks92 2014-03-28 09:57:12 -04:00
commit 62476f6ffe
18 changed files with 1129 additions and 227 deletions

View File

@ -1,4 +1,4 @@
# To change this template, choose Tools | Templates
# and open the template in the editor.
SampleContentViewer.jLabel1.text=jLabel1 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)

View File

@ -29,56 +29,199 @@
*/ */
package org.sleuthkit.autopsy.examples; package org.sleuthkit.autopsy.examples;
import java.util.HashMap;
import java.util.List; 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.Case;
import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleStatusHelper; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleStatusHelper;
import org.sleuthkit.autopsy.ingest.IngestModule; import org.sleuthkit.autopsy.ingest.IngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleAdapter;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; 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 * Sample data source ingest module that doesn't do much. Demonstrates per
* IngestModuleAdapter abstract class could have been used as a base class to * ingest job module settings, use of a subset of the available ingest services
* obtain default implementations of many of the DataSourceIngestModule methods. * and thread-safe sharing of per ingest job resources.
* <p>
* 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 class SampleDataSourceIngestModule implements DataSourceIngestModule {
// provide better documentation, and provide more extensive demonstration of how to
// use various ingest services.
class SampleDataSourceIngestModule extends IngestModuleAdapter implements DataSourceIngestModule {
private static final Logger logger = Logger.getLogger(SampleDataSourceIngestModule.class); private static final HashMap<Long, Integer> moduleReferenceCountsForIngestJobs = new HashMap<>();
private static final HashMap<Long, Long> 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 @Override
public ProcessResult process(Content dataSource, DataSourceIngestModuleStatusHelper statusHelper) { public ProcessResult process(Content dataSource, DataSourceIngestModuleStatusHelper statusHelper) {
Case case1 = Case.getCurrentCase(); Case autopsyCase = Case.getCurrentCase();
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); SleuthkitCase sleuthkitCase = autopsyCase.getSleuthkitCase();
Services services = new Services(sleuthkitCase); Services services = new Services(sleuthkitCase);
FileManager fileManager = services.getFileManager(); FileManager fileManager = services.getFileManager();
try { try {
// Get count of files with .doc extension.
long fileCount = 0;
List<AbstractFile> docFiles = fileManager.findFiles(dataSource, "%.doc"); List<AbstractFile> docFiles = fileManager.findFiles(dataSource, "%.doc");
for (AbstractFile file : docFiles) { for (AbstractFile docFile : docFiles) {
// do something with each doc file if (!skipKnownFiles || docFile.getKnown() != TskData.FileKnown.KNOWN) {
++fileCount;
}
} }
// Get files by creation time.
long currentTime = System.currentTimeMillis() / 1000; long currentTime = System.currentTimeMillis() / 1000;
long minTime = currentTime - (14 * 24 * 60 * 60); // Go back two weeks. long minTime = currentTime - (14 * 24 * 60 * 60); // Go back two weeks.
List<FsContent> otherFiles = sleuthkitCase.findFilesWhere("crtime > " + minTime); List<FsContent> 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) { } catch (TskCoreException ex) {
logger.fatal("Error retrieving files from database: " + ex.getLocalizedMessage()); IngestServices ingestServices = IngestServices.getInstance();
return IngestModule.ProcessResult.OK; Logger logger = ingestServices.getLogger(SampleIngestModuleFactory.getModuleName());
logger.log(Level.SEVERE, "File query failed", ex);
return IngestModule.ProcessResult.ERROR;
} }
return IngestModule.ProcessResult.OK; 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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);
}
}
} }

View File

@ -29,85 +29,143 @@
*/ */
package org.sleuthkit.autopsy.examples; package org.sleuthkit.autopsy.examples;
import org.apache.log4j.Logger; import java.util.HashMap;
import org.openide.util.Exceptions; import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.FileIngestModule; import org.sleuthkit.autopsy.ingest.FileIngestModule;
import org.sleuthkit.autopsy.ingest.IngestModule; import org.sleuthkit.autopsy.ingest.IngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleAdapter;
import org.sleuthkit.autopsy.ingest.IngestJobContext; 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.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
/** /**
* This is a sample and simple module. It is a file-level ingest module, meaning * Sample file ingest module that doesn't do much. Demonstrates per ingest job
* that it will get called on each file in the disk image / logical file set. It * module settings, use of a subset of the available ingest services and
* does a stupid calculation of the number of null bytes in the beginning of the * thread-safe sharing of per ingest job resources.
* file in order to show the basic flow. * <p>
* * IMPORTANT TIP: This sample data source ingest module directly implements
* Autopsy has been hard coded to ignore this module based on the it's package * FileIngestModule, which extends IngestModule. A practical alternative,
* name. IngestModuleLoader will not load things from the * recommended if you do not need to provide implementations of all of the
* org.sleuthkit.autopsy.examples package. Either change the package or the * IngestModule methods, is to extend the abstract class IngestModuleAdapter to
* loading code to make this module actually run. * get default "do nothing" implementations of the IngestModule methods.
*/ */
// RJCTODO: Remove inheritance from IngestModuleAdapter to show full implementation of interface class SampleFileIngestModule implements FileIngestModule {
// provide better documentation, and provide more extensive demonstration of how to
// use various ingest services.
class SampleFileIngestModule extends IngestModuleAdapter implements FileIngestModule {
private int attrId = -1; private static final HashMap<Long, Integer> moduleReferenceCountsForIngestJobs = new HashMap<>();
private static final HashMap<Long, Long> artifactCountsForIngestJobs = new HashMap<>();
private static long messageCount = 0;
private static int attrId = -1;
private final boolean skipKnownFiles;
private IngestJobContext context = null;
@Override SampleFileIngestModule(SampleModuleIngestJobSettings settings) {
public void startUp(IngestJobContext initContext) { this.skipKnownFiles = settings.skipKnownFiles();
/* 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;
}
}
} }
/**
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 @Override
public IngestModule.ProcessResult process(AbstractFile abstractFile) { public void startUp(IngestJobContext context) throws IngestModuleException {
// skip non-files this.context = context;
if ((abstractFile.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
|| (abstractFile.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) { 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; return IngestModule.ProcessResult.OK;
} }
// skip NSRL / known files // Skip NSRL / known files.
if (abstractFile.getKnown() == TskData.FileKnown.KNOWN) { if (skipKnownFiles && file.getKnown() == TskData.FileKnown.KNOWN) {
return IngestModule.ProcessResult.OK; return IngestModule.ProcessResult.OK;
} }
/* Do a non-sensical calculation of the number of 0x00 bytes // Do a nonsensical calculation of the number of 0x00 bytes
* in the first 1024-bytes of the file. This is for demo // in the first 1024-bytes of the file. This is for demo
* purposes only. // purposes only.
*/
try { try {
byte buffer[] = new byte[1024]; byte buffer[] = new byte[1024];
int len = abstractFile.read(buffer, 0, 1024); int len = file.read(buffer, 0, 1024);
int count = 0; int count = 0;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (buffer[i] == 0x00) { 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 attribute type that
// Make an attribute using the ID for the private type that we previously created. // was previously created.
BlackboardAttribute attr = new BlackboardAttribute(attrId, "SampleFileIngestModule", count); // RJCTODO: Set up factory with static module name function as example BlackboardAttribute attr = new BlackboardAttribute(attrId, SampleIngestModuleFactory.getModuleName(), count);
/* add it to the general info artifact. In real modules, you would likely have // Add the to the general info artifact for the file. In a
* more complex data types and be making more specific artifacts. // real module, you would likely have more complex data types
*/ // and be making more specific artifacts.
BlackboardArtifact art = abstractFile.getGenInfoArtifact(); BlackboardArtifact art = file.getGenInfoArtifact();
art.addAttribute(attr); 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; return IngestModule.ProcessResult.OK;
} catch (TskCoreException ex) { } 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; 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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);
}
}
} }

View File

@ -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 <at> 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* To be discovered at runtime by the ingest framework, IngestModuleFactory
* implementations must be marked with the following NetBeans Service provider
* annotation (see below).
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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);
}
}

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="skipKnownFilesCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="255" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="skipKnownFilesCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="270" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="skipKnownFilesCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/examples/Bundle.properties" key="SampleIngestModuleIngestJobSettingsPanel.skipKnownFilesCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -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 <at> 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")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//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))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox skipKnownFilesCheckBox;
// End of variables declaration//GEN-END:variables
}

View File

@ -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 <at> 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;
}
}

View File

@ -87,7 +87,7 @@ public final class IngestJobLauncher {
// NOTE: In the future, this code will be modified to get the // NOTE: In the future, this code will be modified to get the
// module settings for the current context, if available, from // module settings for the current context, if available, from
// storage; for now always use the defaults. // 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(); String moduleName = moduleTemplate.getModuleName();
if (enabledModuleNames.contains(moduleName)) { if (enabledModuleNames.contains(moduleName)) {
moduleTemplate.setEnabled(true); moduleTemplate.setEnabled(true);

View File

@ -20,28 +20,110 @@ package org.sleuthkit.autopsy.ingest;
/** /**
* The interface that must be implemented by all ingest modules. * The interface that must be implemented by all ingest modules.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 { public interface IngestModule {
/**
* A return code for derived class process() methods.
*/
public enum ProcessResult { public enum ProcessResult {
OK, OK,
ERROR ERROR
}; };
/**
* A custom exception for the use of ingest modules.
*/
public class IngestModuleException extends Exception { public class IngestModuleException extends Exception {
public IngestModuleException() {
}
public IngestModuleException(String message) { public IngestModuleException(String message) {
super(message); super(message);
} }
} }
// RJCTODO: Write header comment, make sure to mention "one module instance per thread"
/** /**
* Invoked by the ingest frame * Invoked by Autopsy to allow an ingest module instance to set up any
* @param context * internal data structures and acquire any private resources it will need
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException * during an ingest job.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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; 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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); void shutDown(boolean ingestJobWasCancelled);
} }

View File

@ -19,34 +19,50 @@
package org.sleuthkit.autopsy.ingest; package org.sleuthkit.autopsy.ingest;
/** /**
* An interface that must be implemented by all providers of ingest modules. An * An interface that must be implemented by all providers of Autopsy ingest
* ingest module factory will be used to create instances of a type of data * modules. An ingest module factory is used to create instances of a type of
* source ingest module, a type of file ingest module, or both. * data source ingest module, a type of file ingest module, or both.
* <P>
* 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.
* <p> * <p>
* IMPORTANT: The ingest framework will create one or more instances of each * Autopsy will generally use the factory to several instances of each type of
* module type for each ingest job it performs. The ingest framework may use * module for each ingest job it performs. Completing an ingest job entails
* multiple threads to complete an ingest job, but it is guaranteed that there * processing a single data source (e.g., a disk image) and all of the files
* will be no more than one module instance per thread. However, if these * from the data source, including files extracted from archives and any
* instances must share resources, the modules are responsible for synchronizing * unallocated space (made to look like a series of files). The data source is
* access to the shared resources and doing reference counting as required to * passed through one or more pipelines of data source ingest modules. The files
* release those resources correctly. * are passed through one or more pipelines of file ingest modules.
* <p> * <p>
* IMPORTANT: To be discovered at runtime by the ingest framework, * Autopsy may use multiple threads to complete an ingest job, but it is
* IngestModuleFactory implementations must be marked with the following * guaranteed that there will be no more than one module instance per thread.
* NetBeans Service provider annotation: * 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.
* <p>
* 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.
* <p>
* 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) * @ServiceProvider(service=IngestModuleFactory.class)
* <p>
* 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 { public interface IngestModuleFactory {
/** /**
* Gets the display name that identifies the family of ingest modules the * 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. * @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 * 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. * @return The module family description.
*/ */
@ -69,147 +86,154 @@ public interface IngestModuleFactory {
String getModuleVersionNumber(); String getModuleVersionNumber();
/** /**
* Queries the factory to determine if it provides user interface panels to * Queries the factory to determine if it provides a user interface panel to
* configure resources to be used by instances of the family of ingest * allow a user to change settings that are used by all instances of the
* modules the factory creates. For example, the core hash lookup ingest * family of ingest modules the factory creates. For example, the Autopsy
* module factory provides resource configuration panels to import and * core hash lookup ingest module factory provides a global settings panel
* create hash databases. The hash databases are then enabled or disabled * to import and create hash databases. The hash databases are then enabled
* per ingest job using ingest job options panels. If the module family does * or disabled per ingest job using an ingest job settings panel. If the
* not have a resources configuration, the factory should extend * module family does not have global settings, the factory may extend
* IngestModuleFactoryAdapter to get an implementation of this method that * IngestModuleFactoryAdapter to get an implementation of this method that
* returns false. * returns false.
* *
* @return True if the factory provides resource configuration panels. * @return True if the factory provides a global settings panel.
*/ */
boolean hasGlobalSettingsPanel(); boolean hasGlobalSettingsPanel();
/** /**
* Gets a user interface panel that can be used to configure resources for * Gets a user interface panel that allows a user to change settings that
* instances of the family of ingest modules the factory creates. For * are used by all instances of the family of ingest modules the factory
* example, the core hash lookup ingest module factory provides a resource * creates. For example, the Autopsy core hash lookup ingest module factory
* configuration panel to import and create hash databases. The imported * provides a global settings panel to import and create hash databases. The
* hash databases are then enabled or disabled per ingest job using ingest * imported hash databases are then enabled or disabled per ingest job using
* options panels. If the module family does not have a resources * ingest an ingest job settings panel. If the module family does not have a
* configuration, the factory should extend IngestModuleFactoryAdapter to * global settings, the factory may extend IngestModuleFactoryAdapter to get
* get an implementation of this method that throws an * an implementation of this method that throws an
* UnsupportedOperationException. * UnsupportedOperationException.
* <p>
* 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 * @return A global settings panel.
* the panel.
* @return A user interface panel for configuring ingest module resources.
*/ */
IngestModuleGlobalSetttingsPanel getGlobalSettingsPanel(); IngestModuleGlobalSetttingsPanel getGlobalSettingsPanel();
/** /**
* Gets the default per ingest job options for instances of the family of * Gets the default per ingest job settings for instances of the family of
* ingest modules the factory creates. For example, the core hash lookup * ingest modules the factory creates. For example, the Autopsy core hash
* ingest modules family has a resources configuration consisting of hash * lookup ingest modules family uses hash databases imported or created
* databases, all of which are enabled by default for an ingest job. If the * using its global settings panel. All of the hash databases are enabled by
* module family does not have per ingest job options, the factory should * default for an ingest job. If the module family does not have per ingest
* extend IngestModuleFactoryAdapter to get an implementation of this method * job settings, the factory may extend IngestModuleFactoryAdapter to get an
* that returns an instance of the NoIngestJobOptions class. * 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 * Queries the factory to determine if it provides user a interface panel to
* set per ingest job options for instances of the family of ingest modules * allow a user to make per ingest job settings for instances of the family
* the factory creates. For example, the core hash lookup ingest module * of ingest modules the factory creates. For example, the Autopsy core hash
* factory provides ingest options panels to enable or disable hash * lookup ingest module factory provides an ingest job settings panels to
* databases per ingest job. If the module family does not have per ingest * enable or disable hash databases per ingest job. If the module family
* job options, the factory should extend IngestModuleFactoryAdapter to get * does not have per ingest job settings, the factory may extend
* an implementation of this method that returns false. * 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 * 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 * creates. For example, the core hash lookup ingest module factory provides
* ingest options panels to enable or disable hash databases per ingest job. * an ingest job settings panel to enable or disable hash databases per
* If the module family does not have ingest job options, the factory should * ingest job. If the module family does not have per ingest job settings,
* extend IngestModuleFactoryAdapter to get an implementation of this method * the factory may extend IngestModuleFactoryAdapter to get an
* that throws an UnsupportedOperationException. * implementation of this method that throws an
* <p> * 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.
* *
* @param resourcesConfig * @param setting Per ingest job settings to initialize the panel.
* @param ingestOptions Per ingest job options to initialize the panel. * @return An ingest job settings panel.
* @return A user interface 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 * Queries the factory to determine if it is capable of creating data source
* modules. * 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(); boolean isDataSourceIngestModuleFactory();
/** /**
* Creates a data source ingest module instance. * Creates a data source ingest module instance.
* <p> * <p>
* IMPORTANT: The factory should be stateless to support context-sensitive * Autopsy will generally use the factory to several instances of each type
* use of the factory. The ingest framework is responsible for managing * of module for each ingest job it performs. Completing an ingest job
* context switching and the persistence of resource configurations and per * entails processing a single data source (e.g., a disk image) and all of
* ingest job options. A factory should not retain references to the data * the files from the data source, including files extracted from archives
* source ingest module instances it creates. * 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.
* <p> * <p>
* IMPORTANT: The ingest framework will create one or more data source * The ingest framework may use multiple threads to complete an ingest job,
* ingest module instances for each ingest job it performs. The ingest * but it is guaranteed that there will be no more than one module instance
* framework may use multiple threads to complete an ingest job, but it is * per thread. However, if the module instances must share resources, the
* guaranteed that there will be no more than one module instance per * modules are responsible for synchronizing access to the shared resources
* thread. However, if these instances must share resources, the modules are * and doing reference counting as required to release those resources
* responsible for synchronizing access to the shared resources and doing * correctly. Also, more than one ingest job may be in progress at any given
* reference counting as required to release those resources correctly. * time. This must also be taken into consideration when sharing resources
* between module instances. modules.
* <p>
* 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. * @return A data source ingest module instance.
*/ */
DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings); DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings);
/** /**
* Queries the factory to determine if it is capable of creating file ingest * 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(); boolean isFileIngestModuleFactory();
/** /**
* Creates a file ingest module instance. * Creates a file ingest module instance.
* <p> * <p>
* IMPORTANT: The factory should be stateless to support context-sensitive * Autopsy will generally use the factory to several instances of each type
* use of the factory. The ingest framework is responsible for managing * of module for each ingest job it performs. Completing an ingest job
* context switching and the persistence of resource configurations and per * entails processing a single data source (e.g., a disk image) and all of
* ingest job options. A factory should not retain references to the file * the files from the data source, including files extracted from archives
* ingest module instances it creates. * 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.
* <p> * <p>
* IMPORTANT: The ingest framework will create one or more file ingest * The ingest framework may use multiple threads to complete an ingest job,
* module instances for each ingest job it performs. The ingest framework * but it is guaranteed that there will be no more than one module instance
* may use multiple threads to complete an ingest job, but it is guaranteed * per thread. However, if the module instances must share resources, the
* that there will be no more than one module instance per thread. However, * modules are responsible for synchronizing access to the shared resources
* if these instances must share resources, the modules are responsible for * and doing reference counting as required to release those resources
* synchronizing access to the shared resources and doing reference counting * correctly. Also, more than one ingest job may be in progress at any given
* as required to release those resources correctly. * time. This must also be taken into consideration when sharing resources
* between module instances. modules.
* <p>
* 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. * @return A file ingest module instance.
*/ */
FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings); FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings);

View File

@ -44,17 +44,17 @@ public abstract class IngestModuleFactoryAdapter implements IngestModuleFactory
} }
@Override @Override
public IngestModuleIngestJobSettings getDefaultModuleSettings() { public IngestModuleIngestJobSettings getDefaultIngestJobSettings() {
return new NoIngestModuleIngestJobSettings(); return new NoIngestModuleIngestJobSettings();
} }
@Override @Override
public boolean hasModuleSettingsPanel() { public boolean hasIngestJobSettingsPanel() {
return false; return false;
} }
@Override @Override
public IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings ingestOptions) { public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings ingestOptions) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.ingest;
import javax.swing.JPanel; 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 { public abstract class IngestModuleGlobalSetttingsPanel extends JPanel {

View File

@ -50,11 +50,11 @@ final class IngestModuleTemplate {
} }
boolean hasModuleSettingsPanel() { boolean hasModuleSettingsPanel() {
return moduleFactory.hasModuleSettingsPanel(); return moduleFactory.hasIngestJobSettingsPanel();
} }
IngestModuleIngestJobSettingsPanel getModuleSettingsPanel() { IngestModuleIngestJobSettingsPanel getModuleSettingsPanel() {
return moduleFactory.getModuleSettingsPanel(settings); return moduleFactory.getIngestJobSettingsPanel(settings);
} }
boolean hasGlobalSettingsPanel() { boolean hasGlobalSettingsPanel() {

View File

@ -88,7 +88,4 @@ public class ModuleDataEvent extends ChangeEvent {
public String getModuleName() { public String getModuleName() {
return moduleName; return moduleName;
} }
} }

View File

@ -57,17 +57,17 @@ public class FileExtMismatchDetectorModuleFactory extends IngestModuleFactoryAda
} }
@Override @Override
public IngestModuleIngestJobSettings getDefaultModuleSettings() { public IngestModuleIngestJobSettings getDefaultIngestJobSettings() {
return new FileExtMismatchDetectorModuleSettings(); return new FileExtMismatchDetectorModuleSettings();
} }
@Override @Override
public boolean hasModuleSettingsPanel() { public boolean hasIngestJobSettingsPanel() {
return true; return true;
} }
@Override @Override
public IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings settings) { public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
assert settings instanceof FileExtMismatchDetectorModuleSettings; assert settings instanceof FileExtMismatchDetectorModuleSettings;
if (!(settings instanceof FileExtMismatchDetectorModuleSettings)) { if (!(settings instanceof FileExtMismatchDetectorModuleSettings)) {
throw new IllegalArgumentException("Expected settings argument to be instanceof FileExtMismatchDetectorModuleSettings"); throw new IllegalArgumentException("Expected settings argument to be instanceof FileExtMismatchDetectorModuleSettings");

View File

@ -56,17 +56,17 @@ public class FileTypeIdModuleFactory extends IngestModuleFactoryAdapter {
} }
@Override @Override
public IngestModuleIngestJobSettings getDefaultModuleSettings() { public IngestModuleIngestJobSettings getDefaultIngestJobSettings() {
return new FileTypeIdModuleSettings(); return new FileTypeIdModuleSettings();
} }
@Override @Override
public boolean hasModuleSettingsPanel() { public boolean hasIngestJobSettingsPanel() {
return true; return true;
} }
@Override @Override
public IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings settings) { public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
assert settings instanceof FileTypeIdModuleSettings; assert settings instanceof FileTypeIdModuleSettings;
if (!(settings instanceof FileTypeIdModuleSettings)) { if (!(settings instanceof FileTypeIdModuleSettings)) {
throw new IllegalArgumentException("Expected settings argument to be instanceof FileTypeIdModuleSettings"); throw new IllegalArgumentException("Expected settings argument to be instanceof FileTypeIdModuleSettings");

View File

@ -58,7 +58,7 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter {
} }
@Override @Override
public IngestModuleIngestJobSettings getDefaultModuleSettings() { public IngestModuleIngestJobSettings getDefaultIngestJobSettings() {
// All available hash sets are enabled by default. // All available hash sets are enabled by default.
HashDbManager hashDbManager = HashDbManager.getInstance(); HashDbManager hashDbManager = HashDbManager.getInstance();
List<String> knownHashSetNames = getHashSetNames(hashDbManager.getKnownFileHashSets()); List<String> knownHashSetNames = getHashSetNames(hashDbManager.getKnownFileHashSets());
@ -75,12 +75,12 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter {
} }
@Override @Override
public boolean hasModuleSettingsPanel() { public boolean hasIngestJobSettingsPanel() {
return true; return true;
} }
@Override @Override
public IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings settings) { public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
if (!(settings instanceof HashLookupModuleSettings)) { if (!(settings instanceof HashLookupModuleSettings)) {
throw new IllegalArgumentException("Expected settings argument to be instanceof HashLookupModuleSettings"); throw new IllegalArgumentException("Expected settings argument to be instanceof HashLookupModuleSettings");
} }

View File

@ -59,7 +59,7 @@ public class KeywordSearchModuleFactory extends IngestModuleFactoryAdapter {
} }
@Override @Override
public IngestModuleIngestJobSettings getDefaultModuleSettings() { public IngestModuleIngestJobSettings getDefaultIngestJobSettings() {
KeywordSearchListsXML listManager = KeywordSearchListsXML.getCurrent(); KeywordSearchListsXML listManager = KeywordSearchListsXML.getCurrent();
List<String> enabledKeywordLists = new ArrayList<>(); List<String> enabledKeywordLists = new ArrayList<>();
List<KeywordList> keywordLists = listManager.getListsL(); List<KeywordList> keywordLists = listManager.getListsL();
@ -71,12 +71,12 @@ public class KeywordSearchModuleFactory extends IngestModuleFactoryAdapter {
} }
@Override @Override
public boolean hasModuleSettingsPanel() { public boolean hasIngestJobSettingsPanel() {
return true; return true;
} }
@Override @Override
public IngestModuleIngestJobSettingsPanel getModuleSettingsPanel(IngestModuleIngestJobSettings settings) { public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
assert settings instanceof KeywordSearchJobSettings; assert settings instanceof KeywordSearchJobSettings;
if (!(settings instanceof KeywordSearchJobSettings)) { if (!(settings instanceof KeywordSearchJobSettings)) {
throw new IllegalArgumentException("Expected settings argument to be instanceof KeywordSearchJobSettings"); throw new IllegalArgumentException("Expected settings argument to be instanceof KeywordSearchJobSettings");