mirror of
https://github.com/elisspace/autopsy.git
synced 2026-10-02 23:39:50 +00:00
Merge branch 'develop' into 3191-AddRunningIngestToIngestFileFilterTest
This commit is contained in:
+5
-6
@@ -6,7 +6,8 @@
|
||||
<description>Builds, tests, and runs the project org.sleuthkit.autopsy.core</description>
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
<import file="../BootstrapIvy.xml"/>
|
||||
|
||||
<import file="../TSKVersion.xml"/>
|
||||
|
||||
|
||||
<property name="thirdparty.dir" value="${basedir}/../thirdparty" />
|
||||
<property name="modules.dir" value="${basedir}/release/modules/" />
|
||||
@@ -18,9 +19,7 @@
|
||||
<fileset dir="{basedir}/../../InternalPythonModules"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<property name="VERSION" value="4.6.0"/>
|
||||
|
||||
|
||||
<target name="get-thirdparty-dependencies" description="get third-party dependencies">
|
||||
<!--Copy openCV dependencies to release-->
|
||||
<copy todir="${modules.dir}" >
|
||||
@@ -58,8 +57,8 @@
|
||||
|
||||
<target name="getTSKJars" depends="findTSK">
|
||||
<property environment="env"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/dist/sleuthkit-postgresql-${VERSION}.jar"
|
||||
tofile="${ext.dir}/sleuthkit-postgresql-${VERSION}.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/dist/sleuthkit-postgresql-${TSK_VERSION}.jar"
|
||||
tofile="${ext.dir}/sleuthkit-postgresql-${TSK_VERSION}.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/sqlite-jdbc-3.8.11.jar"
|
||||
tofile="${ext.dir}/sqlite-jdbc-3.8.11.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/postgresql-9.4.1211.jre7.jar"
|
||||
|
||||
+40
-18
@@ -57,7 +57,8 @@ public class IngestEventsListener {
|
||||
private static final Logger LOGGER = Logger.getLogger(CorrelationAttribute.class.getName());
|
||||
|
||||
final Collection<String> recentlyAddedCeArtifacts = new LinkedHashSet<>();
|
||||
private static int ceModuleInstanceCount = 0;
|
||||
private static int correlationModuleInstanceCount;
|
||||
private static boolean flagNotableItems;
|
||||
private final ExecutorService jobProcessingExecutor;
|
||||
private static final String INGEST_EVENT_THREAD_NAME = "Ingest-Event-Listener-%d";
|
||||
private final PropertyChangeListener pcl1 = new IngestModuleEventListener();
|
||||
@@ -88,21 +89,20 @@ public class IngestEventsListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable this IngestEventsListener to add contents to the Correlation
|
||||
* Engine.
|
||||
*
|
||||
* Increase the number of IngestEventsListeners adding contents to the
|
||||
* Correlation Engine.
|
||||
*/
|
||||
public synchronized static void incrementCorrelationEngineModuleCount() {
|
||||
ceModuleInstanceCount++; //Should be called once in the Correlation Engine module's startup method.
|
||||
correlationModuleInstanceCount++; //Should be called once in the Correlation Engine module's startup method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable this IngestEventsListener from adding contents to the Correlation
|
||||
* Engine.
|
||||
* Decrease the number of IngestEventsListeners adding contents to the
|
||||
* Correlation Engine.
|
||||
*/
|
||||
public synchronized static void decrementCorrelationEngineModuleCount() {
|
||||
if (getCeModuleInstanceCount() > 0) { //prevent it ingestJobCounter from going negative
|
||||
ceModuleInstanceCount--; //Should be called once in the Correlation Engine module's shutdown method.
|
||||
correlationModuleInstanceCount--; //Should be called once in the Correlation Engine module's shutdown method.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,17 +111,35 @@ public class IngestEventsListener {
|
||||
* is being run during injest to 0.
|
||||
*/
|
||||
synchronized static void resetCeModuleInstanceCount() {
|
||||
ceModuleInstanceCount = 0; //called when a case is opened in case for some reason counter was not reset
|
||||
correlationModuleInstanceCount = 0; //called when a case is opened in case for some reason counter was not reset
|
||||
}
|
||||
|
||||
/**
|
||||
* Wether or not the Correlation Engine Module is enabled for any of the
|
||||
* Whether or not the Correlation Engine Module is enabled for any of the
|
||||
* currently running ingest jobs.
|
||||
*
|
||||
* @return boolean True for Correlation Engine enabled, False for disabled
|
||||
*/
|
||||
private synchronized static int getCeModuleInstanceCount() {
|
||||
return ceModuleInstanceCount;
|
||||
public synchronized static int getCeModuleInstanceCount() {
|
||||
return correlationModuleInstanceCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are notable items being flagged?
|
||||
*
|
||||
* @return True if flagging notable items; otherwise false.
|
||||
*/
|
||||
public synchronized static boolean isFlagNotableItems() {
|
||||
return flagNotableItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the listener to flag notable items or not.
|
||||
*
|
||||
* @param value True to flag notable items; otherwise false.
|
||||
*/
|
||||
public synchronized static void setFlagNotableItems(boolean value) {
|
||||
flagNotableItems = value;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
|
||||
@@ -174,7 +192,7 @@ public class IngestEventsListener {
|
||||
}
|
||||
switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) {
|
||||
case DATA_ADDED: {
|
||||
jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt));
|
||||
jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, isFlagNotableItems()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -212,10 +230,12 @@ public class IngestEventsListener {
|
||||
|
||||
private final EamDb dbManager;
|
||||
private final PropertyChangeEvent event;
|
||||
private final boolean flagNotableItemsEnabled;
|
||||
|
||||
private DataAddedTask(EamDb db, PropertyChangeEvent evt) {
|
||||
private DataAddedTask(EamDb db, PropertyChangeEvent evt, boolean flagNotableItemsEnabled) {
|
||||
dbManager = db;
|
||||
event = evt;
|
||||
this.flagNotableItemsEnabled = flagNotableItemsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -241,10 +261,12 @@ public class IngestEventsListener {
|
||||
// query db for artifact instances having this TYPE/VALUE and knownStatus = "Bad".
|
||||
// if gettKnownStatus() is "Unknown" and this artifact instance was marked bad in a previous case,
|
||||
// create TSK_INTERESTING_ARTIFACT_HIT artifact on BB.
|
||||
List<String> caseDisplayNames = dbManager.getListCasesHavingArtifactInstancesKnownBad(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue());
|
||||
if (!caseDisplayNames.isEmpty()) {
|
||||
postCorrelatedBadArtifactToBlackboard(bbArtifact,
|
||||
caseDisplayNames);
|
||||
if (flagNotableItemsEnabled) {
|
||||
List<String> caseDisplayNames = dbManager.getListCasesHavingArtifactInstancesKnownBad(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue());
|
||||
if (!caseDisplayNames.isEmpty()) {
|
||||
postCorrelatedBadArtifactToBlackboard(bbArtifact,
|
||||
caseDisplayNames);
|
||||
}
|
||||
}
|
||||
eamArtifacts.add(eamArtifact);
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
IngestSettingsPanel.ingestSettingsLabel.text=Ingest Settings
|
||||
IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable
|
||||
@@ -56,9 +56,11 @@ import org.sleuthkit.autopsy.centralrepository.eventlisteners.IngestEventsListen
|
||||
*/
|
||||
@Messages({"IngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
|
||||
"IngestModule.prevCaseComment.text=Previous Case: "})
|
||||
class IngestModule implements FileIngestModule {
|
||||
final class IngestModule implements FileIngestModule {
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(IngestModule.class.getName());
|
||||
static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = true;
|
||||
|
||||
private final static Logger logger = Logger.getLogger(IngestModule.class.getName());
|
||||
private final IngestServices services = IngestServices.getInstance();
|
||||
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
||||
private static final IngestModuleReferenceCounter warningMsgRefCounter = new IngestModuleReferenceCounter();
|
||||
@@ -68,8 +70,19 @@ class IngestModule implements FileIngestModule {
|
||||
private Blackboard blackboard;
|
||||
private CorrelationAttribute.Type filesType;
|
||||
|
||||
private final boolean flagTaggedNotableItems;
|
||||
|
||||
/**
|
||||
* Instantiate the Correlation Engine ingest module.
|
||||
*
|
||||
* @param settings The ingest settings for the module instance.
|
||||
*/
|
||||
IngestModule(IngestSettings settings) {
|
||||
flagTaggedNotableItems = settings.isFlagTaggedNotableItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessResult process(AbstractFile af) {
|
||||
public ProcessResult process(AbstractFile abstractFile) {
|
||||
if (EamDb.isEnabled() == false) {
|
||||
/*
|
||||
* Not signaling an error for now. This is a workaround for the way
|
||||
@@ -83,11 +96,11 @@ class IngestModule implements FileIngestModule {
|
||||
try {
|
||||
blackboard = Case.getOpenCase().getServices().getBlackboard();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
if (!EamArtifactUtil.isValidCentralRepoFile(af)) {
|
||||
if (!EamArtifactUtil.isValidCentralRepoFile(abstractFile)) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
@@ -95,7 +108,7 @@ class IngestModule implements FileIngestModule {
|
||||
try {
|
||||
dbManager = EamDb.getInstance();
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
|
||||
logger.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
@@ -105,21 +118,23 @@ class IngestModule implements FileIngestModule {
|
||||
}
|
||||
|
||||
// get the hash because we're going to correlate it
|
||||
String md5 = af.getMd5Hash();
|
||||
String md5 = abstractFile.getMd5Hash();
|
||||
if ((md5 == null) || (HashUtility.isNoDataMd5(md5))) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
/* Search the central repo to see if this file was previously
|
||||
* marked as being bad. Create artifact if it was. */
|
||||
if (af.getKnown() != TskData.FileKnown.KNOWN) {
|
||||
/*
|
||||
* Search the central repo to see if this file was previously marked as
|
||||
* being bad. Create artifact if it was.
|
||||
*/
|
||||
if (abstractFile.getKnown() != TskData.FileKnown.KNOWN && flagTaggedNotableItems) {
|
||||
try {
|
||||
List<String> caseDisplayNames = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5);
|
||||
if (!caseDisplayNames.isEmpty()) {
|
||||
postCorrelatedBadFileToBlackboard(af, caseDisplayNames);
|
||||
List<String> caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5);
|
||||
if (!caseDisplayNamesList.isEmpty()) {
|
||||
postCorrelatedBadFileToBlackboard(abstractFile, caseDisplayNamesList);
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error searching database for artifact.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Error searching database for artifact.", ex); // NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
}
|
||||
@@ -130,14 +145,14 @@ class IngestModule implements FileIngestModule {
|
||||
CorrelationAttributeInstance cefi = new CorrelationAttributeInstance(
|
||||
eamCase,
|
||||
eamDataSource,
|
||||
af.getParentPath() + af.getName(),
|
||||
abstractFile.getParentPath() + abstractFile.getName(),
|
||||
null,
|
||||
TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database.
|
||||
TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database.
|
||||
);
|
||||
eamArtifact.addInstance(cefi);
|
||||
dbManager.prepareBulkArtifact(eamArtifact);
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
@@ -147,6 +162,7 @@ class IngestModule implements FileIngestModule {
|
||||
@Override
|
||||
public void shutDown() {
|
||||
IngestEventsListener.decrementCorrelationEngineModuleCount();
|
||||
|
||||
if ((EamDb.isEnabled() == false) || (eamCase == null) || (eamDataSource == null)) {
|
||||
return;
|
||||
}
|
||||
@@ -154,19 +170,19 @@ class IngestModule implements FileIngestModule {
|
||||
try {
|
||||
dbManager = EamDb.getInstance();
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
|
||||
logger.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dbManager.bulkInsertArtifacts();
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS
|
||||
}
|
||||
try {
|
||||
Long count = dbManager.getCountArtifactInstancesByCaseDataSource(eamCase.getCaseUUID(), eamDataSource.getDeviceID());
|
||||
LOGGER.log(Level.INFO, "{0} artifacts in db for case: {1} ds:{2}", new Object[]{count, eamCase.getDisplayName(), eamDataSource.getName()}); // NON-NLS
|
||||
logger.log(Level.INFO, "{0} artifacts in db for case: {1} ds:{2}", new Object[]{count, eamCase.getDisplayName(), eamDataSource.getName()}); // NON-NLS
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error counting artifacts.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Error counting artifacts.", ex); // NON-NLS
|
||||
}
|
||||
|
||||
// TODO: once we implement shared cache, if refCounter is 1, then submit data in bulk.
|
||||
@@ -181,6 +197,25 @@ class IngestModule implements FileIngestModule {
|
||||
@Override
|
||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||
IngestEventsListener.incrementCorrelationEngineModuleCount();
|
||||
|
||||
/*
|
||||
* Tell the IngestEventsListener to flag notable items based on the
|
||||
* current module's configuration. This is a work around for the lack of
|
||||
* an artifacts pipeline. Note that this can be changed by another
|
||||
* module instance. All modules are affected by the value. While not
|
||||
* ideal, this will be good enough until a better solution can be
|
||||
* posited.
|
||||
*
|
||||
* Note: Flagging cannot be disabled if any other instances of the
|
||||
* Correlation Engine module are running. This restriction is to prevent
|
||||
* missing results in the case where the first module is flagging
|
||||
* notable items, and the proceeding module (with flagging disabled)
|
||||
* causes the first to stop flagging.
|
||||
*/
|
||||
if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.isFlagNotableItems()) {
|
||||
IngestEventsListener.setFlagNotableItems(flagTaggedNotableItems);
|
||||
}
|
||||
|
||||
if (EamDb.isEnabled() == false) {
|
||||
/*
|
||||
* Not throwing the customary exception for now. This is a
|
||||
@@ -200,14 +235,14 @@ class IngestModule implements FileIngestModule {
|
||||
try {
|
||||
autopsyCase = Case.getOpenCase();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
throw new IngestModuleException("Exception while getting open case.", ex);
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
throw new IngestModuleException("Exception while getting open case.", ex);
|
||||
}
|
||||
|
||||
|
||||
// Don't allow sqlite central repo databases to be used for multi user cases
|
||||
if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
|
||||
&& (EamDbPlatformEnum.getSelectedPlatform() == EamDbPlatformEnum.SQLITE)) {
|
||||
LOGGER.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository.");
|
||||
logger.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository.");
|
||||
throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS
|
||||
}
|
||||
jobId = context.getJobId();
|
||||
@@ -216,14 +251,14 @@ class IngestModule implements FileIngestModule {
|
||||
try {
|
||||
centralRepoDb = EamDb.getInstance();
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error connecting to central repository database.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Error connecting to central repository database.", ex); // NON-NLS
|
||||
throw new IngestModuleException("Error connecting to central repository database.", ex); // NON-NLS
|
||||
}
|
||||
|
||||
try {
|
||||
filesType = centralRepoDb.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
|
||||
throw new IngestModuleException("Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
|
||||
}
|
||||
|
||||
@@ -237,15 +272,15 @@ class IngestModule implements FileIngestModule {
|
||||
try {
|
||||
eamCase = centralRepoDb.newCase(autopsyCase);
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error creating new case in ingest module start up.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Error creating new case in ingest module start up.", ex); // NON-NLS
|
||||
throw new IngestModuleException("Error creating new case in ingest module start up.", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
eamDataSource = CorrelationDataSource.fromTSKDataSource(eamCase, context.getDataSource());
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error getting data source info.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Error getting data source info.", ex); // NON-NLS
|
||||
throw new IngestModuleException("Error getting data source info.", ex); // NON-NLS
|
||||
}
|
||||
// TODO: once we implement a shared cache, load/init it here w/ syncronized and define reference counter
|
||||
@@ -259,7 +294,7 @@ class IngestModule implements FileIngestModule {
|
||||
centralRepoDb.newDataSource(eamDataSource);
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding data source to Central Repository.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Error adding data source to Central Repository.", ex); // NON-NLS
|
||||
throw new IngestModuleException("Error adding data source to Central Repository.", ex); // NON-NLS
|
||||
}
|
||||
|
||||
@@ -282,7 +317,7 @@ class IngestModule implements FileIngestModule {
|
||||
// index the artifact for keyword search
|
||||
blackboard.indexArtifact(tifArtifact);
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS
|
||||
}
|
||||
|
||||
// send inbox message
|
||||
@@ -291,9 +326,9 @@ class IngestModule implements FileIngestModule {
|
||||
// fire event to notify UI of this new artifact
|
||||
services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT));
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS
|
||||
} catch (IllegalStateException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS
|
||||
logger.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
-6
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-2017 Basis Technology Corp.
|
||||
* Copyright 2015-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -25,6 +25,9 @@ import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
||||
import org.sleuthkit.autopsy.centralrepository.optionspanel.GlobalSettingsPanel;
|
||||
import org.sleuthkit.autopsy.coreutils.Version;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
|
||||
import org.sleuthkit.autopsy.ingest.NoIngestModuleIngestJobSettings;
|
||||
|
||||
/**
|
||||
* Factory for Central Repository ingest modules
|
||||
@@ -34,8 +37,11 @@ import org.sleuthkit.autopsy.centralrepository.optionspanel.GlobalSettingsPanel;
|
||||
"IngestModuleFactory.ingestmodule.desc=Saves properties to the central repository for later correlation"})
|
||||
public class IngestModuleFactory extends IngestModuleFactoryAdapter {
|
||||
|
||||
private static final String VERSION_NUMBER = "0.8.0";
|
||||
|
||||
/**
|
||||
* Get the name of the module.
|
||||
*
|
||||
* @return The module name.
|
||||
*/
|
||||
static String getModuleName() {
|
||||
return Bundle.IngestModuleFactory_ingestmodule_name();
|
||||
}
|
||||
@@ -52,7 +58,7 @@ public class IngestModuleFactory extends IngestModuleFactoryAdapter {
|
||||
|
||||
@Override
|
||||
public String getModuleVersionNumber() {
|
||||
return VERSION_NUMBER;
|
||||
return Version.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,8 +67,8 @@ public class IngestModuleFactory extends IngestModuleFactoryAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings ingestOptions) {
|
||||
return new IngestModule();
|
||||
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) {
|
||||
return new IngestModule((IngestSettings) settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,5 +82,30 @@ public class IngestModuleFactory extends IngestModuleFactoryAdapter {
|
||||
globalOptionsPanel.load();
|
||||
return globalOptionsPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IngestModuleIngestJobSettings getDefaultIngestJobSettings() {
|
||||
return new IngestSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIngestJobSettingsPanel() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
|
||||
if (settings instanceof IngestSettings) {
|
||||
return new IngestSettingsPanel((IngestSettings) settings);
|
||||
}
|
||||
/*
|
||||
* Compatibility check for older versions.
|
||||
*/
|
||||
if (settings instanceof NoIngestModuleIngestJobSettings) {
|
||||
return new IngestSettingsPanel(new IngestSettings());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected settings argument to be an instance of IngestSettings");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.centralrepository.ingestmodule;
|
||||
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
||||
|
||||
/**
|
||||
* Ingest job settings for the Correlation Engine module.
|
||||
*/
|
||||
final class IngestSettings implements IngestModuleIngestJobSettings {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private boolean flagTaggedNotableItems;
|
||||
|
||||
/**
|
||||
* Instantiate the ingest job settings with default values.
|
||||
*/
|
||||
IngestSettings() {
|
||||
this.flagTaggedNotableItems = IngestModule.DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate the ingest job settings.
|
||||
*
|
||||
* @param flagTaggedNotableItems Flag previously tagged notable items.
|
||||
*/
|
||||
IngestSettings(boolean flagTaggedNotableItems) {
|
||||
this.flagTaggedNotableItems = flagTaggedNotableItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVersionNumber() {
|
||||
return serialVersionUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are previously tagged notable items to be flagged?
|
||||
*
|
||||
* @return True if flagging; otherwise false.
|
||||
*/
|
||||
boolean isFlagTaggedNotableItems() {
|
||||
return flagTaggedNotableItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag or ignore previously identified notable items.
|
||||
*
|
||||
* @param ignorePreviousNotableItems Are previously tagged notable items to
|
||||
* be flagged?
|
||||
*/
|
||||
void setFlagTaggedNotableItems(boolean flagTaggedNotableItems) {
|
||||
this.flagTaggedNotableItems = flagTaggedNotableItems;
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" 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"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
|
||||
<Component id="flagTaggedNotableItemsCheckbox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="ingestSettingsLabel" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="65" 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="ingestSettingsLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="flagTaggedNotableItemsCheckbox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="245" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="ingestSettingsLabel">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Tahoma" size="11" style="1"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties" key="IngestSettingsPanel.ingestSettingsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="flagTaggedNotableItemsCheckbox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties" key="IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.centralrepository.ingestmodule;
|
||||
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
|
||||
|
||||
/**
|
||||
* Ingest job settings panel for the Correlation Engine module.
|
||||
*/
|
||||
final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {
|
||||
|
||||
/**
|
||||
* Creates new form IngestSettingsPanel
|
||||
*/
|
||||
public IngestSettingsPanel(IngestSettings settings) {
|
||||
initComponents();
|
||||
customizeComponents(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update components with values from the ingest job settings.
|
||||
*
|
||||
* @param settings The ingest job settings.
|
||||
*/
|
||||
private void customizeComponents(IngestSettings settings) {
|
||||
flagTaggedNotableItemsCheckbox.setSelected(settings.isFlagTaggedNotableItems());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IngestModuleIngestJobSettings getSettings() {
|
||||
return new IngestSettings(flagTaggedNotableItemsCheckbox.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() {
|
||||
|
||||
ingestSettingsLabel = new javax.swing.JLabel();
|
||||
flagTaggedNotableItemsCheckbox = new javax.swing.JCheckBox();
|
||||
|
||||
ingestSettingsLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(ingestSettingsLabel, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.ingestSettingsLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(flagTaggedNotableItemsCheckbox, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.flagTaggedNotableItemsCheckbox.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()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(10, 10, 10)
|
||||
.addComponent(flagTaggedNotableItemsCheckbox))
|
||||
.addComponent(ingestSettingsLabel))
|
||||
.addContainerGap(65, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(ingestSettingsLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(flagTaggedNotableItemsCheckbox)
|
||||
.addContainerGap(245, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JCheckBox flagTaggedNotableItemsCheckbox;
|
||||
private javax.swing.JLabel ingestSettingsLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
||||
+7
-7
@@ -44,7 +44,7 @@
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
|
||||
<Component id="pnSQLiteSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<EmptySpace pref="11" max="32767" attributes="0"/>
|
||||
<Component id="pnButtons" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@@ -133,7 +133,7 @@
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="cbDatabaseType" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="lbSingleUserSqLite" max="32767" attributes="0"/>
|
||||
<Component id="lbSingleUserSqLite" pref="467" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="9" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
@@ -173,29 +173,29 @@
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="lbDatabasePath" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<Component id="lbDatabasePath" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="tfDatabasePath" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<Component id="bnDatabasePathFileOpen" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="tbDbHostname" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbHostName" alignment="3" min="-2" pref="22" max="-2" attributes="0"/>
|
||||
<Component id="lbHostName" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="tbDbPort" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbPort" alignment="3" min="-2" pref="20" max="-2" attributes="0"/>
|
||||
<Component id="lbPort" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="tbDbUsername" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbUserName" alignment="3" min="-2" pref="20" max="-2" attributes="0"/>
|
||||
<Component id="lbUserName" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jpDbPassword" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbUserPassword" min="-2" pref="20" max="-2" attributes="0"/>
|
||||
<Component id="lbUserPassword" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
|
||||
+7
-7
@@ -243,7 +243,7 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
.addGroup(pnSQLiteSettingsLayout.createSequentialGroup()
|
||||
.addComponent(cbDatabaseType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(lbSingleUserSqLite, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(lbSingleUserSqLite, javax.swing.GroupLayout.DEFAULT_SIZE, 467, Short.MAX_VALUE)
|
||||
.addGap(9, 9, 9))
|
||||
.addGroup(pnSQLiteSettingsLayout.createSequentialGroup()
|
||||
.addComponent(tfDatabasePath)
|
||||
@@ -273,25 +273,25 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
.addComponent(lbDatabaseType, javax.swing.GroupLayout.Alignment.TRAILING))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(lbDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(lbDatabasePath)
|
||||
.addComponent(tfDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(bnDatabasePathFileOpen))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(tbDbHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(lbHostName, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(lbHostName))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(tbDbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(lbPort, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(lbPort))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(tbDbUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(lbUserName, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(lbUserName))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jpDbPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(lbUserPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(lbUserPassword))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(lbFullDbPath, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
@@ -317,7 +317,7 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(10, 10, 10)
|
||||
.addComponent(pnSQLiteSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE)
|
||||
.addComponent(pnButtons, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(10, 10, 10))
|
||||
);
|
||||
|
||||
@@ -23,12 +23,14 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.Utilities;
|
||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
@@ -41,6 +43,7 @@ import org.sleuthkit.datamodel.LocalFile;
|
||||
import org.sleuthkit.datamodel.LocalDirectory;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
import org.sleuthkit.datamodel.Volume;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? extends Action>> {
|
||||
|
||||
@@ -71,8 +74,12 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
|
||||
public List<? extends Action> visit(final Image img) {
|
||||
List<Action> lst = new ArrayList<>();
|
||||
//TODO lst.add(new ExtractAction("Extract Image", img));
|
||||
lst.add(new ExtractUnallocAction(
|
||||
try {
|
||||
lst.add(new ExtractUnallocAction(
|
||||
NbBundle.getMessage(this.getClass(), "ExplorerNodeActionVisitor.action.extUnallocToSingleFiles"), img));
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
Logger.getLogger(ExplorerNodeActionVisitor.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
|
||||
@@ -85,7 +92,8 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
|
||||
public List<? extends Action> visit(final Volume vol) {
|
||||
List<AbstractAction> lst = new ArrayList<>();
|
||||
lst.add(new ExtractUnallocAction(
|
||||
NbBundle.getMessage(this.getClass(), "ExplorerNodeActionVisitor.action.extUnallocToSingleFile"), vol));
|
||||
NbBundle.getMessage(this.getClass(), "ExplorerNodeActionVisitor.action.extUnallocToSingleFile"), vol));
|
||||
|
||||
return lst;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,14 +69,20 @@ final class ExtractUnallocAction extends AbstractAction {
|
||||
private long currentImage = 0L;
|
||||
private final boolean isImage;
|
||||
|
||||
public ExtractUnallocAction(String title, Volume volume) {
|
||||
public ExtractUnallocAction(String title, Volume volume){
|
||||
super(title);
|
||||
isImage = false;
|
||||
OutputFileData outputFileData = new OutputFileData(volume);
|
||||
filesToExtract.add(outputFileData);
|
||||
try {
|
||||
OutputFileData outputFileData = new OutputFileData(volume);
|
||||
filesToExtract.add(outputFileData);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ExtractUnallocAction(String title, Image image) {
|
||||
public ExtractUnallocAction(String title, Image image) throws NoCurrentCaseException {
|
||||
super(title);
|
||||
isImage = true;
|
||||
currentImage = image.getId();
|
||||
@@ -595,15 +601,17 @@ final class ExtractUnallocAction extends AbstractAction {
|
||||
* Contingency constructor in event no VolumeSystem exists on an Image.
|
||||
*
|
||||
* @param img Image file to be analyzed
|
||||
*
|
||||
* @throws NoCurrentCaseException if there is no open case.
|
||||
*/
|
||||
OutputFileData(Image img) {
|
||||
OutputFileData(Image img) throws NoCurrentCaseException {
|
||||
this.layoutFiles = getUnallocFiles(img);
|
||||
Collections.sort(layoutFiles, new SortObjId());
|
||||
this.volumeId = 0;
|
||||
this.imageId = img.getId();
|
||||
this.imageName = img.getName();
|
||||
this.fileName = this.imageName + "-Unalloc-" + this.imageId + "-" + 0 + ".dat"; //NON-NLS
|
||||
this.fileInstance = new File(Case.getCurrentCase().getExportDirectory() + File.separator + this.fileName);
|
||||
this.fileInstance = new File(Case.getOpenCase().getExportDirectory() + File.separator + this.fileName);
|
||||
this.sizeInBytes = calcSizeInBytes();
|
||||
}
|
||||
|
||||
@@ -611,8 +619,10 @@ final class ExtractUnallocAction extends AbstractAction {
|
||||
* Default constructor for extracting info from Volumes.
|
||||
*
|
||||
* @param volume Volume file to be analyzed
|
||||
*
|
||||
* @throws NoCurrentCaseException if there is no open case.
|
||||
*/
|
||||
OutputFileData(Volume volume) {
|
||||
OutputFileData(Volume volume) throws NoCurrentCaseException {
|
||||
try {
|
||||
this.imageName = volume.getDataSource().getName();
|
||||
this.imageId = volume.getDataSource().getId();
|
||||
@@ -623,7 +633,7 @@ final class ExtractUnallocAction extends AbstractAction {
|
||||
this.imageId = 0;
|
||||
}
|
||||
this.fileName = this.imageName + "-Unalloc-" + this.imageId + "-" + volumeId + ".dat"; //NON-NLS
|
||||
this.fileInstance = new File(Case.getCurrentCase().getExportDirectory() + File.separator + this.fileName);
|
||||
this.fileInstance = new File(Case.getOpenCase().getExportDirectory() + File.separator + this.fileName);
|
||||
this.layoutFiles = getUnallocFiles(volume);
|
||||
Collections.sort(layoutFiles, new SortObjId());
|
||||
this.sizeInBytes = calcSizeInBytes();
|
||||
|
||||
@@ -518,7 +518,7 @@ final class DataSourceIngestJob {
|
||||
*/
|
||||
if (this.hasFirstStageDataSourceIngestPipeline() && this.hasFileIngestPipeline()) {
|
||||
logger.log(Level.INFO, "Scheduling first stage data source and file level analysis tasks for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS
|
||||
DataSourceIngestJob.taskScheduler.scheduleIngestTasks(this, this.files);
|
||||
DataSourceIngestJob.taskScheduler.scheduleIngestTasks(this);
|
||||
} else if (this.hasFirstStageDataSourceIngestPipeline()) {
|
||||
logger.log(Level.INFO, "Scheduling first stage data source level analysis tasks for {0} (jobId={1}), no file level analysis configured", new Object[]{dataSource.getName(), this.id}); //NON-NLS
|
||||
DataSourceIngestJob.taskScheduler.scheduleDataSourceIngestTask(this);
|
||||
@@ -827,7 +827,7 @@ final class DataSourceIngestJob {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds more files from the data source for this job to the job, i.e., adds
|
||||
* Adds more files from the data source for this job to the job, e.g., adds
|
||||
* extracted or carved files. Not currently supported for the second stage
|
||||
* of the job.
|
||||
*
|
||||
@@ -835,9 +835,7 @@ final class DataSourceIngestJob {
|
||||
*/
|
||||
void addFiles(List<AbstractFile> files) {
|
||||
if (DataSourceIngestJob.Stages.FIRST == this.stage) {
|
||||
for (AbstractFile file : files) {
|
||||
DataSourceIngestJob.taskScheduler.scheduleFastTrackedFileIngestTask(this, file);
|
||||
}
|
||||
DataSourceIngestJob.taskScheduler.scheduleFileIngestTasks(this, files);
|
||||
} else {
|
||||
DataSourceIngestJob.logger.log(Level.SEVERE, "Adding files during second stage not supported"); //NON-NLS
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ public class IngestManager {
|
||||
private final AtomicLong nextIngestManagerTaskId = new AtomicLong(0L);
|
||||
private final ExecutorService startIngestJobsExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-start-ingest-jobs-%d").build()); //NON-NLS;
|
||||
private final Map<Long, Future<Void>> startIngestJobFutures = new ConcurrentHashMap<>();
|
||||
private final Map<Long, IngestJob> ingestJobsById = new ConcurrentHashMap<>();
|
||||
private final Map<Long, IngestJob> ingestJobsById = new HashMap<>();
|
||||
private final ExecutorService dataSourceLevelIngestJobTasksExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-data-source-ingest-%d").build()); //NON-NLS;
|
||||
private final ExecutorService fileLevelIngestJobTasksExecutor;
|
||||
private final ExecutorService eventPublishingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-ingest-events-%d").build()); //NON-NLS;
|
||||
@@ -399,13 +399,17 @@ public class IngestManager {
|
||||
ingestMonitor.start();
|
||||
}
|
||||
|
||||
ingestJobsById.put(job.getId(), job);
|
||||
synchronized (ingestJobsById) {
|
||||
ingestJobsById.put(job.getId(), job);
|
||||
}
|
||||
errors = job.start();
|
||||
if (errors.isEmpty()) {
|
||||
this.fireIngestJobStarted(job.getId());
|
||||
IngestManager.logger.log(Level.INFO, "Ingest job {0} started", job.getId()); //NON-NLS
|
||||
} else {
|
||||
this.ingestJobsById.remove(job.getId());
|
||||
synchronized (ingestJobsById) {
|
||||
this.ingestJobsById.remove(job.getId());
|
||||
}
|
||||
for (IngestModuleError error : errors) {
|
||||
logger.log(Level.SEVERE, String.format("Error starting %s ingest module for job %d", error.getModuleDisplayName(), job.getId()), error.getThrowable()); //NON-NLS
|
||||
}
|
||||
@@ -438,7 +442,9 @@ public class IngestManager {
|
||||
*/
|
||||
void finishIngestJob(IngestJob job) {
|
||||
long jobId = job.getId();
|
||||
ingestJobsById.remove(jobId);
|
||||
synchronized (ingestJobsById) {
|
||||
ingestJobsById.remove(jobId);
|
||||
}
|
||||
if (!job.isCancelled()) {
|
||||
IngestManager.logger.log(Level.INFO, "Ingest job {0} completed", jobId); //NON-NLS
|
||||
fireIngestJobCompleted(jobId);
|
||||
@@ -455,7 +461,9 @@ public class IngestManager {
|
||||
* @return True or false.
|
||||
*/
|
||||
public boolean isIngestRunning() {
|
||||
return !ingestJobsById.isEmpty();
|
||||
synchronized (ingestJobsById) {
|
||||
return !ingestJobsById.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -467,9 +475,11 @@ public class IngestManager {
|
||||
startIngestJobFutures.values().forEach((handle) -> {
|
||||
handle.cancel(true);
|
||||
});
|
||||
this.ingestJobsById.values().forEach((job) -> {
|
||||
job.cancel(reason);
|
||||
});
|
||||
synchronized (ingestJobsById) {
|
||||
this.ingestJobsById.values().forEach((job) -> {
|
||||
job.cancel(reason);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -770,9 +780,11 @@ public class IngestManager {
|
||||
*/
|
||||
List<DataSourceIngestJob.Snapshot> getIngestJobSnapshots() {
|
||||
List<DataSourceIngestJob.Snapshot> snapShots = new ArrayList<>();
|
||||
ingestJobsById.values().forEach((job) -> {
|
||||
snapShots.addAll(job.getDataSourceIngestJobSnapshots());
|
||||
});
|
||||
synchronized (ingestJobsById) {
|
||||
ingestJobsById.values().forEach((job) -> {
|
||||
snapShots.addAll(job.getDataSourceIngestJobSnapshots());
|
||||
});
|
||||
}
|
||||
return snapShots;
|
||||
}
|
||||
|
||||
@@ -808,7 +820,9 @@ public class IngestManager {
|
||||
public Void call() {
|
||||
try {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
ingestJobsById.remove(job.getId());
|
||||
synchronized (ingestJobsById) {
|
||||
ingestJobsById.remove(job.getId());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,13 @@ package org.sleuthkit.autopsy.ingest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.BlockingDeque;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.logging.Level;
|
||||
@@ -40,64 +41,20 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* Creates ingest tasks for ingest jobs, queuing the tasks in priority order for
|
||||
* execution by the ingest manager's ingest threads.
|
||||
* Creates ingest tasks for data source ingest jobs, queueing the tasks in
|
||||
* priority order for execution by the ingest manager's ingest threads.
|
||||
*/
|
||||
final class IngestTasksScheduler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(IngestTasksScheduler.class.getName());
|
||||
private static final int FAT_NTFS_FLAGS = TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_FAT12.getValue() | TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_FAT16.getValue() | TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_FAT32.getValue() | TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_NTFS.getValue();
|
||||
private static final Logger logger = Logger.getLogger(IngestTasksScheduler.class.getName());
|
||||
private static IngestTasksScheduler instance;
|
||||
|
||||
/**
|
||||
* Scheduling of data source ingest tasks is accomplished by putting them in
|
||||
* a FIFO queue to be consumed by the ingest threads, so the queue is
|
||||
* wrapped in a "dispenser" that implements the IngestTaskQueue interface
|
||||
* and is exposed via a getter method.
|
||||
*/
|
||||
private final LinkedBlockingQueue<DataSourceIngestTask> pendingDataSourceTasks;
|
||||
private final DataSourceIngestTaskQueue dataSourceTasksDispenser;
|
||||
|
||||
/**
|
||||
* Scheduling of file ingest tasks is accomplished by "shuffling" them
|
||||
* through a sequence of internal queues that allows for the interleaving of
|
||||
* tasks from different ingest jobs based on priority. These scheduling
|
||||
* queues are:
|
||||
*
|
||||
* 1. Root directory tasks (priority queue)
|
||||
*
|
||||
* 2. Directory tasks (FIFO queue)
|
||||
*
|
||||
* 3. Pending file tasks (LIFO queue).
|
||||
*
|
||||
* The pending file tasks queue is LIFO to handle large numbers of files
|
||||
* extracted from archive files. At least one image has been processed that
|
||||
* had a folder full of archive files. The queue grew to have thousands of
|
||||
* entries, as each successive archive file was expanded, so now extracted
|
||||
* files get added to the front of the queue so that in such a scenario they
|
||||
* would be processed before the expansion of the next archive file.
|
||||
*
|
||||
* Tasks in the pending file tasks queue are ready to be consumed by the
|
||||
* ingest threads, so the queue is wrapped in a "dispenser" that implements
|
||||
* the IngestTaskQueue interface and is exposed via a getter method.
|
||||
*/
|
||||
private final TreeSet<FileIngestTask> rootDirectoryTasks;
|
||||
private final List<FileIngestTask> directoryTasks;
|
||||
private final BlockingDeque<FileIngestTask> pendingFileTasks;
|
||||
private final FileIngestTaskQueue fileTasksDispenser;
|
||||
|
||||
/**
|
||||
* The ingest tasks scheduler allows ingest jobs to query it to see if there
|
||||
* are any tasks in progress for the job. To make this possible, the ingest
|
||||
* tasks scheduler needs to keep track not only of the tasks in its queues,
|
||||
* but also of the tasks that have been handed out for processing by the
|
||||
* ingest threads. Therefore all ingest tasks are added to this list when
|
||||
* they are created and are not removed when an ingest thread takes an
|
||||
* ingest task. Instead, the ingest thread calls back into the scheduler
|
||||
* when the task is completed, at which time the task will be removed from
|
||||
* this list.
|
||||
*/
|
||||
private final Set<IngestTask> tasksInProgress;
|
||||
private final DataSourceIngestTaskQueue dataSourceTaskQueueForIngestThreads;
|
||||
private final List<DataSourceIngestTask> queuedAndRunningDataSourceTasks;
|
||||
private final TreeSet<FileIngestTask> rootFileTaskQueue;
|
||||
private final Deque<FileIngestTask> directoryFileTaskQueue;
|
||||
private final FileIngestTaskQueue fileTaskQueueForIngestThreads;
|
||||
private final List<FileIngestTask> queuedAndRunningFileTasks;
|
||||
|
||||
/**
|
||||
* Gets the ingest tasks scheduler singleton.
|
||||
@@ -113,52 +70,53 @@ final class IngestTasksScheduler {
|
||||
* Constructs an ingest tasks scheduler.
|
||||
*/
|
||||
private IngestTasksScheduler() {
|
||||
this.pendingDataSourceTasks = new LinkedBlockingQueue<>();
|
||||
this.dataSourceTasksDispenser = new DataSourceIngestTaskQueue();
|
||||
this.rootDirectoryTasks = new TreeSet<>(new RootDirectoryTaskComparator());
|
||||
this.directoryTasks = new ArrayList<>();
|
||||
this.pendingFileTasks = new LinkedBlockingDeque<>();
|
||||
this.fileTasksDispenser = new FileIngestTaskQueue();
|
||||
this.tasksInProgress = new HashSet<>();
|
||||
this.queuedAndRunningDataSourceTasks = new LinkedList<>();
|
||||
this.dataSourceTaskQueueForIngestThreads = new DataSourceIngestTaskQueue();
|
||||
this.rootFileTaskQueue = new TreeSet<>(new RootDirectoryTaskComparator());
|
||||
this.directoryFileTaskQueue = new LinkedList<>();
|
||||
this.queuedAndRunningFileTasks = new LinkedList<>();
|
||||
this.fileTaskQueueForIngestThreads = new FileIngestTaskQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this ingest task scheduler's implementation of the IngestTaskQueue
|
||||
* interface for data source ingest tasks.
|
||||
* Gets the data source level ingest tasks queue. This queue is a blocking
|
||||
* queue intended for use by the ingest manager's data source ingest
|
||||
* threads.
|
||||
*
|
||||
* @return The data source ingest tasks queue.
|
||||
* @return The queue.
|
||||
*/
|
||||
IngestTaskQueue getDataSourceIngestTaskQueue() {
|
||||
return this.dataSourceTasksDispenser;
|
||||
return this.dataSourceTaskQueueForIngestThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this ingest task scheduler's implementation of the IngestTaskQueue
|
||||
* interface for file ingest tasks.
|
||||
* Gets the file level ingest tasks queue. This queue is a blocking queue
|
||||
* intended for use by the ingest manager's file ingest threads.
|
||||
*
|
||||
* @return The file ingest tasks queue.
|
||||
* @return The queue.
|
||||
*/
|
||||
IngestTaskQueue getFileIngestTaskQueue() {
|
||||
return this.fileTasksDispenser;
|
||||
return this.fileTaskQueueForIngestThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a data source level ingest task and file level ingest tasks for
|
||||
* an ingest job. Either all of the files in the data source or a given
|
||||
* subset of the files will be scheduled.
|
||||
* a data source ingest job.
|
||||
*
|
||||
* @param job The data source ingest job.
|
||||
* @param files A subset of the files for the data source.
|
||||
* @param job The data source ingest job.
|
||||
*/
|
||||
synchronized void scheduleIngestTasks(DataSourceIngestJob job, List<AbstractFile> files) {
|
||||
synchronized void scheduleIngestTasks(DataSourceIngestJob job) {
|
||||
if (!job.isCancelled()) {
|
||||
// Scheduling of both a data source ingest task and file ingest tasks
|
||||
// for a job must be an atomic operation. Otherwise, the data source
|
||||
// task might be completed before the file tasks are scheduled,
|
||||
// resulting in a potential false positive when another thread checks
|
||||
// whether or not all the tasks for the job are completed.
|
||||
/*
|
||||
* Scheduling of both the data source ingest task and the initial
|
||||
* file ingest tasks for a job must be an atomic operation.
|
||||
* Otherwise, the data source task might be completed before the
|
||||
* file tasks are scheduled, resulting in a potential false positive
|
||||
* when another thread checks whether or not all the tasks for the
|
||||
* job are completed.
|
||||
*/
|
||||
this.scheduleDataSourceIngestTask(job);
|
||||
this.scheduleFileIngestTasks(job, files);
|
||||
this.scheduleFileIngestTasks(job);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,41 +128,29 @@ final class IngestTasksScheduler {
|
||||
synchronized void scheduleDataSourceIngestTask(DataSourceIngestJob job) {
|
||||
if (!job.isCancelled()) {
|
||||
DataSourceIngestTask task = new DataSourceIngestTask(job);
|
||||
this.tasksInProgress.add(task);
|
||||
this.queuedAndRunningDataSourceTasks.add(task);
|
||||
try {
|
||||
this.pendingDataSourceTasks.put(task);
|
||||
this.dataSourceTaskQueueForIngestThreads.add(task);
|
||||
} catch (InterruptedException ex) {
|
||||
/**
|
||||
* The current thread was interrupted while blocked on a full
|
||||
* queue. Discard the task and reset the interrupted flag.
|
||||
*/
|
||||
this.tasksInProgress.remove(task);
|
||||
IngestTasksScheduler.logger.log(Level.INFO, "Ingest cancelled while a data source ingest thread was blocked on a full queue", ex);
|
||||
this.queuedAndRunningDataSourceTasks.remove(task);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules file level ingest tasks for a data source ingest job. Either
|
||||
* all of the files in the data source or a given subset of the files will
|
||||
* be scheduled.
|
||||
* Schedules file level ingest tasks for a data source ingest job.
|
||||
*
|
||||
* @param job The data source ingest job.
|
||||
* @param files A subset of the files for the data source.
|
||||
* @param job The data source ingest job.
|
||||
*/
|
||||
synchronized void scheduleFileIngestTasks(DataSourceIngestJob job, List<AbstractFile> files) {
|
||||
synchronized void scheduleFileIngestTasks(DataSourceIngestJob job) {
|
||||
if (!job.isCancelled()) {
|
||||
List<AbstractFile> candidateFiles = new ArrayList<>();
|
||||
if (files.isEmpty()) {
|
||||
getTopLevelFiles(job.getDataSource(), candidateFiles);
|
||||
} else {
|
||||
candidateFiles.addAll(files);
|
||||
}
|
||||
for (AbstractFile firstLevelFile : candidateFiles) {
|
||||
FileIngestTask task = new FileIngestTask(job, firstLevelFile);
|
||||
List<AbstractFile> candidateFiles = getTopLevelFiles(job.getDataSource());
|
||||
for (AbstractFile file : candidateFiles) {
|
||||
FileIngestTask task = new FileIngestTask(job, file);
|
||||
if (IngestTasksScheduler.shouldEnqueueFileTask(task)) {
|
||||
this.tasksInProgress.add(task);
|
||||
this.rootDirectoryTasks.add(task);
|
||||
this.rootFileTaskQueue.add(task);
|
||||
}
|
||||
}
|
||||
shuffleFileTaskQueues();
|
||||
@@ -212,73 +158,119 @@ final class IngestTasksScheduler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a file ingest task for a data source ingest job. The task that
|
||||
* is created is added directly to the pending file tasks queues, i.e., it
|
||||
* is "fast tracked."
|
||||
* Schedules file level ingest tasks for a subset of the files for a data
|
||||
* source ingest job.
|
||||
*
|
||||
* @param job The data source ingest job.
|
||||
* @param file A file.
|
||||
* @param job The data source ingest job.
|
||||
* @param files A subset of the files for the data source.
|
||||
*/
|
||||
synchronized void scheduleFastTrackedFileIngestTask(DataSourceIngestJob job, AbstractFile file) {
|
||||
synchronized void scheduleFileIngestTasks(DataSourceIngestJob job, Collection<AbstractFile> files) {
|
||||
if (!job.isCancelled()) {
|
||||
FileIngestTask task = new FileIngestTask(job, file);
|
||||
if (IngestTasksScheduler.shouldEnqueueFileTask(task)) {
|
||||
this.tasksInProgress.add(task);
|
||||
addToPendingFileTasksQueue(task);
|
||||
List<FileIngestTask> newTasksForFileIngestThreads = new LinkedList<>();
|
||||
for (AbstractFile file : files) {
|
||||
/*
|
||||
* Put the file directly into the queue for the file ingest
|
||||
* threads, if it passes the filter for the job. The file is
|
||||
* added to the queue for the ingest threads BEFORE the other
|
||||
* queued tasks because the primary use case for this method is
|
||||
* adding derived files from a higher priority task that
|
||||
* preceded the tasks currently in the queue.
|
||||
*/
|
||||
FileIngestTask task = new FileIngestTask(job, file);
|
||||
if (shouldEnqueueFileTask(task)) {
|
||||
newTasksForFileIngestThreads.add(task);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the file or directory that was just queued has children,
|
||||
* try to queue tasks for the children. Each child task will go
|
||||
* into either the directory queue if it is a directory, or
|
||||
* directly into the queue for the file ingest threads, if it
|
||||
* passes the filter for the job.
|
||||
*/
|
||||
try {
|
||||
for (Content child : file.getChildren()) {
|
||||
if (child instanceof AbstractFile) {
|
||||
AbstractFile childFile = (AbstractFile) child;
|
||||
FileIngestTask childTask = new FileIngestTask(job, childFile);
|
||||
if (childFile.hasChildren()) {
|
||||
this.directoryFileTaskQueue.add(childTask);
|
||||
} else if (shouldEnqueueFileTask(childTask)) {
|
||||
newTasksForFileIngestThreads.add(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error getting the children of %s (objId=%d)", file.getName(), file.getId()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The files are added to the queue for the ingest threads BEFORE
|
||||
* the other queued tasks because the primary use case for this
|
||||
* method is adding derived files from a higher priority task that
|
||||
* preceded the tasks currently in the queue.
|
||||
*/
|
||||
for (FileIngestTask newTask : newTasksForFileIngestThreads) {
|
||||
try {
|
||||
this.queuedAndRunningFileTasks.add(newTask);
|
||||
this.fileTaskQueueForIngestThreads.addFirst(newTask);
|
||||
} catch (InterruptedException ex) {
|
||||
this.queuedAndRunningFileTasks.remove(newTask);
|
||||
IngestTasksScheduler.logger.log(Level.INFO, "Ingest cancelled while blocked on a full file ingest threads queue", ex);
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows an ingest thread to notify this ingest task scheduler that a task
|
||||
* has been completed.
|
||||
* Allows an ingest thread to notify this ingest task scheduler that a data
|
||||
* source level task has been completed.
|
||||
*
|
||||
* @param task The completed task.
|
||||
*/
|
||||
synchronized void notifyTaskCompleted(IngestTask task) {
|
||||
tasksInProgress.remove(task);
|
||||
synchronized void notifyTaskCompleted(DataSourceIngestTask task) {
|
||||
this.queuedAndRunningDataSourceTasks.remove(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the task scheduler to determine whether or not all current ingest
|
||||
* tasks for an ingest job are completed.
|
||||
* Allows an ingest thread to notify this ingest task scheduler that a file
|
||||
* level task has been completed.
|
||||
*
|
||||
* @param job The job for which the query is to be performed.
|
||||
* @param task The completed task.
|
||||
*/
|
||||
synchronized void notifyTaskCompleted(FileIngestTask task) {
|
||||
this.queuedAndRunningFileTasks.remove(task);
|
||||
shuffleFileTaskQueues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the task scheduler to determine whether or not all of the ingest
|
||||
* tasks for a data source ingest job have been completed.
|
||||
*
|
||||
* @param job The data source ingest job.
|
||||
*
|
||||
* @return True or false.
|
||||
*/
|
||||
synchronized boolean tasksForJobAreCompleted(DataSourceIngestJob job) {
|
||||
for (IngestTask task : tasksInProgress) {
|
||||
if (task.getIngestJob().getId() == job.getId()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return !hasTasksForJob(this.queuedAndRunningDataSourceTasks, job)
|
||||
&& !hasTasksForJob(this.rootFileTaskQueue, job)
|
||||
&& !hasTasksForJob(this.directoryFileTaskQueue, job)
|
||||
&& !hasTasksForJob(this.queuedAndRunningFileTasks, job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the "upstream" task scheduling queues for an ingest job, but does
|
||||
* nothing about tasks that have already been shuffled into the concurrently
|
||||
* accessed blocking queues shared with the ingest threads. Note that tasks
|
||||
* in the "downstream" queues or already taken by the ingest threads will be
|
||||
* flushed out when the ingest threads call back with their task completed
|
||||
* notifications.
|
||||
* Clears the "upstream" task scheduling queues for a data source ingest
|
||||
* job, but does nothing about tasks that have already been moved into the
|
||||
* queue that is consumed by the file ingest threads.
|
||||
*
|
||||
* @param job The job for which the tasks are to to canceled.
|
||||
* @param job The data source ingest job.
|
||||
*/
|
||||
synchronized void cancelPendingTasksForIngestJob(DataSourceIngestJob job) {
|
||||
/**
|
||||
* This code should not flush the blocking queues that are concurrently
|
||||
* accessed by the ingest threads. This is because the "lock striping"
|
||||
* and "weakly consistent" iterators of these collections make it so
|
||||
* that this code could have a different view of the queues than the
|
||||
* ingest threads. It does clean out the directory level tasks before
|
||||
* they are exploded into file tasks.
|
||||
*/
|
||||
long jobId = job.getId();
|
||||
this.removeTasksForJob(this.rootDirectoryTasks, jobId);
|
||||
this.removeTasksForJob(this.directoryTasks, jobId);
|
||||
this.shuffleFileTaskQueues();
|
||||
this.removeTasksForJob(this.rootFileTaskQueue, job);
|
||||
this.removeTasksForJob(this.directoryFileTaskQueue, job);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,10 +278,12 @@ final class IngestTasksScheduler {
|
||||
* files and virtual directories for a data source. Used to create file
|
||||
* tasks to put into the root directories queue.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param topLevelFiles The top level files are added to this list.
|
||||
* @param dataSource The data source.
|
||||
*
|
||||
* @return The top level files.
|
||||
*/
|
||||
private static void getTopLevelFiles(Content dataSource, List<AbstractFile> topLevelFiles) {
|
||||
private static List<AbstractFile> getTopLevelFiles(Content dataSource) {
|
||||
List<AbstractFile> topLevelFiles = new ArrayList<>();
|
||||
Collection<AbstractFile> rootObjects = dataSource.accept(new GetRootDirectoryVisitor());
|
||||
if (rootObjects.isEmpty() && dataSource instanceof AbstractFile) {
|
||||
// The data source is itself a file to be processed.
|
||||
@@ -317,74 +311,113 @@ final class IngestTasksScheduler {
|
||||
}
|
||||
}
|
||||
}
|
||||
return topLevelFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Shuffles" the file task queues to ensure that there is at least one task
|
||||
* in the pending file ingest tasks queue, as long as there are still file
|
||||
* ingest tasks to be performed.
|
||||
* Schedules file ingest tasks for the ingest manager's file ingest threads
|
||||
* by "shuffling" them through a sequence of three queues that allows for
|
||||
* the interleaving of tasks from different data source ingest jobs based on
|
||||
* priority. The sequence of queues is:
|
||||
*
|
||||
* 1. The root file tasks priority queue, which contains file tasks for the
|
||||
* root objects of the data sources that are being analyzed. For example,
|
||||
* the root tasks for a disk image data source are typically the tasks for
|
||||
* the contents of the root directories of the file systems. This queue is a
|
||||
* priority queue that attempts to ensure that user directory content is
|
||||
* analyzed before general file system content. It feeds into the directory
|
||||
* tasks queue.
|
||||
*
|
||||
* 2. The directory file tasks queue, which contains root file tasks
|
||||
* shuffled out of the root tasks queue, plus directory tasks discovered in
|
||||
* the descent from the root tasks to the final leaf tasks in the content
|
||||
* trees that are being analyzed for the data source ingest jobs. This queue
|
||||
* is a FIFO queue. It feeds into the file tasks queue for the ingest
|
||||
* manager's file ingest threads.
|
||||
*
|
||||
* 3. The file tasks queue for the ingest manager's file ingest threads.
|
||||
* This queue is a blocking deque that is FIFO during a shuffle to maintain
|
||||
* task prioritization, but LIFO when adding derived files to it directly
|
||||
* during ingest. The reason for the LIFO additions is to give priority
|
||||
* derived files of priority files.
|
||||
*
|
||||
* There is a fourth collection of file tasks, a "tracking" list, that keeps
|
||||
* track of the file tasks that are either in the tasks queue for the file
|
||||
* ingest threads, or are in the process of being analyzed in a file ingest
|
||||
* thread. This queue is vital to the ingest task scheduler's ability to
|
||||
* determine when all of the ingest tasks for a data source ingest job have
|
||||
* been completed. It is also used to drive this shuffling algorithm -
|
||||
* whenever this list is empty, the two "upstream" queues are "shuffled" to
|
||||
* queue more tasks for the file ingest threads.
|
||||
*/
|
||||
synchronized private void shuffleFileTaskQueues() {
|
||||
// This is synchronized because it is called both by synchronized
|
||||
// methods of this ingest scheduler and an unsynchronized method of its
|
||||
// file tasks "dispenser".
|
||||
while (true) {
|
||||
// Loop until either the pending file tasks queue is NOT empty
|
||||
// or the upstream queues that feed into it ARE empty.
|
||||
if (!this.pendingFileTasks.isEmpty()) {
|
||||
// There are file tasks ready to be consumed, exit.
|
||||
return;
|
||||
}
|
||||
if (this.directoryTasks.isEmpty()) {
|
||||
if (this.rootDirectoryTasks.isEmpty()) {
|
||||
// There are no root directory tasks to move into the
|
||||
// directory queue, exit.
|
||||
return;
|
||||
List<FileIngestTask> newTasksForFileIngestThreads = new LinkedList<>();
|
||||
while (this.queuedAndRunningFileTasks.isEmpty()) {
|
||||
/*
|
||||
* If the directory file task queue is empty, move the highest
|
||||
* priority root file task, if there is one, into it. If both the
|
||||
* root and the directory file task queues are empty, there is
|
||||
* nothing left to shuffle, so exit.
|
||||
*/
|
||||
if (this.directoryFileTaskQueue.isEmpty()) {
|
||||
if (!this.rootFileTaskQueue.isEmpty()) {
|
||||
this.directoryFileTaskQueue.add(this.rootFileTaskQueue.pollFirst());
|
||||
} else {
|
||||
// Move the next root directory task into the
|
||||
// directories queue. Note that the task was already
|
||||
// added to the tasks in progress list when the task was
|
||||
// created in scheduleFileIngestTasks().
|
||||
this.directoryTasks.add(this.rootDirectoryTasks.pollFirst());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to add the most recently added directory from the
|
||||
// directory tasks queue to the pending file tasks queue.
|
||||
FileIngestTask directoryTask = this.directoryTasks.remove(this.directoryTasks.size() - 1);
|
||||
/*
|
||||
* Try to move the next task from the directory task queue into the
|
||||
* queue for the file ingest threads, if it passes the filter for
|
||||
* the job. The file is added to the queue for the ingest threads
|
||||
* AFTER the higher priority tasks that preceded it.
|
||||
*/
|
||||
final FileIngestTask directoryTask = this.directoryFileTaskQueue.pollLast();
|
||||
if (shouldEnqueueFileTask(directoryTask)) {
|
||||
addToPendingFileTasksQueue(directoryTask);
|
||||
} else {
|
||||
this.tasksInProgress.remove(directoryTask);
|
||||
newTasksForFileIngestThreads.add(directoryTask);
|
||||
this.queuedAndRunningFileTasks.add(directoryTask);
|
||||
}
|
||||
|
||||
// If the directory contains subdirectories or files, try to
|
||||
// enqueue tasks for them as well.
|
||||
/*
|
||||
* If the directory (or root level file) that was just queued has
|
||||
* children, try to queue tasks for the children. Each child task
|
||||
* will go into either the directory queue if it is a directory, or
|
||||
* into the queue for the file ingest threads, if it passes the
|
||||
* filter for the job. The file is added to the queue for the ingest
|
||||
* threads AFTER the higher priority tasks that preceded it.
|
||||
*/
|
||||
final AbstractFile directory = directoryTask.getFile();
|
||||
try {
|
||||
for (Content child : directory.getChildren()) {
|
||||
if (child instanceof AbstractFile) {
|
||||
AbstractFile file = (AbstractFile) child;
|
||||
FileIngestTask childTask = new FileIngestTask(directoryTask.getIngestJob(), file);
|
||||
if (file.hasChildren()) {
|
||||
// Found a subdirectory, put the task in the
|
||||
// pending directory tasks queue. Note the
|
||||
// addition of the task to the tasks in progress
|
||||
// list. This is necessary because this is the
|
||||
// first appearance of this task in the queues.
|
||||
this.tasksInProgress.add(childTask);
|
||||
this.directoryTasks.add(childTask);
|
||||
AbstractFile childFile = (AbstractFile) child;
|
||||
FileIngestTask childTask = new FileIngestTask(directoryTask.getIngestJob(), childFile);
|
||||
if (childFile.hasChildren()) {
|
||||
this.directoryFileTaskQueue.add(childTask);
|
||||
} else if (shouldEnqueueFileTask(childTask)) {
|
||||
// Found a file, put the task directly into the
|
||||
// pending file tasks queue.
|
||||
this.tasksInProgress.add(childTask);
|
||||
addToPendingFileTasksQueue(childTask);
|
||||
newTasksForFileIngestThreads.add(childTask);
|
||||
this.queuedAndRunningFileTasks.add(childTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
String errorMessage = String.format("An error occurred getting the children of %s", directory.getName()); //NON-NLS
|
||||
logger.log(Level.SEVERE, errorMessage, ex);
|
||||
logger.log(Level.SEVERE, String.format("Error getting the children of %s (objId=%d)", directory.getName(), directory.getId()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The files are added to the queue for the ingest threads AFTER the
|
||||
* higher priority tasks that preceded them.
|
||||
*/
|
||||
for (FileIngestTask newTask : newTasksForFileIngestThreads) {
|
||||
try {
|
||||
this.fileTaskQueueForIngestThreads.addFirst(newTask);
|
||||
} catch (InterruptedException ex) {
|
||||
this.queuedAndRunningFileTasks.remove(newTask);
|
||||
IngestTasksScheduler.logger.log(Level.INFO, "Ingest cancelled while blocked on a full file ingest threads queue", ex);
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,44 +496,44 @@ final class IngestTasksScheduler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file ingest task to the blocking pending tasks queue.
|
||||
* Checks whether or not a collection of ingest tasks includes a task for a
|
||||
* given data source ingest job.
|
||||
*
|
||||
* @param task The task to add.
|
||||
* @param tasks The tasks.
|
||||
* @param job The data source ingest job.
|
||||
*
|
||||
* @return True if there are no tasks for the job, false otherwise.
|
||||
*/
|
||||
synchronized private void addToPendingFileTasksQueue(FileIngestTask task) {
|
||||
try {
|
||||
this.pendingFileTasks.putFirst(task);
|
||||
} catch (InterruptedException ex) {
|
||||
/**
|
||||
* The current thread was interrupted while blocked on a full queue.
|
||||
* Discard the task and reset the interrupted flag.
|
||||
*/
|
||||
this.tasksInProgress.remove(task);
|
||||
Thread.currentThread().interrupt();
|
||||
synchronized private boolean hasTasksForJob(Collection<? extends IngestTask> tasks, DataSourceIngestJob job) {
|
||||
long jobId = job.getId();
|
||||
for (IngestTask task : tasks) {
|
||||
if (task.getIngestJob().getId() == jobId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all of the ingest tasks associated with an ingest job from a
|
||||
* tasks queue. The task is removed from the the tasks in progress list as
|
||||
* well.
|
||||
* Removes all of the ingest tasks associated with a data source ingest job
|
||||
* from a tasks collection.
|
||||
*
|
||||
* @param taskQueue The queue from which to remove the tasks.
|
||||
* @param jobId The id of the job for which the tasks are to be removed.
|
||||
* @param tasks The collection from which to remove the tasks.
|
||||
* @param job THe data source ingest job.
|
||||
*/
|
||||
synchronized private void removeTasksForJob(Collection<? extends IngestTask> taskQueue, long jobId) {
|
||||
Iterator<? extends IngestTask> iterator = taskQueue.iterator();
|
||||
synchronized private void removeTasksForJob(Collection<? extends IngestTask> tasks, DataSourceIngestJob job) {
|
||||
long jobId = job.getId();
|
||||
Iterator<? extends IngestTask> iterator = tasks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
IngestTask task = iterator.next();
|
||||
if (task.getIngestJob().getId() == jobId) {
|
||||
this.tasksInProgress.remove(task);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of ingest tasks in a task queue for a given job.
|
||||
* Counts the number of ingest tasks in a tasks collection for a given job.
|
||||
*
|
||||
* @param queue The queue for which to count tasks.
|
||||
* @param jobId The id of the job for which the tasks are to be counted.
|
||||
@@ -511,7 +544,7 @@ final class IngestTasksScheduler {
|
||||
Iterator<? extends IngestTask> iterator = queue.iterator();
|
||||
int count = 0;
|
||||
while (iterator.hasNext()) {
|
||||
IngestTask task = (IngestTask) iterator.next();
|
||||
IngestTask task = iterator.next();
|
||||
if (task.getIngestJob().getId() == jobId) {
|
||||
count++;
|
||||
}
|
||||
@@ -549,8 +582,15 @@ final class IngestTasksScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to prioritize file ingest tasks in the root tasks queue so that
|
||||
* user content is processed first.
|
||||
*/
|
||||
private static class AbstractFilePriority {
|
||||
|
||||
private AbstractFilePriority() {
|
||||
}
|
||||
|
||||
enum Priority {
|
||||
|
||||
LAST, LOW, MEDIUM, HIGH
|
||||
@@ -642,28 +682,43 @@ final class IngestTasksScheduler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps access to pending data source ingest tasks in the interface
|
||||
* required by the ingest threads.
|
||||
* A blocking queue of data source ingest tasks for the ingest manager's
|
||||
* data source ingest threads.
|
||||
*/
|
||||
private final class DataSourceIngestTaskQueue implements IngestTaskQueue {
|
||||
|
||||
private final BlockingQueue<DataSourceIngestTask> tasks = new LinkedBlockingQueue<>();
|
||||
|
||||
private void add(DataSourceIngestTask task) throws InterruptedException {
|
||||
this.tasks.put(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IngestTask getNextTask() throws InterruptedException {
|
||||
return IngestTasksScheduler.this.pendingDataSourceTasks.take();
|
||||
return tasks.take();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps access to pending file ingest tasks in the interface required by
|
||||
* the ingest threads.
|
||||
* A blocking, LIFO queue of data source ingest tasks for the ingest
|
||||
* manager's data source ingest threads.
|
||||
*/
|
||||
private final class FileIngestTaskQueue implements IngestTaskQueue {
|
||||
|
||||
private final BlockingDeque<FileIngestTask> tasks = new LinkedBlockingDeque<>();
|
||||
|
||||
private void addFirst(FileIngestTask task) throws InterruptedException {
|
||||
this.tasks.putFirst(task);
|
||||
}
|
||||
|
||||
private void addLast(FileIngestTask task) throws InterruptedException {
|
||||
this.tasks.putLast(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IngestTask getNextTask() throws InterruptedException {
|
||||
FileIngestTask task = IngestTasksScheduler.this.pendingFileTasks.takeFirst();
|
||||
shuffleFileTaskQueues();
|
||||
return task;
|
||||
return tasks.takeFirst();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -674,10 +729,10 @@ final class IngestTasksScheduler {
|
||||
class IngestJobTasksSnapshot {
|
||||
|
||||
private final long jobId;
|
||||
private final long dsQueueSize;
|
||||
private final long rootQueueSize;
|
||||
private final long dirQueueSize;
|
||||
private final long fileQueueSize;
|
||||
private final long dsQueueSize;
|
||||
private final long runningListSize;
|
||||
|
||||
/**
|
||||
@@ -687,11 +742,11 @@ final class IngestTasksScheduler {
|
||||
*/
|
||||
IngestJobTasksSnapshot(long jobId) {
|
||||
this.jobId = jobId;
|
||||
this.rootQueueSize = countTasksForJob(IngestTasksScheduler.this.rootDirectoryTasks, jobId);
|
||||
this.dirQueueSize = countTasksForJob(IngestTasksScheduler.this.directoryTasks, jobId);
|
||||
this.fileQueueSize = countTasksForJob(IngestTasksScheduler.this.pendingFileTasks, jobId);
|
||||
this.dsQueueSize = countTasksForJob(IngestTasksScheduler.this.pendingDataSourceTasks, jobId);
|
||||
this.runningListSize = countTasksForJob(IngestTasksScheduler.this.tasksInProgress, jobId);
|
||||
this.rootQueueSize = countTasksForJob(IngestTasksScheduler.this.rootFileTaskQueue, jobId);
|
||||
this.dirQueueSize = countTasksForJob(IngestTasksScheduler.this.directoryFileTaskQueue, jobId);
|
||||
this.fileQueueSize = countTasksForJob(IngestTasksScheduler.this.fileTaskQueueForIngestThreads.tasks, jobId);
|
||||
this.dsQueueSize = countTasksForJob(IngestTasksScheduler.this.dataSourceTaskQueueForIngestThreads.tasks, jobId);
|
||||
this.runningListSize = countTasksForJob(IngestTasksScheduler.this.queuedAndRunningDataSourceTasks, jobId) + countTasksForJob(IngestTasksScheduler.this.queuedAndRunningFileTasks, jobId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ public final class RunIngestModulesAction extends AbstractAction {
|
||||
* Constructs an action that invokes the Run Ingest Modules wizard for the
|
||||
* children of a file.
|
||||
*
|
||||
* @param file The file.
|
||||
* @param parentFile The file.
|
||||
*/
|
||||
public RunIngestModulesAction(AbstractFile parentFile) {
|
||||
this.putValue(Action.NAME, Bundle.RunIngestModulesAction_name());
|
||||
|
||||
@@ -547,7 +547,7 @@ public final class FilesSet implements Serializable {
|
||||
/**
|
||||
* Construct a meta-type condition.
|
||||
*
|
||||
* @param metaType The meta-type to match, must.
|
||||
* @param type The meta-type to match, must.
|
||||
*/
|
||||
public MetaTypeCondition(Type type) {
|
||||
this.type = type;
|
||||
|
||||
+64
-22
@@ -38,7 +38,11 @@ import java.util.logging.Level;
|
||||
import javax.swing.DefaultListSelectionModel;
|
||||
import java.awt.Color;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
@@ -149,6 +153,11 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
|
||||
private Color pendingTableBackground;
|
||||
private Color pendingTablelForeground;
|
||||
|
||||
/**
|
||||
* Maintain a mapping of each service to it's last status update.
|
||||
*/
|
||||
private final ConcurrentHashMap<String, String> statusByService;
|
||||
|
||||
/*
|
||||
* The enum is used in conjunction with the DefaultTableModel class to
|
||||
* provide table models for the JTables used to display a view of the
|
||||
@@ -235,6 +244,8 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
|
||||
* controlling automated ingest for a single node within the cluster.
|
||||
*/
|
||||
private AutoIngestControlPanel() {
|
||||
|
||||
this.statusByService = new ConcurrentHashMap<>();
|
||||
|
||||
//Disable the main window so they can only use the dashboard (if we used setVisible the taskBar icon would go away)
|
||||
WindowManager.getDefault().getMainWindow().setEnabled(false);
|
||||
@@ -248,6 +259,9 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
|
||||
completedTableModel = new AutoIngestTableModel(JobsTableModelColumns.headers, 0);
|
||||
|
||||
initComponents(); // Generated code.
|
||||
statusByService.put(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString(), NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.tbServicesStatusMessage.Message.Down"));
|
||||
statusByService.put(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString(), NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.tbServicesStatusMessage.Message.Down"));
|
||||
statusByService.put(ServicesMonitor.Service.MESSAGING.toString(), NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.tbServicesStatusMessage.Message.Down"));
|
||||
setServicesStatusMessage();
|
||||
initPendingJobsTable();
|
||||
initRunningJobsTable();
|
||||
@@ -260,6 +274,25 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
|
||||
UIManager.put("PopupMenu.consumeEventOnClose", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update status of the services on the dashboard
|
||||
*/
|
||||
private void displayServicesStatus() {
|
||||
tbServicesStatusMessage.setText(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.tbServicesStatusMessage.Message",
|
||||
statusByService.get(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString()),
|
||||
statusByService.get(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString()),
|
||||
statusByService.get(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString()),
|
||||
statusByService.get(ServicesMonitor.Service.MESSAGING.toString())));
|
||||
String upStatus = NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.tbServicesStatusMessage.Message.Up");
|
||||
if (statusByService.get(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString()).compareTo(upStatus) != 0
|
||||
|| statusByService.get(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString()).compareTo(upStatus) != 0
|
||||
|| statusByService.get(ServicesMonitor.Service.MESSAGING.toString()).compareTo(upStatus) != 0) {
|
||||
tbServicesStatusMessage.setForeground(Color.RED);
|
||||
} else {
|
||||
tbServicesStatusMessage.setForeground(Color.BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the services monitor and sets the text for the services status
|
||||
* text box.
|
||||
@@ -274,15 +307,11 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
|
||||
private void setServicesStatusMessage() {
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
String caseDatabaseServerStatus = ServicesMonitor.ServiceStatus.DOWN.toString();
|
||||
String keywordSearchServiceStatus = ServicesMonitor.ServiceStatus.DOWN.toString();
|
||||
String messagingStatus = ServicesMonitor.ServiceStatus.DOWN.toString();
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
caseDatabaseServerStatus = getServiceStatus(ServicesMonitor.Service.REMOTE_CASE_DATABASE);
|
||||
keywordSearchServiceStatus = getServiceStatus(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH);
|
||||
messagingStatus = getServiceStatus(ServicesMonitor.Service.MESSAGING);
|
||||
statusByService.put(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString(), getServiceStatus(ServicesMonitor.Service.REMOTE_CASE_DATABASE));
|
||||
statusByService.put(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString(), getServiceStatus(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH));
|
||||
statusByService.put(ServicesMonitor.Service.MESSAGING.toString(), getServiceStatus(ServicesMonitor.Service.MESSAGING));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -311,15 +340,7 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
tbServicesStatusMessage.setText(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.tbServicesStatusMessage.Message", caseDatabaseServerStatus, keywordSearchServiceStatus, keywordSearchServiceStatus, messagingStatus));
|
||||
String upStatus = NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.tbServicesStatusMessage.Message.Up");
|
||||
if (caseDatabaseServerStatus.compareTo(upStatus) != 0
|
||||
|| keywordSearchServiceStatus.compareTo(upStatus) != 0
|
||||
|| messagingStatus.compareTo(upStatus) != 0) {
|
||||
tbServicesStatusMessage.setForeground(Color.RED);
|
||||
} else {
|
||||
tbServicesStatusMessage.setForeground(Color.BLACK);
|
||||
}
|
||||
displayServicesStatus();
|
||||
}
|
||||
|
||||
}.execute();
|
||||
@@ -682,12 +703,33 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subscribe to services monitor events.
|
||||
*/
|
||||
ServicesMonitor.getInstance().addSubscriber((PropertyChangeEvent evt) -> {
|
||||
setServicesStatusMessage();
|
||||
});
|
||||
PropertyChangeListener propChangeListener = (PropertyChangeEvent evt) -> {
|
||||
|
||||
String serviceDisplayName = ServicesMonitor.Service.valueOf(evt.getPropertyName()).toString();
|
||||
String status = evt.getNewValue().toString();
|
||||
|
||||
if (status.equals(ServicesMonitor.ServiceStatus.UP.toString())) {
|
||||
status = NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.tbServicesStatusMessage.Message.Up");
|
||||
} else if (status.equals(ServicesMonitor.ServiceStatus.DOWN.toString())) {
|
||||
status = NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.tbServicesStatusMessage.Message.Down");
|
||||
SYS_LOGGER.log(Level.SEVERE, "Connection to {0} is down", serviceDisplayName); //NON-NLS
|
||||
}
|
||||
|
||||
// if the status update is for an existing service who's status hasn't changed - do nothing.
|
||||
if (statusByService.containsKey(serviceDisplayName) && status.equals(statusByService.get(serviceDisplayName))) {
|
||||
return;
|
||||
}
|
||||
|
||||
statusByService.put(serviceDisplayName, status);
|
||||
displayServicesStatus();
|
||||
};
|
||||
|
||||
// Subscribe to all multi-user services in order to display their status
|
||||
Set<String> servicesList = new HashSet<>();
|
||||
servicesList.add(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString());
|
||||
servicesList.add(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString());
|
||||
servicesList.add(ServicesMonitor.Service.MESSAGING.toString());
|
||||
ServicesMonitor.getInstance().addSubscriber(servicesList, propChangeListener);
|
||||
|
||||
/*
|
||||
* Register with the AIM as an observer.
|
||||
|
||||
+70
-20
@@ -31,6 +31,10 @@ import java.util.logging.Level;
|
||||
import javax.swing.DefaultListSelectionModel;
|
||||
import java.awt.Color;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingWorker;
|
||||
@@ -82,6 +86,11 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
private final DefaultTableModel runningTableModel;
|
||||
private final DefaultTableModel completedTableModel;
|
||||
private AutoIngestMonitor autoIngestMonitor;
|
||||
|
||||
/**
|
||||
* Maintain a mapping of each service to it's last status update.
|
||||
*/
|
||||
private final ConcurrentHashMap<String, String> statusByService;
|
||||
|
||||
/**
|
||||
* Creates a dashboard for monitoring an automated ingest cluster.
|
||||
@@ -105,6 +114,8 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
* Constructs a panel for monitoring an automated ingest cluster.
|
||||
*/
|
||||
private AutoIngestDashboard() {
|
||||
this.statusByService = new ConcurrentHashMap<>();
|
||||
|
||||
pendingTableModel = new AutoIngestTableModel(JobsTableModelColumns.headers, 0);
|
||||
|
||||
runningTableModel = new AutoIngestTableModel(JobsTableModelColumns.headers, 0);
|
||||
@@ -112,6 +123,9 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
completedTableModel = new AutoIngestTableModel(JobsTableModelColumns.headers, 0);
|
||||
|
||||
initComponents();
|
||||
statusByService.put(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString(), NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Down"));
|
||||
statusByService.put(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString(), NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Down"));
|
||||
statusByService.put(ServicesMonitor.Service.MESSAGING.toString(), NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Down"));
|
||||
setServicesStatusMessage();
|
||||
initPendingJobsTable();
|
||||
initRunningJobsTable();
|
||||
@@ -122,6 +136,25 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
*/
|
||||
UIManager.put("PopupMenu.consumeEventOnClose", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update status of the services on the dashboard
|
||||
*/
|
||||
private void displayServicesStatus() {
|
||||
tbServicesStatusMessage.setText(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message",
|
||||
statusByService.get(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString()),
|
||||
statusByService.get(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString()),
|
||||
statusByService.get(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString()),
|
||||
statusByService.get(ServicesMonitor.Service.MESSAGING.toString())));
|
||||
String upStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Up");
|
||||
if (statusByService.get(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString()).compareTo(upStatus) != 0
|
||||
|| statusByService.get(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString()).compareTo(upStatus) != 0
|
||||
|| statusByService.get(ServicesMonitor.Service.MESSAGING.toString()).compareTo(upStatus) != 0) {
|
||||
tbServicesStatusMessage.setForeground(Color.RED);
|
||||
} else {
|
||||
tbServicesStatusMessage.setForeground(Color.BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the services monitor and sets the text for the services status
|
||||
@@ -129,15 +162,12 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
*/
|
||||
private void setServicesStatusMessage() {
|
||||
new SwingWorker<Void, Void>() {
|
||||
String caseDatabaseServerStatus = ServicesMonitor.ServiceStatus.DOWN.toString();
|
||||
String keywordSearchServiceStatus = ServicesMonitor.ServiceStatus.DOWN.toString();
|
||||
String messagingStatus = ServicesMonitor.ServiceStatus.DOWN.toString();
|
||||
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
caseDatabaseServerStatus = getServiceStatus(ServicesMonitor.Service.REMOTE_CASE_DATABASE);
|
||||
keywordSearchServiceStatus = getServiceStatus(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH);
|
||||
messagingStatus = getServiceStatus(ServicesMonitor.Service.MESSAGING);
|
||||
statusByService.put(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString(), getServiceStatus(ServicesMonitor.Service.REMOTE_CASE_DATABASE));
|
||||
statusByService.put(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString(), getServiceStatus(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH));
|
||||
statusByService.put(ServicesMonitor.Service.MESSAGING.toString(), getServiceStatus(ServicesMonitor.Service.MESSAGING));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -166,15 +196,7 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
tbServicesStatusMessage.setText(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message", caseDatabaseServerStatus, keywordSearchServiceStatus, keywordSearchServiceStatus, messagingStatus));
|
||||
String upStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Up");
|
||||
if (caseDatabaseServerStatus.compareTo(upStatus) != 0
|
||||
|| keywordSearchServiceStatus.compareTo(upStatus) != 0
|
||||
|| messagingStatus.compareTo(upStatus) != 0) {
|
||||
tbServicesStatusMessage.setForeground(Color.RED);
|
||||
} else {
|
||||
tbServicesStatusMessage.setForeground(Color.BLACK);
|
||||
}
|
||||
displayServicesStatus();
|
||||
}
|
||||
|
||||
}.execute();
|
||||
@@ -413,10 +435,38 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
* auto ingest job tables.
|
||||
*/
|
||||
private void startUp() throws AutoIngestMonitor.AutoIngestMonitorException {
|
||||
setServicesStatusMessage();
|
||||
ServicesMonitor.getInstance().addSubscriber((PropertyChangeEvent evt) -> {
|
||||
setServicesStatusMessage();
|
||||
});
|
||||
|
||||
PropertyChangeListener propChangeListener = (PropertyChangeEvent evt) -> {
|
||||
|
||||
String serviceDisplayName = ServicesMonitor.Service.valueOf(evt.getPropertyName()).toString();
|
||||
String status = evt.getNewValue().toString();
|
||||
|
||||
if (status.equals(ServicesMonitor.ServiceStatus.UP.toString())) {
|
||||
status = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Up");
|
||||
LOGGER.log(Level.INFO, "Connection to {0} is up", serviceDisplayName); //NON-NLS
|
||||
} else if (status.equals(ServicesMonitor.ServiceStatus.DOWN.toString())) {
|
||||
status = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Down");
|
||||
LOGGER.log(Level.SEVERE, "Connection to {0} is down", serviceDisplayName); //NON-NLS
|
||||
} else {
|
||||
LOGGER.log(Level.INFO, "Status for {0} is {1}", new Object[]{serviceDisplayName, status}); //NON-NLS
|
||||
}
|
||||
|
||||
// if the status update is for an existing service who's status hasn't changed - do nothing.
|
||||
if (statusByService.containsKey(serviceDisplayName) && status.equals(statusByService.get(serviceDisplayName))) {
|
||||
return;
|
||||
}
|
||||
|
||||
statusByService.put(serviceDisplayName, status);
|
||||
displayServicesStatus();
|
||||
};
|
||||
|
||||
// Subscribe to all multi-user services in order to display their status
|
||||
Set<String> servicesList = new HashSet<>();
|
||||
servicesList.add(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString());
|
||||
servicesList.add(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString());
|
||||
servicesList.add(ServicesMonitor.Service.MESSAGING.toString());
|
||||
ServicesMonitor.getInstance().addSubscriber(servicesList, propChangeListener);
|
||||
|
||||
autoIngestMonitor = new AutoIngestMonitor();
|
||||
autoIngestMonitor.addObserver(this);
|
||||
autoIngestMonitor.startUp();
|
||||
|
||||
@@ -874,7 +874,7 @@ public class Server {
|
||||
* if this does not exist then no server is recorded.
|
||||
*
|
||||
* Format of solrServerList.txt:
|
||||
* <host>,<port>
|
||||
* (host),(port)
|
||||
* Ex: 10.1.2.34,8983
|
||||
*
|
||||
* @param rootOutputDirectory
|
||||
|
||||
@@ -41,17 +41,32 @@ import org.sleuthkit.datamodel.*;
|
||||
|
||||
abstract class Extract {
|
||||
|
||||
protected Case currentCase = Case.getCurrentCase();
|
||||
protected SleuthkitCase tskCase = currentCase.getSleuthkitCase();
|
||||
protected Case currentCase;
|
||||
protected SleuthkitCase tskCase;
|
||||
private final Logger logger = Logger.getLogger(this.getClass().getName());
|
||||
private final ArrayList<String> errorMessages = new ArrayList<>();
|
||||
String moduleName = "";
|
||||
boolean dataFound = false;
|
||||
|
||||
Extract() {
|
||||
Extract() {
|
||||
}
|
||||
|
||||
void init() throws IngestModuleException {
|
||||
final void init() throws IngestModuleException {
|
||||
try {
|
||||
currentCase = Case.getOpenCase();
|
||||
tskCase = currentCase.getSleuthkitCase();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new IngestModuleException(Bundle.Extract_indexError_message(), ex);
|
||||
}
|
||||
configExtractor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to add any module-specific configuration
|
||||
*
|
||||
* @throws IngestModuleException
|
||||
*/
|
||||
void configExtractor() throws IngestModuleException {
|
||||
}
|
||||
|
||||
abstract void process(Content dataSource, IngestJobContext context);
|
||||
|
||||
+1
-1
@@ -393,7 +393,7 @@ class SearchEngineURLQueryAnalyzer extends Extract {
|
||||
}
|
||||
|
||||
@Override
|
||||
void init() throws IngestModuleException {
|
||||
void configExtractor() throws IngestModuleException {
|
||||
try {
|
||||
PlatformUtil.extractResourceToUserConfigDir(SearchEngineURLQueryAnalyzer.class, XMLFILE, true);
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<project name="TSK_VERSION">
|
||||
<property name="TSK_VERSION" value="4.6.0"/>
|
||||
</project>
|
||||
@@ -6,7 +6,7 @@
|
||||
<description>Builds the module suite Autopsy 4.</description>
|
||||
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
|
||||
<import file="${basedir}/TSKVersion.xml"/>
|
||||
<!-- IMPORTANT: nbproject/platform.properties has a netbeans-plat-version property that MUST be kept in sync (manually) -->
|
||||
<property name="netbeans-plat-version" value="8.2" />
|
||||
<property name="nbplatform.active.dir" value="${basedir}/netbeans-plat/${netbeans-plat-version}" />
|
||||
@@ -79,6 +79,10 @@
|
||||
<delete dir="${zip-tmp}"/>
|
||||
<mkdir dir="${zip-tmp}"/>
|
||||
<unzip src="${nbdist.dir}/${app.name}.zip" dest="${zip-tmp}"/>
|
||||
|
||||
<!-- Disable the Experimental module by default for the installed version. Need to update .lastModified time for change to be seen. -->
|
||||
<replace file="${zip-tmp}/autopsy/autopsy/config/Modules/org-sleuthkit-autopsy-experimental.xml" token="<param name="enabled">true</param>" value="<param name="enabled">false</param>"/>
|
||||
<echo file="${zip-tmp}/autopsy/autopsy/.lastModified" message="" />
|
||||
|
||||
<!-- step (3) do your copying stuff here, check the ant doc for copy, move, etc file -->
|
||||
<copy file="${nbplatform.active.dir}/platform/modules/ext/junit-4.10.jar"
|
||||
@@ -87,7 +91,20 @@
|
||||
<copy file="${basedir}/LICENSE-2.0.txt" tofile="${zip-tmp}/${app.name}/LICENSE-2.0.txt"/>
|
||||
<copy file="${basedir}/NEWS.txt" tofile="${zip-tmp}/${app.name}/NEWS.txt"/>
|
||||
<copy file="${basedir}/KNOWN_ISSUES.txt" tofile="${zip-tmp}/${app.name}/KNOWN_ISSUES.txt"/>
|
||||
<unzip src="${thirdparty.dir}/gstreamer/${os.family}/i386/0.10.7/gstreamer.zip" dest="${zip-tmp}/${app.name}/gstreamer"/>
|
||||
<if>
|
||||
<equals arg1="${os.family}" arg2="unix"/>
|
||||
<then>
|
||||
<copy file="${basedir}/unix_setup.sh" tofile="${zip-tmp}/${app.name}/unix_setup.sh"/>
|
||||
<replaceregexp file="${zip-tmp}/${app.name}/unix_setup.sh" match="TSK_VERSION=(.*)" replace="TSK_VERSION=${TSK_VERSION}" byline="true"/>
|
||||
</then>
|
||||
</if>
|
||||
<if>
|
||||
<equals arg1="${os.family}" arg2="windows"/>
|
||||
<then>
|
||||
<unzip src="${thirdparty.dir}/gstreamer/${os.family}/i386/0.10.7/gstreamer.zip" dest="${zip-tmp}/${app.name}/gstreamer"/>
|
||||
</then>
|
||||
</if>
|
||||
|
||||
<copy file="${basedir}/icons/icon.ico" tofile="${zip-tmp}/${app.name}/icon.ico" overwrite="true"/>
|
||||
|
||||
<!-- Copy the Autopsy documentation to the docs folder -->
|
||||
@@ -96,6 +113,7 @@
|
||||
</copy>
|
||||
|
||||
<property name="app.property.file" value="${zip-tmp}/${app.name}/etc/${app.name}.conf" />
|
||||
<var name="jvm-value" value="--branding ${app.name} -J-Xms24m -J-Xmx4G -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication"/>
|
||||
<!-- for Japanese localized version add option: -Duser.language=ja -->
|
||||
|
||||
|
||||
@@ -279,10 +297,6 @@
|
||||
<target name="build-installer" depends="getProps, doxygen, build-zip" description="Builds Autopsy installer.">
|
||||
<delete dir="${nbdist.dir}/${app.name}-installer" quiet="true"/>
|
||||
<unzip src="${nbdist.dir}/${app.name}-${app.version}.zip" dest="${nbdist.dir}/${app.name}-installer"/>
|
||||
|
||||
<!-- Disable the Experimental module by default for the installed version. Need to update .lastModified time for change to be seen. -->
|
||||
<replace file="${nbdist.dir}/${app.name}-installer/autopsy/config/modules/org-sleuthkit-autopsy-experimental.xml" token="<param name="enabled">true</param>" value="<param name="enabled">false</param>"/>
|
||||
<echo file="${nbdist.dir}/${app.name}-installer/autopsy/.lastModified" message="" />
|
||||
|
||||
<antcall target="build-installer-${os.family}" />
|
||||
</target>
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.apache.james.mime4j.stream.MimeConfig;
|
||||
import org.apache.tika.parser.txt.CharsetDetector;
|
||||
import org.apache.tika.parser.txt.CharsetMatch;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.EncodedFileOutputStream;
|
||||
@@ -267,8 +268,18 @@ class MboxParser {
|
||||
* @param email
|
||||
* @param e
|
||||
*/
|
||||
@NbBundle.Messages ({"MboxParser.handleAttch.noOpenCase.errMsg=Exception while getting open case."})
|
||||
private void handleAttachment(EmailMessage email, Entity e, long fileID, int index) {
|
||||
String outputDirPath = ThunderbirdMboxFileIngestModule.getModuleOutputPath() + File.separator;
|
||||
String outputDirPath;
|
||||
String relModuleOutputPath;
|
||||
try {
|
||||
outputDirPath = ThunderbirdMboxFileIngestModule.getModuleOutputPath() + File.separator;
|
||||
relModuleOutputPath = ThunderbirdMboxFileIngestModule.getRelModuleOutputPath() + File.separator;
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
addErrorMessage(Bundle.MboxParser_handleAttch_noOpenCase_errMsg());
|
||||
logger.log(Level.SEVERE, Bundle.MboxParser_handleAttch_noOpenCase_errMsg(), ex); //NON-NLS
|
||||
return;
|
||||
}
|
||||
String filename = e.getFilename();
|
||||
|
||||
// sanitize name. Had an attachment with a Japanese encoded path that
|
||||
@@ -325,8 +336,7 @@ class MboxParser {
|
||||
|
||||
EmailMessage.Attachment attach = new EmailMessage.Attachment();
|
||||
attach.setName(filename);
|
||||
attach.setLocalPath(ThunderbirdMboxFileIngestModule.getRelModuleOutputPath()
|
||||
+ File.separator + uniqueFilename);
|
||||
attach.setLocalPath(relModuleOutputPath + uniqueFilename);
|
||||
attach.setSize(new File(outPath).length());
|
||||
attach.setEncodingType(TskData.EncodingType.XOR1);
|
||||
email.addAttachment(attach);
|
||||
|
||||
@@ -33,6 +33,8 @@ import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModule;
|
||||
import org.sleuthkit.autopsy.ingest.IngestMonitor;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import static org.sleuthkit.autopsy.thunderbirdparser.ThunderbirdMboxFileIngestModule.getRelModuleOutputPath;
|
||||
@@ -204,9 +206,16 @@ class PstParser {
|
||||
* @param email
|
||||
* @param msg
|
||||
*/
|
||||
@NbBundle.Messages({"PstParser.noOpenCase.errMsg=Exception while getting open case."})
|
||||
private void extractAttachments(EmailMessage email, PSTMessage msg, long fileID) {
|
||||
int numberOfAttachments = msg.getNumberOfAttachments();
|
||||
String outputDirPath = ThunderbirdMboxFileIngestModule.getModuleOutputPath() + File.separator;
|
||||
String outputDirPath;
|
||||
try {
|
||||
outputDirPath = ThunderbirdMboxFileIngestModule.getModuleOutputPath() + File.separator;
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
return;
|
||||
}
|
||||
for (int x = 0; x < numberOfAttachments; x++) {
|
||||
String filename = "";
|
||||
try {
|
||||
@@ -246,6 +255,9 @@ class PstParser {
|
||||
NbBundle.getMessage(this.getClass(), "PstParser.extractAttch.errMsg.failedToExtractToDisk",
|
||||
filename));
|
||||
logger.log(Level.WARNING, "Failed to extract attachment from pst file.", ex); //NON-NLS
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
addErrorMessage(Bundle.PstParser_noOpenCase_errMsg());
|
||||
logger.log(Level.SEVERE, Bundle.PstParser_noOpenCase_errMsg(), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+73
-20
@@ -31,6 +31,7 @@ import java.util.regex.Pattern;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
|
||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@@ -74,15 +75,26 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Messages ({"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
|
||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||
this.context = context;
|
||||
fileManager = Case.getCurrentCase().getServices().getFileManager();
|
||||
try {
|
||||
fileManager = Case.getOpenCase().getServices().getFileManager();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
throw new IngestModuleException(Bundle.ThunderbirdMboxFileIngestModule_noOpenCase_errMsg(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessResult process(AbstractFile abstractFile) {
|
||||
|
||||
blackboard = Case.getCurrentCase().getServices().getBlackboard();
|
||||
try {
|
||||
blackboard = Case.getOpenCase().getServices().getBlackboard();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
// skip known
|
||||
if (abstractFile.getKnown().equals(TskData.FileKnown.KNOWN)) {
|
||||
@@ -133,8 +145,14 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
*/
|
||||
@Messages({"ThunderbirdMboxFileIngestModule.processPst.indexError.message=Failed to index encryption detected artifact for keyword search."})
|
||||
private ProcessResult processPst(AbstractFile abstractFile) {
|
||||
String fileName = getTempPath() + File.separator + abstractFile.getName()
|
||||
String fileName;
|
||||
try {
|
||||
fileName = getTempPath() + File.separator + abstractFile.getName()
|
||||
+ "-" + String.valueOf(abstractFile.getId());
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
File file = new File(fileName);
|
||||
|
||||
long freeSpace = services.getFreeDiskSpace();
|
||||
@@ -159,8 +177,14 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
PstParser.ParseResult result = parser.parse(file, abstractFile.getId());
|
||||
|
||||
if (result == PstParser.ParseResult.OK) {
|
||||
// parse success: Process email and add artifacts
|
||||
processEmails(parser.getResults(), abstractFile);
|
||||
try {
|
||||
// parse success: Process email and add artifacts
|
||||
processEmails(parser.getResults(), abstractFile);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
} else if (result == PstParser.ParseResult.ENCRYPT) {
|
||||
// encrypted pst: Add encrypted file artifact
|
||||
try {
|
||||
@@ -225,8 +249,14 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
emailFolder = emailFolder + mboxFileName;
|
||||
emailFolder = emailFolder.replaceAll(".sbd", ""); //NON-NLS
|
||||
|
||||
String fileName = getTempPath() + File.separator + abstractFile.getName()
|
||||
String fileName;
|
||||
try {
|
||||
fileName = getTempPath() + File.separator + abstractFile.getName()
|
||||
+ "-" + String.valueOf(abstractFile.getId());
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
File file = new File(fileName);
|
||||
|
||||
long freeSpace = services.getFreeDiskSpace();
|
||||
@@ -249,7 +279,12 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
|
||||
MboxParser parser = new MboxParser(services, emailFolder);
|
||||
List<EmailMessage> emails = parser.parse(file, abstractFile.getId());
|
||||
processEmails(emails, abstractFile);
|
||||
try {
|
||||
processEmails(emails, abstractFile);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
if (file.delete() == false) {
|
||||
logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
|
||||
@@ -268,10 +303,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
/**
|
||||
* Get a path to a temporary folder.
|
||||
*
|
||||
* @return
|
||||
* @throws NoCurrentCaseException if there is no open case.
|
||||
* @return the temporary folder
|
||||
*/
|
||||
public static String getTempPath() {
|
||||
String tmpDir = Case.getCurrentCase().getTempDirectory() + File.separator
|
||||
public static String getTempPath() throws NoCurrentCaseException {
|
||||
String tmpDir = Case.getOpenCase().getTempDirectory() + File.separator
|
||||
+ "EmailParser"; //NON-NLS
|
||||
File dir = new File(tmpDir);
|
||||
if (dir.exists() == false) {
|
||||
@@ -280,8 +316,14 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
return tmpDir;
|
||||
}
|
||||
|
||||
public static String getModuleOutputPath() {
|
||||
String outDir = Case.getCurrentCase().getModuleDirectory() + File.separator
|
||||
/**
|
||||
* Get a module output folder.
|
||||
*
|
||||
* @throws NoCurrentCaseException if there is no open case.
|
||||
* @return the module output folder
|
||||
*/
|
||||
public static String getModuleOutputPath() throws NoCurrentCaseException {
|
||||
String outDir = Case.getOpenCase().getModuleDirectory() + File.separator
|
||||
+ EmailParserModuleFactory.getModuleName();
|
||||
File dir = new File(outDir);
|
||||
if (dir.exists() == false) {
|
||||
@@ -290,8 +332,14 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
return outDir;
|
||||
}
|
||||
|
||||
public static String getRelModuleOutputPath() {
|
||||
return Case.getCurrentCase().getModuleOutputDirectoryRelativePath() + File.separator
|
||||
/**
|
||||
* Get a relative path of a module output folder.
|
||||
*
|
||||
* @throws NoCurrentCaseException if there is no open case.
|
||||
* @return the relative path of the module output folder
|
||||
*/
|
||||
public static String getRelModuleOutputPath() throws NoCurrentCaseException {
|
||||
return Case.getOpenCase().getModuleOutputDirectoryRelativePath() + File.separator
|
||||
+ EmailParserModuleFactory.getModuleName();
|
||||
}
|
||||
|
||||
@@ -301,8 +349,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
*
|
||||
* @param emails
|
||||
* @param abstractFile
|
||||
* @throws NoCurrentCaseException if there is no open case.
|
||||
*/
|
||||
private void processEmails(List<EmailMessage> emails, AbstractFile abstractFile) {
|
||||
private void processEmails(List<EmailMessage> emails, AbstractFile abstractFile) throws NoCurrentCaseException {
|
||||
List<AbstractFile> derivedFiles = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -386,9 +435,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
*
|
||||
* @param email
|
||||
* @param abstractFile
|
||||
* @throws NoCurrentCaseException if there is no open case.
|
||||
*/
|
||||
@Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
|
||||
private BlackboardArtifact addArtifact(EmailMessage email, AbstractFile abstractFile) {
|
||||
private BlackboardArtifact addArtifact(EmailMessage email, AbstractFile abstractFile) throws NoCurrentCaseException {
|
||||
BlackboardArtifact bbart = null;
|
||||
List<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||
String to = email.getRecipients();
|
||||
@@ -408,11 +458,14 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
String senderAddress;
|
||||
senderAddressList.addAll(findEmailAddresess(from));
|
||||
|
||||
AccountFileInstance senderAccountInstance = null;
|
||||
AccountFileInstance senderAccountInstance = null;
|
||||
|
||||
Case openCase = Case.getOpenCase();
|
||||
|
||||
if (senderAddressList.size() == 1) {
|
||||
senderAddress = senderAddressList.get(0);
|
||||
try {
|
||||
senderAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, senderAddress, EmailParserModuleFactory.getModuleName(), abstractFile);
|
||||
senderAccountInstance = openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, senderAddress, EmailParserModuleFactory.getModuleName(), abstractFile);
|
||||
}
|
||||
catch(TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS
|
||||
@@ -431,7 +484,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
recipientAddresses.forEach((addr) -> {
|
||||
try {
|
||||
AccountFileInstance recipientAccountInstance =
|
||||
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr,
|
||||
openCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr,
|
||||
EmailParserModuleFactory.getModuleName(), abstractFile);
|
||||
recipientAccountInstances.add(recipientAccountInstance);
|
||||
}
|
||||
@@ -467,7 +520,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
bbart.addAttributes(bbattributes);
|
||||
|
||||
// Add account relationships
|
||||
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL);
|
||||
openCase.getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL);
|
||||
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Verifies programs are installed and copies native code into the Autopsy folder structure
|
||||
|
||||
TSK_VERSION=4.6.0
|
||||
|
||||
# Verify PhotoRec was installed
|
||||
photorec_filepath=/usr/bin/photorec
|
||||
if [ -f "$photorec_filepath" ]; then
|
||||
echo "$photorec_filepath found"
|
||||
else
|
||||
echo "ERROR: Photorec not found, please install the testdisk package"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify Java was installed and configured
|
||||
if [ -n "$JAVA_HOME" ]; then
|
||||
if [ -x "$JAVA_HOME/bin/java" ]; then
|
||||
echo "Java found in $JAVA_HOME"
|
||||
else
|
||||
echo "ERROR: Java was not found in $JAVA_HOME"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "ERROR: JAVA_HOME environment variable must be defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify Sleuth Kit Java was installed
|
||||
sleuthkit_jar_filepath=/usr/share/java/sleuthkit-$TSK_VERSION.jar;
|
||||
ext_jar_filepath=$PWD/autopsy/modules/ext/sleuthkit-postgresql-$TSK_VERSION.jar;
|
||||
if [ -f "$sleuthkit_jar_filepath" ]; then
|
||||
echo "$sleuthkit_jar_filepath found"
|
||||
echo "Copying into the Autopsy directory"
|
||||
rm $ext_jar_filepath;
|
||||
if [ "$?" -gt 0 ]; then #checking if remove operation failed
|
||||
echo "exiting .."
|
||||
exit 1
|
||||
else
|
||||
cp $sleuthkit_jar_filepath $ext_jar_filepath
|
||||
if [ "$?" -ne 0 ]; then # checking copy operation was successful
|
||||
echo "exiting..."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "ERROR: $sleuthkit_jar_filepath not found, please install the sleuthkit-java.deb file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# make sure it is executable
|
||||
chmod +x bin/autopsy
|
||||
|
||||
echo "Autopsy is now configured. You can execute bin/autopsy to start it"
|
||||
Reference in New Issue
Block a user