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.Iterator;
31 import java.util.logging.Level;
32 import org.netbeans.spi.sendopts.OptionProcessor;
33 import org.openide.LifecycleManager;
34 import org.openide.util.Lookup;
82 LOGGER.log(Level.WARNING,
"Unable to close the case while shutting down command line ingest manager", ex);
86 LifecycleManager.getDefault().exit();
94 ingestLock =
new Object();
97 LOGGER.log(Level.INFO,
"Set running with desktop GUI runtime property to false");
99 LOGGER.log(Level.SEVERE,
"Failed to set running with desktop GUI runtime property to false", ex);
104 LOGGER.log(Level.INFO,
"Job processing task started");
108 LOGGER.log(Level.INFO,
"Autopsy is running from command line");
109 String dataSourcePath =
"";
110 String baseCaseName =
"";
113 Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
114 Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
115 while (optionsIterator.hasNext()) {
117 OptionProcessor processor = optionsIterator.next();
120 dataSourcePath = ((CommandLineOptionProcessor) processor).getPathToDataSource();
121 baseCaseName = ((CommandLineOptionProcessor) processor).getBaseCaseName();
125 LOGGER.log(Level.INFO,
"Data source path = {0}", dataSourcePath);
126 LOGGER.log(Level.INFO,
"Case name = {0}", baseCaseName);
127 System.out.println(
"Data source path = " + dataSourcePath);
128 System.out.println(
"Case name = " + baseCaseName);
131 if (dataSourcePath.isEmpty()) {
132 LOGGER.log(Level.SEVERE,
"Data source path not specified");
133 System.out.println(
"Data source path not specified");
137 if (baseCaseName.isEmpty()) {
138 LOGGER.log(Level.SEVERE,
"Case name not specified");
139 System.out.println(
"Case name not specified");
143 if (!(
new File(dataSourcePath).exists())) {
144 LOGGER.log(Level.SEVERE,
"Data source file not found {0}", dataSourcePath);
145 System.out.println(
"Data source file not found " + dataSourcePath);
151 LOGGER.log(Level.INFO,
"Output directory = {0}", rootOutputDir);
152 System.out.println(
"Output directoryh = " + rootOutputDir);
154 if (rootOutputDir.isEmpty()) {
155 LOGGER.log(Level.SEVERE,
"Output directory not specified, please configure Command Line Options Panel (in Tools -> Options)");
156 System.out.println(
"Output directory not specified, please configure Command Line Options Panel (in Tools -> Options)");
160 if (!(
new File(rootOutputDir).exists())) {
161 LOGGER.log(Level.SEVERE,
"The output directory doesn't exist {0}", rootOutputDir);
162 System.out.println(
"The output directory doesn't exist " + rootOutputDir);
165 rootOutputDirectory = Paths.get(rootOutputDir);
170 caseForJob =
openCase(baseCaseName);
172 LOGGER.log(Level.SEVERE,
"Error creating or opening case " + baseCaseName, ex);
173 System.out.println(
"Error creating or opening case " + baseCaseName);
177 if (caseForJob == null) {
178 LOGGER.log(Level.SEVERE,
"Error creating or opening case {0}", baseCaseName);
179 System.out.println(
"Error creating or opening case " + baseCaseName);
197 LOGGER.log(Level.SEVERE,
"Unable to ingest data source " + dataSourcePath +
". Exiting...", ex);
198 System.out.println(
"Unable to ingest data source " + dataSourcePath +
". Exiting...");
199 }
catch (Throwable ex) {
206 LOGGER.log(Level.SEVERE,
"Unexpected error while ingesting data source " + dataSourcePath, ex);
207 System.out.println(
"Unexpected error while ingesting data source " + dataSourcePath +
". Exiting...");
213 LOGGER.log(Level.WARNING,
"Exception while closing case", ex);
214 System.out.println(
"Exception while closing case");
219 LOGGER.log(Level.INFO,
"Job processing task finished");
220 System.out.println(
"Job processing task finished");
235 Content content = dataSource.
getContent().get(0);
236 return content.getId();
241 LOGGER.log(Level.INFO,
"Opening case {0}", baseCaseName);
243 Path caseDirectoryPath = findCaseDirectory(rootOutputDirectory, baseCaseName);
244 if (null != caseDirectoryPath) {
246 LOGGER.log(Level.SEVERE,
"Case {0} already exists. Case name must be unique. Exiting", baseCaseName);
247 throw new CaseActionException(
"Case " + baseCaseName +
" already exists. Case name must be unique. Exiting");
249 caseDirectoryPath = createCaseFolderPath(rootOutputDirectory, baseCaseName);
259 LOGGER.log(Level.INFO,
"Opened case {0}", caseForJob.
getName());
279 LOGGER.log(Level.INFO,
"Adding data source {0} ", dataSource.getPath().toString());
282 List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
286 LOGGER.log(Level.SEVERE,
"Exception while determining best data source processor for {0}", dataSource.getPath());
292 if (validDataSourceProcessors.isEmpty()) {
294 LOGGER.log(Level.SEVERE,
"Unsupported data source {0}", dataSource.getPath());
302 UUID taskId = UUID.randomUUID();
303 caseForJob.notifyAddingDataSource(taskId);
305 caseForJob.notifyAddingDataSource(taskId);
306 LOGGER.log(Level.INFO,
"Identified data source type for {0} as {1}",
new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
307 selectedProcessor.process(dataSource.getDeviceId(), dataSource.getPath(), progressMonitor, callBack);
312 if ((dataSource.getResultDataSourceProcessorResultCode() == CRITICAL_ERRORS)
313 || dataSource.getContent().isEmpty()) {
323 LOGGER.log(Level.SEVERE,
"All data source processors failed to process {0}", dataSource.getPath());
338 if (null != resultCode) {
339 switch (resultCode) {
341 LOGGER.log(Level.INFO,
"Added data source to case");
343 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
347 case NONCRITICAL_ERRORS:
349 LOGGER.log(Level.WARNING,
"Non-critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
351 LOGGER.log(Level.INFO,
"Added data source to case");
353 LOGGER.log(Level.SEVERE,
"Data source failed to produce content");
357 case CRITICAL_ERRORS:
359 LOGGER.log(Level.SEVERE,
"Critical error running data source processor for {0}: {1}",
new Object[]{dataSource.getPath(), errorMessage});
361 LOGGER.log(Level.SEVERE,
"Failed to add data source to case");
365 LOGGER.log(Level.WARNING,
"No result code for data source processor for {0}", dataSource.
getPath());
384 LOGGER.log(Level.INFO,
"Starting ingest modules analysis for {0} ", dataSource.getPath());
390 List<String> settingsWarnings = ingestJobSettings.
getWarnings();
391 if (settingsWarnings.isEmpty()) {
394 if (null != ingestJob) {
401 LOGGER.log(Level.INFO,
"Finished ingest modules analysis for {0} ", dataSource.getPath());
404 if (!snapshot.isCancelled()) {
405 List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
406 if (!cancelledModules.isEmpty()) {
407 LOGGER.log(Level.WARNING, String.format(
"Ingest module(s) cancelled for %s", dataSource.getPath()));
408 for (String module : snapshot.getCancelledDataSourceIngestModules()) {
409 LOGGER.log(Level.WARNING, String.format(
"%s ingest module cancelled for %s", module, dataSource.getPath()));
412 LOGGER.log(Level.INFO,
"Analysis of data source completed");
414 LOGGER.log(Level.WARNING,
"Analysis of data source cancelled");
417 throw new AnalysisStartupException(String.format(
"Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), dataSource.getPath()));
423 LOGGER.log(Level.SEVERE, String.format(
"%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.getPath()), error.getThrowable());
425 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to ingest job startup error");
426 throw new AnalysisStartupException(String.format(
"Error(s) during ingest module startup for %s", dataSource.getPath()));
428 LOGGER.log(Level.SEVERE, String.format(
"Ingest manager ingest job start error for %s", dataSource.getPath()), ingestJobStartResult.
getStartupException());
429 throw new AnalysisStartupException(
"Ingest manager error starting job", ingestJobStartResult.
getStartupException());
432 for (String warning : settingsWarnings) {
433 LOGGER.log(Level.SEVERE,
"Ingest job settings error for {0}: {1}",
new Object[]{dataSource.getPath(), warning});
435 LOGGER.log(Level.SEVERE,
"Failed to analyze data source due to settings errors");
436 throw new AnalysisStartupException(
"Error(s) in ingest job settings");
453 Path createCaseFolderPath(Path caseFoldersPath, String caseName) {
455 return Paths.get(caseFoldersPath.toString(), folderName);
468 Path findCaseDirectory(Path folderToSearch, String caseName) {
469 File searchFolder =
new File(folderToSearch.toString());
470 if (!searchFolder.isDirectory()) {
473 Path caseFolderPath = null;
474 String[] candidateFolders = searchFolder.list(
new CaseFolderFilter(caseName));
475 long mostRecentModified = 0;
476 for (String candidateFolder : candidateFolders) {
477 File file =
new File(candidateFolder);
478 if (file.lastModified() >= mostRecentModified) {
479 mostRecentModified = file.lastModified();
480 caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
483 return caseFolderPath;
507 String eventType =
event.getPropertyName();
557 private final class AnalysisStartupException
extends Exception {
566 super(message, cause);
581 public boolean accept(File folder, String fileName) {
582 File file =
new File(folder, fileName);
585 if (null != caseName) {
587 if (fileNamePrefix.equals(caseName)) {
607 for (File file : folder.listFiles()) {
608 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)
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)