Merge pull request #4540 from eugene7646/command_line_reports_4673

4673 Command line reports
This commit is contained in:
Richard Cordovano 2019-03-14 09:50:20 -04:00 committed by GitHub
commit c11e5a6620
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 2326 additions and 208 deletions

View File

@ -21,7 +21,11 @@ package org.sleuthkit.autopsy.casemodule;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import org.netbeans.spi.sendopts.OptionProcessor;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.commandlineingest.CommandLineIngestManager;
import org.sleuthkit.autopsy.commandlineingest.CommandLineOptionProcessor;
import org.sleuthkit.autopsy.commandlineingest.CommandLineStartupWindow;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
@ -54,6 +58,17 @@ public class StartupWindowProvider implements StartupWindowInterface {
private void init() {
if (startupWindowToUse == null) {
// first check whether we are running from command line
if (isRunningFromCommandLine()) {
// Autopsy is running from command line
logger.log(Level.INFO, "Running from command line"); //NON-NLS
System.out.println("Running from command line");
startupWindowToUse = new CommandLineStartupWindow();
// kick off command line processing
new CommandLineIngestManager().start();
return;
}
//discover the registered windows
Collection<? extends StartupWindowInterface> startupWindows
= Lookup.getDefault().lookupAll(StartupWindowInterface.class);
@ -93,6 +108,30 @@ public class StartupWindowProvider implements StartupWindowInterface {
}
}
/**
* Checks whether Autopsy is running from command line. There is an
* OptionProcessor that is responsible for processing command line inputs.
* If Autopsy is indeed running from command line, then use the command line
* startup window.
*
* @return True if running from command line, false otherwise
*/
private boolean isRunningFromCommandLine() {
// first look up all OptionProcessors and see if running from command line option is set
Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
while (optionsIterator.hasNext()) {
// find CommandLineOptionProcessor
OptionProcessor processor = optionsIterator.next();
if ((processor instanceof CommandLineOptionProcessor)) {
// check if we are running from command line
return ((CommandLineOptionProcessor) processor).isRunFromCommandLine();
}
}
return false;
}
@Override
public void open() {
if (startupWindowToUse != null) {

View File

@ -0,0 +1,96 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.util.List;
import java.util.UUID;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.datamodel.Content;
/**
* A "callback" that collects the results of running a data source processor on
* a data source and unblocks the job processing thread when the data source
* processor finishes running in its own thread.
*/
class AddDataSourceCallback extends DataSourceProcessorCallback {
private final Case caseForJob;
private final DataSource dataSourceInfo;
private final UUID taskId;
private final Object lock;
/**
* Constructs a "callback" that collects the results of running a data
* source processor on a data source and unblocks the job processing thread
* when the data source processor finishes running in its own thread.
*
* @param caseForJob The case for the current job.
* @param dataSourceInfo The data source
* @param taskId The task id to associate with ingest job events.
*/
AddDataSourceCallback(Case caseForJob, DataSource dataSourceInfo, UUID taskId, Object lock) {
this.caseForJob = caseForJob;
this.dataSourceInfo = dataSourceInfo;
this.taskId = taskId;
this.lock = lock;
}
/**
* Called by the data source processor when it finishes running in its own
* thread.
*
* @param result The result code for the processing of the data source.
* @param errorMessages Any error messages generated during the processing
* of the data source.
* @param dataSourceContent The content produced by processing the data
* source.
*/
@Override
public void done(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errorMessages, List<Content> dataSourceContent) {
if (!dataSourceContent.isEmpty()) {
caseForJob.notifyDataSourceAdded(dataSourceContent.get(0), taskId);
} else {
caseForJob.notifyFailedAddingDataSource(taskId);
}
dataSourceInfo.setDataSourceProcessorOutput(result, errorMessages, dataSourceContent);
dataSourceContent.addAll(dataSourceContent);
synchronized (lock) {
lock.notifyAll();
}
}
/**
* Called by the data source processor when it finishes running in its own
* thread, if that thread is the AWT (Abstract Window Toolkit) event
* dispatch thread (EDT).
*
* @param result The result code for the processing of the data source.
* @param errorMessages Any error messages generated during the processing
* of the data source.
* @param dataSourceContent The content produced by processing the data
* source.
*/
@Override
public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errorMessages, List<Content> dataSources) {
done(result, errorMessages, dataSources);
}
}

View File

@ -0,0 +1,17 @@
OpenIDE-Module-Name=CommandLineAutopsy
OptionsCategory_Keywords_Command_Line_Ingest_Settings=Command Line Ingest Settings
OptionsCategory_Keywords_General=Options
OptionsCategory_Name_Command_Line_Ingest=Command Line Ingest
CommandLineIngestSettingsPanel.ResultsDirectoryUnspecified=Output folder must be set
CommandLineIngestSettingsPanel.PathInvalid=Path is not valid
CommandLineIngestSettingsPanel.CannotAccess=Cannot access
CommandLineIngestSettingsPanel.CheckPermissions=Check permissions.
CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text=Select output folder:
CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text=jLabelInvalidOutputFolder
CommandLineIngestSettingsPanel.outputPathTextField.toolTipText=Output folder for command line processing, i.e., the location where case folder will be created by command line processing mode.
CommandLineIngestSettingsPanel.outputPathTextField.text=
CommandLineIngestSettingsPanel.browseOutputFolderButton.text=Browse
CommandLineIngestSettingsPanel.bnEditIngestSettings.toolTipText=Ingest job settings for the command line processing mode context.
CommandLineIngestSettingsPanel.bnEditIngestSettings.text=Ingest Module Settings
CommandLinePanel.jLabel1.text=Ingest is running from command line
CommandLineStartupWindow.title.text=Running in Command Line Mode

View File

@ -0,0 +1,17 @@
OpenIDE-Module-Name=CommandLineAutopsy
OptionsCategory_Keywords_Command_Line_Ingest_Settings=Command Line Ingest Settings
OptionsCategory_Keywords_General=Options
OptionsCategory_Name_Command_Line_Ingest=Command Line Ingest
CommandLineIngestSettingsPanel.ResultsDirectoryUnspecified=Output folder must be set
CommandLineIngestSettingsPanel.PathInvalid=Path is not valid
CommandLineIngestSettingsPanel.CannotAccess=Cannot access
CommandLineIngestSettingsPanel.CheckPermissions=Check permissions.
CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text=Select output folder:
CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text=jLabelInvalidOutputFolder
CommandLineIngestSettingsPanel.outputPathTextField.toolTipText=Output folder for command line processing, i.e., the location where case folder will be created by command line processing mode.
CommandLineIngestSettingsPanel.outputPathTextField.text=
CommandLineIngestSettingsPanel.browseOutputFolderButton.text=Browse
CommandLineIngestSettingsPanel.bnEditIngestSettings.toolTipText=Ingest job settings for the command line processing mode context.
CommandLineIngestSettingsPanel.bnEditIngestSettings.text=Ingest Module Settings
CommandLinePanel.jLabel1.text=Ingest is running from command line
CommandLineStartupWindow.title.text=Running in Command Line Mode

View File

@ -0,0 +1,613 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FilenameFilter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.UUID;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import org.netbeans.spi.sendopts.OptionProcessor;
import org.openide.LifecycleManager;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CaseActionException;
import org.sleuthkit.autopsy.casemodule.CaseDetails;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import static org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestJob;
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestJobStartResult;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleError;
import org.sleuthkit.autopsy.report.ReportProgressPanel;
import org.sleuthkit.autopsy.report.caseuco.CaseUcoFormatExporter;
import org.sleuthkit.autopsy.report.caseuco.ReportCaseUco;
import org.sleuthkit.datamodel.Content;
/**
* Allows Autopsy to be invoked with a command line arguments. Causes Autopsy to
* create a case, add a specified data source, run ingest on that data source,
* produce a CASE/UCO report and exit.
*/
public class CommandLineIngestManager {
private static final Logger LOGGER = Logger.getLogger(CommandLineIngestManager.class.getName());
private Path rootOutputDirectory;
public CommandLineIngestManager() {
}
public void start() {
new Thread(new JobProcessingTask()).start();
}
public void stop() {
try {
// close current case if there is one open
Case.closeCurrentCase();
} catch (CaseActionException ex) {
LOGGER.log(Level.WARNING, "Unable to close the case while shutting down command line ingest manager", ex); //NON-NLS
}
// shut down Autopsy
LifecycleManager.getDefault().exit();
}
private final class JobProcessingTask implements Runnable {
private final Object ingestLock;
private JobProcessingTask() {
ingestLock = new Object();
try {
RuntimeProperties.setRunningWithGUI(false);
LOGGER.log(Level.INFO, "Set running with desktop GUI runtime property to false");
} catch (RuntimeProperties.RuntimePropertiesException ex) {
LOGGER.log(Level.SEVERE, "Failed to set running with desktop GUI runtime property to false", ex);
}
}
public void run() {
LOGGER.log(Level.INFO, "Job processing task started");
try {
// read command line inputs
LOGGER.log(Level.INFO, "Autopsy is running from command line"); //NON-NLS
String dataSourcePath = "";
String baseCaseName = "";
// first look up all OptionProcessors and get input data from CommandLineOptionProcessor
Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
while (optionsIterator.hasNext()) {
// find CommandLineOptionProcessor
OptionProcessor processor = optionsIterator.next();
if (processor instanceof CommandLineOptionProcessor) {
// check if we are running from command line
dataSourcePath = ((CommandLineOptionProcessor) processor).getPathToDataSource();
baseCaseName = ((CommandLineOptionProcessor) processor).getBaseCaseName();
}
}
LOGGER.log(Level.INFO, "Data source path = {0}", dataSourcePath); //NON-NLS
LOGGER.log(Level.INFO, "Case name = {0}", baseCaseName); //NON-NLS
System.out.println("Data source path = " + dataSourcePath);
System.out.println("Case name = " + baseCaseName);
// verify inputs
if (dataSourcePath.isEmpty()) {
LOGGER.log(Level.SEVERE, "Data source path not specified");
System.out.println("Data source path not specified");
return;
}
if (baseCaseName.isEmpty()) {
LOGGER.log(Level.SEVERE, "Case name not specified");
System.out.println("Case name not specified");
return;
}
if (!(new File(dataSourcePath).exists())) {
LOGGER.log(Level.SEVERE, "Data source file not found {0}", dataSourcePath);
System.out.println("Data source file not found " + dataSourcePath);
return;
}
// read options panel configuration
String rootOutputDir = UserPreferences.getCommandLineModeResultsFolder();
LOGGER.log(Level.INFO, "Output directory = {0}", rootOutputDir); //NON-NLS
System.out.println("Output directoryh = " + rootOutputDir);
if (rootOutputDir.isEmpty()) {
LOGGER.log(Level.SEVERE, "Output directory not specified, please configure Command Line Options Panel (in Tools -> Options)");
System.out.println("Output directory not specified, please configure Command Line Options Panel (in Tools -> Options)");
return;
}
if (!(new File(rootOutputDir).exists())) {
LOGGER.log(Level.SEVERE, "The output directory doesn't exist {0}", rootOutputDir);
System.out.println("The output directory doesn't exist " + rootOutputDir);
return;
}
rootOutputDirectory = Paths.get(rootOutputDir);
// open case
Case caseForJob;
try {
caseForJob = openCase(baseCaseName);
} catch (CaseActionException ex) {
LOGGER.log(Level.SEVERE, "Error creating or opening case " + baseCaseName, ex);
System.out.println("Error creating or opening case " + baseCaseName);
return;
}
if (caseForJob == null) {
LOGGER.log(Level.SEVERE, "Error creating or opening case {0}", baseCaseName);
System.out.println("Error creating or opening case " + baseCaseName);
return;
}
DataSource dataSource = new DataSource("", Paths.get(dataSourcePath));
try {
// run data source processor
runDataSourceProcessor(caseForJob, dataSource);
// run ingest manager
analyze(dataSource);
// generate CASE-UCO report
Long selectedDataSourceId = getDataSourceId(dataSource);
Path reportFolderPath = Paths.get(caseForJob.getReportDirectory(), "CASE-UCO", "Data_Source_ID_" + selectedDataSourceId.toString() + "_" + TimeStampUtils.createTimeStamp(), ReportCaseUco.getReportFileName()); //NON_NLS
ReportProgressPanel progressPanel = new ReportProgressPanel("CASE_UCO", rootOutputDir); // dummy progress panel
CaseUcoFormatExporter.generateReport(selectedDataSourceId, reportFolderPath.toString(), progressPanel);
} catch (InterruptedException | AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException | AnalysisStartupException ex) {
LOGGER.log(Level.SEVERE, "Unable to ingest data source " + dataSourcePath + ". Exiting...", ex);
System.out.println("Unable to ingest data source " + dataSourcePath + ". Exiting...");
} catch (Throwable ex) {
/*
* Unexpected runtime exceptions firewall. This task is designed to
* be able to be run in an executor service thread pool without
* calling get() on the task's Future<Void>, so this ensures that
* such errors get logged.
*/
LOGGER.log(Level.SEVERE, "Unexpected error while ingesting data source " + dataSourcePath, ex);
System.out.println("Unexpected error while ingesting data source " + dataSourcePath + ". Exiting...");
} finally {
try {
Case.closeCurrentCase();
} catch (CaseActionException ex) {
LOGGER.log(Level.WARNING, "Exception while closing case", ex);
System.out.println("Exception while closing case");
}
}
} finally {
LOGGER.log(Level.INFO, "Job processing task finished");
System.out.println("Job processing task finished");
// shut down Autopsy
stop();
}
}
/**
* Provides object ID of the data source by reading it from Content
* object.
*
* @param dataSource DataSource object
* @return object ID
*/
private Long getDataSourceId(DataSource dataSource) {
Content content = dataSource.getContent().get(0);
return content.getId();
}
private Case openCase(String baseCaseName) throws CaseActionException {
LOGGER.log(Level.INFO, "Opening case {0}", baseCaseName);
Path caseDirectoryPath = findCaseDirectory(rootOutputDirectory, baseCaseName);
if (null != caseDirectoryPath) {
// found an existing case directory for same case name. the input case name must be unique. Exit.
LOGGER.log(Level.SEVERE, "Case {0} already exists. Case name must be unique. Exiting", baseCaseName);
throw new CaseActionException("Case " + baseCaseName + " already exists. Case name must be unique. Exiting");
} else {
caseDirectoryPath = createCaseFolderPath(rootOutputDirectory, baseCaseName);
// Create the case directory
Case.createCaseDirectory(caseDirectoryPath.toString(), Case.CaseType.SINGLE_USER_CASE);
CaseDetails caseDetails = new CaseDetails(baseCaseName);
Case.createAsCurrentCase(Case.CaseType.SINGLE_USER_CASE, caseDirectoryPath.toString(), caseDetails);
}
Case caseForJob = Case.getCurrentCase();
LOGGER.log(Level.INFO, "Opened case {0}", caseForJob.getName());
return caseForJob;
}
/**
* Passes the data source for the current job through a data source
* processor that adds it to the case database.
*
* @param dataSource The data source.
*
* @throws
* AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
* if there was a DSP processing error
*
* @throws InterruptedException if the thread running the job processing
* task is interrupted while blocked, i.e., if auto ingest is shutting
* down.
*/
private void runDataSourceProcessor(Case caseForJob, DataSource dataSource) throws InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException {
LOGGER.log(Level.INFO, "Adding data source {0} ", dataSource.getPath().toString());
// Get an ordered list of data source processors to try
List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
try {
validDataSourceProcessors = DataSourceProcessorUtility.getOrderedListOfDataSourceProcessors(dataSource.getPath());
} catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException ex) {
LOGGER.log(Level.SEVERE, "Exception while determining best data source processor for {0}", dataSource.getPath());
// rethrow the exception.
throw ex;
}
// did we find a data source processor that can process the data source
if (validDataSourceProcessors.isEmpty()) {
// This should never happen. We should add all unsupported data sources as logical files.
LOGGER.log(Level.SEVERE, "Unsupported data source {0}", dataSource.getPath()); // NON-NLS
return;
}
DataSourceProcessorProgressMonitor progressMonitor = new DoNothingDSPProgressMonitor();
synchronized (ingestLock) {
// Try each DSP in decreasing order of confidence
for (AutoIngestDataSourceProcessor selectedProcessor : validDataSourceProcessors) {
UUID taskId = UUID.randomUUID();
caseForJob.notifyAddingDataSource(taskId);
DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId, ingestLock);
caseForJob.notifyAddingDataSource(taskId);
LOGGER.log(Level.INFO, "Identified data source type for {0} as {1}", new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
selectedProcessor.process(dataSource.getDeviceId(), dataSource.getPath(), progressMonitor, callBack);
ingestLock.wait();
// at this point we got the content object(s) from the current DSP.
// check whether the data source was processed successfully
if ((dataSource.getResultDataSourceProcessorResultCode() == CRITICAL_ERRORS)
|| dataSource.getContent().isEmpty()) {
// move onto the the next DSP that can process this data source
logDataSourceProcessorResult(dataSource);
continue;
}
logDataSourceProcessorResult(dataSource);
return;
}
// If we get to this point, none of the processors were successful
LOGGER.log(Level.SEVERE, "All data source processors failed to process {0}", dataSource.getPath());
// Throw an exception. It will get caught & handled upstream and will result in AIM auto-pause.
throw new AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException("Failed to process " + dataSource.getPath() + " with all data source processors");
}
}
/**
* Logs the results of running a data source processor on the data
* source for the current job.
*
* @param dataSource The data source.
*/
private void logDataSourceProcessorResult(DataSource dataSource) {
DataSourceProcessorCallback.DataSourceProcessorResult resultCode = dataSource.getResultDataSourceProcessorResultCode();
if (null != resultCode) {
switch (resultCode) {
case NO_ERRORS:
LOGGER.log(Level.INFO, "Added data source to case");
if (dataSource.getContent().isEmpty()) {
LOGGER.log(Level.SEVERE, "Data source failed to produce content");
}
break;
case NONCRITICAL_ERRORS:
for (String errorMessage : dataSource.getDataSourceProcessorErrorMessages()) {
LOGGER.log(Level.WARNING, "Non-critical error running data source processor for {0}: {1}", new Object[]{dataSource.getPath(), errorMessage});
}
LOGGER.log(Level.INFO, "Added data source to case");
if (dataSource.getContent().isEmpty()) {
LOGGER.log(Level.SEVERE, "Data source failed to produce content");
}
break;
case CRITICAL_ERRORS:
for (String errorMessage : dataSource.getDataSourceProcessorErrorMessages()) {
LOGGER.log(Level.SEVERE, "Critical error running data source processor for {0}: {1}", new Object[]{dataSource.getPath(), errorMessage});
}
LOGGER.log(Level.SEVERE, "Failed to add data source to case");
break;
}
} else {
LOGGER.log(Level.WARNING, "No result code for data source processor for {0}", dataSource.getPath());
}
}
/**
* Analyzes the data source content returned by the data source
* processor using the configured set of data source level and file
* level analysis modules.
*
* @param dataSource The data source to analyze.
*
* @throws AnalysisStartupException if there is an error analyzing the
* data source.
* @throws InterruptedException if the thread running the job processing
* task is interrupted while blocked, i.e., if auto ingest is shutting
* down.
*/
private void analyze(DataSource dataSource) throws AnalysisStartupException, InterruptedException {
LOGGER.log(Level.INFO, "Starting ingest modules analysis for {0} ", dataSource.getPath());
IngestJobEventListener ingestJobEventListener = new IngestJobEventListener();
IngestManager.getInstance().addIngestJobEventListener(ingestJobEventListener);
try {
synchronized (ingestLock) {
IngestJobSettings ingestJobSettings = new IngestJobSettings(UserPreferences.getCommandLineModeIngestModuleContextString());
List<String> settingsWarnings = ingestJobSettings.getWarnings();
if (settingsWarnings.isEmpty()) {
IngestJobStartResult ingestJobStartResult = IngestManager.getInstance().beginIngestJob(dataSource.getContent(), ingestJobSettings);
IngestJob ingestJob = ingestJobStartResult.getJob();
if (null != ingestJob) {
/*
* Block until notified by the ingest job event
* listener or until interrupted because auto ingest
* is shutting down.
*/
ingestLock.wait();
LOGGER.log(Level.INFO, "Finished ingest modules analysis for {0} ", dataSource.getPath());
IngestJob.ProgressSnapshot jobSnapshot = ingestJob.getSnapshot();
for (IngestJob.ProgressSnapshot.DataSourceProcessingSnapshot snapshot : jobSnapshot.getDataSourceSnapshots()) {
if (!snapshot.isCancelled()) {
List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
if (!cancelledModules.isEmpty()) {
LOGGER.log(Level.WARNING, String.format("Ingest module(s) cancelled for %s", dataSource.getPath()));
for (String module : snapshot.getCancelledDataSourceIngestModules()) {
LOGGER.log(Level.WARNING, String.format("%s ingest module cancelled for %s", module, dataSource.getPath()));
}
}
LOGGER.log(Level.INFO, "Analysis of data source completed");
} else {
LOGGER.log(Level.WARNING, "Analysis of data source cancelled");
IngestJob.CancellationReason cancellationReason = snapshot.getCancellationReason();
if (IngestJob.CancellationReason.NOT_CANCELLED != cancellationReason && IngestJob.CancellationReason.USER_CANCELLED != cancellationReason) {
throw new AnalysisStartupException(String.format("Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), dataSource.getPath()));
}
}
}
} else if (!ingestJobStartResult.getModuleErrors().isEmpty()) {
for (IngestModuleError error : ingestJobStartResult.getModuleErrors()) {
LOGGER.log(Level.SEVERE, String.format("%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.getPath()), error.getThrowable());
}
LOGGER.log(Level.SEVERE, "Failed to analyze data source due to ingest job startup error");
throw new AnalysisStartupException(String.format("Error(s) during ingest module startup for %s", dataSource.getPath()));
} else {
LOGGER.log(Level.SEVERE, String.format("Ingest manager ingest job start error for %s", dataSource.getPath()), ingestJobStartResult.getStartupException());
throw new AnalysisStartupException("Ingest manager error starting job", ingestJobStartResult.getStartupException());
}
} else {
for (String warning : settingsWarnings) {
LOGGER.log(Level.SEVERE, "Ingest job settings error for {0}: {1}", new Object[]{dataSource.getPath(), warning});
}
LOGGER.log(Level.SEVERE, "Failed to analyze data source due to settings errors");
throw new AnalysisStartupException("Error(s) in ingest job settings");
}
}
} finally {
IngestManager.getInstance().removeIngestJobEventListener(ingestJobEventListener);
}
}
/**
* Creates a case folder path. Does not create the folder described by
* the path.
*
* @param caseFoldersPath The root case folders path.
* @param caseName The name of the case.
*
* @return A case folder path with a time stamp suffix.
*/
Path createCaseFolderPath(Path caseFoldersPath, String caseName) {
String folderName = caseName + "_" + TimeStampUtils.createTimeStamp();
return Paths.get(caseFoldersPath.toString(), folderName);
}
/**
* Searches a given folder for the most recently modified case folder
* for a case.
*
* @param folderToSearch The folder to be searched.
* @param caseName The name of the case for which a case folder is to be
* found.
*
* @return The path of the case folder, or null if it is not found.
*/
Path findCaseDirectory(Path folderToSearch, String caseName) {
File searchFolder = new File(folderToSearch.toString());
if (!searchFolder.isDirectory()) {
return null;
}
Path caseFolderPath = null;
String[] candidateFolders = searchFolder.list(new CaseFolderFilter(caseName));
long mostRecentModified = 0;
for (String candidateFolder : candidateFolders) {
File file = new File(candidateFolder);
if (file.lastModified() >= mostRecentModified) {
mostRecentModified = file.lastModified();
caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
}
}
return caseFolderPath;
}
/**
* An ingest job event listener that allows the job processing task to
* block until the analysis of a data source by the data source level
* and file level ingest modules is completed.
* <p>
* Note that the ingest job can spawn "child" ingest jobs (e.g., if an
* embedded virtual machine is found), so the job processing task must
* remain blocked until ingest is no longer running.
*/
private class IngestJobEventListener implements PropertyChangeListener {
/**
* Listens for local ingest job completed or cancelled events and
* notifies the job processing thread when such an event occurs and
* there are no "child" ingest jobs running.
*
* @param event
*/
@Override
public void propertyChange(PropertyChangeEvent event) {
if (AutopsyEvent.SourceType.LOCAL == ((AutopsyEvent) event).getSourceType()) {
String eventType = event.getPropertyName();
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
synchronized (ingestLock) {
ingestLock.notify();
}
}
}
}
};
/**
* A data source processor progress monitor does nothing. There is
* currently no mechanism for showing or recording data source processor
* progress during an ingest job.
*/
private class DoNothingDSPProgressMonitor implements DataSourceProcessorProgressMonitor {
/**
* Does nothing.
*
* @param indeterminate
*/
@Override
public void setIndeterminate(final boolean indeterminate) {
}
/**
* Does nothing.
*
* @param progress
*/
@Override
public void setProgress(final int progress) {
}
/**
* Does nothing.
*
* @param text
*/
@Override
public void setProgressText(final String text) {
}
}
/**
* Exception type thrown when there is a problem analyzing a data source
* with data source level and file level ingest modules for an ingest
* job.
*/
private final class AnalysisStartupException extends Exception {
private static final long serialVersionUID = 1L;
private AnalysisStartupException(String message) {
super(message);
}
private AnalysisStartupException(String message, Throwable cause) {
super(message, cause);
}
}
}
private static class CaseFolderFilter implements FilenameFilter {
private final String caseName;
private final static String CASE_METADATA_EXT = CaseMetadata.getFileExtension();
CaseFolderFilter(String caseName) {
this.caseName = caseName;
}
@Override
public boolean accept(File folder, String fileName) {
File file = new File(folder, fileName);
if (fileName.length() > TimeStampUtils.getTimeStampLength() && file.isDirectory()) {
if (TimeStampUtils.endsWithTimeStamp(fileName)) {
if (null != caseName) {
String fileNamePrefix = fileName.substring(0, fileName.length() - TimeStampUtils.getTimeStampLength());
if (fileNamePrefix.equals(caseName)) {
return hasCaseMetadataFile(file);
}
} else {
return hasCaseMetadataFile(file);
}
}
}
return false;
}
/**
* Determines whether or not there is a case metadata file in a given
* folder.
*
* @param folder The file object representing the folder to search.
*
* @return True or false.
*/
private static boolean hasCaseMetadataFile(File folder) {
for (File file : folder.listFiles()) {
if (file.getName().toLowerCase().endsWith(CASE_METADATA_EXT) && file.isFile()) {
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="nodeScrollPane" alignment="1" pref="864" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="nodeScrollPane" alignment="0" pref="421" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="nodeScrollPane">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="nodePanel">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 100]"/>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="outputPathTextField" min="-2" pref="630" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="browseOutputFolderButton" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="bnEditIngestSettings" alignment="0" min="-2" pref="155" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="jLabelSelectOutputFolder" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="jLabelInvalidResultsFolder" min="-2" pref="544" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace pref="355" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabelSelectOutputFolder" alignment="3" min="-2" pref="21" max="-2" attributes="0"/>
<Component id="jLabelInvalidResultsFolder" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="browseOutputFolderButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="outputPathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="25" max="-2" attributes="0"/>
<Component id="bnEditIngestSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="389" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="bnEditIngestSettings">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.bnEditIngestSettings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.bnEditIngestSettings.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commandlineingest/Bundle.properties" key="CommandLineIngestSettingsPanel.bnEditIngestSettings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnEditIngestSettingsActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="browseOutputFolderButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.browseOutputFolderButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commandlineingest/Bundle.properties" key="CommandLineIngestSettingsPanel.browseOutputFolderButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseOutputFolderButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="outputPathTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.outputPathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.outputPathTextField.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jLabelInvalidResultsFolder">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commandlineingest/Bundle.properties" key="CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
</Component>
<Component class="javax.swing.JLabel" name="jLabelSelectOutputFolder">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="verticalAlignment" type="int" value="3"/>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commandlineingest/Bundle.properties" key="CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,395 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.io.File;
import java.nio.file.Files;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestJobSettingsPanel;
import java.nio.file.Paths;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Configuration panel for auto ingest settings.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class CommandLineIngestSettingsPanel extends javax.swing.JPanel {
private final CommandLineIngestSettingsPanelController controller;
private final JFileChooser fc = new JFileChooser();
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(CommandLineIngestSettingsPanel.class.getName());
/**
* Creates new form AutoIngestSettingsPanel
*
* @param theController Controller to notify of changes.
*/
public CommandLineIngestSettingsPanel(CommandLineIngestSettingsPanelController theController) {
controller = theController;
initComponents();
load(true);
outputPathTextField.getDocument().addDocumentListener(new MyDocumentListener());
jLabelInvalidResultsFolder.setText("");
}
private class MyDocumentListener implements DocumentListener {
@Override
public void changedUpdate(DocumentEvent e) {
valid();
controller.changed();
}
@Override
public void removeUpdate(DocumentEvent e) {
valid();
controller.changed();
}
@Override
public void insertUpdate(DocumentEvent e) {
valid();
controller.changed();
}
};
/**
* Load mode from persistent storage.
*
* @param inStartup True if we're doing the initial population of the UI
*/
final void load(boolean inStartup) {
String results = org.sleuthkit.autopsy.commandlineingest.UserPreferences.getCommandLineModeResultsFolder();
if (results != null) {
outputPathTextField.setText(results);
} else {
outputPathTextField.setText("");
}
valid();
}
/**
* Save mode to persistent storage.
*/
void store() {
String resultsFolderPath = getNormalizedFolderPath(outputPathTextField.getText().trim());
org.sleuthkit.autopsy.commandlineingest.UserPreferences.setCommandLineModeResultsFolder(resultsFolderPath);
}
/**
* Validate current panel settings.
*/
boolean valid() {
if (validateResultsPath()) {
return true;
}
return false;
}
/**
* Normalizes a path to make sure there are no "space" characters at the end
*
* @param path Path to a directory
*
* @return Path without "space" characters at the end
*/
String normalizePath(String path) {
while (path.length() > 0) {
if (path.charAt(path.length() - 1) == ' ') {
path = path.substring(0, path.length() - 1);
} else {
break;
}
}
return path;
}
/**
* Validates that a path is valid and points to a folder.
*
* @param path A path to be validated
*
* @return boolean returns true if valid and points to a folder, false
* otherwise
*/
boolean isFolderPathValid(String path) {
try {
File file = new File(normalizePath(path));
// check if it's a symbolic link
if (Files.isSymbolicLink(file.toPath())) {
return true;
}
// local folder
if (file.exists() && file.isDirectory()) {
return true;
}
} catch (Exception ex) {
// Files.isSymbolicLink (and other "files" methods) throw exceptions on seemingly innocent inputs.
// For example, it will throw an exception when either " " is last character in path or
// a path starting with ":".
// We can just ignore these exceptions as they occur in process of user typing in the path.
return false;
}
return false;
}
/**
* Returns a path that was normalized by file system.
*
* @param path A path to be normalized. Normalization occurs inside a call
* to new File().
*
* @return String returns normalized OS path
*/
String getNormalizedFolderPath(String path) {
// removes "/", "\", and " " characters at the end of path string.
// normalizePath() removes spaces at the end of path and a call to "new File()"
// internally formats the path string to remove "/" and "\" characters at the end of path.
File file = new File(normalizePath(path));
return file.getPath();
}
/**
* Validate results path. Display warnings if invalid.
*/
boolean validateResultsPath() {
String outputPath = outputPathTextField.getText().trim();
if (outputPath.isEmpty()) {
jLabelInvalidResultsFolder.setVisible(true);
jLabelInvalidResultsFolder.setText(NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.ResultsDirectoryUnspecified"));
return false;
}
if (!isFolderPathValid(outputPath)) {
jLabelInvalidResultsFolder.setVisible(true);
jLabelInvalidResultsFolder.setText(NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.PathInvalid"));
return false;
}
if (false == permissionsAppropriate(outputPath)) {
jLabelInvalidResultsFolder.setVisible(true);
jLabelInvalidResultsFolder.setText(NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.CannotAccess")
+ " " + outputPath + " "
+ NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.CheckPermissions"));
return false;
}
jLabelInvalidResultsFolder.setVisible(false);
return true;
}
private void displayIngestJobSettingsPanel() {
this.getParent().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
IngestJobSettings ingestJobSettings = new IngestJobSettings(org.sleuthkit.autopsy.commandlineingest.UserPreferences.getCommandLineModeIngestModuleContextString());
showWarnings(ingestJobSettings);
IngestJobSettingsPanel ingestJobSettingsPanel = new IngestJobSettingsPanel(ingestJobSettings);
add(ingestJobSettingsPanel, BorderLayout.PAGE_START);
if (JOptionPane.showConfirmDialog(this, ingestJobSettingsPanel, "Ingest Module Configuration", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE) == JOptionPane.OK_OPTION) {
// store the updated settings
ingestJobSettings = ingestJobSettingsPanel.getSettings();
ingestJobSettings.save();
showWarnings(ingestJobSettings);
}
this.getParent().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
private static void showWarnings(IngestJobSettings ingestJobSettings) {
List<String> warnings = ingestJobSettings.getWarnings();
if (warnings.isEmpty() == false) {
StringBuilder warningMessage = new StringBuilder();
for (String warning : warnings) {
warningMessage.append(warning).append("\n");
}
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), warningMessage.toString());
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
nodeScrollPane = new javax.swing.JScrollPane();
nodePanel = new javax.swing.JPanel();
bnEditIngestSettings = new javax.swing.JButton();
browseOutputFolderButton = new javax.swing.JButton();
outputPathTextField = new javax.swing.JTextField();
jLabelInvalidResultsFolder = new javax.swing.JLabel();
jLabelSelectOutputFolder = new javax.swing.JLabel();
nodeScrollPane.setMinimumSize(new java.awt.Dimension(0, 0));
nodePanel.setMinimumSize(new java.awt.Dimension(100, 100));
org.openide.awt.Mnemonics.setLocalizedText(bnEditIngestSettings, org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.bnEditIngestSettings.text")); // NOI18N
bnEditIngestSettings.setToolTipText(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.bnEditIngestSettings.toolTipText")); // NOI18N
bnEditIngestSettings.setActionCommand(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.bnEditIngestSettings.text")); // NOI18N
bnEditIngestSettings.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnEditIngestSettingsActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(browseOutputFolderButton, org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.browseOutputFolderButton.text")); // NOI18N
browseOutputFolderButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
browseOutputFolderButtonActionPerformed(evt);
}
});
outputPathTextField.setText(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.outputPathTextField.text")); // NOI18N
outputPathTextField.setToolTipText(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.outputPathTextField.toolTipText")); // NOI18N
jLabelInvalidResultsFolder.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(jLabelInvalidResultsFolder, org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectOutputFolder, org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text")); // NOI18N
jLabelSelectOutputFolder.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM);
javax.swing.GroupLayout nodePanelLayout = new javax.swing.GroupLayout(nodePanel);
nodePanel.setLayout(nodePanelLayout);
nodePanelLayout.setHorizontalGroup(
nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(nodePanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(nodePanelLayout.createSequentialGroup()
.addComponent(outputPathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 630, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(browseOutputFolderButton))
.addComponent(bnEditIngestSettings, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(nodePanelLayout.createSequentialGroup()
.addComponent(jLabelSelectOutputFolder)
.addGap(18, 18, 18)
.addComponent(jLabelInvalidResultsFolder, javax.swing.GroupLayout.PREFERRED_SIZE, 544, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap(355, Short.MAX_VALUE))
);
nodePanelLayout.setVerticalGroup(
nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(nodePanelLayout.createSequentialGroup()
.addGap(40, 40, 40)
.addGroup(nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabelSelectOutputFolder, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabelInvalidResultsFolder))
.addGap(1, 1, 1)
.addGroup(nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(browseOutputFolderButton)
.addComponent(outputPathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(25, 25, 25)
.addComponent(bnEditIngestSettings)
.addContainerGap(389, Short.MAX_VALUE))
);
browseOutputFolderButton.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.browseOutputFolderButton.text")); // NOI18N
jLabelInvalidResultsFolder.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text")); // NOI18N
jLabelSelectOutputFolder.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text")); // NOI18N
nodeScrollPane.setViewportView(nodePanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(nodeScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 864, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(nodeScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 421, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
private void browseOutputFolderButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseOutputFolderButtonActionPerformed
String oldText = outputPathTextField.getText().trim();
// set the current directory of the FileChooser if the oldText is valid
File currentDir = new File(oldText);
if (currentDir.exists()) {
fc.setCurrentDirectory(currentDir);
}
fc.setDialogTitle("Select case output folder:");
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int retval = fc.showOpenDialog(this);
if (retval == JFileChooser.APPROVE_OPTION) {
String path = fc.getSelectedFile().getPath();
outputPathTextField.setText(path);
valid();
controller.changed();
}
}//GEN-LAST:event_browseOutputFolderButtonActionPerformed
private void bnEditIngestSettingsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnEditIngestSettingsActionPerformed
displayIngestJobSettingsPanel();
}//GEN-LAST:event_bnEditIngestSettingsActionPerformed
boolean permissionsAppropriate(String path) {
return FileUtil.hasReadWriteAccess(Paths.get(path));
}
private void resetUI() {
load(true);
controller.changed();
}
void setEnabledState(boolean enabled) {
bnEditIngestSettings.setEnabled(enabled);
browseOutputFolderButton.setEnabled(enabled);
jLabelInvalidResultsFolder.setEnabled(enabled);
jLabelSelectOutputFolder.setEnabled(enabled);
outputPathTextField.setEnabled(enabled);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton bnEditIngestSettings;
private javax.swing.JButton browseOutputFolderButton;
private javax.swing.JLabel jLabelInvalidResultsFolder;
private javax.swing.JLabel jLabelSelectOutputFolder;
private javax.swing.JPanel nodePanel;
private javax.swing.JScrollPane nodeScrollPane;
private javax.swing.JTextField outputPathTextField;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,132 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.JComponent;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
@OptionsPanelController.TopLevelRegistration(categoryName = "#OptionsCategory_Name_Command_Line_Ingest",
iconBase = "org/sleuthkit/autopsy/images/command_line_icon.png",
position = 17,
keywords = "#OptionsCategory_Keywords_Command_Line_Ingest_Settings",
keywordsCategory = "Command Line Ingest")
public final class CommandLineIngestSettingsPanelController extends OptionsPanelController {
private CommandLineIngestSettingsPanel panel;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private boolean changed;
private static final Logger logger = Logger.getLogger(CommandLineIngestSettingsPanelController.class.getName());
@Override
public void update() {
getPanel().load(false);
changed = false;
}
@Override
public void applyChanges() {
getPanel().store();
changed = false;
}
@Override
public void cancel() {
}
@Override
public boolean isValid() {
return getPanel().valid();
}
@Override
public boolean isChanged() {
return changed;
}
@Override
public HelpCtx getHelpCtx() {
return null;
}
@Override
public JComponent getComponent(Lookup masterLookup) {
return getPanel();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
if (pcs.getPropertyChangeListeners().length == 0) {
pcs.addPropertyChangeListener(l);
}
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
/**
* Note the NetBeans Framework does not appear to call this at all. We
* are using NetBeans 7.3.1 Build 201306052037. Perhaps in a future
* version of the Framework this will be resolved, but for now, simply
* don't unregister anything and add one time only in the
* addPropertyChangeListener() method above.
*/
}
private CommandLineIngestSettingsPanel getPanel() {
if (panel == null) {
panel = new CommandLineIngestSettingsPanel(this);
panel.setSize(750, 600); //makes the panel large enough to hide the scroll bar
}
return panel;
}
void changed() {
if (!changed) {
changed = true;
try {
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
} catch (Exception e) {
logger.log(Level.SEVERE, "GeneralOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
}
try {
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
} catch (Exception e) {
logger.log(Level.SEVERE, "GeneralOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
}
}

View File

@ -0,0 +1,168 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.io.File;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.sendopts.CommandException;
import org.netbeans.spi.sendopts.Env;
import org.netbeans.spi.sendopts.Option;
import org.netbeans.spi.sendopts.OptionProcessor;
import org.openide.util.lookup.ServiceProvider;
/**
* This class can be used to add command line options to Autopsy
*/
@ServiceProvider(service = OptionProcessor.class)
public class CommandLineOptionProcessor extends OptionProcessor {
private static final Logger logger = Logger.getLogger(CommandLineOptionProcessor.class.getName());
private final Option pathToDataSourceOption = Option.optionalArgument('l', "inputPath");
private final Option caseNameOption = Option.optionalArgument('2', "caseName");
private final Option runFromCommandLineOption = Option.optionalArgument('3', "runFromCommandLine");
private String pathToDataSource;
private String baseCaseName;
private boolean runFromCommandLine = false;
@Override
protected Set<Option> getOptions() {
Set<Option> set = new HashSet<>();
set.add(pathToDataSourceOption);
set.add(caseNameOption);
set.add(runFromCommandLineOption);
return set;
}
@Override
protected void process(Env env, Map<Option, String[]> values) throws CommandException {
logger.log(Level.INFO, "Processing Autopsy command line options"); //NON-NLS
System.out.println("Processing Autopsy command line options using CommandLineOptionProcessor");
if (values.containsKey(pathToDataSourceOption) && values.containsKey(caseNameOption) && values.containsKey(runFromCommandLineOption)) {
// parse input parameters
String inputPath;
String inputCaseName;
String modeString;
if (values.size() < 3) {
logger.log(Level.SEVERE, "Insufficient number of input arguments. Exiting");
System.out.println("Insufficient number of input arguments. Exiting");
this.runFromCommandLine = false;
return;
} else {
String[] argDirs = values.get(pathToDataSourceOption);
if (argDirs.length < 1) {
logger.log(Level.SEVERE, "Missing argument 'inputPath'. Exiting");
System.out.println("Missing argument 'inputPath'. Exiting");
this.runFromCommandLine = false;
return;
}
inputPath = argDirs[0];
argDirs = values.get(caseNameOption);
if (argDirs.length < 1) {
logger.log(Level.SEVERE, "Missing argument 'caseName'. Exiting");
System.out.println("Missing argument 'caseName'. Exiting");
this.runFromCommandLine = false;
return;
}
inputCaseName = argDirs[0];
argDirs = values.get(runFromCommandLineOption);
if (argDirs.length < 1) {
logger.log(Level.SEVERE, "Missing argument 'runFromCommandLine'. Exiting");
System.out.println("Missing argument 'runFromCommandLine'. Exiting");
this.runFromCommandLine = false;
return;
}
modeString = argDirs[0];
// verify inputs
if (modeString == null || modeString.isEmpty()) {
this.runFromCommandLine = false;
System.out.println("runFromCommandLine argument is empty");
return;
}
if (modeString.equalsIgnoreCase("true")) {
this.runFromCommandLine = true;
}
System.out.println("runFromCommandLine = " + this.runFromCommandLine);
}
// verify inputs
if (inputPath == null || inputPath.isEmpty() || !(new File(inputPath).exists())) {
logger.log(Level.SEVERE, "Input file {0} doesn''t exist. Exiting", inputPath);
System.out.println("Input file " + inputPath + " doesn't exist. Exiting");
this.runFromCommandLine = false;
return;
}
if (inputCaseName == null || inputCaseName.isEmpty()) {
logger.log(Level.SEVERE, "Case name argument is empty. Exiting");
System.out.println("Case name argument is empty. Exiting");
this.runFromCommandLine = false;
return;
}
// save the inputs
this.pathToDataSource = inputPath;
this.baseCaseName = inputCaseName;
logger.log(Level.INFO, "Input file = {0}", this.pathToDataSource); //NON-NLS
logger.log(Level.INFO, "Case name = {0}", this.baseCaseName); //NON-NLS
logger.log(Level.INFO, "runFromCommandLine = {0}", this.runFromCommandLine); //NON-NLS
System.out.println("Input file = " + this.pathToDataSource);
System.out.println("Case name = " + this.baseCaseName);
System.out.println("runFromCommandLine = " + this.runFromCommandLine);
} else {
System.out.println("Missing input arguments for CommandLineOptionProcessor. Exiting");
logger.log(Level.SEVERE, "Missing input arguments. Exiting");
}
}
/**
* Returns user specified path to data source
*
* @return the inputPath
*/
String getPathToDataSource() {
return pathToDataSource;
}
/**
* Returns user specified case name
*
* @return the inputCaseName
*/
String getBaseCaseName() {
return baseCaseName;
}
/**
* Returns whether Autopsy should be running in command line mode or not.
*
* @return true if running in command line mode, false otherwise.
*/
public boolean isRunFromCommandLine() {
return runFromCommandLine;
}
}

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 65]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[403, 65]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
<Component id="jLabel1" pref="305" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="27" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="18" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commandlineingest/Bundle.properties" key="CommandLinePanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,71 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import javax.swing.JPanel;
/**
* Panel that is displayed when Autopsy is running from command line.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
class CommandLinePanel extends JPanel {
CommandLinePanel() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
setMinimumSize(new java.awt.Dimension(0, 65));
setPreferredSize(new java.awt.Dimension(403, 65));
jLabel1.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CommandLinePanel.class, "CommandLinePanel.jLabel1.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(20, 20, 20)
.addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, 305, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel1)
.addContainerGap(27, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jLabel1;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,57 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.awt.Dimension;
import javax.swing.JDialog;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.StartupWindowInterface;
/**
* Implementation of startup window for running Autopsy from command line.
*
* IMPORTANT: We do NOT want to register this class as ServiceProvider. We want only
* the default Autopsy StartupWindow to be found via
* Lookup.getDefault().lookupAll(StartupWindowInterface.class);
*/
public class CommandLineStartupWindow extends JDialog implements StartupWindowInterface {
private static final String TITLE = NbBundle.getMessage(CommandLineStartupWindow.class, "CommandLineStartupWindow.title.text");
private static final Dimension DIMENSIONS = new Dimension(300, 50);
public CommandLineStartupWindow() {
super(WindowManager.getDefault().getMainWindow(), TITLE, true);
setSize(DIMENSIONS);
add(new CommandLinePanel());
pack();
setResizable(false);
}
@Override
public void open() {
setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setVisible(true);
}
@Override
public void close() {
setVisible(false);
}
}

View File

@ -0,0 +1,66 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult;
import org.sleuthkit.datamodel.Content;
class DataSource {
private final String deviceId;
private final Path path;
private DataSourceProcessorResult resultCode;
private List<String> errorMessages;
private List<Content> content;
DataSource(String deviceId, Path path) {
this.deviceId = deviceId;
this.path = path;
}
String getDeviceId() {
return deviceId;
}
Path getPath() {
return this.path;
}
synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List<String> errorMessages, List<Content> content) {
this.resultCode = result;
this.errorMessages = new ArrayList<>(errorMessages);
this.content = new ArrayList<>(content);
}
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() {
return resultCode;
}
synchronized List<String> getDataSourceProcessorErrorMessages() {
return new ArrayList<>(errorMessages);
}
synchronized List<Content> getContent() {
return new ArrayList<>(content);
}
}

View File

@ -0,0 +1,121 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
/**
* A utility class to find Data Source Processors
*/
final class DataSourceProcessorUtility {
private DataSourceProcessorUtility() {
}
/**
* A utility method to find all Data Source Processors (DSP) that are able
* to process the input data source. Only the DSPs that implement
* AutoIngestDataSourceProcessor interface are used.
*
* @param dataSourcePath Full path to the data source
* @return Hash map of all DSPs that can process the data source along with
* their confidence score
* @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
static Map<AutoIngestDataSourceProcessor, Integer> getDataSourceProcessorForFile(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = new HashMap<>();
for (AutoIngestDataSourceProcessor processor : processorCandidates) {
int confidence = processor.canProcess(dataSourcePath);
if (confidence > 0) {
validDataSourceProcessorsMap.put(processor, confidence);
}
}
return validDataSourceProcessorsMap;
}
/**
* A utility method to find all Data Source Processors (DSP) that are able
* to process the input data source. Only the DSPs that implement
* AutoIngestDataSourceProcessor interface are used. Returns ordered list of
* data source processors. DSPs are ordered in descending order from highest
* confidence to lowest.
*
* @param dataSourcePath Full path to the data source
*
* @return Ordered list of data source processors. DSPs are ordered in
* descending order from highest confidence to lowest.
*
* @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
// lookup all AutomatedIngestDataSourceProcessors
Collection<? extends AutoIngestDataSourceProcessor> processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class);
return getOrderedListOfDataSourceProcessors(dataSourcePath, processorCandidates);
}
/**
* A utility method to find all Data Source Processors (DSP) that are able
* to process the input data source. Only the DSPs that implement
* AutoIngestDataSourceProcessor interface are used. Returns ordered list of
* data source processors. DSPs are ordered in descending order from highest
* confidence to lowest.
*
* @param dataSourcePath Full path to the data source
* @param processorCandidates Collection of AutoIngestDataSourceProcessor objects to use
*
* @return Ordered list of data source processors. DSPs are ordered in
* descending order from highest confidence to lowest.
*
* @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = getDataSourceProcessorForFile(dataSourcePath, processorCandidates);
return orderDataSourceProcessorsByConfidence(validDataSourceProcessorsMap);
}
/**
* A utility method to get an ordered list of data source processors. DSPs
* are ordered in descending order from highest confidence to lowest.
*
* @param validDataSourceProcessorsMap Hash map of all DSPs that can process
* the data source along with their confidence score
* @return Ordered list of data source processors
*/
static List<AutoIngestDataSourceProcessor> orderDataSourceProcessorsByConfidence(Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap) {
List<AutoIngestDataSourceProcessor> validDataSourceProcessors = validDataSourceProcessorsMap.entrySet().stream()
.sorted(Map.Entry.<AutoIngestDataSourceProcessor, Integer>comparingByValue().reversed())
.map(Map.Entry::getKey)
.collect(Collectors.toList());
return validDataSourceProcessors;
}
}

View File

@ -0,0 +1,64 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import org.openide.util.NbPreferences;
/**
* Provides convenient access to a UserPreferences node for user preferences
* with default values.
*/
public final class UserPreferences {
private static final java.util.prefs.Preferences preferences = NbPreferences.forModule(UserPreferences.class);
private static final String COMMAND_LINE_MODE_RESULTS_FOLDER = "CommandLineModeResultsFolder"; // NON-NLS
private static final String COMMAND_LINE_MODE_CONTEXT_STRING = "CommandLineModeContext"; // NON-NLS
// Prevent instantiation.
private UserPreferences() {
}
/**
* Get results folder for command line mode from persistent storage.
*
* @return String Selected output folder.
*/
public static String getCommandLineModeResultsFolder() {
return preferences.get(COMMAND_LINE_MODE_RESULTS_FOLDER, "");
}
/**
* Set results folder for command line mode from persistent storage.
*
* @param folder Selected output folder.
*/
public static void setCommandLineModeResultsFolder(String folder) {
preferences.put(COMMAND_LINE_MODE_RESULTS_FOLDER, folder);
}
/**
* Get context string for command line mode ingest module settings.
*
* @return String Context string for command line mode ingest module
* settings.
*/
public static String getCommandLineModeIngestModuleContextString() {
return COMMAND_LINE_MODE_CONTEXT_STRING;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

View File

@ -1,37 +1,35 @@
/*
*
/*
* Autopsy Forensic Browser
*
* Copyright 2012-2018 Basis Technology Corp.
* Project Contact/Architect: carrier <at> sleuthkit <dot> org
*
*
* Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.case_uco;
package org.sleuthkit.autopsy.report.caseuco;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.logging.Level;
import javax.swing.JPanel;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.SimpleTimeZone;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -39,44 +37,30 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.report.GeneralReportModule;
import org.sleuthkit.autopsy.report.ReportProgressPanel;
import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus;
import org.sleuthkit.datamodel.*;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* ReportCaseUco generates a report in the CASE-UCO format. It saves basic
file info like full caseDirPath, name, MIME type, times, and hash.
* Generates CASE-UCO report file for a data source
*/
class ReportCaseUco implements GeneralReportModule {
public final class CaseUcoFormatExporter {
private static final Logger logger = Logger.getLogger(ReportCaseUco.class.getName());
private static ReportCaseUco instance = null;
private ReportCaseUcoConfigPanel configPanel;
private static final String REPORT_FILE_NAME = "CASE_UCO_output.json-ld";
// Hidden constructor for the report
private ReportCaseUco() {
}
private static final Logger logger = Logger.getLogger(CaseUcoFormatExporter.class.getName());
// Get the default implementation of this report
public static synchronized ReportCaseUco getDefault() {
if (instance == null) {
instance = new ReportCaseUco();
}
return instance;
private CaseUcoFormatExporter() {
}
/**
* Generates a CASE-UCO format report.
* Generates CASE-UCO report for the selected data source.
*
* @param baseReportDir caseDirPath to save the report
* @param progressPanel panel to update the report's progress
* @param selectedDataSourceId Object ID of the data source
* @param reportOutputPath Full path to directory where to save CASE-UCO
* report file
* @param progressPanel ReportProgressPanel to update progress
*/
@NbBundle.Messages({
"ReportCaseUco.notInitialized=CASE-UCO settings panel has not been initialized",
"ReportCaseUco.noDataSourceSelected=No data source selected for CASE-UCO report",
"ReportCaseUco.noCaseOpen=Unable to open currect case",
"ReportCaseUco.unableToCreateDirectories=Unable to create directory for CASE-UCO report",
"ReportCaseUco.initializing=Creating directories...",
@ -85,88 +69,70 @@ class ReportCaseUco implements GeneralReportModule {
"ReportCaseUco.processing=Saving files in CASE-UCO format...",
"ReportCaseUco.srcModuleName.text=CASE-UCO Report"
})
@Override
@SuppressWarnings("deprecation")
public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) {
public static void generateReport(Long selectedDataSourceId, String reportOutputPath, ReportProgressPanel progressPanel) {
if (configPanel == null) {
logger.log(Level.SEVERE, "CASE-UCO settings panel has not been initialized"); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
progressPanel.complete(ReportStatus.ERROR);
return;
}
Long selectedDataSourceId = configPanel.getSelectedDataSourceId();
if (selectedDataSourceId == ReportCaseUcoConfigPanel.NO_DATA_SOURCE_SELECTED) {
logger.log(Level.SEVERE, "No data source selected for CASE-UCO report"); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_noDataSourceSelected());
progressPanel.complete(ReportStatus.ERROR);
return;
}
// Start the progress bar and setup the report
progressPanel.setIndeterminate(false);
progressPanel.start();
progressPanel.updateStatusLabel(Bundle.ReportCaseUco_initializing());
// Create the JSON generator
JsonFactory jsonGeneratorFactory = new JsonFactory();
String reportPath = baseReportDir + getRelativeFilePath();
java.io.File reportFile = Paths.get(reportPath).toFile();
java.io.File reportFile = Paths.get(reportOutputPath).toFile();
try {
Files.createDirectories(Paths.get(reportFile.getParent()));
} catch (IOException ex) {
logger.log(Level.SEVERE, "Unable to create directory for CASE-UCO report", ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_unableToCreateDirectories());
progressPanel.complete(ReportStatus.ERROR);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
return;
}
// Check if ingest has finished
if (IngestManager.getInstance().isIngestRunning()) {
MessageNotifyUtil.Message.warn(Bundle.ReportCaseUco_ingestWarning());
}
JsonGenerator jsonGenerator = null;
SimpleTimeZone timeZone = new SimpleTimeZone(0, "GMT");
try {
jsonGenerator = jsonGeneratorFactory.createGenerator(reportFile, JsonEncoding.UTF8);
// instert \n after each field for more readable formatting
jsonGenerator.setPrettyPrinter(new DefaultPrettyPrinter().withObjectIndenter(new DefaultIndenter(" ", "\n")));
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
progressPanel.updateStatusLabel(Bundle.ReportCaseUco_querying());
// create the required CASE-UCO entries at the beginning of the output file
initializeJsonOutputFile(jsonGenerator);
// create CASE-UCO entry for the Autopsy case
String caseTraceId = saveCaseInfo(skCase, jsonGenerator);
// create CASE-UCO data source entry
String dataSourceTraceId = saveDataSourceInfo(selectedDataSourceId, caseTraceId, skCase, jsonGenerator);
// Run getAllFilesQuery to get all files, exclude directories
final String getAllFilesQuery = "select obj_id, name, size, crtime, atime, mtime, md5, parent_path, mime_type, extension from tsk_files where "
+ "data_source_obj_id = " + Long.toString(selectedDataSourceId)
+ " AND ((meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.getValue()
+ ") OR (meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()
+ ") OR (meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT.getValue() + "))"; //NON-NLS
try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery(getAllFilesQuery)) {
ResultSet resultSet = queryResult.getResultSet();
progressPanel.updateStatusLabel(Bundle.ReportCaseUco_processing());
// Loop files and write info to CASE-UCO report
while (resultSet.next()) {
if (progressPanel.getStatus() == ReportStatus.CANCELED) {
if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
break;
}
Long objectId = resultSet.getLong(1);
String fileName = resultSet.getString(2);
long size = resultSet.getLong("size");
@ -177,29 +143,29 @@ class ReportCaseUco implements GeneralReportModule {
String parent_path = resultSet.getString("parent_path");
String mime_type = resultSet.getString("mime_type");
String extension = resultSet.getString("extension");
saveFileInCaseUcoFormat(objectId, fileName, parent_path, md5Hash, mime_type, size, crtime, atime, mtime, extension, jsonGenerator, dataSourceTraceId);
}
}
// create the required CASE-UCO entries at the end of the output file
finilizeJsonOutputFile(jsonGenerator);
Case.getCurrentCaseThrows().addReport(reportPath, Bundle.ReportCaseUco_srcModuleName_text(), "");
progressPanel.complete(ReportStatus.COMPLETE);
Case.getCurrentCaseThrows().addReport(reportOutputPath, Bundle.ReportCaseUco_srcModuleName_text(), "");
progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to get list of files from case database", ex); //NON-NLS
progressPanel.complete(ReportStatus.ERROR);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Failed to create JSON output for the CASE-UCO report", ex); //NON-NLS
progressPanel.complete(ReportStatus.ERROR);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
} catch (SQLException ex) {
logger.log(Level.WARNING, "Unable to read result set", ex); //NON-NLS
progressPanel.complete(ReportStatus.ERROR);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "No current case open", ex); //NON-NLS
progressPanel.complete(ReportStatus.ERROR);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
} finally {
if (jsonGenerator != null) {
try {
@ -210,20 +176,31 @@ class ReportCaseUco implements GeneralReportModule {
}
}
}
private void initializeJsonOutputFile(JsonGenerator catalog) throws IOException {
private static void initializeJsonOutputFile(JsonGenerator catalog) throws IOException {
catalog.writeStartObject();
catalog.writeFieldName("@graph");
catalog.writeStartArray();
}
private void finilizeJsonOutputFile(JsonGenerator catalog) throws IOException {
private static void finilizeJsonOutputFile(JsonGenerator catalog) throws IOException {
catalog.writeEndArray();
catalog.writeEndObject();
}
private String saveCaseInfo(SleuthkitCase skCase, JsonGenerator catalog) throws TskCoreException, SQLException, IOException, NoCurrentCaseException {
/**
* Save info about the Autopsy case in CASE-UCo format
*
* @param skCase SleuthkitCase object
* @param catalog JsonGenerator object
* @return CASE-UCO trace ID object for the Autopsy case entry
* @throws TskCoreException
* @throws SQLException
* @throws IOException
* @throws NoCurrentCaseException
*/
private static String saveCaseInfo(SleuthkitCase skCase, JsonGenerator catalog) throws TskCoreException, SQLException, IOException, NoCurrentCaseException {
// create a "trace" entry for the Autopsy case iteself
String uniqueCaseName;
String dbFileName;
@ -235,7 +212,7 @@ class ReportCaseUco implements GeneralReportModule {
uniqueCaseName = skCase.getDatabaseName();
dbFileName = "";
}
String caseDirPath = skCase.getDbDirPath();
String caseTraceId = "case-" + uniqueCaseName;
catalog.writeStartObject();
@ -243,19 +220,19 @@ class ReportCaseUco implements GeneralReportModule {
catalog.writeStringField("@type", "Trace");
catalog.writeFieldName("propertyBundle");
catalog.writeStartArray();
catalog.writeStartArray();
catalog.writeStartObject();
// replace double slashes with single ones
caseDirPath = caseDirPath.replaceAll("\\\\", "/");
catalog.writeStringField("@type", "File");
if (dbType == TskData.DbType.SQLITE) {
catalog.writeStringField("filePath", caseDirPath + "/" + dbFileName);
catalog.writeBooleanField("isDirectory", false);
} else {
catalog.writeStringField("filePath", caseDirPath);
catalog.writeBooleanField("isDirectory", true);
catalog.writeBooleanField("isDirectory", true);
}
catalog.writeEndObject();
@ -264,12 +241,24 @@ class ReportCaseUco implements GeneralReportModule {
return caseTraceId;
}
private String saveDataSourceInfo(Long selectedDataSourceId, String caseTraceId, SleuthkitCase skCase, JsonGenerator jsonGenerator) throws TskCoreException, SQLException, IOException {
/**
* Save info about the data source in CASE-UCo format
*
* @param selectedDataSourceId Object ID of the data source
* @param caseTraceId CASE-UCO trace ID object for the Autopsy case entry
* @param skCase SleuthkitCase object
* @param catalog JsonGenerator object
* @return
* @throws TskCoreException
* @throws SQLException
* @throws IOException
*/
private static String saveDataSourceInfo(Long selectedDataSourceId, String caseTraceId, SleuthkitCase skCase, JsonGenerator jsonGenerator) throws TskCoreException, SQLException, IOException {
Long imageSize = (long) 0;
String imageName = "";
boolean isImageDataSource = false;
String imageName = "";
boolean isImageDataSource = false;
String getImageDataSourceQuery = "select size from tsk_image_info where obj_id = " + selectedDataSourceId;
try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery(getImageDataSourceQuery)) {
ResultSet resultSet = queryResult.getResultSet();
@ -281,7 +270,7 @@ class ReportCaseUco implements GeneralReportModule {
break;
}
}
if (isImageDataSource) {
// get caseDirPath to image file
String getPathToDataSourceQuery = "select name from tsk_image_names where obj_id = " + selectedDataSourceId;
@ -303,40 +292,40 @@ class ReportCaseUco implements GeneralReportModule {
}
}
}
return saveDataSourceInCaseUcoFormat(jsonGenerator, imageName, imageSize, selectedDataSourceId, caseTraceId);
}
private String saveDataSourceInCaseUcoFormat(JsonGenerator catalog, String imageName, Long imageSize, Long selectedDataSourceId, String caseTraceId) throws IOException {
private static String saveDataSourceInCaseUcoFormat(JsonGenerator catalog, String imageName, Long imageSize, Long selectedDataSourceId, String caseTraceId) throws IOException {
// create a "trace" entry for the data source
String dataSourceTraceId = "data-source-"+selectedDataSourceId;
String dataSourceTraceId = "data-source-" + selectedDataSourceId;
catalog.writeStartObject();
catalog.writeStringField("@id", dataSourceTraceId);
catalog.writeStringField("@type", "Trace");
catalog.writeFieldName("propertyBundle");
catalog.writeStartArray();
catalog.writeStartObject();
catalog.writeStringField("@type", "File");
// replace double back slashes with single ones
imageName = imageName.replaceAll("\\\\", "/");
catalog.writeStringField("filePath", imageName);
catalog.writeEndObject();
if (imageSize > 0) {
catalog.writeStartObject();
catalog.writeStringField("@type", "ContentData");
catalog.writeStringField("sizeInBytes", Long.toString(imageSize));
catalog.writeEndObject();
}
catalog.writeEndArray();
catalog.writeEndObject();
// create a "relationship" entry between the case and the data source
catalog.writeStartObject();
catalog.writeStringField("@id", "relationship-" + caseTraceId);
@ -345,33 +334,33 @@ class ReportCaseUco implements GeneralReportModule {
catalog.writeStringField("target", caseTraceId);
catalog.writeStringField("kindOfRelationship", "contained-within");
catalog.writeBooleanField("isDirectional", true);
catalog.writeFieldName("propertyBundle");
catalog.writeStartArray();
catalog.writeStartArray();
catalog.writeStartObject();
catalog.writeStringField("@type", "PathRelation");
catalog.writeStringField("path", imageName);
catalog.writeEndObject();
catalog.writeEndArray();
catalog.writeEndObject();
return dataSourceTraceId;
}
private void saveFileInCaseUcoFormat(Long objectId, String fileName, String parent_path, String md5Hash, String mime_type, long size, String ctime,
private static void saveFileInCaseUcoFormat(Long objectId, String fileName, String parent_path, String md5Hash, String mime_type, long size, String ctime,
String atime, String mtime, String extension, JsonGenerator catalog, String dataSourceTraceId) throws IOException {
String fileTraceId = "file-" + objectId;
// create a "trace" entry for the file
catalog.writeStartObject();
catalog.writeStringField("@id", fileTraceId);
catalog.writeStringField("@type", "Trace");
catalog.writeFieldName("propertyBundle");
catalog.writeStartArray();
catalog.writeStartObject();
catalog.writeStringField("@type", "File");
catalog.writeStringField("createdTime", ctime);
@ -385,9 +374,9 @@ class ReportCaseUco implements GeneralReportModule {
catalog.writeStringField("filePath", parent_path + fileName);
}
catalog.writeBooleanField("isDirectory", false);
catalog.writeStringField("sizeInBytes", Long.toString(size));
catalog.writeStringField("sizeInBytes", Long.toString(size));
catalog.writeEndObject();
catalog.writeStartObject();
catalog.writeStringField("@type", "ContentData");
if (mime_type != null) {
@ -406,10 +395,10 @@ class ReportCaseUco implements GeneralReportModule {
catalog.writeStringField("sizeInBytes", Long.toString(size));
catalog.writeEndObject();
catalog.writeEndArray();
catalog.writeEndObject();
// create a "relationship" entry between the file and the data source
catalog.writeStartObject();
catalog.writeStringField("@id", "relationship-" + objectId);
@ -418,9 +407,9 @@ class ReportCaseUco implements GeneralReportModule {
catalog.writeStringField("target", dataSourceTraceId);
catalog.writeStringField("kindOfRelationship", "contained-within");
catalog.writeBooleanField("isDirectional", true);
catalog.writeFieldName("propertyBundle");
catalog.writeStartArray();
catalog.writeStartArray();
catalog.writeStartObject();
catalog.writeStringField("@type", "PathRelation");
if (parent_path != null) {
@ -428,38 +417,9 @@ class ReportCaseUco implements GeneralReportModule {
} else {
catalog.writeStringField("path", fileName);
}
catalog.writeEndObject();
catalog.writeEndObject();
catalog.writeEndArray();
catalog.writeEndObject();
}
@Override
public String getName() {
String name = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getName.text");
return name;
}
@Override
public String getRelativeFilePath() {
return REPORT_FILE_NAME;
}
@Override
public String getDescription() {
String desc = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getDesc.text");
return desc;
}
@Override
public JPanel getConfigurationPanel() {
try {
configPanel = new ReportCaseUcoConfigPanel();
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
logger.log(Level.SEVERE, "Failed to initialize CASE-UCO settings panel", ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
configPanel = null;
}
return configPanel;
}
}

View File

@ -0,0 +1,128 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018-2019 Basis Technology Corp.
* Project Contact/Architect: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.report.caseuco;
import java.util.logging.Level;
import javax.swing.JPanel;
import java.sql.SQLException;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.report.GeneralReportModule;
import org.sleuthkit.autopsy.report.ReportProgressPanel;
import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus;
import org.sleuthkit.datamodel.*;
/**
* ReportCaseUco generates a report in the CASE-UCO format. It saves basic file
* info like full caseDirPath, name, MIME type, times, and hash.
*/
public final class ReportCaseUco implements GeneralReportModule {
private static final Logger logger = Logger.getLogger(ReportCaseUco.class.getName());
private static ReportCaseUco instance = null;
private ReportCaseUcoConfigPanel configPanel;
private static final String REPORT_FILE_NAME = "CASE_UCO_output.json-ld";
// Hidden constructor for the report
private ReportCaseUco() {
}
// Get the default implementation of this report
public static synchronized ReportCaseUco getDefault() {
if (instance == null) {
instance = new ReportCaseUco();
}
return instance;
}
@Override
public String getName() {
String name = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getName.text");
return name;
}
@Override
public String getRelativeFilePath() {
return REPORT_FILE_NAME;
}
@Override
public String getDescription() {
String desc = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getDesc.text");
return desc;
}
@Override
public JPanel getConfigurationPanel() {
try {
configPanel = new ReportCaseUcoConfigPanel();
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
logger.log(Level.SEVERE, "Failed to initialize CASE-UCO settings panel", ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
configPanel = null;
}
return configPanel;
}
/**
* Returns CASE-UCO report file name
*
* @return the REPORT_FILE_NAME
*/
public static String getReportFileName() {
return REPORT_FILE_NAME;
}
/**
* Generates a CASE-UCO format report.
*
* @param baseReportDir caseDirPath to save the report
* @param progressPanel panel to update the report's progress
*/
@NbBundle.Messages({
"ReportCaseUco.notInitialized=CASE-UCO settings panel has not been initialized",
"ReportCaseUco.noDataSourceSelected=No data source selected for CASE-UCO report"
})
@Override
@SuppressWarnings("deprecation")
public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) {
if (configPanel == null) {
logger.log(Level.SEVERE, "CASE-UCO settings panel has not been initialized"); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
progressPanel.complete(ReportStatus.ERROR);
return;
}
Long selectedDataSourceId = configPanel.getSelectedDataSourceId();
if (selectedDataSourceId == ReportCaseUcoConfigPanel.NO_DATA_SOURCE_SELECTED) {
logger.log(Level.SEVERE, "No data source selected for CASE-UCO report"); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_noDataSourceSelected());
progressPanel.complete(ReportStatus.ERROR);
return;
}
String reportPath = baseReportDir + getRelativeFilePath();
CaseUcoFormatExporter.generateReport(selectedDataSourceId, reportPath, progressPanel);
}
}

View File

@ -56,7 +56,7 @@
<Component class="javax.swing.JLabel" name="jLabelSelectDataSource">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/case_uco/Bundle.properties" key="ReportCaseUcoConfigPanel.jLabelSelectDataSource.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/report/caseuco/Bundle.properties" key="ReportCaseUcoConfigPanel.jLabelSelectDataSource.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>

View File

@ -2,7 +2,7 @@
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,7 +17,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.case_uco;
package org.sleuthkit.autopsy.report.caseuco;
import java.sql.SQLException;
import java.util.Map;

View File

@ -1,44 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.configuration;
import javax.swing.JPanel;
/**
* Interface to run an ingest job in the background.
*/
public interface IngestJobRunningService {
/**
* Starts the service
*/
void start();
/**
* Stops the service
*/
void stop();
/**
* Returns a panel to be displayed while using this service
*
* @return panel to be displayed while using this service
*/
JPanel getStartupWindow();
}