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;
97 LOGGER.log(Level.WARNING,
"Unable to close the case while shutting down command line ingest manager", ex);
101 LifecycleManager.getDefault().exit();
109 ingestLock =
new Object();
112 LOGGER.log(Level.INFO,
"Set running with desktop GUI runtime property to false");
114 LOGGER.log(Level.SEVERE,
"Failed to set running with desktop GUI runtime property to false", ex);
124 LOGGER.log(Level.INFO,
"Job processing task started");
128 LOGGER.log(Level.INFO,
"Autopsy is running from command line");
129 List<CommandLineCommand> commands = null;
132 Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
133 Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
134 while (optionsIterator.hasNext()) {
136 OptionProcessor processor = optionsIterator.next();
139 commands = ((CommandLineOptionProcessor) processor).getCommands();
143 if (commands == null || commands.isEmpty()) {
144 LOGGER.log(Level.SEVERE,
"No command line commands specified");
145 System.err.println(
"No command line commands specified");
151 for (CommandLineCommand command : commands) {
152 CommandLineCommand.CommandType type = command.getType();
156 LOGGER.log(Level.INFO,
"Processing 'Create Case' command");
157 System.out.println(
"Processing 'Create Case' command");
158 Map<String, String> inputs = command.getInputs();
159 String baseCaseName = inputs.get(CommandLineCommand.InputType.CASE_NAME.name());
160 String rootOutputDirectory = inputs.get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
162 String caseTypeString = inputs.get(CommandLineCommand.InputType.CASE_TYPE.name());
166 openCase(baseCaseName, rootOutputDirectory, caseType);
169 OutputGenerator.saveCreateCaseOutput(caseForJob, outputDirPath, baseCaseName);
171 String baseCaseName = command.getInputs().get(CommandLineCommand.InputType.CASE_NAME.name());
172 LOGGER.log(Level.SEVERE,
"Error creating or opening case " + baseCaseName, ex);
173 System.err.println(
"Error creating or opening case " + baseCaseName);
178 case ADD_DATA_SOURCE:
180 LOGGER.log(Level.INFO,
"Processing 'Add Data Source' command");
181 System.out.println(
"Processing 'Add Data Source' command");
182 Map<String, String> inputs = command.getInputs();
185 if (caseForJob == null) {
186 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
190 String dataSourcePath = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
195 OutputGenerator.saveAddDataSourceOutput(caseForJob, dataSource, outputDirPath);
197 String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
198 LOGGER.log(Level.SEVERE,
"Error adding data source " + dataSourcePath, ex);
199 System.err.println(
"Error adding data source " + dataSourcePath);
206 LOGGER.log(Level.INFO,
"Processing 'Run Ingest' command");
207 System.out.println(
"Processing 'Run Ingest' command");
208 Map<String, String> inputs = command.getInputs();
211 if (caseForJob == null) {
212 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
217 if (dataSource == null) {
219 String dataSourceId = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_ID.name());
220 Long dataSourceObjId = Long.valueOf(dataSourceId);
223 Content content = null;
226 }
catch (TskCoreException ex) {
227 LOGGER.log(Level.SEVERE,
"Exception while trying to find data source with object ID " + dataSourceId, ex);
228 System.err.println(
"Exception while trying to find data source with object ID " + dataSourceId);
233 if (content == null) {
234 LOGGER.log(Level.SEVERE,
"Unable to find data source with object ID {0}", dataSourceId);
235 System.out.println(
"Unable to find data source with object ID " + dataSourceId);
242 List<Content> contentList = Arrays.asList(
new Content[]{content});
243 List<String> errorList =
new ArrayList<>();
248 String ingestProfile = inputs.get(CommandLineCommand.InputType.INGEST_PROFILE_NAME.name());
249 analyze(dataSource, ingestProfile);
251 String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
252 LOGGER.log(Level.SEVERE,
"Error running ingest on data source " + dataSourcePath, ex);
253 System.err.println(
"Error running ingest on data source " + dataSourcePath);
259 case LIST_ALL_DATA_SOURCES:
261 LOGGER.log(Level.INFO,
"Processing 'List All Data Sources' command");
262 System.out.println(
"Processing 'List All Data Sources' command");
263 Map<String, String> inputs = command.getInputs();
266 if (caseForJob == null) {
267 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
272 OutputGenerator.listAllDataSources(caseForJob, outputDirPath);
274 String caseDirPath = command.getInputs().get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
275 LOGGER.log(Level.SEVERE,
"Error opening case in case directory: " + caseDirPath, ex);
276 System.err.println(
"Error opening case in case directory: " + caseDirPath);
282 case GENERATE_REPORTS:
284 LOGGER.log(Level.INFO,
"Processing 'Generate Reports' command");
285 System.out.println(
"Processing 'Generate Reports' command");
286 Map<String, String> inputs = command.getInputs();
289 if (caseForJob == null) {
290 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
299 String caseDirPath = command.getInputs().get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
300 LOGGER.log(Level.SEVERE,
"Error opening case in case directory: " + caseDirPath, ex);
301 System.err.println(
"Error opening case in case directory: " + caseDirPath);
310 }
catch (Throwable ex) {
318 LOGGER.log(Level.SEVERE,
"Unexpected error", ex);
319 System.err.println(
"Unexpected error. Exiting...");
325 LOGGER.log(Level.WARNING,
"Exception while closing case", ex);
326 System.err.println(
"Exception while closing case");
331 LOGGER.log(Level.INFO,
"Job processing task finished");
332 System.out.println(
"Job processing task finished");
352 LOGGER.log(Level.INFO,
"Opening case {0} in directory {1}",
new Object[]{baseCaseName, rootOutputDirectory});
353 Path caseDirectoryPath =
findCaseDirectory(Paths.get(rootOutputDirectory), baseCaseName);
354 if (null != caseDirectoryPath) {
356 LOGGER.log(Level.SEVERE,
"Case {0} already exists. Case name must be unique. Exiting", baseCaseName);
357 throw new CaseActionException(
"Case " + baseCaseName +
" already exists. Case name must be unique. Exiting");
369 LOGGER.log(Level.INFO,
"Opened case {0}", caseForJob.
getName());
381 LOGGER.log(Level.INFO,
"Opening case in directory {0}", caseFolderPath);
383 String metadataFilePath =
findAutFile(caseFolderPath);
387 LOGGER.log(Level.INFO,
"Opened case {0}", caseForJob.
getName());
400 File caseFolder = Paths.get(caseDirectory).toFile();
401 if (caseFolder.exists()) {
405 File[] fileArray = caseFolder.listFiles();
406 if (fileArray == null) {
409 String autFilePath = null;
410 for (File file : fileArray) {
411 String name = file.getName().toLowerCase();
412 if (autFilePath == null && name.endsWith(getFileExtension())) {
413 return file.getAbsolutePath();
442 LOGGER.log(Level.INFO,
"Adding data source {0} ", dataSource.
getPath().toString());
445 List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
449 LOGGER.log(Level.SEVERE,
"Exception while determining best data source processor for {0}", dataSource.
getPath());
455 if (validDataSourceProcessors.isEmpty()) {
457 LOGGER.log(Level.SEVERE,
"Unsupported data source {0}", dataSource.
getPath());
465 UUID taskId = UUID.randomUUID();
469 LOGGER.log(Level.INFO,
"Identified data source type for {0} as {1}",
new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
470 selectedProcessor.process(dataSource.
getDeviceId(), dataSource.
getPath(), progressMonitor, callBack);
486 LOGGER.log(Level.SEVERE,
"All data source processors failed to process {0}", dataSource.
getPath());
501 if (null != resultCode) {
502 switch (resultCode) {
504 LOGGER.log(Level.INFO,
"Added data source to case");
506 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
510 case NONCRITICAL_ERRORS:
512 LOGGER.log(Level.WARNING,
"Non-critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
514 LOGGER.log(Level.INFO,
"Added data source to case");
516 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
520 case CRITICAL_ERRORS:
522 LOGGER.log(Level.SEVERE,
"Critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
524 LOGGER.log(Level.SEVERE,
"Failed to add data source to case");
528 LOGGER.log(Level.WARNING,
"No result code for data source processor for {0}", dataSource.
getPath());
551 LOGGER.log(Level.INFO,
"Starting ingest modules analysis for {0} ", dataSource.
getPath());
556 if (!ingestProfileName.isEmpty()) {
558 if (selectedProfile == null) {
560 LOGGER.log(Level.SEVERE,
"Unable to find ingest profile: {0}. Ingest cancelled!", ingestProfileName);
561 System.err.println(
"Unable to find ingest profile: " + ingestProfileName +
". Ingest cancelled!");
567 if (selectedFileSet == null) {
569 LOGGER.log(Level.SEVERE,
"Unable to find file filter {0} for ingest profile: {1}. Ingest cancelled!",
new Object[]{selectedProfile.getFileIngestFilter(), ingestProfileName});
570 System.err.println(
"Unable to find file filter " + selectedProfile.getFileIngestFilter() +
" for ingest profile: " + ingestProfileName +
". Ingest cancelled!");
580 if (selectedProfile == null || selectedFileSet == null) {
589 List<String> settingsWarnings = ingestJobSettings.
getWarnings();
590 if (settingsWarnings.isEmpty()) {
593 if (null != ingestJob) {
600 LOGGER.log(Level.INFO,
"Finished ingest modules analysis for {0} ", dataSource.
getPath());
603 if (!snapshot.isCancelled()) {
604 List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
605 if (!cancelledModules.isEmpty()) {
606 LOGGER.log(Level.WARNING, String.format(
"Ingest module(s) cancelled for %s", dataSource.
getPath()));
607 for (String module : snapshot.getCancelledDataSourceIngestModules()) {
608 LOGGER.log(Level.WARNING, String.format(
"%s ingest module cancelled for %s", module, dataSource.
getPath()));
611 LOGGER.log(Level.INFO,
"Analysis of data source completed");
613 LOGGER.log(Level.WARNING,
"Analysis of data source cancelled");
616 throw new AnalysisStartupException(String.format(
"Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), dataSource.
getPath()));
622 LOGGER.log(Level.SEVERE, String.format(
"%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.
getPath()), error.getThrowable());
624 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to ingest job startup error");
625 throw new AnalysisStartupException(String.format(
"Error(s) during ingest module startup for %s", dataSource.
getPath()));
627 LOGGER.log(Level.SEVERE, String.format(
"Ingest manager ingest job start error for %s", dataSource.
getPath()), ingestJobStartResult.
getStartupException());
628 throw new AnalysisStartupException(
"Ingest manager error starting job", ingestJobStartResult.
getStartupException());
631 for (String warning : settingsWarnings) {
632 LOGGER.log(Level.SEVERE,
"Ingest job settings error for {0}: {1}",
new Object[]{dataSource.getPath(), warning});
634 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to settings errors");
635 throw new AnalysisStartupException(
"Error(s) in ingest job settings");
656 if (profile.toString().equalsIgnoreCase(ingestProfileName)) {
658 selectedProfile = profile;
662 return selectedProfile;
678 fileIngestFilters.put(fSet.getName(), fSet);
680 return fileIngestFilters.get(filterName);
682 LOGGER.log(Level.SEVERE,
"Failed to get file ingest filter: " + filterName, ex);
698 return Paths.get(caseFoldersPath.toString(), folderName);
712 File searchFolder =
new File(folderToSearch.toString());
713 if (!searchFolder.isDirectory()) {
716 Path caseFolderPath = null;
717 String[] candidateFolders = searchFolder.list(
new CaseFolderFilter(caseName));
718 long mostRecentModified = 0;
719 for (String candidateFolder : candidateFolders) {
720 File file =
new File(candidateFolder);
721 if (file.lastModified() >= mostRecentModified) {
722 mostRecentModified = file.lastModified();
723 caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
726 return caseFolderPath;
761 String eventType =
event.getPropertyName();
811 private final class AnalysisStartupException
extends Exception {
820 super(message, cause);
835 public boolean accept(File folder, String fileName) {
836 File file =
new File(folder, fileName);
839 if (null != caseName) {
841 if (fileNamePrefix.equals(caseName)) {
861 for (File file : folder.listFiles()) {
862 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)
void openCase(String baseCaseName, String rootOutputDirectory, CaseType caseType)
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)
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()