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;
 
   96             LOGGER.log(Level.WARNING, 
"Unable to close the case while shutting down command line ingest manager", ex); 
 
  100         LifecycleManager.getDefault().exit();
 
  108             ingestLock = 
new Object();
 
  111                 LOGGER.log(Level.INFO, 
"Set running with desktop GUI runtime property to false");
 
  113                 LOGGER.log(Level.SEVERE, 
"Failed to set running with desktop GUI runtime property to false", ex);
 
  123             LOGGER.log(Level.INFO, 
"Job processing task started");
 
  127                 LOGGER.log(Level.INFO, 
"Autopsy is running from command line"); 
 
  128                 List<CommandLineCommand> commands = null;
 
  131                 Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
 
  132                 Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
 
  133                 while (optionsIterator.hasNext()) {
 
  135                     OptionProcessor processor = optionsIterator.next();
 
  138                         commands = ((CommandLineOptionProcessor) processor).getCommands();
 
  142                 if (commands == null || commands.isEmpty()) {
 
  143                     LOGGER.log(Level.SEVERE, 
"No command line commands specified");
 
  144                     System.err.println(
"No command line commands specified");
 
  150                     for (CommandLineCommand command : commands) {
 
  151                         CommandLineCommand.CommandType type = command.getType();
 
  155                                     LOGGER.log(Level.INFO, 
"Processing 'Create Case' command");
 
  156                                     System.out.println(
"Processing 'Create Case' command");
 
  157                                     Map<String, String> inputs = command.getInputs();
 
  158                                     String baseCaseName = inputs.get(CommandLineCommand.InputType.CASE_NAME.name());
 
  159                                     String rootOutputDirectory = inputs.get(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name());
 
  160                                     openCase(baseCaseName, rootOutputDirectory);
 
  163                                     OutputGenerator.saveCreateCaseOutput(caseForJob, outputDirPath, baseCaseName);
 
  165                                     String baseCaseName = command.getInputs().get(CommandLineCommand.InputType.CASE_NAME.name());
 
  166                                     LOGGER.log(Level.SEVERE, 
"Error creating or opening case " + baseCaseName, ex);
 
  167                                     System.err.println(
"Error creating or opening case " + baseCaseName);
 
  172                             case ADD_DATA_SOURCE:
 
  174                                     LOGGER.log(Level.INFO, 
"Processing 'Add Data Source' command");
 
  175                                     System.out.println(
"Processing 'Add Data Source' command");
 
  176                                     Map<String, String> inputs = command.getInputs();
 
  179                                     if (caseForJob == null) {
 
  180                                         String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
 
  184                                     String dataSourcePath = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
 
  189                                     OutputGenerator.saveAddDataSourceOutput(caseForJob, dataSource, outputDirPath);
 
  191                                     String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
 
  192                                     LOGGER.log(Level.SEVERE, 
"Error adding data source " + dataSourcePath, ex);
 
  193                                     System.err.println(
"Error adding data source " + dataSourcePath);
 
  200                                     LOGGER.log(Level.INFO, 
"Processing 'Run Ingest' command");
 
  201                                     System.out.println(
"Processing 'Run Ingest' command");
 
  202                                     Map<String, String> inputs = command.getInputs();
 
  205                                     if (caseForJob == null) {
 
  206                                         String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
 
  211                                     if (dataSource == null) {
 
  213                                         String dataSourceId = inputs.get(CommandLineCommand.InputType.DATA_SOURCE_ID.name());
 
  214                                         Long dataSourceObjId = Long.valueOf(dataSourceId);
 
  217                                         Content content = null;
 
  220                                         } 
catch (TskCoreException ex) {
 
  221                                             LOGGER.log(Level.SEVERE, 
"Exception while trying to find data source with object ID " + dataSourceId, ex);
 
  222                                             System.err.println(
"Exception while trying to find data source with object ID " + dataSourceId);
 
  227                                         if (content == null) {
 
  228                                             LOGGER.log(Level.SEVERE, 
"Unable to find data source with object ID {0}", dataSourceId);
 
  229                                             System.out.println(
"Unable to find data source with object ID " + dataSourceId);
 
  236                                         List<Content> contentList = Arrays.asList(
new Content[]{content});
 
  237                                         List<String> errorList = 
new ArrayList<>();
 
  242                                     String ingestProfile = inputs.get(CommandLineCommand.InputType.INGEST_PROFILE_NAME.name());
 
  243                                     analyze(dataSource, ingestProfile);
 
  245                                     String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name());
 
  246                                     LOGGER.log(Level.SEVERE, 
"Error running ingest on data source " + dataSourcePath, ex);
 
  247                                     System.err.println(
"Error running ingest on data source " + dataSourcePath);
 
  253                             case LIST_ALL_DATA_SOURCES:
 
  255                                     LOGGER.log(Level.INFO, 
"Processing 'List All Data Sources' command");
 
  256                                     System.out.println(
"Processing 'List All Data Sources' command");
 
  257                                     Map<String, String> inputs = command.getInputs();
 
  260                                     if (caseForJob == null) {
 
  261                                         String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
 
  266                                     OutputGenerator.listAllDataSources(caseForJob, outputDirPath);
 
  268                                     String caseDirPath = command.getInputs().get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
 
  269                                     LOGGER.log(Level.SEVERE, 
"Error opening case in case directory: " + caseDirPath, ex);
 
  270                                     System.err.println(
"Error opening case in case directory: " + caseDirPath);
 
  276                             case GENERATE_REPORTS:
 
  278                                     LOGGER.log(Level.INFO, 
"Processing 'Generate Reports' command");
 
  279                                     System.out.println(
"Processing 'Generate Reports' command");
 
  280                                     Map<String, String> inputs = command.getInputs();
 
  283                                     if (caseForJob == null) {
 
  284                                         String caseDirPath = inputs.get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
 
  293                                     String caseDirPath = command.getInputs().get(CommandLineCommand.InputType.CASE_FOLDER_PATH.name());
 
  294                                     LOGGER.log(Level.SEVERE, 
"Error opening case in case directory: " + caseDirPath, ex);
 
  295                                     System.err.println(
"Error opening case in case directory: " + caseDirPath);
 
  304                 } 
catch (Throwable ex) {
 
  312                     LOGGER.log(Level.SEVERE, 
"Unexpected error", ex);
 
  313                     System.err.println(
"Unexpected error. Exiting...");
 
  319                         LOGGER.log(Level.WARNING, 
"Exception while closing case", ex);
 
  320                         System.err.println(
"Exception while closing case");
 
  325                 LOGGER.log(Level.INFO, 
"Job processing task finished");
 
  326                 System.out.println(
"Job processing task finished");
 
  345             LOGGER.log(Level.INFO, 
"Opening case {0} in directory {1}", 
new Object[]{baseCaseName, rootOutputDirectory});
 
  346             Path caseDirectoryPath = 
findCaseDirectory(Paths.get(rootOutputDirectory), baseCaseName);
 
  347             if (null != caseDirectoryPath) {
 
  349                 LOGGER.log(Level.SEVERE, 
"Case {0} already exists. Case name must be unique. Exiting", baseCaseName);
 
  350                 throw new CaseActionException(
"Case " + baseCaseName + 
" already exists. Case name must be unique. Exiting");
 
  362             LOGGER.log(Level.INFO, 
"Opened case {0}", caseForJob.
getName());
 
  374             LOGGER.log(Level.INFO, 
"Opening case in directory {0}", caseFolderPath);
 
  376             String metadataFilePath = 
findAutFile(caseFolderPath);
 
  380             LOGGER.log(Level.INFO, 
"Opened case {0}", caseForJob.
getName());
 
  393             File caseFolder = Paths.get(caseDirectory).toFile();
 
  394             if (caseFolder.exists()) {
 
  398                 File[] fileArray = caseFolder.listFiles();
 
  399                 if (fileArray == null) {
 
  402                 String autFilePath = null;
 
  403                 for (File file : fileArray) {
 
  404                     String name = file.getName().toLowerCase();
 
  405                     if (autFilePath == null && name.endsWith(getFileExtension())) {
 
  406                         return file.getAbsolutePath();
 
  435             LOGGER.log(Level.INFO, 
"Adding data source {0} ", dataSource.
getPath().toString());
 
  438             List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
 
  442                 LOGGER.log(Level.SEVERE, 
"Exception while determining best data source processor for {0}", dataSource.
getPath());
 
  448             if (validDataSourceProcessors.isEmpty()) {
 
  450                 LOGGER.log(Level.SEVERE, 
"Unsupported data source {0}", dataSource.
getPath());  
 
  458                     UUID taskId = UUID.randomUUID();
 
  462                     LOGGER.log(Level.INFO, 
"Identified data source type for {0} as {1}", 
new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
 
  463                     selectedProcessor.process(dataSource.
getDeviceId(), dataSource.
getPath(), progressMonitor, callBack);
 
  479                 LOGGER.log(Level.SEVERE, 
"All data source processors failed to process {0}", dataSource.
getPath());
 
  494             if (null != resultCode) {
 
  495                 switch (resultCode) {
 
  497                         LOGGER.log(Level.INFO, 
"Added data source to case");
 
  499                             LOGGER.log(Level.SEVERE, 
"Data source failed to produce content");
 
  503                     case NONCRITICAL_ERRORS:
 
  505                             LOGGER.log(Level.WARNING, 
"Non-critical error running data source processor for {0}: {1}", 
new Object[]{dataSource.getPath(), errorMessage});
 
  507                         LOGGER.log(Level.INFO, 
"Added data source to case");
 
  509                             LOGGER.log(Level.SEVERE, 
"Data source failed to produce content");
 
  513                     case CRITICAL_ERRORS:
 
  515                             LOGGER.log(Level.SEVERE, 
"Critical error running data source processor for {0}: {1}", 
new Object[]{dataSource.getPath(), errorMessage});
 
  517                         LOGGER.log(Level.SEVERE, 
"Failed to add data source to case");
 
  521                 LOGGER.log(Level.WARNING, 
"No result code for data source processor for {0}", dataSource.
getPath());
 
  544             LOGGER.log(Level.INFO, 
"Starting ingest modules analysis for {0} ", dataSource.
getPath());
 
  549             if (!ingestProfileName.isEmpty()) {
 
  551                 if (selectedProfile == null) {
 
  553                     LOGGER.log(Level.SEVERE, 
"Unable to find ingest profile: {0}. Ingest cancelled!", ingestProfileName);
 
  554                     System.err.println(
"Unable to find ingest profile: " + ingestProfileName + 
". Ingest cancelled!");
 
  560                 if (selectedFileSet == null) {
 
  562                     LOGGER.log(Level.SEVERE, 
"Unable to find file filter {0} for ingest profile: {1}. Ingest cancelled!", 
new Object[]{selectedProfile.getFileIngestFilter(), ingestProfileName});
 
  563                     System.err.println(
"Unable to find file filter " + selectedProfile.getFileIngestFilter() + 
" for ingest profile: " + ingestProfileName + 
". Ingest cancelled!");
 
  573                     if (selectedProfile == null || selectedFileSet == null) {
 
  582                     List<String> settingsWarnings = ingestJobSettings.
getWarnings();
 
  583                     if (settingsWarnings.isEmpty()) {
 
  586                         if (null != ingestJob) {
 
  593                             LOGGER.log(Level.INFO, 
"Finished ingest modules analysis for {0} ", dataSource.
getPath());
 
  596                                 if (!snapshot.isCancelled()) {
 
  597                                     List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
 
  598                                     if (!cancelledModules.isEmpty()) {
 
  599                                         LOGGER.log(Level.WARNING, String.format(
"Ingest module(s) cancelled for %s", dataSource.
getPath()));
 
  600                                         for (String module : snapshot.getCancelledDataSourceIngestModules()) {
 
  601                                             LOGGER.log(Level.WARNING, String.format(
"%s ingest module cancelled for %s", module, dataSource.
getPath()));
 
  604                                     LOGGER.log(Level.INFO, 
"Analysis of data source completed");
 
  606                                     LOGGER.log(Level.WARNING, 
"Analysis of data source cancelled");
 
  609                                         throw new AnalysisStartupException(String.format(
"Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), dataSource.
getPath()));
 
  615                                 LOGGER.log(Level.SEVERE, String.format(
"%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.
getPath()), error.getThrowable());
 
  617                             LOGGER.log(Level.SEVERE, 
"Failed to analyze data source due to ingest job startup error");
 
  618                             throw new AnalysisStartupException(String.format(
"Error(s) during ingest module startup for %s", dataSource.
getPath()));
 
  620                             LOGGER.log(Level.SEVERE, String.format(
"Ingest manager ingest job start error for %s", dataSource.
getPath()), ingestJobStartResult.
getStartupException());
 
  621                             throw new AnalysisStartupException(
"Ingest manager error starting job", ingestJobStartResult.
getStartupException());
 
  624                         for (String warning : settingsWarnings) {
 
  625                             LOGGER.log(Level.SEVERE, 
"Ingest job settings error for {0}: {1}", 
new Object[]{dataSource.getPath(), warning});
 
  627                         LOGGER.log(Level.SEVERE, 
"Failed to analyze data source due to settings errors");
 
  628                         throw new AnalysisStartupException(
"Error(s) in ingest job settings");
 
  649                 if (profile.toString().equalsIgnoreCase(ingestProfileName)) {
 
  651                     selectedProfile = profile;
 
  655             return selectedProfile;
 
  671                     fileIngestFilters.put(fSet.getName(), fSet);
 
  673                 return fileIngestFilters.get(filterName);
 
  675                 LOGGER.log(Level.SEVERE, 
"Failed to get file ingest filter: " + filterName, ex); 
 
  691             return Paths.get(caseFoldersPath.toString(), folderName);
 
  705             File searchFolder = 
new File(folderToSearch.toString());
 
  706             if (!searchFolder.isDirectory()) {
 
  709             Path caseFolderPath = null;
 
  710             String[] candidateFolders = searchFolder.list(
new CaseFolderFilter(caseName));
 
  711             long mostRecentModified = 0;
 
  712             for (String candidateFolder : candidateFolders) {
 
  713                 File file = 
new File(candidateFolder);
 
  714                 if (file.lastModified() >= mostRecentModified) {
 
  715                     mostRecentModified = file.lastModified();
 
  716                     caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
 
  719             return caseFolderPath;
 
  754                     String eventType = 
event.getPropertyName();
 
  804         private final class AnalysisStartupException 
extends Exception {
 
  813                 super(message, cause);
 
  828         public boolean accept(File folder, String fileName) {
 
  829             File file = 
new File(folder, fileName);
 
  832                     if (null != caseName) {
 
  834                         if (fileNamePrefix.equals(caseName)) {
 
  854             for (File file : folder.listFiles()) {
 
  855                 if (file.getName().toLowerCase().endsWith(CASE_METADATA_EXT) && file.isFile()) {
 
synchronized List< String > getDataSourceProcessorErrorMessages()
void openCase(String caseFolderPath)
static List< AutoIngestDataSourceProcessor > getOrderedListOfDataSourceProcessors(Path dataSourcePath)
void logDataSourceProcessorResult(AutoIngestDataSource dataSource)
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)
void openCase(String baseCaseName, String rootOutputDirectory)
ProgressSnapshot getSnapshot()
String getOutputDirPath(Case caseForJob)
void setProgressText(final String text)
static synchronized List< IngestProfile > getIngestProfiles()
static void openAsCurrentCase(String caseMetadataFilePath)
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)
String findAutFile(String caseDirectory)
static boolean hasCaseMetadataFile(File folder)
static Case getCurrentCase()
synchronized static Logger getLogger(String name)
static String getReportingConfigName()
static Case getCurrentCaseThrows()
void setIndeterminate(final boolean indeterminate)
static String getCommandLineModeIngestModuleContextString()
void setProgress(final int progress)
void notifyAddingDataSource(UUID eventId)
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
IngestManager.IngestManagerException getStartupException()