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.out.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());
161 String caseTypeString = inputs.get(CommandLineCommand.InputType.CASE_TYPE.name());
165 openCase(baseCaseName, rootOutputDirectory, caseType);
168 OutputGenerator.saveCreateCaseOutput(caseForJob, outputDirPath, baseCaseName);
170 String baseCaseName = command.getInputs().get(CommandLineCommand.InputType.CASE_NAME.name());
171 LOGGER.log(Level.SEVERE,
"Error creating or opening case " + baseCaseName, ex);
172 System.out.println(
"Error creating or opening case " + baseCaseName);
177 case ADD_DATA_SOURCE:
179 LOGGER.log(Level.INFO,
"Processing 'Add Data Source' command");
180 System.out.println(
"Processing 'Add Data Source' command");
181 Map<String, String> inputs = command.getInputs();
184 if (caseForJob == null) {
185 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
189 String dataSourcePath = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
194 OutputGenerator.saveAddDataSourceOutput(caseForJob, dataSource, outputDirPath);
196 String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
197 LOGGER.log(Level.SEVERE,
"Error adding data source " + dataSourcePath, ex);
198 System.out.println(
"Error adding data source " + dataSourcePath);
205 LOGGER.log(Level.INFO,
"Processing 'Run Ingest' command");
206 System.out.println(
"Processing 'Run Ingest' command");
207 Map<String, String> inputs = command.getInputs();
210 if (caseForJob == null) {
211 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
216 if (dataSource == null) {
218 String dataSourceId = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_ID.name());
219 Long dataSourceObjId = Long.valueOf(dataSourceId);
222 Content content = null;
225 }
catch (TskCoreException ex) {
226 LOGGER.log(Level.SEVERE,
"Exception while trying to find data source with object ID " + dataSourceId, ex);
227 System.out.println(
"Exception while trying to find data source with object ID " + dataSourceId);
232 if (content == null) {
233 LOGGER.log(Level.SEVERE,
"Unable to find data source with object ID {0}", dataSourceId);
234 System.out.println(
"Unable to find data source with object ID " + dataSourceId);
241 List<Content> contentList = Arrays.asList(
new Content[]{content});
242 List<String> errorList =
new ArrayList<>();
247 String ingestProfile = inputs.get(CommandLineCommand.InputType.INGEST_PROFILE_NAME.name());
248 analyze(dataSource, ingestProfile);
250 String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
251 LOGGER.log(Level.SEVERE,
"Error running ingest on data source " + dataSourcePath, ex);
252 System.out.println(
"Error running ingest on data source " + dataSourcePath);
258 case LIST_ALL_DATA_SOURCES:
260 LOGGER.log(Level.INFO,
"Processing 'List All Data Sources' command");
261 System.out.println(
"Processing 'List All Data Sources' command");
262 Map<String, String> inputs = command.getInputs();
265 if (caseForJob == null) {
266 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
271 OutputGenerator.listAllDataSources(caseForJob, outputDirPath);
273 String caseDirPath = command.getInputs().get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
274 LOGGER.log(Level.SEVERE,
"Error opening case in case directory: " + caseDirPath, ex);
275 System.out.println(
"Error opening case in case directory: " + caseDirPath);
281 case GENERATE_REPORTS:
283 LOGGER.log(Level.INFO,
"Processing 'Generate Reports' command");
284 System.out.println(
"Processing 'Generate Reports' command");
285 Map<String, String> inputs = command.getInputs();
288 if (caseForJob == null) {
289 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
293 String reportName = inputs.get(CommandLineCommand.InputType.REPORT_PROFILE_NAME.name());
294 if (reportName == null) {
303 String caseDirPath = command.getInputs().get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
304 LOGGER.log(Level.SEVERE,
"Error opening case in case directory: " + caseDirPath, ex);
305 System.out.println(
"Error opening case in case directory: " + caseDirPath);
314 }
catch (Throwable ex) {
322 LOGGER.log(Level.SEVERE,
"Unexpected error", ex);
323 System.out.println(
"Unexpected error. Exiting...");
329 LOGGER.log(Level.WARNING,
"Exception while closing case", ex);
330 System.out.println(
"Exception while closing case");
335 LOGGER.log(Level.INFO,
"Job processing task finished");
336 System.out.println(
"Job processing task finished");
356 LOGGER.log(Level.INFO,
"Opening case {0} in directory {1}",
new Object[]{baseCaseName, rootOutputDirectory});
357 Path caseDirectoryPath =
findCaseDirectory(Paths.get(rootOutputDirectory), baseCaseName);
358 if (null != caseDirectoryPath) {
360 LOGGER.log(Level.SEVERE,
"Case {0} already exists. Case name must be unique. Exiting", baseCaseName);
361 throw new CaseActionException(
"Case " + baseCaseName +
" already exists. Case name must be unique. Exiting");
373 LOGGER.log(Level.INFO,
"Opened case {0}", caseForJob.
getName());
391 LOGGER.log(Level.INFO,
"Adding data source {0} ", dataSource.
getPath().toString());
394 List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
398 LOGGER.log(Level.SEVERE,
"Exception while determining best data source processor for {0}", dataSource.
getPath());
404 if (validDataSourceProcessors.isEmpty()) {
406 LOGGER.log(Level.SEVERE,
"Unsupported data source {0}", dataSource.
getPath());
414 UUID taskId = UUID.randomUUID();
418 LOGGER.log(Level.INFO,
"Identified data source type for {0} as {1}",
new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
419 selectedProcessor.process(dataSource.
getDeviceId(), dataSource.
getPath(), progressMonitor, callBack);
435 LOGGER.log(Level.SEVERE,
"All data source processors failed to process {0}", dataSource.
getPath());
450 if (null != resultCode) {
451 switch (resultCode) {
453 LOGGER.log(Level.INFO,
"Added data source to case");
455 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
459 case NONCRITICAL_ERRORS:
461 LOGGER.log(Level.WARNING,
"Non-critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
463 LOGGER.log(Level.INFO,
"Added data source to case");
465 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
469 case CRITICAL_ERRORS:
471 LOGGER.log(Level.SEVERE,
"Critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
473 LOGGER.log(Level.SEVERE,
"Failed to add data source to case");
477 LOGGER.log(Level.WARNING,
"No result code for data source processor for {0}", dataSource.
getPath());
500 LOGGER.log(Level.INFO,
"Starting ingest modules analysis for {0} ", dataSource.
getPath());
505 if (!ingestProfileName.isEmpty()) {
507 if (selectedProfile == null) {
509 LOGGER.log(Level.SEVERE,
"Unable to find ingest profile: {0}. Ingest cancelled!", ingestProfileName);
510 System.out.println(
"Unable to find ingest profile: " + ingestProfileName +
". Ingest cancelled!");
516 if (selectedFileSet == null) {
518 LOGGER.log(Level.SEVERE,
"Unable to find file filter {0} for ingest profile: {1}. Ingest cancelled!",
new Object[]{selectedProfile.getFileIngestFilter(), ingestProfileName});
519 System.out.println(
"Unable to find file filter " + selectedProfile.getFileIngestFilter() +
" for ingest profile: " + ingestProfileName +
". Ingest cancelled!");
529 if (selectedProfile == null || selectedFileSet == null) {
538 List<String> settingsWarnings = ingestJobSettings.
getWarnings();
539 if (settingsWarnings.isEmpty()) {
542 if (null != ingestJob) {
549 LOGGER.log(Level.INFO,
"Finished ingest modules analysis for {0} ", dataSource.
getPath());
552 if (!snapshot.isCancelled()) {
553 List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
554 if (!cancelledModules.isEmpty()) {
555 LOGGER.log(Level.WARNING, String.format(
"Ingest module(s) cancelled for %s", dataSource.
getPath()));
556 for (String module : snapshot.getCancelledDataSourceIngestModules()) {
557 LOGGER.log(Level.WARNING, String.format(
"%s ingest module cancelled for %s", module, dataSource.
getPath()));
560 LOGGER.log(Level.INFO,
"Analysis of data source completed");
562 LOGGER.log(Level.WARNING,
"Analysis of data source cancelled");
565 throw new AnalysisStartupException(String.format(
"Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), dataSource.
getPath()));
571 LOGGER.log(Level.SEVERE, String.format(
"%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.
getPath()), error.getThrowable());
573 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to ingest job startup error");
574 throw new AnalysisStartupException(String.format(
"Error(s) during ingest module startup for %s", dataSource.
getPath()));
576 LOGGER.log(Level.SEVERE, String.format(
"Ingest manager ingest job start error for %s", dataSource.
getPath()), ingestJobStartResult.
getStartupException());
577 throw new AnalysisStartupException(
"Ingest manager error starting job", ingestJobStartResult.
getStartupException());
580 for (String warning : settingsWarnings) {
581 LOGGER.log(Level.SEVERE,
"Ingest job settings error for {0}: {1}",
new Object[]{dataSource.getPath(), warning});
583 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to settings errors");
584 throw new AnalysisStartupException(
"Error(s) in ingest job settings");
605 if (profile.toString().equalsIgnoreCase(ingestProfileName)) {
607 selectedProfile = profile;
611 return selectedProfile;
627 fileIngestFilters.put(fSet.getName(), fSet);
629 return fileIngestFilters.get(filterName);
631 LOGGER.log(Level.SEVERE,
"Failed to get file ingest filter: " + filterName, ex);
647 return Paths.get(caseFoldersPath.toString(), folderName);
661 File searchFolder =
new File(folderToSearch.toString());
662 if (!searchFolder.isDirectory()) {
665 Path caseFolderPath = null;
666 String[] candidateFolders = searchFolder.list(
new CaseFolderFilter(caseName));
667 long mostRecentModified = 0;
668 for (String candidateFolder : candidateFolders) {
669 File file =
new File(candidateFolder);
670 if (file.lastModified() >= mostRecentModified) {
671 mostRecentModified = file.lastModified();
672 caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
675 return caseFolderPath;
710 String eventType =
event.getPropertyName();
760 private final class AnalysisStartupException
extends Exception {
769 super(message, cause);
784 public boolean accept(File folder, String fileName) {
785 File file =
new File(folder, fileName);
788 if (null != caseName) {
790 if (fileNamePrefix.equals(caseName)) {
810 for (File file : folder.listFiles()) {
811 if (file.getName().toLowerCase().endsWith(CASE_METADATA_EXT) && file.isFile()) {
synchronized List< String > getDataSourceProcessorErrorMessages()
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()
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)
static boolean hasCaseMetadataFile(File folder)
static Case getCurrentCase()
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
void setIndeterminate(final boolean indeterminate)
static String getCommandLineModeIngestModuleContextString()
void setProgress(final int progress)
void notifyAddingDataSource(UUID eventId)
static String getDefaultReportingConfigName()
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
IngestManager.IngestManagerException getStartupException()