19 package org.sleuthkit.autopsy.commandlineingest;
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
24 import java.io.FilenameFilter;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.List;
30 import java.util.UUID;
31 import java.util.Collection;
32 import java.util.EnumSet;
33 import java.util.Iterator;
36 import java.util.logging.Level;
37 import org.netbeans.spi.sendopts.OptionProcessor;
38 import org.openide.LifecycleManager;
39 import org.openide.util.Lookup;
96 LOGGER.log(Level.WARNING,
"Unable to close the case while shutting down command line ingest manager", ex);
100 LifecycleManager.getDefault().exit();
108 ingestLock =
new Object();
111 LOGGER.log(Level.INFO,
"Set running with desktop GUI runtime property to false");
113 LOGGER.log(Level.SEVERE,
"Failed to set running with desktop GUI runtime property to false", ex);
123 LOGGER.log(Level.INFO,
"Job processing task started");
127 LOGGER.log(Level.INFO,
"Autopsy is running from command line");
128 List<CommandLineCommand> commands = null;
131 Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
132 Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
133 while (optionsIterator.hasNext()) {
135 OptionProcessor processor = optionsIterator.next();
138 commands = ((CommandLineOptionProcessor) processor).getCommands();
142 if (commands == null || commands.isEmpty()) {
143 LOGGER.log(Level.SEVERE,
"No command line commands specified");
144 System.err.println(
"No command line commands specified");
150 for (CommandLineCommand command : commands) {
151 CommandLineCommand.CommandType type = command.getType();
155 LOGGER.log(Level.INFO,
"Processing 'Create Case' command");
156 System.out.println(
"Processing 'Create Case' command");
157 Map<String, String> inputs = command.getInputs();
158 String baseCaseName = inputs.get(CommandLineCommand.InputType.CASE_NAME.name());
159 String rootOutputDirectory = inputs.get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
160 openCase(baseCaseName, rootOutputDirectory);
163 OutputGenerator.saveCreateCaseOutput(caseForJob, outputDirPath, baseCaseName);
165 String baseCaseName = command.getInputs().get(CommandLineCommand.InputType.CASE_NAME.name());
166 LOGGER.log(Level.SEVERE,
"Error creating or opening case " + baseCaseName, ex);
167 System.err.println(
"Error creating or opening case " + baseCaseName);
172 case ADD_DATA_SOURCE:
174 LOGGER.log(Level.INFO,
"Processing 'Add Data Source' command");
175 System.out.println(
"Processing 'Add Data Source' command");
176 Map<String, String> inputs = command.getInputs();
179 if (caseForJob == null) {
180 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
184 String dataSourcePath = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
189 OutputGenerator.saveAddDataSourceOutput(caseForJob, dataSource, outputDirPath);
191 String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
192 LOGGER.log(Level.SEVERE,
"Error adding data source " + dataSourcePath, ex);
193 System.err.println(
"Error adding data source " + dataSourcePath);
200 LOGGER.log(Level.INFO,
"Processing 'Run Ingest' command");
201 System.out.println(
"Processing 'Run Ingest' command");
202 Map<String, String> inputs = command.getInputs();
205 if (caseForJob == null) {
206 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
211 if (dataSource == null) {
213 String dataSourceId = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_ID.name());
214 Long dataSourceObjId = Long.valueOf(dataSourceId);
217 Content content = null;
220 }
catch (TskCoreException ex) {
221 LOGGER.log(Level.SEVERE,
"Exception while trying to find data source with object ID " + dataSourceId, ex);
222 System.err.println(
"Exception while trying to find data source with object ID " + dataSourceId);
227 if (content == null) {
228 LOGGER.log(Level.SEVERE,
"Unable to find data source with object ID {0}", dataSourceId);
229 System.out.println(
"Unable to find data source with object ID " + dataSourceId);
236 List<Content> contentList = Arrays.asList(
new Content[]{content});
237 List<String> errorList =
new ArrayList<>();
242 String ingestProfile = inputs.get(CommandLineCommand.InputType.INGEST_PROFILE_NAME.name());
243 analyze(dataSource, ingestProfile);
245 String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
246 LOGGER.log(Level.SEVERE,
"Error running ingest on data source " + dataSourcePath, ex);
247 System.err.println(
"Error running ingest on data source " + dataSourcePath);
253 case LIST_ALL_DATA_SOURCES:
255 LOGGER.log(Level.INFO,
"Processing 'List All Data Sources' command");
256 System.out.println(
"Processing 'List All Data Sources' command");
257 Map<String, String> inputs = command.getInputs();
260 if (caseForJob == null) {
261 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
266 OutputGenerator.listAllDataSources(caseForJob, outputDirPath);
268 String caseDirPath = command.getInputs().get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
269 LOGGER.log(Level.SEVERE,
"Error opening case in case directory: " + caseDirPath, ex);
270 System.err.println(
"Error opening case in case directory: " + caseDirPath);
276 case GENERATE_REPORTS:
278 LOGGER.log(Level.INFO,
"Processing 'Generate Reports' command");
279 System.out.println(
"Processing 'Generate Reports' command");
280 Map<String, String> inputs = command.getInputs();
283 if (caseForJob == null) {
284 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
293 String caseDirPath = command.getInputs().get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
294 LOGGER.log(Level.SEVERE,
"Error opening case in case directory: " + caseDirPath, ex);
295 System.err.println(
"Error opening case in case directory: " + caseDirPath);
304 }
catch (Throwable ex) {
312 LOGGER.log(Level.SEVERE,
"Unexpected error", ex);
313 System.err.println(
"Unexpected error. Exiting...");
319 LOGGER.log(Level.WARNING,
"Exception while closing case", ex);
320 System.err.println(
"Exception while closing case");
325 LOGGER.log(Level.INFO,
"Job processing task finished");
326 System.out.println(
"Job processing task finished");
345 LOGGER.log(Level.INFO,
"Opening case {0} in directory {1}",
new Object[]{baseCaseName, rootOutputDirectory});
346 Path caseDirectoryPath =
findCaseDirectory(Paths.get(rootOutputDirectory), baseCaseName);
347 if (null != caseDirectoryPath) {
349 LOGGER.log(Level.SEVERE,
"Case {0} already exists. Case name must be unique. Exiting", baseCaseName);
350 throw new CaseActionException(
"Case " + baseCaseName +
" already exists. Case name must be unique. Exiting");
362 LOGGER.log(Level.INFO,
"Opened case {0}", caseForJob.
getName());
374 LOGGER.log(Level.INFO,
"Opening case in directory {0}", caseFolderPath);
376 String metadataFilePath =
findAutFile(caseFolderPath);
380 LOGGER.log(Level.INFO,
"Opened case {0}", caseForJob.
getName());
393 File caseFolder = Paths.get(caseDirectory).toFile();
394 if (caseFolder.exists()) {
398 File[] fileArray = caseFolder.listFiles();
399 if (fileArray == null) {
402 String autFilePath = null;
403 for (File file : fileArray) {
404 String name = file.getName().toLowerCase();
405 if (autFilePath == null && name.endsWith(getFileExtension())) {
406 return file.getAbsolutePath();
435 LOGGER.log(Level.INFO,
"Adding data source {0} ", dataSource.
getPath().toString());
438 List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
442 LOGGER.log(Level.SEVERE,
"Exception while determining best data source processor for {0}", dataSource.
getPath());
448 if (validDataSourceProcessors.isEmpty()) {
450 LOGGER.log(Level.SEVERE,
"Unsupported data source {0}", dataSource.
getPath());
458 UUID taskId = UUID.randomUUID();
462 LOGGER.log(Level.INFO,
"Identified data source type for {0} as {1}",
new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
463 selectedProcessor.process(dataSource.
getDeviceId(), dataSource.
getPath(), progressMonitor, callBack);
479 LOGGER.log(Level.SEVERE,
"All data source processors failed to process {0}", dataSource.
getPath());
494 if (null != resultCode) {
495 switch (resultCode) {
497 LOGGER.log(Level.INFO,
"Added data source to case");
499 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
503 case NONCRITICAL_ERRORS:
505 LOGGER.log(Level.WARNING,
"Non-critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
507 LOGGER.log(Level.INFO,
"Added data source to case");
509 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
513 case CRITICAL_ERRORS:
515 LOGGER.log(Level.SEVERE,
"Critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
517 LOGGER.log(Level.SEVERE,
"Failed to add data source to case");
521 LOGGER.log(Level.WARNING,
"No result code for data source processor for {0}", dataSource.
getPath());
544 LOGGER.log(Level.INFO,
"Starting ingest modules analysis for {0} ", dataSource.
getPath());
549 if (!ingestProfileName.isEmpty()) {
551 if (selectedProfile == null) {
553 LOGGER.log(Level.SEVERE,
"Unable to find ingest profile: {0}. Ingest cancelled!", ingestProfileName);
554 System.err.println(
"Unable to find ingest profile: " + ingestProfileName +
". Ingest cancelled!");
560 if (selectedFileSet == null) {
562 LOGGER.log(Level.SEVERE,
"Unable to find file filter {0} for ingest profile: {1}. Ingest cancelled!",
new Object[]{selectedProfile.getFileIngestFilter(), ingestProfileName});
563 System.err.println(
"Unable to find file filter " + selectedProfile.getFileIngestFilter() +
" for ingest profile: " + ingestProfileName +
". Ingest cancelled!");
573 if (selectedProfile == null || selectedFileSet == null) {
582 List<String> settingsWarnings = ingestJobSettings.
getWarnings();
583 if (settingsWarnings.isEmpty()) {
586 if (null != ingestJob) {
593 LOGGER.log(Level.INFO,
"Finished ingest modules analysis for {0} ", dataSource.
getPath());
596 if (!snapshot.isCancelled()) {
597 List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
598 if (!cancelledModules.isEmpty()) {
599 LOGGER.log(Level.WARNING, String.format(
"Ingest module(s) cancelled for %s", dataSource.
getPath()));
600 for (String module : snapshot.getCancelledDataSourceIngestModules()) {
601 LOGGER.log(Level.WARNING, String.format(
"%s ingest module cancelled for %s", module, dataSource.
getPath()));
604 LOGGER.log(Level.INFO,
"Analysis of data source completed");
606 LOGGER.log(Level.WARNING,
"Analysis of data source cancelled");
609 throw new AnalysisStartupException(String.format(
"Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), dataSource.
getPath()));
615 LOGGER.log(Level.SEVERE, String.format(
"%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.
getPath()), error.getThrowable());
617 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to ingest job startup error");
618 throw new AnalysisStartupException(String.format(
"Error(s) during ingest module startup for %s", dataSource.
getPath()));
620 LOGGER.log(Level.SEVERE, String.format(
"Ingest manager ingest job start error for %s", dataSource.
getPath()), ingestJobStartResult.
getStartupException());
621 throw new AnalysisStartupException(
"Ingest manager error starting job", ingestJobStartResult.
getStartupException());
624 for (String warning : settingsWarnings) {
625 LOGGER.log(Level.SEVERE,
"Ingest job settings error for {0}: {1}",
new Object[]{dataSource.getPath(), warning});
627 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to settings errors");
628 throw new AnalysisStartupException(
"Error(s) in ingest job settings");
649 if (profile.toString().equalsIgnoreCase(ingestProfileName)) {
651 selectedProfile = profile;
655 return selectedProfile;
671 fileIngestFilters.put(fSet.getName(), fSet);
673 return fileIngestFilters.get(filterName);
675 LOGGER.log(Level.SEVERE,
"Failed to get file ingest filter: " + filterName, ex);
691 return Paths.get(caseFoldersPath.toString(), folderName);
705 File searchFolder =
new File(folderToSearch.toString());
706 if (!searchFolder.isDirectory()) {
709 Path caseFolderPath = null;
710 String[] candidateFolders = searchFolder.list(
new CaseFolderFilter(caseName));
711 long mostRecentModified = 0;
712 for (String candidateFolder : candidateFolders) {
713 File file =
new File(candidateFolder);
714 if (file.lastModified() >= mostRecentModified) {
715 mostRecentModified = file.lastModified();
716 caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
719 return caseFolderPath;
754 String eventType =
event.getPropertyName();
804 private final class AnalysisStartupException
extends Exception {
813 super(message, cause);
828 public boolean accept(File folder, String fileName) {
829 File file =
new File(folder, fileName);
832 if (null != caseName) {
834 if (fileNamePrefix.equals(caseName)) {
854 for (File file : folder.listFiles()) {
855 if (file.getName().toLowerCase().endsWith(CASE_METADATA_EXT) && file.isFile()) {
synchronized List< String > getDataSourceProcessorErrorMessages()
void openCase(String caseFolderPath)
static List< AutoIngestDataSourceProcessor > getOrderedListOfDataSourceProcessors(Path dataSourcePath)
void logDataSourceProcessorResult(AutoIngestDataSource dataSource)
static synchronized IngestManager getInstance()
static List< FilesSet > getStandardFileIngestFilters()
static void closeCurrentCase()
static void createCaseDirectory(String caseDirPath, CaseType caseType)
String getCaseDirectory()
IngestProfiles.IngestProfile getSelectedProfile(String ingestProfileName)
static final String CASE_METADATA_EXT
IngestJobStartResult beginIngestJob(Collection< Content > dataSources, IngestJobSettings settings)
AutoIngestDataSource dataSource
AnalysisStartupException(String message)
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode()
CommandLineIngestManager()
static synchronized FilesSetsManager getInstance()
Path findCaseDirectory(Path folderToSearch, String caseName)
Path createCaseFolderPath(Path caseFoldersPath, String caseName)
void runDataSourceProcessor(Case caseForJob, AutoIngestDataSource dataSource)
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
void propertyChange(PropertyChangeEvent event)
static boolean endsWithTimeStamp(String inputString)
static final String LOG_DIR_NAME
static String createTimeStamp()
void removeIngestJobEventListener(final PropertyChangeListener listener)
Map< String, FilesSet > getCustomFileIngestFilters()
List< IngestModuleError > getModuleErrors()
static final Logger LOGGER
static final long serialVersionUID
void addIngestJobEventListener(final PropertyChangeListener listener)
void setFileFilter(FilesSet fileIngestFilter)
void openCase(String baseCaseName, String rootOutputDirectory)
ProgressSnapshot getSnapshot()
String getOutputDirPath(Case caseForJob)
void setProgressText(final String text)
static synchronized List< IngestProfile > getIngestProfiles()
static void openAsCurrentCase(String caseMetadataFilePath)
AnalysisStartupException(String message, Throwable cause)
SleuthkitCase getSleuthkitCase()
FilesSet getSelectedFilter(String filterName)
boolean accept(File folder, String fileName)
void analyze(AutoIngestDataSource dataSource, String ingestProfileName)
static int getTimeStampLength()
static synchronized void setRunningWithGUI(boolean runningWithGUI)
List< String > getWarnings()
synchronized List< Content > getContent()
synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List< String > errorMessages, List< Content > content)
String findAutFile(String caseDirectory)
static boolean hasCaseMetadataFile(File folder)
static Case getCurrentCase()
synchronized static Logger getLogger(String name)
static String getReportingConfigName()
static Case getCurrentCaseThrows()
void setIndeterminate(final boolean indeterminate)
static String getCommandLineModeIngestModuleContextString()
void setProgress(final int progress)
void notifyAddingDataSource(UUID eventId)
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
IngestManager.IngestManagerException getStartupException()