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)