/*
* Sample module in the public domain. Feel free to use this as a template
* for your modules.
*
* 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 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.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestMessage;
import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
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;
/**
* 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 data.
*/
class SampleFileIngestModule implements FileIngestModule {
private static final HashMap<Long, Long> artifactCountsForIngestJobs = new HashMap<>();
private static BlackboardAttribute.ATTRIBUTE_TYPE attrType = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT;
private final boolean skipKnownFiles;
private IngestJobContext context = null;
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
SampleFileIngestModule(SampleModuleIngestJobSettings settings) {
this.skipKnownFiles = settings.skipKnownFiles();
}
@Override
public void startUp(IngestJobContext context) throws IngestModuleException {
this.context = context;
refCounter.incrementAndGet(context.getJobId());
}
@Override
public IngestModule.ProcessResult process(AbstractFile file) {
// 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)
|| (file.isFile() == false)) {
return IngestModule.ProcessResult.OK;
}
// Skip NSRL / known files.
if (skipKnownFiles && file.getKnown() == TskData.FileKnown.KNOWN) {
return IngestModule.ProcessResult.OK;
}
// 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 = file.read(buffer, 0, 1024);
int count = 0;
for (int i = 0; i < len; i++) {
if (buffer[i] == 0x00) {
count++;
}
}
// Make an attribute using the ID for the attribute attrType that
// was previously created.
BlackboardAttribute attr = new BlackboardAttribute(attrType, SampleIngestModuleFactory.getModuleName(), count);
// 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);
// This method is thread-safe with per ingest job reference counted
// management of shared data.
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) {
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;
}
}
@Override
public void shutDown() {
// This method is thread-safe with per ingest job reference counted
// management of shared data.
reportBlackboardPostCount(context.getJobId());
}
synchronized static void addToBlackboardPostCount(long ingestJobId, long countToAdd) {
Long fileCount = artifactCountsForIngestJobs.get(ingestJobId);
// Ensures that this job has an entry
if (fileCount == null) {
fileCount = 0L;
artifactCountsForIngestJobs.put(ingestJobId, fileCount);
}
fileCount += countToAdd;
artifactCountsForIngestJobs.put(ingestJobId, fileCount);
}
synchronized static void reportBlackboardPostCount(long ingestJobId) {
Long refCount = refCounter.decrementAndGet(ingestJobId);
if (refCount == 0) {
Long filesCount = artifactCountsForIngestJobs.remove(ingestJobId);
String msgText = String.format("Posted %d times to the blackboard", filesCount);
IngestMessage message = IngestMessage.createMessage(
IngestMessage.MessageType.INFO,
SampleIngestModuleFactory.getModuleName(),
msgText);
IngestServices.getInstance().postMessage(message);
}
}
}