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;
95 LOGGER.log(Level.WARNING,
"Unable to close the case while shutting down command line ingest manager", ex);
99 LifecycleManager.getDefault().exit();
107 ingestLock =
new Object();
110 LOGGER.log(Level.INFO,
"Set running with desktop GUI runtime property to false");
112 LOGGER.log(Level.SEVERE,
"Failed to set running with desktop GUI runtime property to false", ex);
122 LOGGER.log(Level.INFO,
"Job processing task started");
126 LOGGER.log(Level.INFO,
"Autopsy is running from command line");
127 List<CommandLineCommand> commands = null;
130 Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
131 Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
132 while (optionsIterator.hasNext()) {
134 OptionProcessor processor = optionsIterator.next();
137 commands = ((CommandLineOptionProcessor) processor).getCommands();
141 if (commands == null || commands.isEmpty()) {
142 LOGGER.log(Level.SEVERE,
"No command line commands specified");
143 System.out.println(
"No command line commands specified");
149 for (CommandLineCommand command : commands) {
150 CommandLineCommand.CommandType type = command.getType();
154 LOGGER.log(Level.INFO,
"Processing 'Create Case' command");
155 System.out.println(
"Processing 'Create Case' command");
156 Map<String, String> inputs = command.getInputs();
157 String baseCaseName = inputs.get(CommandLineCommand.InputType.CASE_NAME.name());
158 String rootOutputDirectory = inputs.get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
160 String caseTypeString = inputs.get(CommandLineCommand.InputType.CASE_TYPE.name());
164 openCase(baseCaseName, rootOutputDirectory, caseType);
167 OutputGenerator.saveCreateCaseOutput(caseForJob, outputDirPath, baseCaseName);
169 String baseCaseName = command.getInputs().get(CommandLineCommand.InputType.CASE_NAME.name());
170 LOGGER.log(Level.SEVERE,
"Error creating or opening case " + baseCaseName, ex);
171 System.out.println(
"Error creating or opening case " + baseCaseName);
176 case ADD_DATA_SOURCE:
178 LOGGER.log(Level.INFO,
"Processing 'Add Data Source' command");
179 System.out.println(
"Processing 'Add Data Source' command");
180 Map<String, String> inputs = command.getInputs();
183 if (caseForJob == null) {
184 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
188 String dataSourcePath = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
193 OutputGenerator.saveAddDataSourceOutput(caseForJob, dataSource, outputDirPath);
195 String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
196 LOGGER.log(Level.SEVERE,
"Error adding data source " + dataSourcePath, ex);
197 System.out.println(
"Error adding data source " + dataSourcePath);
204 LOGGER.log(Level.INFO,
"Processing 'Run Ingest' command");
205 System.out.println(
"Processing 'Run Ingest' command");
206 Map<String, String> inputs = command.getInputs();
209 if (caseForJob == null) {
210 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
215 if (dataSource == null) {
217 String dataSourceId = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_ID.name());
218 Long dataSourceObjId = Long.valueOf(dataSourceId);
221 Content content = null;
224 }
catch (TskCoreException ex) {
225 LOGGER.log(Level.SEVERE,
"Exception while trying to find data source with object ID " + dataSourceId, ex);
226 System.out.println(
"Exception while trying to find data source with object ID " + dataSourceId);
231 if (content == null) {
232 LOGGER.log(Level.SEVERE,
"Unable to find data source with object ID {0}", dataSourceId);
233 System.out.println(
"Unable to find data source with object ID " + dataSourceId);
240 List<Content> contentList = Arrays.asList(
new Content[]{content});
241 List<String> errorList =
new ArrayList<>();
246 String ingestProfile = inputs.get(CommandLineCommand.InputType.INGEST_PROFILE_NAME.name());
247 analyze(dataSource, ingestProfile);
249 String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
250 LOGGER.log(Level.SEVERE,
"Error running ingest on data source " + dataSourcePath, ex);
251 System.out.println(
"Error running ingest on data source " + dataSourcePath);
257 case LIST_ALL_DATA_SOURCES:
259 LOGGER.log(Level.INFO,
"Processing 'List All Data Sources' command");
260 System.out.println(
"Processing 'List All Data Sources' command");
261 Map<String, String> inputs = command.getInputs();
264 if (caseForJob == null) {
265 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
270 OutputGenerator.listAllDataSources(caseForJob, outputDirPath);
272 String caseDirPath = command.getInputs().get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
273 LOGGER.log(Level.SEVERE,
"Error opening case in case directory: " + caseDirPath, ex);
274 System.out.println(
"Error opening case in case directory: " + caseDirPath);
280 case GENERATE_REPORTS:
282 LOGGER.log(Level.INFO,
"Processing 'Generate Reports' command");
283 System.out.println(
"Processing 'Generate Reports' command");
284 Map<String, String> inputs = command.getInputs();
287 if (caseForJob == null) {
288 String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
292 String reportName = inputs.get(CommandLineCommand.InputType.REPORT_PROFILE_NAME.name());
293 if (reportName == null) {
302 String caseDirPath = command.getInputs().get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
303 LOGGER.log(Level.SEVERE,
"Error opening case in case directory: " + caseDirPath, ex);
304 System.out.println(
"Error opening case in case directory: " + caseDirPath);
313 }
catch (Throwable ex) {
321 LOGGER.log(Level.SEVERE,
"Unexpected error", ex);
322 System.out.println(
"Unexpected error. Exiting...");
328 LOGGER.log(Level.WARNING,
"Exception while closing case", ex);
329 System.out.println(
"Exception while closing case");
334 LOGGER.log(Level.INFO,
"Job processing task finished");
335 System.out.println(
"Job processing task finished");
355 LOGGER.log(Level.INFO,
"Opening case {0} in directory {1}",
new Object[]{baseCaseName, rootOutputDirectory});
356 Path caseDirectoryPath =
findCaseDirectory(Paths.get(rootOutputDirectory), baseCaseName);
357 if (null != caseDirectoryPath) {
359 LOGGER.log(Level.SEVERE,
"Case {0} already exists. Case name must be unique. Exiting", baseCaseName);
360 throw new CaseActionException(
"Case " + baseCaseName +
" already exists. Case name must be unique. Exiting");
372 LOGGER.log(Level.INFO,
"Opened case {0}", caseForJob.
getName());
393 LOGGER.log(Level.INFO,
"Adding data source {0} ", dataSource.
getPath().toString());
396 List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
400 LOGGER.log(Level.SEVERE,
"Exception while determining best data source processor for {0}", dataSource.
getPath());
406 if (validDataSourceProcessors.isEmpty()) {
408 LOGGER.log(Level.SEVERE,
"Unsupported data source {0}", dataSource.
getPath());
416 UUID taskId = UUID.randomUUID();
420 LOGGER.log(Level.INFO,
"Identified data source type for {0} as {1}",
new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
421 selectedProcessor.process(dataSource.
getDeviceId(), dataSource.
getPath(), progressMonitor, callBack);
437 LOGGER.log(Level.SEVERE,
"All data source processors failed to process {0}", dataSource.
getPath());
452 if (null != resultCode) {
453 switch (resultCode) {
455 LOGGER.log(Level.INFO,
"Added data source to case");
457 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
461 case NONCRITICAL_ERRORS:
463 LOGGER.log(Level.WARNING,
"Non-critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
465 LOGGER.log(Level.INFO,
"Added data source to case");
467 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
471 case CRITICAL_ERRORS:
473 LOGGER.log(Level.SEVERE,
"Critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
475 LOGGER.log(Level.SEVERE,
"Failed to add data source to case");
479 LOGGER.log(Level.WARNING,
"No result code for data source processor for {0}", dataSource.
getPath());
502 LOGGER.log(Level.INFO,
"Starting ingest modules analysis for {0} ", dataSource.
getPath());
507 if (!ingestProfileName.isEmpty()) {
509 if (selectedProfile == null) {
511 LOGGER.log(Level.SEVERE,
"Unable to find ingest profile: {0}. Ingest cancelled!", ingestProfileName);
512 System.out.println(
"Unable to find ingest profile: " + ingestProfileName +
". Ingest cancelled!");
518 if (selectedFileSet == null) {
520 LOGGER.log(Level.SEVERE,
"Unable to find file filter {0} for ingest profile: {1}. Ingest cancelled!",
new Object[]{selectedProfile.getFileIngestFilter(), ingestProfileName});
521 System.out.println(
"Unable to find file filter " + selectedProfile.getFileIngestFilter() +
" for ingest profile: " + ingestProfileName +
". Ingest cancelled!");
531 if (selectedProfile == null || selectedFileSet == null) {
540 List<String> settingsWarnings = ingestJobSettings.
getWarnings();
541 if (settingsWarnings.isEmpty()) {
544 if (null != ingestJob) {
551 LOGGER.log(Level.INFO,
"Finished ingest modules analysis for {0} ", dataSource.
getPath());
554 if (!snapshot.isCancelled()) {
556 if (!cancelledModules.isEmpty()) {
557 LOGGER.log(Level.WARNING, String.format(
"Ingest module(s) cancelled for %s", dataSource.
getPath()));
558 for (String module : snapshot.getCancelledDataSourceIngestModules()) {
559 LOGGER.log(Level.WARNING, String.format(
"%s ingest module cancelled for %s", module, dataSource.
getPath()));
562 LOGGER.log(Level.INFO,
"Analysis of data source completed");
564 LOGGER.log(Level.WARNING,
"Analysis of data source cancelled");
567 throw new AnalysisStartupException(String.format(
"Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), dataSource.
getPath()));
572 LOGGER.log(Level.SEVERE, String.format(
"%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.
getPath()), error.getThrowable());
574 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to ingest job startup error");
575 throw new AnalysisStartupException(String.format(
"Error(s) during ingest module startup for %s", dataSource.
getPath()));
577 LOGGER.log(Level.SEVERE, String.format(
"Ingest manager ingest job start error for %s", dataSource.
getPath()), ingestJobStartResult.
getStartupException());
578 throw new AnalysisStartupException(
"Ingest manager error starting job", ingestJobStartResult.
getStartupException());
581 for (String warning : settingsWarnings) {
582 LOGGER.log(Level.SEVERE,
"Ingest job settings error for {0}: {1}",
new Object[]{dataSource.getPath(), warning});
584 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to settings errors");
585 throw new AnalysisStartupException(
"Error(s) in ingest job settings");
606 if (profile.toString().equalsIgnoreCase(ingestProfileName)) {
608 selectedProfile = profile;
612 return selectedProfile;
628 fileIngestFilters.put(fSet.getName(), fSet);
630 return fileIngestFilters.get(filterName);
632 LOGGER.log(Level.SEVERE,
"Failed to get file ingest filter: " + filterName, ex);
648 return Paths.get(caseFoldersPath.toString(), folderName);
662 File searchFolder =
new File(folderToSearch.toString());
663 if (!searchFolder.isDirectory()) {
666 Path caseFolderPath = null;
667 String[] candidateFolders = searchFolder.list(
new CaseFolderFilter(caseName));
668 long mostRecentModified = 0;
669 for (String candidateFolder : candidateFolders) {
670 File file =
new File(candidateFolder);
671 if (file.lastModified() >= mostRecentModified) {
672 mostRecentModified = file.lastModified();
673 caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
676 return caseFolderPath;
711 String eventType =
event.getPropertyName();
761 private final class AnalysisStartupException
extends Exception {
770 super(message, cause);
785 public boolean accept(File folder, String fileName) {
786 File file =
new File(folder, fileName);
789 if (null != caseName) {
791 if (fileNamePrefix.equals(caseName)) {
811 for (File file : folder.listFiles()) {
812 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)
List< String > getCancelledDataSourceIngestModules()
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()