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()) {
555 List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
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()));
573 LOGGER.log(Level.SEVERE, String.format(
"%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.
getPath()), error.getThrowable());
575 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to ingest job startup error");
576 throw new AnalysisStartupException(String.format(
"Error(s) during ingest module startup for %s", dataSource.
getPath()));
578 LOGGER.log(Level.SEVERE, String.format(
"Ingest manager ingest job start error for %s", dataSource.
getPath()), ingestJobStartResult.
getStartupException());
579 throw new AnalysisStartupException(
"Ingest manager error starting job", ingestJobStartResult.
getStartupException());
582 for (String warning : settingsWarnings) {
583 LOGGER.log(Level.SEVERE,
"Ingest job settings error for {0}: {1}",
new Object[]{dataSource.getPath(), warning});
585 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to settings errors");
586 throw new AnalysisStartupException(
"Error(s) in ingest job settings");
607 if (profile.toString().equalsIgnoreCase(ingestProfileName)) {
609 selectedProfile = profile;
613 return selectedProfile;
629 fileIngestFilters.put(fSet.getName(), fSet);
631 return fileIngestFilters.get(filterName);
633 LOGGER.log(Level.SEVERE,
"Failed to get file ingest filter: " + filterName, ex);
649 return Paths.get(caseFoldersPath.toString(), folderName);
663 File searchFolder =
new File(folderToSearch.toString());
664 if (!searchFolder.isDirectory()) {
667 Path caseFolderPath = null;
668 String[] candidateFolders = searchFolder.list(
new CaseFolderFilter(caseName));
669 long mostRecentModified = 0;
670 for (String candidateFolder : candidateFolders) {
671 File file =
new File(candidateFolder);
672 if (file.lastModified() >= mostRecentModified) {
673 mostRecentModified = file.lastModified();
674 caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
677 return caseFolderPath;
712 String eventType =
event.getPropertyName();
762 private final class AnalysisStartupException
extends Exception {
771 super(message, cause);
786 public boolean accept(File folder, String fileName) {
787 File file =
new File(folder, fileName);
790 if (null != caseName) {
792 if (fileNamePrefix.equals(caseName)) {
812 for (File file : folder.listFiles()) {
813 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()