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.List;
28 import java.util.UUID;
29 import java.util.Collection;
30 import java.util.EnumSet;
31 import java.util.Iterator;
33 import java.util.logging.Level;
34 import org.netbeans.spi.sendopts.OptionProcessor;
35 import org.openide.LifecycleManager;
36 import org.openide.util.Lookup;
85 LOGGER.log(Level.WARNING,
"Unable to close the case while shutting down command line ingest manager", ex);
89 LifecycleManager.getDefault().exit();
97 ingestLock =
new Object();
100 LOGGER.log(Level.INFO,
"Set running with desktop GUI runtime property to false");
102 LOGGER.log(Level.SEVERE,
"Failed to set running with desktop GUI runtime property to false", ex);
108 LOGGER.log(Level.INFO,
"Job processing task started");
112 LOGGER.log(Level.INFO,
"Autopsy is running from command line");
113 String dataSourcePath =
"";
114 String baseCaseName =
"";
117 Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
118 Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
119 while (optionsIterator.hasNext()) {
121 OptionProcessor processor = optionsIterator.next();
124 dataSourcePath = ((CommandLineOptionProcessor) processor).getPathToDataSource();
125 baseCaseName = ((CommandLineOptionProcessor) processor).getBaseCaseName();
129 LOGGER.log(Level.INFO,
"Data source path = {0}", dataSourcePath);
130 LOGGER.log(Level.INFO,
"Case name = {0}", baseCaseName);
131 System.out.println(
"Data source path = " + dataSourcePath);
132 System.out.println(
"Case name = " + baseCaseName);
135 if (dataSourcePath.isEmpty()) {
136 LOGGER.log(Level.SEVERE,
"Data source path not specified");
137 System.out.println(
"Data source path not specified");
141 if (baseCaseName.isEmpty()) {
142 LOGGER.log(Level.SEVERE,
"Case name not specified");
143 System.out.println(
"Case name not specified");
147 if (!(
new File(dataSourcePath).exists())) {
148 LOGGER.log(Level.SEVERE,
"Data source file not found {0}", dataSourcePath);
149 System.out.println(
"Data source file not found " + dataSourcePath);
155 LOGGER.log(Level.INFO,
"Output directory = {0}", rootOutputDir);
156 System.out.println(
"Output directory = " + rootOutputDir);
158 if (rootOutputDir.isEmpty()) {
159 LOGGER.log(Level.SEVERE,
"Output directory not specified, please configure Command Line Options Panel (in Tools -> Options)");
160 System.out.println(
"Output directory not specified, please configure Command Line Options Panel (in Tools -> Options)");
164 if (!(
new File(rootOutputDir).exists())) {
165 LOGGER.log(Level.SEVERE,
"The output directory doesn't exist {0}", rootOutputDir);
166 System.out.println(
"The output directory doesn't exist " + rootOutputDir);
169 rootOutputDirectory = Paths.get(rootOutputDir);
174 caseForJob =
openCase(baseCaseName);
176 LOGGER.log(Level.SEVERE,
"Error creating or opening case " + baseCaseName, ex);
177 System.out.println(
"Error creating or opening case " + baseCaseName);
181 if (caseForJob == null) {
182 LOGGER.log(Level.SEVERE,
"Error creating or opening case {0}", baseCaseName);
183 System.out.println(
"Error creating or opening case " + baseCaseName);
201 LOGGER.log(Level.SEVERE,
"Unable to ingest data source " + dataSourcePath +
". Exiting...", ex);
202 System.out.println(
"Unable to ingest data source " + dataSourcePath +
". Exiting...");
203 }
catch (Throwable ex) {
211 LOGGER.log(Level.SEVERE,
"Unexpected error while ingesting data source " + dataSourcePath, ex);
212 System.out.println(
"Unexpected error while ingesting data source " + dataSourcePath +
". Exiting...");
218 LOGGER.log(Level.WARNING,
"Exception while closing case", ex);
219 System.out.println(
"Exception while closing case");
224 LOGGER.log(Level.INFO,
"Job processing task finished");
225 System.out.println(
"Job processing task finished");
241 Content content = dataSource.
getContent().get(0);
242 return content.getId();
247 LOGGER.log(Level.INFO,
"Opening case {0}", baseCaseName);
249 Path caseDirectoryPath = findCaseDirectory(rootOutputDirectory, baseCaseName);
250 if (null != caseDirectoryPath) {
252 LOGGER.log(Level.SEVERE,
"Case {0} already exists. Case name must be unique. Exiting", baseCaseName);
253 throw new CaseActionException(
"Case " + baseCaseName +
" already exists. Case name must be unique. Exiting");
255 caseDirectoryPath = createCaseFolderPath(rootOutputDirectory, baseCaseName);
265 LOGGER.log(Level.INFO,
"Opened case {0}", caseForJob.
getName());
285 LOGGER.log(Level.INFO,
"Adding data source {0} ", dataSource.getPath().toString());
288 List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
292 LOGGER.log(Level.SEVERE,
"Exception while determining best data source processor for {0}", dataSource.getPath());
298 if (validDataSourceProcessors.isEmpty()) {
300 LOGGER.log(Level.SEVERE,
"Unsupported data source {0}", dataSource.getPath());
308 UUID taskId = UUID.randomUUID();
309 caseForJob.notifyAddingDataSource(taskId);
311 caseForJob.notifyAddingDataSource(taskId);
312 LOGGER.log(Level.INFO,
"Identified data source type for {0} as {1}",
new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
313 selectedProcessor.process(dataSource.getDeviceId(), dataSource.getPath(), progressMonitor, callBack);
318 if ((dataSource.getResultDataSourceProcessorResultCode() == CRITICAL_ERRORS)
319 || dataSource.getContent().isEmpty()) {
329 LOGGER.log(Level.SEVERE,
"All data source processors failed to process {0}", dataSource.getPath());
344 if (null != resultCode) {
345 switch (resultCode) {
347 LOGGER.log(Level.INFO,
"Added data source to case");
349 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
353 case NONCRITICAL_ERRORS:
355 LOGGER.log(Level.WARNING,
"Non-critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
357 LOGGER.log(Level.INFO,
"Added data source to case");
359 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
363 case CRITICAL_ERRORS:
365 LOGGER.log(Level.SEVERE,
"Critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
367 LOGGER.log(Level.SEVERE,
"Failed to add data source to case");
371 LOGGER.log(Level.WARNING,
"No result code for data source processor for {0}", dataSource.
getPath());
390 LOGGER.log(Level.INFO,
"Starting ingest modules analysis for {0} ", dataSource.getPath());
396 List<String> settingsWarnings = ingestJobSettings.
getWarnings();
397 if (settingsWarnings.isEmpty()) {
400 if (null != ingestJob) {
407 LOGGER.log(Level.INFO,
"Finished ingest modules analysis for {0} ", dataSource.getPath());
410 if (!snapshot.isCancelled()) {
411 List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
412 if (!cancelledModules.isEmpty()) {
413 LOGGER.log(Level.WARNING, String.format(
"Ingest module(s) cancelled for %s", dataSource.getPath()));
414 for (String module : snapshot.getCancelledDataSourceIngestModules()) {
415 LOGGER.log(Level.WARNING, String.format(
"%s ingest module cancelled for %s", module, dataSource.getPath()));
418 LOGGER.log(Level.INFO,
"Analysis of data source completed");
420 LOGGER.log(Level.WARNING,
"Analysis of data source cancelled");
423 throw new AnalysisStartupException(String.format(
"Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), dataSource.getPath()));
429 LOGGER.log(Level.SEVERE, String.format(
"%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.getPath()), error.getThrowable());
431 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to ingest job startup error");
432 throw new AnalysisStartupException(String.format(
"Error(s) during ingest module startup for %s", dataSource.getPath()));
434 LOGGER.log(Level.SEVERE, String.format(
"Ingest manager ingest job start error for %s", dataSource.getPath()), ingestJobStartResult.
getStartupException());
435 throw new AnalysisStartupException(
"Ingest manager error starting job", ingestJobStartResult.
getStartupException());
438 for (String warning : settingsWarnings) {
439 LOGGER.log(Level.SEVERE,
"Ingest job settings error for {0}: {1}",
new Object[]{dataSource.getPath(), warning});
441 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to settings errors");
442 throw new AnalysisStartupException(
"Error(s) in ingest job settings");
459 Path createCaseFolderPath(Path caseFoldersPath, String caseName) {
461 return Paths.get(caseFoldersPath.toString(), folderName);
474 Path findCaseDirectory(Path folderToSearch, String caseName) {
475 File searchFolder =
new File(folderToSearch.toString());
476 if (!searchFolder.isDirectory()) {
479 Path caseFolderPath = null;
480 String[] candidateFolders = searchFolder.list(
new CaseFolderFilter(caseName));
481 long mostRecentModified = 0;
482 for (String candidateFolder : candidateFolders) {
483 File file =
new File(candidateFolder);
484 if (file.lastModified() >= mostRecentModified) {
485 mostRecentModified = file.lastModified();
486 caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
489 return caseFolderPath;
513 String eventType =
event.getPropertyName();
563 private final class AnalysisStartupException
extends Exception {
572 super(message, cause);
587 public boolean accept(File folder, String fileName) {
588 File file =
new File(folder, fileName);
591 if (null != caseName) {
593 if (fileNamePrefix.equals(caseName)) {
613 for (File file : folder.listFiles()) {
614 if (file.getName().toLowerCase().endsWith(CASE_METADATA_EXT) && file.isFile()) {
synchronized List< String > getDataSourceProcessorErrorMessages()
static List< AutoIngestDataSourceProcessor > getOrderedListOfDataSourceProcessors(Path dataSourcePath)
void logDataSourceProcessorResult(AutoIngestDataSource dataSource)
static synchronized IngestManager getInstance()
static void closeCurrentCase()
void analyze(AutoIngestDataSource dataSource)
static String getReportFileName()
static void createCaseDirectory(String caseDirPath, CaseType caseType)
static final String CASE_METADATA_EXT
IngestJobStartResult beginIngestJob(Collection< Content > dataSources, IngestJobSettings settings)
AnalysisStartupException(String message)
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode()
CommandLineIngestManager()
String getReportDirectory()
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 String createTimeStamp()
void removeIngestJobEventListener(final PropertyChangeListener listener)
static String getCommandLineModeResultsFolder()
List< IngestModuleError > getModuleErrors()
static final Logger LOGGER
static final long serialVersionUID
void addIngestJobEventListener(final PropertyChangeListener listener)
ProgressSnapshot getSnapshot()
void setProgressText(final String text)
AnalysisStartupException(String message, Throwable cause)
boolean accept(File folder, String fileName)
static int getTimeStampLength()
static synchronized void setRunningWithGUI(boolean runningWithGUI)
Case openCase(String baseCaseName)
List< String > getWarnings()
synchronized List< Content > getContent()
static boolean hasCaseMetadataFile(File folder)
static Case getCurrentCase()
synchronized static Logger getLogger(String name)
void setIndeterminate(final boolean indeterminate)
static String getCommandLineModeIngestModuleContextString()
void setProgress(final int progress)
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
IngestManager.IngestManagerException getStartupException()
Long getDataSourceId(AutoIngestDataSource dataSource)