/*
* Autopsy Forensic Browser
*
* Copyright 2013-2016 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.casemodule;
import java.io.File;
import java.nio.file.Path;
import javax.swing.JPanel;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;
import javax.swing.filechooser.FileFilter;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.DataSourceUtils;
import org.sleuthkit.autopsy.corecomponentinterfaces.AutoIngestDataSourceProcessor;
/**
* A image file data source processor that implements the DataSourceProcessor
* service provider interface to allow integration with the add data source
* wizard. It also provides a run method overload to allow it to be used
* independently of the wizard.
*/
@ServiceProviders(value={
@ServiceProvider(service=DataSourceProcessor.class),
@ServiceProvider(service=AutoIngestDataSourceProcessor.class)}
)
public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSourceProcessor {
private final static String DATA_SOURCE_TYPE = NbBundle.getMessage(ImageDSProcessor.class, "ImageDSProcessor.dsType.text");
private static final List<String> allExt = new ArrayList<>();
private static final GeneralFilter rawFilter = new GeneralFilter(GeneralFilter.RAW_IMAGE_EXTS, GeneralFilter.RAW_IMAGE_DESC);
private static final GeneralFilter encaseFilter = new GeneralFilter(GeneralFilter.ENCASE_IMAGE_EXTS, GeneralFilter.ENCASE_IMAGE_DESC);
private static final GeneralFilter virtualMachineFilter = new GeneralFilter(GeneralFilter.VIRTUAL_MACHINE_EXTS, GeneralFilter.VIRTUAL_MACHINE_DESC);
private static final String ALL_DESC = NbBundle.getMessage(ImageDSProcessor.class, "ImageDSProcessor.allDesc.text");
private static final GeneralFilter allFilter = new GeneralFilter(allExt, ALL_DESC);
private static final List<FileFilter> filtersList = new ArrayList<>();
private final ImageFilePanel configPanel;
private AddImageTask addImageTask;
/*
* TODO: Remove the setDataSourceOptionsCalled flag and the settings fields
* when the deprecated method setDataSourceOptions is removed.
*/
private String deviceId;
private String imagePath;
private String timeZone;
private boolean ignoreFatOrphanFiles;
private boolean setDataSourceOptionsCalled;
static {
filtersList.add(allFilter);
filtersList.add(rawFilter);
filtersList.add(encaseFilter);
filtersList.add(virtualMachineFilter);
allExt.addAll(GeneralFilter.RAW_IMAGE_EXTS);
allExt.addAll(GeneralFilter.ENCASE_IMAGE_EXTS);
allExt.addAll(GeneralFilter.VIRTUAL_MACHINE_EXTS);
}
/**
* Constructs an image file data source processor that implements the
* DataSourceProcessor service provider interface to allow integration with
* the add data source wizard. It also provides a run method overload to
* allow it to be used independently of the wizard.
*/
public ImageDSProcessor() {
configPanel = ImageFilePanel.createInstance(ImageDSProcessor.class.getName(), filtersList);
}
/**
* Gets a string that describes the type of data sources this processor is
* able to add to the case database. The string is suitable for display in a
* type selection UI component (e.g., a combo box).
*
* @return A data source type display string for this data source processor.
*/
public static String getType() {
return DATA_SOURCE_TYPE;
}
/**
* Gets a string that describes the type of data sources this processor is
* able to add to the case database. The string is suitable for display in a
* type selection UI component (e.g., a combo box).
*
* @return A data source type display string for this data source processor.
*/
@Override
public String getDataSourceType() {
return getType();
}
/**
* Gets the panel that allows a user to select a data source and do any
* configuration required by the data source. The panel is less than 544
* pixels wide and less than 173 pixels high.
*
* @return A selection and configuration panel for this data source
* processor.
*/
@Override
public JPanel getPanel() {
configPanel.readSettings();
configPanel.select();
return configPanel;
}
/**
* Indicates whether the settings in the selection and configuration panel
* are valid and complete.
*
* @return True if the settings are valid and complete and the processor is
* ready to have its run method called, false otherwise.
*/
@Override
public boolean isPanelValid() {
return configPanel.validatePanel();
}
/**
* Adds a data source to the case database using a background task in a
* separate thread and the settings provided by the selection and
* configuration panel. Returns as soon as the background task is started.
* The background task uses a callback object to signal task completion and
* return results.
*
* This method should not be called unless isPanelValid returns true.
*
* @param progressMonitor Progress monitor that will be used by the
* background task to report progress.
* @param callback Callback that will be used by the background task
* to return results.
*/
@Override
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
if (!setDataSourceOptionsCalled) {
configPanel.storeSettings();
deviceId = UUID.randomUUID().toString();
imagePath = configPanel.getContentPaths();
timeZone = configPanel.getTimeZone();
ignoreFatOrphanFiles = configPanel.getNoFatOrphans();
}
run(deviceId, imagePath, timeZone, ignoreFatOrphanFiles, progressMonitor, callback);
}
/**
* Adds a data source to the case database using a background task in a
* separate thread and the given settings instead of those provided by the
* selection and configuration panel. Returns as soon as the background task
* is started and uses the callback object to signal task completion and
* return results.
*
* @param deviceId An ASCII-printable identifier for the device
* associated with the data source that is
* intended to be unique across multiple cases
* (e.g., a UUID).
* @param imagePath Path to the image file.
* @param timeZone The time zone to use when processing dates
* and times for the image, obtained from
* java.util.TimeZone.getID.
* @param ignoreFatOrphanFiles Whether to parse orphans if the image has a
* FAT filesystem.
* @param progressMonitor Progress monitor for reporting progress
* during processing.
* @param callback Callback to call when processing is done.
*/
public void run(String deviceId, String imagePath, String timeZone, boolean ignoreFatOrphanFiles, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
addImageTask = new AddImageTask(deviceId, imagePath, timeZone, ignoreFatOrphanFiles, progressMonitor, callback);
new Thread(addImageTask).start();
}
/**
* Requests cancellation of the background task that adds a data source to
* the case database, after the task is started using the run method. This
* is a "best effort" cancellation, with no guarantees that the case
* database will be unchanged. If cancellation succeeded, the list of new
* data sources returned by the background task will be empty.
*/
@Override
public void cancel() {
if (null != addImageTask) {
addImageTask.cancelTask();
}
}
/**
* Resets the selection and configuration panel for this data source
* processor.
*/
@Override
public void reset() {
deviceId = null;
imagePath = null;
timeZone = null;
ignoreFatOrphanFiles = false;
configPanel.reset();
setDataSourceOptionsCalled = false;
}
private static boolean isAcceptedByFiler(File file, List<FileFilter> filters) {
for (FileFilter filter : filters) {
if (filter.accept(file)) {
return true;
}
}
return false;
}
@Override
public int canProcess(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
// check file extension for supported types
if (!isAcceptedByFiler(dataSourcePath.toFile(), filtersList)) {
return 0;
}
try {
// verify that the image has a file system that TSK can process
Case currentCase = Case.getCurrentCase();
if (!DataSourceUtils.imageHasFileSystem(dataSourcePath)) {
// image does not have a file system that TSK can process
return 0;
}
} catch (Exception ex) {
throw new AutoIngestDataSourceProcessorException("Exception inside canProcess() method", ex);
}
// able to process the data source
return 100;
}
@Override
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) throws AutoIngestDataSourceProcessorException {
this.deviceId = deviceId;
this.imagePath = dataSourcePath.toString();
this.timeZone = Calendar.getInstance().getTimeZone().getID();
this.ignoreFatOrphanFiles = false;
setDataSourceOptionsCalled = true;
run(deviceId, dataSourcePath.toString(), timeZone, ignoreFatOrphanFiles, progressMonitor, callBack);
}
/**
* Sets the configuration of the data source processor without using the
* selection and configuration panel.
*
* @param imagePath Path to the image file.
* @param timeZone The time zone to use when processing dates
* and times for the image, obtained from
* java.util.TimeZone.getID.
* @param ignoreFatOrphanFiles Whether to parse orphans if the image has a
* FAT filesystem.
*
* @deprecated Use the provided overload of the run method instead.
*/
@Deprecated
public void setDataSourceOptions(String imagePath, String timeZone, boolean ignoreFatOrphanFiles) {
this.deviceId = UUID.randomUUID().toString();
this.imagePath = imagePath;
this.timeZone = Calendar.getInstance().getTimeZone().getID();
this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
setDataSourceOptionsCalled = true;
}
}